diff --git a/viewer/JFJochImageReadingWorker.cpp b/viewer/JFJochImageReadingWorker.cpp index 08eb7ad7..6582700c 100644 --- a/viewer/JFJochImageReadingWorker.cpp +++ b/viewer/JFJochImageReadingWorker.cpp @@ -729,18 +729,31 @@ void JFJochImageReadingWorker::EnsurePixelRefine_i() { pixel_pred_ = CreateBraggPrediction(curr_experiment.IsRotationIndexing()); } -bool JFJochImageReadingWorker::BuildPixelSeed_i(PixelRefineData &d, const PixelRefineParams &p) const { - if (!current_image_ptr || !index_and_refine || !current_image.has_value()) +bool JFJochImageReadingWorker::BuildPixelSeed_i(PixelRefineData &d, const PixelRefineParams &p, QString &reason) const { + if (!current_image_ptr || !index_and_refine || !current_image.has_value()) { + reason = "No image loaded"; return false; + } const auto &outcomes = index_and_refine->GetIntegrationOutcome(); const int64_t n = current_image.value(); - if (n < 0 || n >= static_cast(outcomes.size())) + if (n < 0 || n >= static_cast(outcomes.size())) { + reason = "No analysis result for current image"; return false; + } const auto &io = outcomes[n]; - if (io.reflections.empty()) // image not indexed/integrated + if (io.reflections.empty()) { + // The "indexed" badge in the viewer comes from indexing (lattice_type); + // PixelRefine instead seeds from the *integration* outcome, which is only + // populated when Quick integration is enabled and succeeds for this image. + // Distinguish the two so the user knows what to turn on. + if (current_image_ptr->ImageData().lattice_type.has_value()) + reason = "Image is indexed but not integrated - enable Quick integration"; + else + reason = "Current image is not indexed"; return false; + } d.geom = io.geom; d.latt = io.latt; @@ -773,7 +786,15 @@ bool JFJochImageReadingWorker::BuildPixelSeed_i(PixelRefineData &d, const PixelR std::shared_ptr JFJochImageReadingWorker::WrapFloatImage_i(const std::vector &img) const { auto si = std::make_shared(); - si->image = CompressedImage(img, curr_experiment.GetXPixelsNum(), curr_experiment.GetYPixelsNum()); + // CompressedImage is a non-owning view over its data pointer. predictedImageReady + // is a queued cross-thread connection, so the source float vector must outlive the + // emit: copy it into SimpleImage::buffer (which the shared_ptr keeps alive) instead + // of aliasing the caller's temporary, otherwise loadImageInternal() reads freed + // memory in the GUI thread (SIGSEGV). + si->buffer.resize(img.size() * sizeof(float)); + std::memcpy(si->buffer.data(), img.data(), si->buffer.size()); + si->image = CompressedImage(si->buffer, curr_experiment.GetXPixelsNum(), curr_experiment.GetYPixelsNum(), + CompressedImageMode::Float32); return si; } @@ -803,8 +824,9 @@ void JFJochImageReadingWorker::PixelRefinePreview(PixelRefineParams params) { } PixelRefineData d; - if (!BuildPixelSeed_i(d, params)) { - emit pixelRefineStatus("Current image is not indexed"); + QString seed_reason; + if (!BuildPixelSeed_i(d, params, seed_reason)) { + emit pixelRefineStatus(seed_reason); return; } @@ -838,8 +860,9 @@ void JFJochImageReadingWorker::PixelRefineRun(PixelRefineParams params) { } PixelRefineData d; - if (!BuildPixelSeed_i(d, params)) { - emit pixelRefineStatus("Current image is not indexed"); + QString seed_reason; + if (!BuildPixelSeed_i(d, params, seed_reason)) { + emit pixelRefineStatus(seed_reason); return; } if (d.max_iterations <= 0) diff --git a/viewer/JFJochImageReadingWorker.h b/viewer/JFJochImageReadingWorker.h index a5b219df..93d4240b 100644 --- a/viewer/JFJochImageReadingWorker.h +++ b/viewer/JFJochImageReadingWorker.h @@ -69,7 +69,7 @@ private: std::unique_ptr pixel_pred_; void EnsurePixelRefine_i(); - bool BuildPixelSeed_i(PixelRefineData &d, const PixelRefineParams &p) const; + bool BuildPixelSeed_i(PixelRefineData &d, const PixelRefineParams &p, QString &reason) const; std::shared_ptr WrapFloatImage_i(const std::vector &img) const; std::unique_ptr roi; diff --git a/viewer/windows/JFJochPixelRefineWindow.cpp b/viewer/windows/JFJochPixelRefineWindow.cpp index 6cce6a99..bd4e1927 100644 --- a/viewer/windows/JFJochPixelRefineWindow.cpp +++ b/viewer/windows/JFJochPixelRefineWindow.cpp @@ -145,16 +145,26 @@ void JFJochPixelRefineWindow::onControlChanged() { } void JFJochPixelRefineWindow::imageLoaded(std::shared_ptr image) { - if (m_beamInit || !image) + if (!image) return; - const auto geom = image->Dataset().experiment.GetDiffractionGeometry(); - m_beamx->setMax(static_cast(image->Dataset().experiment.GetXPixelsNum())); - m_beamy->setMax(static_cast(image->Dataset().experiment.GetYPixelsNum())); - m_suppress = true; - m_beamx->setValue(geom.GetBeamX_pxl()); - m_beamy->setValue(geom.GetBeamY_pxl()); - m_suppress = false; - m_beamInit = true; + + // Initialise the beam-centre sliders from the geometry once. + if (!m_beamInit) { + const auto geom = image->Dataset().experiment.GetDiffractionGeometry(); + m_beamx->setMax(static_cast(image->Dataset().experiment.GetXPixelsNum())); + m_beamy->setMax(static_cast(image->Dataset().experiment.GetYPixelsNum())); + m_suppress = true; + m_beamx->setValue(geom.GetBeamX_pxl()); + m_beamy->setValue(geom.GetBeamY_pxl()); + m_suppress = false; + m_beamInit = true; + } + + // Request a predicted-image preview for the (re)loaded image. Without this the + // preview only refreshed on a slider change, so opening/reanalyzing an image + // left the predicted view empty. The worker no-ops if there is no reference or + // the image is not integrated yet. + onControlChanged(); } void JFJochPixelRefineWindow::setPredictedImage(std::shared_ptr image) {