jfjoch_viewer: ROI calculated inside image viewer
This commit is contained in:
@@ -69,6 +69,7 @@ struct ROIMessage {
|
||||
uint64_t sum_square;
|
||||
int64_t max_count;
|
||||
uint64_t pixels;
|
||||
uint64_t pixels_masked; // only used in the viewer for now
|
||||
int64_t x_weighted;
|
||||
int64_t y_weighted;
|
||||
};
|
||||
|
||||
@@ -23,8 +23,7 @@ JFJochReaderImage::JFJochReaderImage(const JFJochReaderImage &other)
|
||||
message(other.message),
|
||||
saturated_pixel(other.saturated_pixel),
|
||||
error_pixel(other.error_pixel),
|
||||
valid_pixel(other.valid_pixel),
|
||||
roi(other.roi) {
|
||||
valid_pixel(other.valid_pixel) {
|
||||
// Need to make image use local copy
|
||||
message.image = CompressedImage(image, dataset->experiment.GetXPixelsNum(),
|
||||
dataset->experiment.GetYPixelsNum());
|
||||
@@ -201,48 +200,6 @@ void JFJochReaderImage::AddImage(const JFJochReaderImage &other) {
|
||||
CalcAutoContrast();
|
||||
}
|
||||
|
||||
void JFJochReaderImage::CalcROI(const ROIElement *input) {
|
||||
std::unique_lock ul(roi_mutex);
|
||||
|
||||
if (!input) {
|
||||
roi = {};
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t width = dataset->experiment.GetXPixelsNum();
|
||||
int64_t height = dataset->experiment.GetYPixelsNum();
|
||||
|
||||
int64_t roi_val = 0;
|
||||
uint64_t roi_val_2 = 0;
|
||||
int64_t roi_max = INT64_MIN;
|
||||
uint64_t roi_npixel = 0;
|
||||
|
||||
for (int64_t y = 0; y < height; y++) {
|
||||
for (int64_t x = 0; x < width; x++) {
|
||||
int32_t val = image[x + width * y];
|
||||
if (input->CheckROI(x, y, 0.0) && (val != SATURATED_PXL_VALUE)
|
||||
&& (val != ERROR_PXL_VALUE) && (val != GAP_PXL_VALUE)) {
|
||||
roi_val += val;
|
||||
roi_val_2 += val * val;
|
||||
if (val > roi_max) roi_max = val;
|
||||
roi_npixel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
roi = ROIMessage{
|
||||
.sum = roi_val,
|
||||
.sum_square = roi_val_2,
|
||||
.max_count = roi_max,
|
||||
.pixels = roi_npixel
|
||||
};
|
||||
}
|
||||
|
||||
const std::optional<ROIMessage> &JFJochReaderImage::GetROI() const {
|
||||
std::unique_lock ul(roi_mutex);
|
||||
return roi;
|
||||
}
|
||||
|
||||
std::vector<float> JFJochReaderImage::GetAzInt1D() const {
|
||||
if (dataset->azimuthal_bins <= 1) {
|
||||
return message.az_int_profile;
|
||||
|
||||
@@ -25,8 +25,6 @@ class JFJochReaderImage {
|
||||
std::unordered_set<int64_t> saturated_pixel;
|
||||
std::unordered_set<int64_t> error_pixel;
|
||||
std::vector<std::pair<int32_t, int32_t>> valid_pixel;
|
||||
std::optional<ROIMessage> roi;
|
||||
mutable std::mutex roi_mutex;
|
||||
|
||||
constexpr static float auto_foreground_range = 0.001f;
|
||||
int32_t auto_foreground;
|
||||
@@ -48,15 +46,9 @@ public:
|
||||
const std::vector<std::pair<int32_t, int32_t>> &ValidPixels() const;
|
||||
const JFJochReaderDataset &Dataset() const;
|
||||
|
||||
const std::optional<ROIMessage> &GetROI() const;
|
||||
|
||||
void AddImage(const JFJochReaderImage& other);
|
||||
std::vector<float> GetAzInt1D() const;
|
||||
std::vector<float> GetAzInt1D_BinToQ() const;
|
||||
|
||||
// Functions that change the content
|
||||
void CalcROI(const ROIElement *input);
|
||||
|
||||
std::shared_ptr<JFJochReaderDataset> CreateMutableDataset();
|
||||
|
||||
int32_t GetAutoContrastValue() const;
|
||||
|
||||
@@ -98,13 +98,6 @@ void JFJochImageReadingWorker::LoadImage(int64_t image_number, int64_t summation
|
||||
LoadImage_i(image_number, summation);
|
||||
}
|
||||
|
||||
void JFJochImageReadingWorker::CalcROI_i() {
|
||||
if (current_image_ptr) {
|
||||
if (roi)
|
||||
current_image_ptr->CalcROI(roi.get());
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochImageReadingWorker::UpdateAzint_i(const JFJochReaderDataset *dataset) {
|
||||
if (dataset) {
|
||||
azint_mapping = std::make_unique<AzimuthalIntegration>(curr_experiment, dataset->pixel_mask);
|
||||
@@ -143,8 +136,6 @@ void JFJochImageReadingWorker::LoadImage_i(int64_t image_number, int64_t summati
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
CalcROI_i();
|
||||
|
||||
if (auto_reanalyze)
|
||||
ReanalyzeImage_i();
|
||||
|
||||
@@ -168,11 +159,6 @@ void JFJochImageReadingWorker::SetROIBox(QRect box) {
|
||||
roi.reset();
|
||||
|
||||
roi = std::make_unique<ROIBox>("roi1", box.left(), box.right(), box.bottom(), box.top());
|
||||
|
||||
if (current_image_ptr) {
|
||||
current_image_ptr->CalcROI(roi.get());
|
||||
emit imageStatsUpdated(current_image_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochImageReadingWorker::SetROICircle(double x, double y, double radius) {
|
||||
@@ -182,11 +168,6 @@ void JFJochImageReadingWorker::SetROICircle(double x, double y, double radius) {
|
||||
roi.reset();
|
||||
else
|
||||
roi = std::make_unique<ROICircle>("roi1", x, y, radius);
|
||||
|
||||
if (current_image_ptr) {
|
||||
current_image_ptr->CalcROI(roi.get());
|
||||
emit imageStatsUpdated(current_image_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochImageReadingWorker::UpdateDataset_i(const std::optional<DiffractionExperiment> &experiment) {
|
||||
@@ -212,7 +193,7 @@ void JFJochImageReadingWorker::UpdateDataset_i(const std::optional<DiffractionEx
|
||||
emit datasetLoaded(dataset);
|
||||
|
||||
current_image_ptr = std::make_shared<JFJochReaderImage>(current_image_ptr->ImageData(), dataset);
|
||||
CalcROI_i();
|
||||
|
||||
if (auto_reanalyze)
|
||||
ReanalyzeImage_i();
|
||||
emit imageLoaded(current_image_ptr);
|
||||
|
||||
@@ -69,7 +69,6 @@ private:
|
||||
int autoload_interval = 500; // milliseconds
|
||||
|
||||
void LoadImage_i(int64_t image_number, int64_t summation);
|
||||
void CalcROI_i();
|
||||
void ReanalyzeImage_i();
|
||||
void UpdateDataset_i(const std::optional<DiffractionExperiment>& experiment);
|
||||
void UpdateAzint_i(const JFJochReaderDataset *dataset);
|
||||
|
||||
@@ -39,19 +39,24 @@ JFJochViewerImageROIStatistics::JFJochViewerImageROIStatistics(QWidget *parent)
|
||||
circle_settings->Disable();
|
||||
box_radio->setChecked(true);
|
||||
|
||||
QHBoxLayout *label_row = new QHBoxLayout();
|
||||
QHBoxLayout *label_row_1 = new QHBoxLayout();
|
||||
QHBoxLayout *label_row_2 = new QHBoxLayout();
|
||||
|
||||
roi_sum = new QLabel("", this);
|
||||
label_row->addWidget(roi_sum);
|
||||
label_row_1->addWidget(roi_sum);
|
||||
roi_mean = new QLabel("", this);
|
||||
label_row->addWidget(roi_mean);
|
||||
label_row_1->addWidget(roi_mean);
|
||||
roi_var = new QLabel("", this);
|
||||
label_row->addWidget(roi_var);
|
||||
label_row_1->addWidget(roi_var);
|
||||
roi_max = new QLabel("", this);
|
||||
label_row->addWidget(roi_max);
|
||||
label_row_1->addWidget(roi_max);
|
||||
roi_npixel = new QLabel("", this);
|
||||
label_row->addWidget(roi_npixel);
|
||||
layout->addLayout(label_row);
|
||||
label_row_2->addWidget(roi_npixel);
|
||||
roi_masked = new QLabel("", this);
|
||||
label_row_2->addWidget(roi_masked);
|
||||
|
||||
layout->addLayout(label_row_1);
|
||||
layout->addLayout(label_row_2);
|
||||
|
||||
QPushButton *add_button = new QPushButton("Add ROI to user mask", this);
|
||||
connect(add_button, &QPushButton::clicked, [this]() { emit AddROIToUserMask(); });
|
||||
@@ -68,36 +73,29 @@ JFJochViewerImageROIStatistics::JFJochViewerImageROIStatistics(QWidget *parent)
|
||||
|
||||
}
|
||||
|
||||
void JFJochViewerImageROIStatistics::loadImage(std::shared_ptr<const JFJochReaderImage> image) {
|
||||
if (!image) {
|
||||
void JFJochViewerImageROIStatistics::SetROIResult(ROIMessage roi) {
|
||||
if ( roi.pixels > 0) {
|
||||
auto roi_npixel_val = static_cast<double>(roi.pixels);
|
||||
double roi_mean_val = static_cast<double>(roi.sum) / roi_npixel_val;
|
||||
double variance = static_cast<double>(roi.sum_square) / roi_npixel_val - roi_mean_val * roi_mean_val;
|
||||
|
||||
roi_sum->setText(QString("Sum <b>%1</b>").arg(roi.sum));
|
||||
roi_mean->setText(QString("Mean <b>%1</b>").arg(QString::number(roi_mean_val, 'f', 3)));
|
||||
roi_var->setText(QString("Var <b>%1</b>").arg(QString::number(variance, 'f', 3)));
|
||||
roi_max->setText(QString("Max <b>%1</b>").arg(roi.max_count));
|
||||
roi_npixel->setText(QString("Pixels <b>%1</b>").arg(roi.pixels));
|
||||
roi_masked->setText(QString("Masked <b>%1</b>").arg(roi.pixels_masked));
|
||||
} else {
|
||||
roi_sum->setText("");
|
||||
roi_mean->setText("");
|
||||
roi_var->setText("");
|
||||
roi_max->setText("");
|
||||
roi_npixel->setText("");
|
||||
} else {
|
||||
auto roi = image->GetROI();
|
||||
|
||||
if (roi && roi->pixels > 0) {
|
||||
auto roi_npixel_val = static_cast<double>(roi->pixels);
|
||||
double roi_mean_val = static_cast<double>(roi->sum) / roi_npixel_val;
|
||||
double variance = static_cast<double>(roi->sum_square) / roi_npixel_val - roi_mean_val * roi_mean_val;
|
||||
|
||||
roi_sum->setText(QString("Sum <b>%1</b>").arg(roi->sum));
|
||||
roi_mean->setText(QString("Mean <b>%1</b>").arg(QString::number(roi_mean_val, 'f', 3)));
|
||||
roi_var->setText(QString("Var <b>%1</b>").arg(QString::number(variance, 'f', 3)));
|
||||
roi_max->setText(QString("Max <b>%1</b>").arg(roi->max_count));
|
||||
roi_npixel->setText(QString("Pixels <b>%1</b>").arg(roi->pixels));
|
||||
} else {
|
||||
roi_sum->setText("");
|
||||
roi_mean->setText("");
|
||||
roi_var->setText("");
|
||||
roi_max->setText("");
|
||||
roi_npixel->setText("");
|
||||
}
|
||||
roi_masked->setText("");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JFJochViewerImageROIStatistics::SetROIBox(QRect box) {
|
||||
box_radio->setChecked(true);
|
||||
box_settings->ROIBoxConfigured(box);
|
||||
|
||||
@@ -30,15 +30,16 @@ class JFJochViewerImageROIStatistics : public QWidget {
|
||||
QLabel *roi_var;
|
||||
QLabel *roi_max;
|
||||
QLabel *roi_npixel;
|
||||
QLabel *roi_masked;
|
||||
public:
|
||||
JFJochViewerImageROIStatistics(QWidget *parent);
|
||||
private slots:
|
||||
void BoxButtonClicked();
|
||||
void CircleButtonClicked();
|
||||
public slots:
|
||||
void loadImage(std::shared_ptr<const JFJochReaderImage> image);
|
||||
void SetROIBox(QRect box);
|
||||
void SetROICircle(double x, double y, double radius);
|
||||
void SetROIResult(ROIMessage msg);
|
||||
signals:
|
||||
void ROIBoxConfigured(QRect box); // Signal emitted when Box ROI is set
|
||||
void ROICircleConfigured(double center_x, double center_y, double radius); // Signal emitted when Circle ROI is set
|
||||
|
||||
@@ -125,7 +125,6 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
|
||||
|
||||
roi = new JFJochViewerImageROIStatistics(this);
|
||||
layout->addWidget(roi);
|
||||
connect(this, &JFJochViewerSidePanel::imageLoaded, roi, &JFJochViewerImageROIStatistics::loadImage);
|
||||
|
||||
connect(roi, &JFJochViewerImageROIStatistics::ROIBoxConfigured, [this] (QRect box) {
|
||||
emit ROIBoxConfigured(box);
|
||||
@@ -321,3 +320,7 @@ void JFJochViewerSidePanel::SetROIBox(QRect box) {
|
||||
void JFJochViewerSidePanel::SetROICircle(double x, double y, double radius) {
|
||||
roi->SetROICircle(x, y, radius);
|
||||
}
|
||||
|
||||
void JFJochViewerSidePanel::SetROIResult(ROIMessage msg) {
|
||||
roi->SetROIResult(msg);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public slots:
|
||||
void loadImage(std::shared_ptr<const JFJochReaderImage> image);
|
||||
void SetROIBox(QRect box);
|
||||
void SetROICircle(double x, double y, double radius);
|
||||
void SetROIResult(ROIMessage msg);
|
||||
private slots:
|
||||
void editingFinished();
|
||||
void enableResRings(bool input);
|
||||
|
||||
@@ -183,6 +183,9 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
|
||||
connect(viewer, &JFJochDiffractionImage::roiCircleUpdated,
|
||||
side_panel, &JFJochViewerSidePanel::SetROICircle);
|
||||
|
||||
connect(viewer, &JFJochDiffractionImage::roiCalculated,
|
||||
side_panel, &JFJochViewerSidePanel::SetROIResult);
|
||||
|
||||
connect(side_panel, &JFJochViewerSidePanel::ROIBoxConfigured,
|
||||
reading_worker, &JFJochImageReadingWorker::SetROIBox);
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ JFJochImage::JFJochImage(QWidget *parent) : QGraphicsView(parent) {
|
||||
|
||||
// Optional: a sensible default colormap
|
||||
color_scale.Select(ColorScaleEnum::Indigo);
|
||||
|
||||
connect(this, &JFJochImage::roiBoxUpdated, this, &JFJochImage::CalcROIBox);
|
||||
connect(this, &JFJochImage::roiCircleUpdated, this, &JFJochImage::CalcROICircle);
|
||||
}
|
||||
|
||||
void JFJochImage::onScroll(int value) {
|
||||
@@ -575,7 +578,7 @@ void JFJochImage::writePixelLabels() {
|
||||
}
|
||||
|
||||
void JFJochImage::updateOverlay() {
|
||||
if (!scene() || W*H <= 0) return;
|
||||
if (!scene() || W * H <= 0) return;
|
||||
|
||||
scene()->clear();
|
||||
scene()->addItem(new QGraphicsPixmapItem(pixmap));
|
||||
@@ -583,10 +586,96 @@ void JFJochImage::updateOverlay() {
|
||||
if (scale_factor > 30.0)
|
||||
writePixelLabels();
|
||||
|
||||
|
||||
DrawROI();
|
||||
|
||||
addCustomOverlay();
|
||||
}
|
||||
|
||||
void JFJochImage::addCustomOverlay() {}
|
||||
|
||||
ROIMessage JFJochImage::accumulateROI(
|
||||
int64_t xmin, int64_t xmax,
|
||||
int64_t ymin, int64_t ymax,
|
||||
const std::function<bool(int64_t, int64_t)> &inside) {
|
||||
int64_t roi_val = 0;
|
||||
uint64_t roi_val_2 = 0;
|
||||
int64_t roi_max = INT64_MIN;
|
||||
uint64_t roi_npixel = 0;
|
||||
uint64_t roi_npixel_masked = 0;
|
||||
float x_weighted = 0.0f;
|
||||
float y_weighted = 0.0f;
|
||||
|
||||
// Clamp bounds defensively to the image
|
||||
xmin = std::max<int64_t>(0, xmin);
|
||||
ymin = std::max<int64_t>(0, ymin);
|
||||
xmax = std::min<int64_t>(W, xmax);
|
||||
ymax = std::min<int64_t>(H, ymax);
|
||||
|
||||
for (int64_t y = ymin; y < ymax; ++y) {
|
||||
for (int64_t x = xmin; x < xmax; ++x) {
|
||||
if (!inside(x, y)) continue;
|
||||
|
||||
float val = image_fp[x + W * y];
|
||||
|
||||
if (std::isinf(val)) {
|
||||
roi_npixel_masked++;
|
||||
} else if (std::isfinite(val)) {
|
||||
x_weighted += val * x;
|
||||
y_weighted += val * y;
|
||||
roi_val += val;
|
||||
roi_val_2 += val * val;
|
||||
if (val > roi_max) roi_max = val;
|
||||
roi_npixel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ROIMessage{
|
||||
.sum = roi_val,
|
||||
.sum_square = roi_val_2,
|
||||
.max_count = roi_max,
|
||||
.pixels = roi_npixel,
|
||||
.pixels_masked = roi_npixel_masked,
|
||||
.x_weighted = std::lroundf(x_weighted),
|
||||
.y_weighted = std::lroundf(y_weighted),
|
||||
};
|
||||
}
|
||||
|
||||
void JFJochImage::CalcROIBox(QRect box) {
|
||||
auto box_norm = box.normalized();
|
||||
|
||||
// Using the rectangle as-is; you can adjust inclusivity if needed
|
||||
int64_t xmin = box_norm.left();
|
||||
int64_t xmax = box_norm.right();
|
||||
int64_t ymin = box_norm.top();
|
||||
int64_t ymax = box_norm.bottom();
|
||||
|
||||
ROIMessage msg = accumulateROI(
|
||||
xmin, xmax, ymin, ymax,
|
||||
[](int64_t, int64_t) { return true; } // everything in the rectangle
|
||||
);
|
||||
emit roiCalculated(msg);
|
||||
}
|
||||
|
||||
void JFJochImage::CalcROICircle(double in_x, double in_y, double radius) {
|
||||
const double margin = 1.0;
|
||||
int64_t xmin = static_cast<int64_t>(std::floor(in_x - radius - margin));
|
||||
int64_t xmax = static_cast<int64_t>(std::ceil(in_x + radius + margin));
|
||||
int64_t ymin = static_cast<int64_t>(std::floor(in_y - radius - margin));
|
||||
int64_t ymax = static_cast<int64_t>(static_cast<int64_t>(std::ceil(in_y + radius + margin)));
|
||||
|
||||
const float cx = static_cast<float>(in_x);
|
||||
const float cy = static_cast<float>(in_y);
|
||||
const float r2 = static_cast<float>(radius * radius);
|
||||
|
||||
ROIMessage msg = accumulateROI(
|
||||
xmin, xmax, ymin, ymax,
|
||||
[cx, cy, r2](int64_t x, int64_t y) {
|
||||
const float dx = static_cast<float>(x) - cx;
|
||||
const float dy = static_cast<float>(y) - cy;
|
||||
const float dist2 = dx * dx + dy * dy;
|
||||
return dist2 <= r2;
|
||||
}
|
||||
);
|
||||
emit roiCalculated(msg);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <QGraphicsView>
|
||||
#include <QMouseEvent>
|
||||
#include "../../common/ColorScale.h"
|
||||
#include "../../common/JFJochMessages.h"
|
||||
|
||||
// Q_DECLARE_METATYPE(ROIMessage)
|
||||
|
||||
class JFJochImage : public QGraphicsView {
|
||||
Q_OBJECT
|
||||
@@ -20,6 +23,9 @@ class JFJochImage : public QGraphicsView {
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
ROIMessage accumulateROI(int64_t xmin, int64_t xmax, int64_t ymin, int64_t ymax,
|
||||
const std::function<bool(int64_t,int64_t)>& inside);
|
||||
protected:
|
||||
bool show_saturation = false;
|
||||
|
||||
@@ -67,9 +73,11 @@ signals:
|
||||
void writeStatusBar(QString string, int timeout_ms = 0);
|
||||
void roiBoxUpdated(QRect box);
|
||||
void roiCircleUpdated(double x, double y, double radius);
|
||||
|
||||
void roiCalculated(ROIMessage &output);
|
||||
private slots:
|
||||
void onScroll(int value);
|
||||
void CalcROIBox(QRect box);
|
||||
void CalcROICircle(double x, double y, double radius);
|
||||
public slots:
|
||||
void setFeatureColor(QColor input);
|
||||
void setColorMap(int color_map);
|
||||
|
||||
Reference in New Issue
Block a user