diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 8c018458..e0d046ca 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -56,6 +56,8 @@ ADD_EXECUTABLE(jfjoch_viewer jfjoch_viewer.cpp JFJochViewerWindow.cpp JFJochView windows/JFJochViewerReflectionListWindow.h widgets/JFJochOneOverResSqChartView.cpp widgets/JFJochOneOverResSqChartView.h + JFJochViewerImageROIStatistics.cpp + JFJochViewerImageROIStatistics.h ) TARGET_LINK_LIBRARIES(jfjoch_viewer Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Charts Qt6::DBus diff --git a/viewer/JFJochViewerImage.cpp b/viewer/JFJochViewerImage.cpp index be2f9ae6..5b4f255f 100644 --- a/viewer/JFJochViewerImage.cpp +++ b/viewer/JFJochViewerImage.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include // Constructor @@ -107,6 +108,8 @@ void JFJochViewerImage::mousePressEvent(QMouseEvent *event) { else mouse_event_type = MouseEventType::Panning; } + } else if (event->button() == Qt::RightButton) { + mouse_event_type = MouseEventType::RightClickMenu; } QGraphicsView::mousePressEvent(event); // Call base implementation } @@ -175,7 +178,9 @@ void JFJochViewerImage::mouseReleaseEvent(QMouseEvent *event) { if (!scene() || !image) return; setCursor(Qt::CursorShape::ArrowCursor); - if (mouse_event_type == MouseEventType::MovingROI) { + if (mouse_event_type == MouseEventType::RightClickMenu) { + ShowImageMenu(event); + } else if (mouse_event_type == MouseEventType::MovingROI) { if (roi_type == RoiType::RoiBox) { roiStartPos = RoundPoint(roiStartPos); roiEndPos = RoundPoint(roiEndPos); @@ -669,3 +674,57 @@ void JFJochViewerImage::highlightIceRings(bool input) { highlight_ice_rings = input; Redraw(); } + +void JFJochViewerImage::ShowImageMenu(QMouseEvent *event) { + auto mouse_loc = mapToScene(event->pos()); + float radius = roi_box.height() / 2.0; + QPointF center = roi_box.center(); + bool mouse_in_roi = false; + + if (roi_box.width() * roi_box.height() > 0) { + if (roi_box.contains(mouse_loc)) { + if (roi_type == RoiType::RoiCircle) { + float dx = mouse_loc.x() - center.x(); + float dy = mouse_loc.y() - center.y(); + float distance_sq = dx * dx + dy * dy; + + if (distance_sq <= radius * radius) { + mouse_in_roi = true; + } + } else + mouse_in_roi = true; + } + } + + if (mouse_in_roi) { + QMenu menu; + + // Show ROI coordinates (read-only) + + QString roi_coordinates; + if (roi_type == RoiType::RoiBox) { + roi_coordinates = QString("ROI Box x: [%1, %2] y: [%3, %4]") + .arg(roi_box.left()) + .arg(roi_box.right()) + .arg(roi_box.top()) + .arg(roi_box.bottom()); + } else { + roi_coordinates = QString("ROI Circle: center: [%1, %2] r: %3").arg(center.x()).arg(center.y()).arg(radius); + } + + menu.addAction(roi_coordinates)->setDisabled(true); + + // Add or remove from user mask + QAction *addMaskAction = menu.addAction("Add to user mask"); + QAction *removeMaskAction = menu.addAction("Remove from user mask"); + + // Show the menu at the cursor's position + QAction *selectedAction = menu.exec(event->globalPos()); + + if (selectedAction == addMaskAction) { + // Implement "Add to user mask" functionality here + } else if (selectedAction == removeMaskAction) { + // Implement "Remove from user mask" functionality here + } + } +} diff --git a/viewer/JFJochViewerImage.h b/viewer/JFJochViewerImage.h index 728fc5c5..2c55f941 100644 --- a/viewer/JFJochViewerImage.h +++ b/viewer/JFJochViewerImage.h @@ -48,6 +48,9 @@ private: void DrawTopPixels(); void DrawSaturation(); void DrawCross(float x, float y, float size, float width, float z = 1); + + void ShowImageMenu(QMouseEvent *event); + double scale_factor = 1.0; ColorScale color_scale; std::shared_ptr image; @@ -71,7 +74,7 @@ private: QPointF roiEndPos; enum class RoiType {RoiBox, RoiCircle}; - enum class MouseEventType {None, Panning, DrawingROI, MovingROI}; + enum class MouseEventType {None, Panning, DrawingROI, MovingROI, RightClickMenu}; MouseEventType mouse_event_type = MouseEventType::None; RoiType roi_type = RoiType::RoiBox; diff --git a/viewer/JFJochViewerImageROIStatistics.cpp b/viewer/JFJochViewerImageROIStatistics.cpp new file mode 100644 index 00000000..4415fa11 --- /dev/null +++ b/viewer/JFJochViewerImageROIStatistics.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute +// SPDX-License-Identifier: GPL-3.0-only + +#include "JFJochViewerImageROIStatistics.h" + +#include "../../.cache/JetBrains/CLion2025.2/.docker/2025_2/Docker/gitea_psi_ch_leonarski_f_jfjoch_ubuntu2404_2510/usr/include/x86_64-linux-gnu/qt6/QtWidgets/QVBoxLayout" + +JFJochViewerImageROIStatistics::JFJochViewerImageROIStatistics(QWidget *parent) + : QWidget(parent) { + QVBoxLayout* layout = new QVBoxLayout(this); + + roi_pos = new QLabel("None", this); + roi_label = new QLabel("", this); + + layout->addWidget(roi_pos); + layout->addWidget(roi_label); +} + +void JFJochViewerImageROIStatistics::loadImage(std::shared_ptr image) { + if (!image) { + roi_pos->setText("None"); + roi_label->setText(""); + } else { + auto roi = image->GetROI(); + + if (roi && roi->pixels > 0) { + auto roi_npixel = static_cast(roi->pixels); + double roi_mean_val = static_cast(roi->sum) / roi_npixel; + double variance = static_cast(roi->sum_square) / roi_npixel - roi_mean_val * roi_mean_val; + + QString text = QString("Sum %1 Mean %2 Var %3 Max %4") + .arg(roi->sum) + .arg(QString::number(roi_mean_val, 'f', 3)) + .arg(QString::number(variance, 'f', 3)) + .arg(roi->max_count); + roi_label->setText(text); + roi_pos->setText(QString::fromStdString(image->GetROIDescription())); + } else { + roi_label->setText(""); + roi_pos->setText("None"); + } + } +} diff --git a/viewer/JFJochViewerImageROIStatistics.h b/viewer/JFJochViewerImageROIStatistics.h new file mode 100644 index 00000000..ebaeec4c --- /dev/null +++ b/viewer/JFJochViewerImageROIStatistics.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute +// SPDX-License-Identifier: GPL-3.0-only + + +#ifndef JFJOCH_JFJOCHVIEWERIMAGEROISTATISTICS_H +#define JFJOCH_JFJOCHVIEWERIMAGEROISTATISTICS_H + +#include +#include "../reader/JFJochReaderImage.h" + +class JFJochViewerImageROIStatistics : public QWidget { + Q_OBJECT + + QLabel *roi_pos; + QLabel *roi_label; +public: + JFJochViewerImageROIStatistics(QWidget *parent); +public slots: + void loadImage(std::shared_ptr image); +}; + + +#endif //JFJOCH_JFJOCHVIEWERIMAGEROISTATISTICS_H \ No newline at end of file diff --git a/viewer/JFJochViewerImageStatistics.cpp b/viewer/JFJochViewerImageStatistics.cpp index 36d86f4e..1b2b50b1 100644 --- a/viewer/JFJochViewerImageStatistics.cpp +++ b/viewer/JFJochViewerImageStatistics.cpp @@ -128,19 +128,6 @@ JFJochViewerImageStatistics::JFJochViewerImageStatistics(QWidget *parent) : QWid b_factor = new QLabel(this); layout->addRow(new QLabel("B-factor:"), b_factor); - - roi_sum = new QLabel(this); - layout->addRow(new QLabel("ROI sum:"), roi_sum); - - roi_mean = new QLabel(this); - layout->addRow(new QLabel("ROI mean:"), roi_mean); - - roi_stddev = new QLabel(this); - layout->addRow(new QLabel("ROI st. dev.:"), roi_stddev); - - roi_max = new QLabel(this); - layout->addRow(new QLabel("ROI max:"), roi_max); - } QString TrimZeros(double number, int precision) { @@ -172,10 +159,6 @@ void JFJochViewerImageStatistics::loadImage(std::shared_ptrsetText(""); indexed->setText(""); indexed->setToolTip(""); - roi_sum->setText(""); - roi_mean->setText(""); - roi_stddev->setText(""); - roi_max->setText(""); b_factor->setText(""); profile_radius->setText(""); res_estimate->setText(""); @@ -314,27 +297,4 @@ void JFJochViewerImageStatistics::loadImage(std::shared_ptrsetText("N/A"); } - - auto roi = image->GetROI(); - if (roi && roi->pixels > 0) { - text = QString("%1").arg(roi->sum); - roi_sum->setText(text); - - auto roi_npixel = static_cast(roi->pixels); - double roi_mean_val = static_cast(roi->sum) / roi_npixel; - text = QString("%1").arg(QString::number(roi_mean_val, 'f', 3)); - roi_mean->setText(text); - - double variance = static_cast(roi->sum_square) / roi_npixel - roi_mean_val * roi_mean_val; - text = QString("%1").arg(QString::number(sqrt(variance), 'f', 3)); - roi_stddev->setText(text); - - text = QString("%1").arg(roi->max_count); - roi_max->setText(text); - } else { - roi_sum->setText("N/A"); - roi_mean->setText("N/A"); - roi_stddev->setText("N/A"); - roi_max->setText("N/A"); - } } diff --git a/viewer/JFJochViewerImageStatistics.h b/viewer/JFJochViewerImageStatistics.h index 0f60097d..e78fc2b4 100644 --- a/viewer/JFJochViewerImageStatistics.h +++ b/viewer/JFJochViewerImageStatistics.h @@ -25,11 +25,6 @@ class JFJochViewerImageStatistics : public QWidget { QLabel *b_factor; QLabel *profile_radius; - - QLabel *roi_sum; - QLabel *roi_mean; - QLabel *roi_stddev; - QLabel *roi_max; public: JFJochViewerImageStatistics(QWidget *parent); diff --git a/viewer/JFJochViewerSidePanel.cpp b/viewer/JFJochViewerSidePanel.cpp index c373fb2e..5dfd640b 100644 --- a/viewer/JFJochViewerSidePanel.cpp +++ b/viewer/JFJochViewerSidePanel.cpp @@ -10,6 +10,8 @@ #include #include "JFJochViewerSidePanel.h" + +#include "JFJochViewerImageROIStatistics.h" #include "widgets/TitleLabel.h" #include "JFJochViewerImageStatistics.h" @@ -21,8 +23,13 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent) auto stats = new JFJochViewerImageStatistics(this); layout->addWidget(stats); - connect(this, &JFJochViewerSidePanel::imageLoaded, - stats, &JFJochViewerImageStatistics::loadImage); + connect(this, &JFJochViewerSidePanel::imageLoaded, stats, &JFJochViewerImageStatistics::loadImage); + + layout->addWidget(new TitleLabel("ROI", this)); + + auto roi = new JFJochViewerImageROIStatistics(this); + layout->addWidget(roi); + connect(this, &JFJochViewerSidePanel::imageLoaded, roi, &JFJochViewerImageROIStatistics::loadImage); layout->addWidget(new TitleLabel("Image features", this));