The viewer could grow to ~100 GB RAM when live-following an HTTP broker. The rc.153 images_in_flight backpressure only throttled imageLoaded; the heavy per-frame payload rides datasetLoaded, fanned out over ~10 queued cross-thread connections with no cap. In HTTPSyncDataset follow mode (entered when an operator clicks an image while following live) RefreshDatasetOnly_i emits a fresh full dataset every autoload tick with no imageLoaded, so the gate never engaged and the queued events - each pinning a full JFJochReaderDataset (full-detector PixelMask + per-image plots) - accumulated without bound. Backpressure datasetLoaded the same way as imageLoaded: a datasets_in_flight counter (cap 2), all emits routed through EmitDatasetLoaded_i, and AutoLoadTimerExpired gated on it (covers HTTPSyncDataset). The window routes the worker's datasetLoaded through a single OnDatasetReady sink that fans out synchronously via datasetReady and acks with datasetConsumed. Under load stale datasets are dropped; the next tick sends the latest. Share the pixel mask instead of deep-copying it: JFJochReaderDataset::pixel_mask is now shared_ptr<const PixelMask>, so per-frame dataset copies share the ~72 MB mask. UpdateUserMask does copy-on-write; JFJochHttpReader caches the mask by arm_date so a live refresh reuses one shared mask per acquisition. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
119 lines
4.5 KiB
C++
119 lines
4.5 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "JFJochCalibrationWindow.h"
|
|
#include <QCloseEvent>
|
|
#include <QGridLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
|
|
#include "../widgets/JFJochViewerROIResult.h"
|
|
|
|
JFJochCalibrationWindow::JFJochCalibrationWindow(QWidget *parent) : JFJochHelperWindow(parent) {
|
|
QWidget *centralWidget = new QWidget(this);
|
|
setWindowTitle("Calibration image viewer");
|
|
setCentralWidget(centralWidget);
|
|
|
|
auto grid_layout = new QGridLayout();
|
|
|
|
viewer = new JFJochSimpleImage(this);
|
|
|
|
calibration_option = new QComboBox(this);
|
|
color_map_select = new QComboBox(this);
|
|
|
|
color_map_select->addItem("Viridis", static_cast<int>(ColorScaleEnum::Viridis));
|
|
color_map_select->addItem("Heat", static_cast<int>(ColorScaleEnum::Heat));
|
|
color_map_select->addItem("Indigo", static_cast<int>(ColorScaleEnum::Indigo));
|
|
color_map_select->addItem("B/W", static_cast<int>(ColorScaleEnum::BW));
|
|
color_map_select->setCurrentIndex(static_cast<int>(ColorScaleEnum::Indigo));
|
|
|
|
background_slider = new SliderPlusBox(1, 32768, 1.0, 0, this, SliderPlusBox::ScaleType::Linear);
|
|
foreground_slider = new SliderPlusBox(1, 32768, 1.0, 0, this, SliderPlusBox::ScaleType::Linear);
|
|
|
|
auto background_row = new QHBoxLayout();
|
|
auto foreground_row = new QHBoxLayout();
|
|
background_row->addWidget(new QLabel("Background:"));
|
|
background_row->addWidget(background_slider);
|
|
foreground_row->addWidget(new QLabel("Foreground:"));
|
|
foreground_row->addWidget(foreground_slider);
|
|
|
|
auto roi_result = new JFJochViewerROIResult(this);
|
|
|
|
grid_layout->addWidget(calibration_option, 0, 0);
|
|
grid_layout->addWidget(color_map_select, 0, 1);
|
|
grid_layout->addLayout(background_row, 1, 0, 1, 2);
|
|
grid_layout->addLayout(foreground_row, 2, 0, 1, 2);
|
|
grid_layout->addWidget(viewer, 3, 0, 1, 2);
|
|
grid_layout->addWidget(roi_result, 4, 0, 1, 2);
|
|
|
|
connect(viewer, &JFJochSimpleImage::backgroundChanged,
|
|
[this] (float val) {
|
|
QSignalBlocker blocker(background_slider);
|
|
background_slider->setValue(val);
|
|
});
|
|
|
|
connect(viewer, &JFJochSimpleImage::foregroundChanged,
|
|
[this] (float val) {
|
|
QSignalBlocker blocker(foreground_slider);
|
|
foreground_slider->setValue(val);
|
|
});
|
|
connect(viewer, &JFJochSimpleImage::roiCalculated, roi_result, &JFJochViewerROIResult::SetROIResult);
|
|
|
|
connect(background_slider, &SliderPlusBox::valueChanged, viewer, &JFJochSimpleImage::changeBackground);
|
|
connect(foreground_slider, &SliderPlusBox::valueChanged, viewer, &JFJochSimpleImage::changeForeground);
|
|
|
|
connect(color_map_select, &QComboBox::currentIndexChanged, this, [this](int index) {
|
|
viewer->setColorMap(index);
|
|
});
|
|
|
|
connect(calibration_option, &QComboBox::currentIndexChanged, this, &JFJochCalibrationWindow::CalibrationSelected);
|
|
centralWidget->setLayout(grid_layout);
|
|
|
|
statusBar = new QStatusBar(this);
|
|
setStatusBar(statusBar);
|
|
|
|
connect(viewer, &JFJochSimpleImage::writeStatusBar,
|
|
statusBar, &QStatusBar::showMessage);
|
|
}
|
|
|
|
void JFJochCalibrationWindow::datasetLoaded(std::shared_ptr<const JFJochReaderDataset> in_dataset) {
|
|
dataset = std::move(in_dataset);
|
|
|
|
QSignalBlocker b(calibration_option);
|
|
calibration_option->clear();
|
|
if (dataset) {
|
|
calibration_option->addItem("Pixel mask");
|
|
for (auto &calibration : dataset->calibration_data)
|
|
calibration_option->addItem(calibration.c_str());
|
|
}
|
|
LoadMask();
|
|
}
|
|
|
|
void JFJochCalibrationWindow::calibrationLoaded(std::shared_ptr<const SimpleImage> image) {
|
|
viewer->setImage(image);
|
|
}
|
|
|
|
void JFJochCalibrationWindow::LoadMask() {
|
|
if (dataset) {
|
|
auto mask = std::make_shared<SimpleImage>();
|
|
auto tmp = dataset->pixel_mask->GetMask();
|
|
mask->buffer.resize(tmp.size() * sizeof(uint32_t));
|
|
memcpy(mask->buffer.data(), tmp.data(), tmp.size() * sizeof(uint32_t));
|
|
mask->image = CompressedImage(mask->buffer.data(), mask->buffer.size(),
|
|
dataset->experiment.GetXPixelsNum(), dataset->experiment.GetYPixelsNum(), CompressedImageMode::Uint32);
|
|
viewer->setImage(mask);
|
|
}
|
|
}
|
|
|
|
void JFJochCalibrationWindow::CalibrationSelected(int val) {
|
|
if (val == 0)
|
|
LoadMask();
|
|
else
|
|
emit loadCalibration(calibration_option->itemText(val));
|
|
}
|
|
|
|
void JFJochCalibrationWindow::setFeatureColor(QColor input) {
|
|
viewer->setFeatureColor(input);
|
|
}
|
|
|