jfjoch_viewer: Work in (slow) progress
This commit is contained in:
@@ -15,9 +15,6 @@
|
||||
// Constructor
|
||||
JFJochDiffractionImage::JFJochDiffractionImage(QWidget *parent) : JFJochImage(parent) {}
|
||||
|
||||
QPointF RoundPoint(const QPointF &input) {
|
||||
return QPointF(qRound(input.x()), qRound(input.y()));
|
||||
}
|
||||
|
||||
void JFJochDiffractionImage::mousePressEvent(QMouseEvent *event) {
|
||||
if (!scene() || !image) return;
|
||||
|
||||
@@ -29,8 +29,7 @@ private:
|
||||
|
||||
void updateOverlay() override;
|
||||
void Redraw() override;
|
||||
|
||||
void updateROI();
|
||||
void updateROI() override;
|
||||
|
||||
void LoadImageInternal();
|
||||
void DrawResolutionRings();
|
||||
@@ -53,14 +52,6 @@ private:
|
||||
bool highlight_ice_rings = true;
|
||||
|
||||
float ice_ring_width_Q_recipA = 0.01;
|
||||
QPoint lastMousePos; // To track panning movement
|
||||
|
||||
QPointF roiStartPos;
|
||||
QPointF roiEndPos;
|
||||
|
||||
enum class RoiType {RoiBox, RoiCircle};
|
||||
RoiType roi_type = RoiType::RoiBox;
|
||||
|
||||
public slots:
|
||||
void loadImage(std::shared_ptr<const JFJochReaderImage> image);
|
||||
void setAutoForeground(bool input);
|
||||
|
||||
@@ -93,3 +93,7 @@ void JFJochImage::resizeEvent(QResizeEvent *event) {
|
||||
QGraphicsView::resizeEvent(event);
|
||||
updateOverlay();
|
||||
}
|
||||
|
||||
QPointF JFJochImage::RoundPoint(const QPointF &input) {
|
||||
return QPointF(qRound(input.x()), qRound(input.y()));
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#ifndef JFJOCH_JFJOCHIMAGE_H
|
||||
#define JFJOCH_JFJOCHIMAGE_H
|
||||
#pragma once
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include "../../common/ColorScale.h"
|
||||
@@ -12,6 +11,7 @@ class JFJochImage : public QGraphicsView {
|
||||
|
||||
virtual void updateOverlay() = 0;
|
||||
virtual void Redraw() = 0;
|
||||
virtual void updateROI() = 0;
|
||||
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
@@ -26,6 +26,15 @@ protected:
|
||||
QPixmap pixmap;
|
||||
enum class MouseEventType {None, Panning, DrawingROI, MovingROI, ResizingROI};
|
||||
MouseEventType mouse_event_type = MouseEventType::None;
|
||||
enum class RoiType {RoiBox, RoiCircle};
|
||||
RoiType roi_type = RoiType::RoiBox;
|
||||
QPoint lastMousePos; // To track panning movement
|
||||
|
||||
QPointF roiStartPos;
|
||||
QPointF roiEndPos;
|
||||
QRectF roiBox;
|
||||
|
||||
static QPointF RoundPoint(const QPointF& p);
|
||||
|
||||
signals:
|
||||
void foregroundChanged(float v);
|
||||
@@ -42,5 +51,3 @@ public slots:
|
||||
public:
|
||||
explicit JFJochImage(QWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
#endif //JFJOCH_JFJOCHIMAGE_H
|
||||
@@ -29,16 +29,21 @@ void JFJochSimpleImage::clear() {
|
||||
}
|
||||
|
||||
void JFJochSimpleImage::setImage(std::shared_ptr<const SimpleImage> img) {
|
||||
image_ = std::move(img);
|
||||
has_image_ = true;
|
||||
renderImage();
|
||||
updateOverlay();
|
||||
if (img) {
|
||||
image_ = std::move(img);
|
||||
has_image_ = true;
|
||||
Redraw();
|
||||
} else {
|
||||
has_image_ = false;
|
||||
image_.reset();
|
||||
if (scene())
|
||||
scene()->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochSimpleImage::Redraw() {
|
||||
if (has_image_) {
|
||||
renderImage();
|
||||
// Preserve current transform while updating
|
||||
updateOverlay();
|
||||
}
|
||||
}
|
||||
@@ -55,24 +60,23 @@ void JFJochSimpleImage::mousePressEvent(QMouseEvent* event) {
|
||||
active_handle_ = hitTestROIHandle(scenePos, 4.0 / std::sqrt(std::max(1e-4, scale_factor)));
|
||||
if (active_handle_ != ResizeHandle::None && active_handle_ != ResizeHandle::Inside) {
|
||||
mouse_event_type = MouseEventType::ResizingROI;
|
||||
roi_start_pos_ = roi_box_.topLeft();
|
||||
roi_end_pos_ = roi_box_.bottomRight();
|
||||
roiStartPos = roiBox.topLeft();
|
||||
roiEndPos = roiBox.bottomRight();
|
||||
setCursor(Qt::SizeAllCursor);
|
||||
} else if (roi_box_.contains(scenePos)) {
|
||||
} else if (roiBox.contains(scenePos)) {
|
||||
mouse_event_type = MouseEventType::MovingROI;
|
||||
last_mouse_pos_ = event->pos();
|
||||
lastMousePos = event->pos();
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
} else {
|
||||
mouse_event_type = MouseEventType::DrawingROI;
|
||||
roi_start_pos_ = RoundPoint(scenePos);
|
||||
roi_end_pos_ = roi_start_pos_;
|
||||
roiStartPos = RoundPoint(scenePos);
|
||||
roiEndPos = roiStartPos;
|
||||
setCursor(Qt::CrossCursor);
|
||||
}
|
||||
} else {
|
||||
mouse_event_type = MouseEventType::Panning;
|
||||
panning_ = true;
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
last_mouse_pos_ = event->pos();
|
||||
lastMousePos = event->pos();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,27 +90,27 @@ void JFJochSimpleImage::mouseMoveEvent(QMouseEvent* event) {
|
||||
|
||||
switch (mouse_event_type) {
|
||||
case MouseEventType::Panning: {
|
||||
const QPointF delta = mapToScene(event->pos()) - mapToScene(last_mouse_pos_);
|
||||
last_mouse_pos_ = event->pos();
|
||||
const QPointF delta = mapToScene(event->pos()) - mapToScene(lastMousePos);
|
||||
lastMousePos = event->pos();
|
||||
translate(delta.x(), delta.y());
|
||||
updateOverlay();
|
||||
break;
|
||||
}
|
||||
case MouseEventType::DrawingROI: {
|
||||
roi_end_pos_ = RoundPoint(scenePos);
|
||||
roiEndPos = RoundPoint(scenePos);
|
||||
updateROI();
|
||||
break;
|
||||
}
|
||||
case MouseEventType::MovingROI: {
|
||||
const QPointF delta = mapToScene(event->pos()) - mapToScene(last_mouse_pos_);
|
||||
last_mouse_pos_ = event->pos();
|
||||
roi_box_.translate(delta);
|
||||
const QPointF delta = mapToScene(event->pos()) - mapToScene(lastMousePos);
|
||||
lastMousePos = event->pos();
|
||||
roiBox.translate(delta);
|
||||
updateROI();
|
||||
break;
|
||||
}
|
||||
case MouseEventType::ResizingROI: {
|
||||
// Modify the corresponding edges based on active_handle_
|
||||
QRectF r = roi_box_;
|
||||
QRectF r = roiBox;
|
||||
switch (active_handle_) {
|
||||
case ResizeHandle::Left: r.setLeft(scenePos.x()); break;
|
||||
case ResizeHandle::Right: r.setRight(scenePos.x()); break;
|
||||
@@ -118,7 +122,7 @@ void JFJochSimpleImage::mouseMoveEvent(QMouseEvent* event) {
|
||||
case ResizeHandle::BottomRight:r.setBottom(scenePos.y()); r.setRight(scenePos.x()); break;
|
||||
default: break;
|
||||
}
|
||||
roi_box_ = r.normalized();
|
||||
roiBox = r.normalized();
|
||||
updateROI();
|
||||
break;
|
||||
}
|
||||
@@ -151,10 +155,9 @@ void JFJochSimpleImage::mouseReleaseEvent(QMouseEvent* event) {
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (mouse_event_type == MouseEventType::DrawingROI) {
|
||||
roi_end_pos_ = RoundPoint(mapToScene(event->pos()));
|
||||
roiEndPos = RoundPoint(mapToScene(event->pos()));
|
||||
updateROI();
|
||||
}
|
||||
panning_ = false;
|
||||
mouse_event_type = MouseEventType::None;
|
||||
active_handle_ = ResizeHandle::None;
|
||||
setCursor(Qt::ArrowCursor);
|
||||
@@ -331,17 +334,12 @@ void JFJochSimpleImage::renderImage(QImage &qimg, const uint8_t *input) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QPointF JFJochSimpleImage::RoundPoint(const QPointF& p) {
|
||||
return QPointF(qRound(p.x()), qRound(p.y()));
|
||||
}
|
||||
|
||||
JFJochSimpleImage::ResizeHandle
|
||||
JFJochSimpleImage::hitTestROIHandle(const QPointF& scenePos, qreal tol) const {
|
||||
if (roi_box_.isNull() || roi_box_.width() <= 0 || roi_box_.height() <= 0)
|
||||
if (roiBox.isNull() || roiBox.width() <= 0 || roiBox.height() <= 0)
|
||||
return ResizeHandle::None;
|
||||
|
||||
const QRectF r = roi_box_;
|
||||
const QRectF r = roiBox;
|
||||
const QPointF tl = r.topLeft();
|
||||
const QPointF tr = r.topRight();
|
||||
const QPointF bl = r.bottomLeft();
|
||||
@@ -372,8 +370,8 @@ JFJochSimpleImage::hitTestROIHandle(const QPointF& scenePos, qreal tol) const {
|
||||
|
||||
void JFJochSimpleImage::updateROI() {
|
||||
// Normalize box if drawing
|
||||
if (mouse_event_type_ == MouseEventType::DrawingROI) {
|
||||
roi_box_ = QRectF(roi_start_pos_, roi_end_pos_).normalized();
|
||||
if (mouse_event_type == MouseEventType::DrawingROI) {
|
||||
roiBox = QRectF(roiStartPos, roiEndPos).normalized();
|
||||
}
|
||||
|
||||
// Clamp ROI to image bounds
|
||||
@@ -381,19 +379,19 @@ void JFJochSimpleImage::updateROI() {
|
||||
const QRectF bounds(0.0, 0.0,
|
||||
image_->image.GetWidth(),
|
||||
image_->image.GetHeight());
|
||||
roi_box_ = roi_box_.intersected(bounds);
|
||||
roiBox = roiBox.intersected(bounds);
|
||||
}
|
||||
|
||||
scheduleSceneUpdate();
|
||||
}
|
||||
|
||||
void JFJochSimpleImage::drawROI(QGraphicsScene* scn) {
|
||||
if (roi_box_.isNull() || roi_box_.width() <= 0 || roi_box_.height() <= 0) return;
|
||||
if (roiBox.isNull() || roiBox.width() <= 0 || roiBox.height() <= 0) return;
|
||||
|
||||
QPen pen(feature_color, 2);
|
||||
pen.setStyle(Qt::DashLine);
|
||||
pen.setCosmetic(true);
|
||||
scn->addRect(roi_box_, pen);
|
||||
scn->addRect(roiBox, pen);
|
||||
|
||||
// Small corner handles
|
||||
const qreal handleSize = 3.0 / std::sqrt(std::max(1e-4, scale_factor));
|
||||
@@ -401,13 +399,13 @@ void JFJochSimpleImage::drawROI(QGraphicsScene* scn) {
|
||||
scn->addRect(QRectF(p.x() - handleSize, p.y() - handleSize, 2 * handleSize, 2 * handleSize),
|
||||
QPen(feature_color, 1), QBrush(feature_color));
|
||||
};
|
||||
addHandle(roi_box_.topLeft());
|
||||
addHandle(roi_box_.topRight());
|
||||
addHandle(roi_box_.bottomLeft());
|
||||
addHandle(roi_box_.bottomRight());
|
||||
addHandle(roiBox.topLeft());
|
||||
addHandle(roiBox.topRight());
|
||||
addHandle(roiBox.bottomLeft());
|
||||
addHandle(roiBox.bottomRight());
|
||||
|
||||
// Compute stats inside integer pixel ROI
|
||||
QRect roi_px = roi_box_.toRect().normalized();
|
||||
QRect roi_px = roiBox.toRect().normalized();
|
||||
ROIStats stats = computeROIStats(roi_px);
|
||||
|
||||
if (stats.valid()) {
|
||||
@@ -436,7 +434,7 @@ void JFJochSimpleImage::drawROI(QGraphicsScene* scn) {
|
||||
t->setZValue(20.0);
|
||||
|
||||
// Place near top-left of ROI, slightly above
|
||||
QPointF pos = roi_box_.topLeft() + QPointF(2.0, -16.0 / std::sqrt(f));
|
||||
QPointF pos = roiBox.topLeft() + QPointF(2.0, -16.0 / std::sqrt(f));
|
||||
t->setPos(pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,6 @@ private:
|
||||
|
||||
std::vector<double> image_values_; // original scalar values per pixel (for labels and min/max)
|
||||
|
||||
// View/interaction
|
||||
QPoint last_mouse_pos_;
|
||||
bool panning_ = false;
|
||||
|
||||
// Smooth movement/repaint
|
||||
QTimer repaint_timer_;
|
||||
QPointF pan_accum_{0.0, 0.0};
|
||||
@@ -70,18 +66,14 @@ private:
|
||||
template <class T>
|
||||
void renderImage(QImage &qimg, const uint8_t *input);
|
||||
|
||||
QRectF roi_box_; // in scene/image pixel coordinates
|
||||
QPointF roi_start_pos_; // for drawing/resizing
|
||||
QPointF roi_end_pos_; // for drawing/resizing
|
||||
|
||||
// Resizing which edge/corner
|
||||
enum class ResizeHandle { None, Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight, Inside };
|
||||
ResizeHandle active_handle_ = ResizeHandle::None;
|
||||
|
||||
// Helpers
|
||||
static QPointF RoundPoint(const QPointF& p);
|
||||
|
||||
ResizeHandle hitTestROIHandle(const QPointF& scenePos, qreal tol = 3.0) const;
|
||||
void updateROI(); // normalize and request repaint
|
||||
void updateROI() override; // normalize and request repaint
|
||||
|
||||
bool auto_fgbg = true;
|
||||
};
|
||||
|
||||
@@ -42,10 +42,16 @@ JFJochCalibrationWindow::JFJochCalibrationWindow(QWidget *parent) : JFJochHelper
|
||||
grid_layout->addWidget(viewer, 3, 0, 1, 2);
|
||||
|
||||
connect(viewer, &JFJochSimpleImage::backgroundChanged,
|
||||
[this] (float val) {background_slider->setValue(val);});
|
||||
[this] (float val) {
|
||||
QSignalBlocker blocker(background_slider);
|
||||
background_slider->setValue(val);
|
||||
});
|
||||
|
||||
connect(viewer, &JFJochSimpleImage::foregroundChanged,
|
||||
[this] (float val) {foreground_slider->setValue(val);});
|
||||
[this] (float val) {
|
||||
QSignalBlocker blocker(foreground_slider);
|
||||
foreground_slider->setValue(val);
|
||||
});
|
||||
|
||||
connect(background_slider, &SliderPlusBox::valueChanged, viewer, &JFJochSimpleImage::changeBackground);
|
||||
connect(foreground_slider, &SliderPlusBox::valueChanged, viewer, &JFJochSimpleImage::changeForeground);
|
||||
|
||||
Reference in New Issue
Block a user