2511-eiger-mask #2
@@ -304,20 +304,6 @@ void JFJochDiffractionImage::DrawTopPixels() {
|
||||
iter->second / image->Dataset().experiment.GetXPixelsNum() + 0.5, 15, 3);
|
||||
}
|
||||
|
||||
void JFJochDiffractionImage::updateROI() {
|
||||
if (roi_type == RoiType::RoiBox) {
|
||||
QRect roiBox_int = QRectF(RoundPoint(roiStartPos), RoundPoint(roiEndPos)).normalized().toRect();
|
||||
roiBox= roiBox_int;
|
||||
emit roiBoxUpdated(roiBox_int);
|
||||
} else {
|
||||
QPointF delta = roiStartPos - roiEndPos;
|
||||
double radius = std::sqrt(delta.x() * delta.x() + delta.y() * delta.y());
|
||||
roiBox= QRectF(roiStartPos.x() - radius, roiStartPos.y() - radius,
|
||||
2 * radius, 2 * radius).normalized();
|
||||
emit roiCircleUpdated(roiStartPos.x(), roiStartPos.y(), radius);
|
||||
}
|
||||
updateOverlay();
|
||||
}
|
||||
|
||||
void JFJochDiffractionImage::updateOverlay() {
|
||||
if (!scene() || !image) return;
|
||||
@@ -378,21 +364,7 @@ void JFJochDiffractionImage::updateOverlay() {
|
||||
if (show_saturation)
|
||||
DrawSaturation();
|
||||
|
||||
if (roiBox.width() * roiBox.height() > 0) {
|
||||
QPen pen(feature_color, 3);
|
||||
pen.setStyle(Qt::DashLine);
|
||||
pen.setCosmetic(true);
|
||||
if (roi_type == RoiType::RoiBox) {
|
||||
scene()->addRect(roiBox, pen);
|
||||
} else {
|
||||
scene()->addEllipse(roiBox, pen);
|
||||
|
||||
double pointRadius = 0.5;
|
||||
QRectF pointBoundingBox = QRectF(roiStartPos.x() - pointRadius, roiStartPos.y() - pointRadius,
|
||||
2 * pointRadius, 2 * pointRadius);
|
||||
scene()->addEllipse(pointBoundingBox, pen);
|
||||
}
|
||||
}
|
||||
DrawROI();
|
||||
}
|
||||
|
||||
void JFJochDiffractionImage::Redraw() {
|
||||
|
||||
@@ -17,14 +17,11 @@ Q_OBJECT
|
||||
|
||||
public:
|
||||
JFJochDiffractionImage(QWidget *parent = nullptr);
|
||||
signals:
|
||||
void roiBoxUpdated(QRect box);
|
||||
void roiCircleUpdated(double x, double y, double radius);
|
||||
|
||||
private:
|
||||
|
||||
void updateOverlay() override;
|
||||
void Redraw() override;
|
||||
void updateROI() override;
|
||||
|
||||
void LoadImageInternal();
|
||||
void DrawResolutionRings();
|
||||
|
||||
@@ -305,3 +305,110 @@ JFJochImage::hitTestROIHandle(const QPointF& scenePos, qreal tol) const {
|
||||
if (r.contains(scenePos)) return ResizeHandle::Inside;
|
||||
return ResizeHandle::None;
|
||||
}
|
||||
|
||||
void JFJochImage::updateROI() {
|
||||
if (roi_type == RoiType::RoiBox) {
|
||||
QRect roiBox_int = QRectF(RoundPoint(roiStartPos), RoundPoint(roiEndPos)).normalized().toRect();
|
||||
roiBox= roiBox_int;
|
||||
emit roiBoxUpdated(roiBox_int);
|
||||
} else {
|
||||
QPointF delta = roiStartPos - roiEndPos;
|
||||
double radius = std::sqrt(delta.x() * delta.x() + delta.y() * delta.y());
|
||||
roiBox= QRectF(roiStartPos.x() - radius, roiStartPos.y() - radius,
|
||||
2 * radius, 2 * radius).normalized();
|
||||
emit roiCircleUpdated(roiStartPos.x(), roiStartPos.y(), radius);
|
||||
}
|
||||
updateOverlay();
|
||||
}
|
||||
|
||||
void JFJochImage::DrawROI() {
|
||||
if (roiBox.isNull() || roiBox.width() <= 0 || roiBox.height() <= 0) return;
|
||||
|
||||
auto scn = scene();
|
||||
if (!scn)
|
||||
return;
|
||||
|
||||
QPen pen(feature_color, 2);
|
||||
pen.setStyle(Qt::DashLine);
|
||||
pen.setCosmetic(true);
|
||||
|
||||
const qreal f = std::clamp(scale_factor, 0.5, 50.0);
|
||||
const qreal handleSize = 3.0 / std::sqrt(std::max(1e-4, f));
|
||||
|
||||
if (roi_type == RoiType::RoiCircle) {
|
||||
// Draw circle
|
||||
scn->addEllipse(roiBox, pen);
|
||||
|
||||
// A single handle on the circle at the rightmost point
|
||||
const QPointF c = roiBox.center();
|
||||
const qreal rad = 0.5 * (roiBox.width() + roiBox.height()) * 0.5; // average, should be equal
|
||||
QPointF hpos = QPointF(roiBox.right(), c.y());
|
||||
scn->addRect(QRectF(hpos.x() - handleSize, hpos.y() - handleSize, 2 * handleSize, 2 * handleSize),
|
||||
QPen(feature_color, 1), QBrush(feature_color));
|
||||
|
||||
// On hover near perimeter: draw in/out arrows along radius at handle
|
||||
if (hover_handle_ != ResizeHandle::None && hover_handle_ != ResizeHandle::Inside) {
|
||||
QPen apen(feature_color, 1);
|
||||
apen.setCosmetic(true);
|
||||
const qreal arrowLen = 8.0 / std::sqrt(std::max(1e-4, f));
|
||||
// Outward arrow
|
||||
scn->addLine(QLineF(c, c + QPointF(rad + arrowLen, 0)), apen);
|
||||
// Inward arrow
|
||||
scn->addLine(QLineF(c, c + QPointF(rad - arrowLen, 0)), apen);
|
||||
}
|
||||
} else {
|
||||
// Box
|
||||
scn->addRect(roiBox, pen);
|
||||
|
||||
// Corner handles
|
||||
auto addHandle = [&](const QPointF& p) {
|
||||
scn->addRect(QRectF(p.x() - handleSize, p.y() - handleSize, 2 * handleSize, 2 * handleSize),
|
||||
QPen(feature_color, 1), QBrush(feature_color));
|
||||
};
|
||||
addHandle(roiBox.topLeft());
|
||||
addHandle(roiBox.topRight());
|
||||
addHandle(roiBox.bottomLeft());
|
||||
addHandle(roiBox.bottomRight());
|
||||
|
||||
// On hover over a resizable edge/corner: draw small arrows indicating resize direction
|
||||
if (hover_handle_ != ResizeHandle::None && hover_handle_ != ResizeHandle::Inside) {
|
||||
QPen apen(feature_color, 1);
|
||||
apen.setCosmetic(true);
|
||||
const qreal arrowLen = 6.0 / std::sqrt(std::max(1e-4, f));
|
||||
const qreal off = 10.0 / std::sqrt(std::max(1e-4, f));
|
||||
auto drawArrow = [&](const QPointF& a, const QPointF& b) {
|
||||
scn->addLine(QLineF(a, b), apen);
|
||||
};
|
||||
const QRectF r = roiBox;
|
||||
switch (hover_handle_) {
|
||||
case ResizeHandle::Left:
|
||||
drawArrow(QPointF(r.left(), r.center().y() - off), QPointF(r.left() - arrowLen, r.center().y() - off));
|
||||
drawArrow(QPointF(r.left(), r.center().y() + off), QPointF(r.left() - arrowLen, r.center().y() + off));
|
||||
break;
|
||||
case ResizeHandle::Right:
|
||||
drawArrow(QPointF(r.right(), r.center().y() - off), QPointF(r.right() + arrowLen, r.center().y() - off));
|
||||
drawArrow(QPointF(r.right(), r.center().y() + off), QPointF(r.right() + arrowLen, r.center().y() + off));
|
||||
break;
|
||||
case ResizeHandle::Top:
|
||||
drawArrow(QPointF(r.center().x() - off, r.top()), QPointF(r.center().x() - off, r.top() - arrowLen));
|
||||
drawArrow(QPointF(r.center().x() + off, r.top()), QPointF(r.center().x() + off, r.top() - arrowLen));
|
||||
break;
|
||||
case ResizeHandle::Bottom:
|
||||
drawArrow(QPointF(r.center().x() - off, r.bottom()), QPointF(r.center().x() - off, r.bottom() + arrowLen));
|
||||
drawArrow(QPointF(r.center().x() + off, r.bottom()), QPointF(r.center().x() + off, r.bottom() + arrowLen));
|
||||
break;
|
||||
case ResizeHandle::TopLeft:
|
||||
case ResizeHandle::TopRight:
|
||||
case ResizeHandle::BottomLeft:
|
||||
case ResizeHandle::BottomRight:
|
||||
// For corners, show arrows on both axes (simple version)
|
||||
drawArrow(QPointF(r.right(), r.center().y()), QPointF(r.right() + arrowLen, r.center().y()));
|
||||
drawArrow(QPointF(r.left(), r.center().y()), QPointF(r.left() - arrowLen, r.center().y()));
|
||||
drawArrow(QPointF(r.center().x(), r.top()), QPointF(r.center().x(), r.top() - arrowLen));
|
||||
drawArrow(QPointF(r.center().x(), r.bottom()), QPointF(r.center().x(), r.bottom() + arrowLen));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class JFJochImage : public QGraphicsView {
|
||||
|
||||
virtual void updateOverlay() = 0;
|
||||
virtual void Redraw() = 0;
|
||||
virtual void updateROI() = 0;
|
||||
void updateROI();
|
||||
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
@@ -22,7 +22,7 @@ class JFJochImage : public QGraphicsView {
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
protected:
|
||||
virtual void mouseHover(QMouseEvent* event) = 0;
|
||||
|
||||
void DrawROI();
|
||||
double scale_factor = 1.0;
|
||||
|
||||
QColor feature_color = Qt::magenta;
|
||||
@@ -54,6 +54,8 @@ signals:
|
||||
void foregroundChanged(float v);
|
||||
void backgroundChanged(float v);
|
||||
void writeStatusBar(QString string, int timeout_ms = 0);
|
||||
void roiBoxUpdated(QRect box);
|
||||
void roiCircleUpdated(double x, double y, double radius);
|
||||
|
||||
private slots:
|
||||
void onScroll(int value);
|
||||
|
||||
@@ -198,7 +198,7 @@ void JFJochSimpleImage::updateOverlay() {
|
||||
}
|
||||
}
|
||||
|
||||
drawROI(scene());
|
||||
DrawROI();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -236,78 +236,6 @@ void JFJochSimpleImage::renderImage(QImage &qimg, const uint8_t *input) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JFJochSimpleImage::updateROI() {
|
||||
// Normalize box if drawing
|
||||
if (mouse_event_type == MouseEventType::DrawingROI) {
|
||||
roiBox = QRectF(roiStartPos, roiEndPos).normalized();
|
||||
}
|
||||
|
||||
// Clamp ROI to image bounds
|
||||
if (has_image_) {
|
||||
const QRectF bounds(0.0, 0.0,
|
||||
image_->image.GetWidth(),
|
||||
image_->image.GetHeight());
|
||||
roiBox = roiBox.intersected(bounds);
|
||||
}
|
||||
|
||||
scheduleSceneUpdate();
|
||||
}
|
||||
|
||||
void JFJochSimpleImage::drawROI(QGraphicsScene* scn) {
|
||||
if (roiBox.isNull() || roiBox.width() <= 0 || roiBox.height() <= 0) return;
|
||||
|
||||
QPen pen(feature_color, 2);
|
||||
pen.setStyle(Qt::DashLine);
|
||||
pen.setCosmetic(true);
|
||||
scn->addRect(roiBox, pen);
|
||||
|
||||
// Small corner handles
|
||||
const qreal handleSize = 3.0 / std::sqrt(std::max(1e-4, scale_factor));
|
||||
auto addHandle = [&](const QPointF& p) {
|
||||
scn->addRect(QRectF(p.x() - handleSize, p.y() - handleSize, 2 * handleSize, 2 * handleSize),
|
||||
QPen(feature_color, 1), QBrush(feature_color));
|
||||
};
|
||||
addHandle(roiBox.topLeft());
|
||||
addHandle(roiBox.topRight());
|
||||
addHandle(roiBox.bottomLeft());
|
||||
addHandle(roiBox.bottomRight());
|
||||
|
||||
// Compute stats inside integer pixel ROI
|
||||
QRect roi_px = roiBox.toRect().normalized();
|
||||
ROIStats stats = computeROIStats(roi_px);
|
||||
|
||||
if (stats.valid()) {
|
||||
// Compose text
|
||||
QString text = QString("min %1 max %2 avg %3 std %4 n=%5")
|
||||
.arg(stats.min, 0, 'f', 2)
|
||||
.arg(stats.max, 0, 'f', 2)
|
||||
.arg(stats.avg, 0, 'f', 2)
|
||||
.arg(stats.stddev, 0, 'f', 2)
|
||||
.arg(qulonglong(stats.count));
|
||||
|
||||
// Choose background based on local luminance under top-left pixel of ROI
|
||||
const int W = int(image_->image.GetWidth());
|
||||
const int H = int(image_->image.GetHeight());
|
||||
int px = std::clamp(roi_px.left(), 0, std::max(0, W - 1));
|
||||
int py = std::clamp(roi_px.top(), 0, std::max(0, H - 1));
|
||||
const rgb bgc = image_rgb[size_t(py) * size_t(W) + size_t(px)];
|
||||
const bool darkText = luminance(bgc) > 128.0;
|
||||
|
||||
QFont font("Arial");
|
||||
const qreal f = std::clamp(scale_factor, 0.5, 50.0);
|
||||
font.setPointSizeF(14.0 / std::sqrt(f));
|
||||
|
||||
auto* t = scn->addText(text, font);
|
||||
t->setDefaultTextColor(darkText ? Qt::black : Qt::white);
|
||||
t->setZValue(20.0);
|
||||
|
||||
// Place near top-left of ROI, slightly above
|
||||
QPointF pos = roiBox.topLeft() + QPointF(2.0, -16.0 / std::sqrt(f));
|
||||
t->setPos(pos);
|
||||
}
|
||||
}
|
||||
|
||||
JFJochSimpleImage::ROIStats
|
||||
JFJochSimpleImage::computeROIStats(const QRect& roi_px) const {
|
||||
ROIStats s;
|
||||
|
||||
@@ -35,7 +35,6 @@ private:
|
||||
|
||||
void renderImage(); // builds pixmap_ from current image_ + settings
|
||||
void updateOverlay() override; // clears scene, adds pixmap + labels + ROI (if any)
|
||||
void drawROI(QGraphicsScene* scn); // draw box and stats
|
||||
ROIStats computeROIStats(const QRect& roi_px) const;
|
||||
|
||||
// Image and rendering state
|
||||
@@ -55,9 +54,6 @@ private:
|
||||
template <class T>
|
||||
void renderImage(QImage &qimg, const uint8_t *input);
|
||||
|
||||
// Helpers
|
||||
void updateROI() override; // normalize and request repaint
|
||||
|
||||
bool auto_fgbg = true;
|
||||
|
||||
void mouseHover(QMouseEvent* event) override;
|
||||
|
||||
Reference in New Issue
Block a user