Files
Jungfraujoch/viewer/JFJochViewerImageStatistics.cpp
2025-09-21 19:27:51 +02:00

265 lines
9.8 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochViewerImageStatistics.h"
#include <QFormLayout>
static QString mkSourceInstrumentText(const DiffractionExperiment& exp) {
QString src, inst;
const auto &m = exp.GetInstrumentMetadata();
if (!m.GetSourceName().empty()) src = QString::fromStdString(m.GetSourceName());
if (!m.GetInstrumentName().empty()) inst = QString::fromStdString(m.GetInstrumentName());
if (!src.isEmpty() && !inst.isEmpty()) return src + " / " + inst;
if (!src.isEmpty()) return src;
if (!inst.isEmpty()) return inst;
return QString("-");
}
static QString mkSourceInstrumentTooltip(const DiffractionExperiment &exp) {
QStringList tooltips;
if (exp.GetRingCurrent_mA().has_value()) {
tooltips << QString("Ring current: <b>%1</b> mA").arg(exp.GetRingCurrent_mA().value(), 0, 'f', 2);
}
if (exp.GetTotalFlux().has_value()) {
tooltips << QString("Total flux: <b>%1</b> ph/s").arg(exp.GetTotalFlux().value(), 0, 'e', 2);
}
if (exp.GetAttenuatorTransmission().has_value()) {
tooltips << QString("Attenuation: <b>%1</b>").arg(exp.GetAttenuatorTransmission().value(), 0, 'f', 3);
}
return tooltips.join("<br/>");
}
static QString mkSampleText(const DiffractionExperiment& exp) {
const auto& name = exp.GetSampleName();
return name.empty() ? QString("-") : QString::fromStdString(name);
}
static QString mkSampleTooltip(const DiffractionExperiment& exp) {
if (exp.GetSampleTemperature_K().has_value()) {
return QString("Sample temperature: <b>%1</b> K").arg(exp.GetSampleTemperature_K().value(), 0, 'f', 2);
}
return QString();
}
JFJochViewerImageStatistics::JFJochViewerImageStatistics(QWidget *parent) : QWidget(parent) {
QFormLayout* layout = new QFormLayout(this);
dataset_name = new QLabel(this);
layout->addRow(new QLabel("Dataset:"), dataset_name);
detector_name = new QLabel(this);
layout->addRow(new QLabel("Detector:"), detector_name);
source_name = new QLabel(this);
layout->addRow("Source / Instrument:", source_name);
sample_name = new QLabel(this);
layout->addRow("Sample:", sample_name);
error_pixels = new QLabel(this);
layout->addRow(new QLabel("Error pixels:"), error_pixels);
sat_pixels = new QLabel(this);
layout->addRow(new QLabel("Sat. pixels:"), sat_pixels);
valid_values = new QLabel(this);
layout->addRow(new QLabel("Valid values:"), valid_values);
spots = new QLabel(this);
layout->addRow(new QLabel("Spots:"), spots);
bkg_estimate = new QLabel(this);
layout->addRow(new QLabel("Background:"), bkg_estimate);
indexed = new QLabel(this);
layout->addRow(new QLabel("Indexing:"), indexed);
profile_radius = new QLabel(this);
layout->addRow(new QLabel("Profile radius:"), profile_radius);
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);
}
void JFJochViewerImageStatistics::loadImage(std::shared_ptr<const JFJochReaderImage> image) {
if (!image) {
source_name->setText("");
sample_name->setText("");
dataset_name->setText("");
dataset_name->setToolTip("");
detector_name->setText("");
detector_name->setToolTip("");
error_pixels->setText("");
sat_pixels->setText("");
valid_values->setText("");
spots->setText("");
bkg_estimate->setText("");
indexed->setText("");
indexed->setToolTip("");
roi_sum->setText("");
roi_mean->setText("");
roi_stddev->setText("");
roi_max->setText("");
b_factor->setText("");
profile_radius->setText("");
return;
}
QString text;
auto &exp = image->Dataset().experiment;
text = QString("<b>%1</b>").arg(QString::fromStdString(exp.GetFilePrefix()));
dataset_name->setText(text);
dataset_name->setToolTip(QString("Beam center: <b>%1</b> <b>%2</b> pxl<br/>Detector distance: <b>%3</b> mm<br/>Energy: <b>%4</b> eV<br/>Wavelength: <b>%5</b> Å")
.arg(QString::number(exp.GetBeamX_pxl(), 'f', 2))
.arg(QString::number(exp.GetBeamY_pxl(), 'f', 2))
.arg(QString::number(exp.GetDetectorDistance_mm(), 'f', 3))
.arg(QString::number(exp.GetIncidentEnergy_keV() * 1000.0, 'f', 0))
.arg(QString::number(exp.GetWavelength_A(), 'f', 3)));
text = QString("<b>%1</b>").arg(QString::fromStdString(exp.GetDetectorDescription()));
detector_name->setToolTip(QString("Width: <b>%1</b> pxl (<b>%2</b> mm)<br/>Height: <b>%3</b> pxl (<b>%4</b> mm)<br/>Pixel size: <b>%5</b> μm")
.arg(QString::number(exp.GetXPixelsNum()))
.arg(QString::number(exp.GetXPixelsNum() * exp.GetPixelSize_mm(), 'f', 3))
.arg(QString::number(exp.GetYPixelsNum()))
.arg(QString::number(exp.GetYPixelsNum() * exp.GetPixelSize_mm(), 'f', 3))
.arg(QString::number(exp.GetPixelSize_mm() * 1000.0, 'f', 0)));
detector_name->setText(text);
source_name->setText(mkSourceInstrumentText(exp));
source_name->setToolTip(mkSampleTooltip(exp));
sample_name->setText(mkSampleText(exp));
sample_name->setToolTip(mkSampleTooltip(exp));
text = QString("<b>%1</b>").arg(image->ErrorPixels().size());
error_pixels->setText(text);
text = QString("<b>%1</b>").arg(image->SaturatedPixels().size());
sat_pixels->setText(text);
text = QString("<b>%1</b>").arg(image->ImageData().spots.size());
spots->setText(text);
if (image->ImageData().spot_count.has_value())
spots->setToolTip(QString("Unfiltered (total): <b>%1</b> <br/>Low resolution: <b>%2</b><br/>Ice ring: <b>%3</b><br/>Indexed <b>%4</b><br/>")
.arg(image->ImageData().spot_count.value())
.arg(image->ImageData().spot_count_low_res.value_or(0))
.arg(image->ImageData().spot_count_ice_rings.value_or(0))
.arg(image->ImageData().spot_count_indexed.value_or(0)));
text = QString("<b>%1</b> - <b>%2</b>")
.arg(image->ValidPixels().begin()->first)
.arg(image->ValidPixels().rbegin()->first);
valid_values->setText(text);
if (!image->Dataset().bkg_estimate.empty()) {
text = QString("<b>%1</b>").arg(image->ImageData().bkg_estimate.value_or(0));
bkg_estimate->setText(text);
} else {
bkg_estimate->setText("N/A");
}
auto mos = image->ImageData().profile_radius;
if (mos) {
text = QString("<b>%1</b> Å<sup>-1</sup>").arg(QString::number(mos.value(), 'f', 6));
profile_radius->setText(text);
} else {
profile_radius->setText("N/A");
}
auto b = image->ImageData().b_factor;
if (b) {
text = QString("<b>%1</b> Å<sup>2</sup>").arg(QString::number(b.value(), 'f', 2));
b_factor->setText(text);
} else {
b_factor->setText("N/A");
}
if (!image->Dataset().indexing_result.empty()) {
QString tooltip;
auto latt = image->ImageData().indexing_lattice;
if (latt) {
auto uc = latt->GetUnitCell();
text = QString("<span style=\"color: purple;\"><b>%1</b> Å <b>%2</b> Å <b>%3</b> Å <b>%4</b>° <b>%5</b>° <b>%6</b>° </span>")
.arg(QString::number(uc.a, 'f', 1))
.arg(QString::number(uc.b, 'f', 1))
.arg(QString::number(uc.c, 'f', 1))
.arg(QString::number(uc.alpha, 'f', 1))
.arg(QString::number(uc.beta, 'f', 1))
.arg(QString::number(uc.gamma, 'f', 1));
auto vec0 = latt->Vec0();
auto vec1 = latt->Vec1();
auto vec2 = latt->Vec2();
tooltip = QString("<b>Lattice vectors (Å):</b><br/>"
"a = (%1, %2, %3)<br/>"
"b = (%4, %5, %6)<br/>"
"c = (%7, %8, %9)")
.arg(vec0.x, 7, 'f', 1)
.arg(vec0.y, 7, 'f', 1)
.arg(vec0.z, 7, 'f', 1)
.arg(vec1.x, 7, 'f', 1)
.arg(vec1.y, 7, 'f', 1)
.arg(vec1.z, 7, 'f', 1)
.arg(vec2.x, 7, 'f', 1)
.arg(vec2.y, 7, 'f', 1)
.arg(vec2.z, 7, 'f', 1);
if (image->Dataset().experiment.GetSpaceGroupNumber()) {
tooltip += QString("<br/><br/>Space group (user-provided): <b>%1</b>")
.arg(QString::fromStdString(image->Dataset().experiment.GetSpaceGroupName()));
}
} else
text = QString("<span style=\"color: red;\"><b>No lattice</b></span>");
indexed->setToolTip(tooltip);
indexed->setText(text);
} else {
indexed->setText("N/A");
}
auto roi = image->GetROI();
if (roi && roi->pixels > 0) {
text = QString("<b>%1</b>").arg(roi->sum);
roi_sum->setText(text);
auto roi_npixel = static_cast<double>(roi->pixels);
double roi_mean_val = static_cast<double>(roi->sum) / roi_npixel;
text = QString("<b>%1</b>").arg(QString::number(roi_mean_val, 'f', 3));
roi_mean->setText(text);
double variance = static_cast<double>(roi->sum_square) / roi_npixel - roi_mean_val * roi_mean_val;
text = QString("<b>%1</b>").arg(QString::number(sqrt(variance), 'f', 3));
roi_stddev->setText(text);
text = QString("<b>%1</b>").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");
}
}