// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "JFJochViewerImageStatistics.h" #include 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: %1 mA").arg(exp.GetRingCurrent_mA().value(), 0, 'f', 2); } if (exp.GetTotalFlux().has_value()) { tooltips << QString("Total flux: %1 ph/s").arg(exp.GetTotalFlux().value(), 0, 'e', 2); } if (exp.GetAttenuatorTransmission().has_value()) { tooltips << QString("Attenuation: %1").arg(exp.GetAttenuatorTransmission().value(), 0, 'f', 3); } return tooltips.join("
"); } 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: %1 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 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("%1").arg(QString::fromStdString(exp.GetFilePrefix())); dataset_name->setText(text); dataset_name->setToolTip(QString("Beam center: %1 %2 pxl
Detector distance: %3 mm
Energy: %4 eV
Wavelength: %5 Å") .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("%1").arg(QString::fromStdString(exp.GetDetectorDescription())); detector_name->setToolTip(QString("Width: %1 pxl (%2 mm)
Height: %3 pxl (%4 mm)
Pixel size: %5 μ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("%1").arg(image->ErrorPixels().size()); error_pixels->setText(text); text = QString("%1").arg(image->SaturatedPixels().size()); sat_pixels->setText(text); text = QString("%1").arg(image->ImageData().spots.size()); spots->setText(text); if (image->ImageData().spot_count.has_value()) spots->setToolTip(QString("Unfiltered (total): %1
Low resolution: %2
Ice ring: %3
Indexed %4
") .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("%1 - %2") .arg(image->ValidPixels().begin()->first) .arg(image->ValidPixels().rbegin()->first); valid_values->setText(text); if (!image->Dataset().bkg_estimate.empty()) { text = QString("%1").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("%1 Å-1").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("%1 Å2").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("%1 Å %2 Å %3 Å %4° %5° %6° ") .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("Lattice vectors (Å):
" "a = (%1, %2, %3)
" "b = (%4, %5, %6)
" "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("

Space group (user-provided): %1") .arg(QString::fromStdString(image->Dataset().experiment.GetSpaceGroupName())); } } else text = QString("No lattice"); indexed->setToolTip(tooltip); indexed->setText(text); } else { indexed->setText("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"); } }