jfjoch_viewer: Track top-pixels without storing/sorting all valid pixels

This commit is contained in:
2025-12-12 11:36:54 +01:00
parent d70c60c501
commit e03d0677ba
9 changed files with 241 additions and 51 deletions

View File

@@ -6,6 +6,9 @@
#include "JFJochDecompress.h"
#include "../image_analysis/bragg_integration/BraggIntegrate2D.h"
#include <queue>
#include <algorithm>
JFJochReaderImage::JFJochReaderImage(const DataMessage &in_message,
const std::shared_ptr<const JFJochReaderDataset> &in_dataset)
: message(in_message),
@@ -72,11 +75,22 @@ void JFJochReaderImage::ProcessInputImage(const void *data, size_t npixel, int64
const T* img_ptr = reinterpret_cast<const T*>(data);
size_t good_pixel = 0;
// Reset per-image stats
saturated_pixel.clear();
error_pixel.clear();
valid_count = 0;
has_valid = false;
valid_pixel.reserve(image.size());
top_pixels_acc.Clear();
top_pixels.clear();
top_pixels.reserve(top_pixels_acc.Capacity());
for (int i = 0; i < npixel; i++) {
bool has_input_mask = false;
const auto &mask = dataset->pixel_mask.GetMask();
if (mask.size() == npixel)
has_input_mask = true;
for (size_t i = 0; i < npixel; i++) {
int32_t val;
if (img_ptr[i] <= INT32_MAX)
val = static_cast<int32_t>(img_ptr[i]);
@@ -84,44 +98,61 @@ void JFJochReaderImage::ProcessInputImage(const void *data, size_t npixel, int64
val = INT32_MAX;
uint32_t mask_val = 0;
if (!dataset->pixel_mask.GetMask().empty())
mask_val = dataset->pixel_mask.GetMask().at(i);
if (has_input_mask)
mask_val = mask[i];
if ((mask_val & (
(1<<PixelMask::ModuleGapPixelBit)
| (1<<PixelMask::ChipGapPixelBit)
| (1<<PixelMask::ModuleEdgePixelBit))) != 0) {
image[i] = GAP_PXL_VALUE;
} else if ((mask_val != 0) || (img_ptr[i] == special_value)){
} else if ((mask_val != 0) || (img_ptr[i] == special_value)) {
image[i] = ERROR_PXL_VALUE;
error_pixel.emplace(i);
error_pixel.emplace(static_cast<int64_t>(i));
} else if (val >= sat_value) {
image[i] = SATURATED_PXL_VALUE;
saturated_pixel.emplace(i);
saturated_pixel.emplace(static_cast<int64_t>(i));
} else {
good_pixel++;
image[i] = val;
valid_pixel.emplace_back(std::make_pair(val, i));
if (!has_valid) {
has_valid = true;
valid_min = val;
valid_max = val;
} else {
valid_min = std::min(valid_min, val);
valid_max = std::max(valid_max, val);
}
valid_count++;
top_pixels_acc.Add(val, static_cast<int32_t>(i));
}
}
// Sort based on the first element only
std::sort(valid_pixel.begin(), valid_pixel.end(),
[](const auto& a, const auto& b) {return a.first < b.first;});
CalcAutoContrast();
// For now: auto-foreground based purely on max valid element
auto_foreground = has_valid ? std::max(1, valid_max) : 10;
// Export top pixels (already sorted descending) into the existing vector interface
for (int i = 0; i < top_pixels_acc.Size(); i++) {
const auto &e = top_pixels_acc[i];
top_pixels.emplace_back(e.value, e.index);
}
}
void JFJochReaderImage::CalcAutoContrast() {
if (valid_pixel.empty())
auto_foreground = 10;
else {
auto it = valid_pixel.crbegin();
const auto index = static_cast<size_t>(valid_pixel.size() * auto_foreground_range);
std::advance(it, std::max<size_t>(1ULL, index) - 1);
// Now simplified: based on max element (valid_max)
auto_foreground = has_valid ? std::max(1, valid_max) : 10;
}
auto_foreground = std::max(1, it->first);
}
std::optional<std::pair<int32_t, int32_t>> JFJochReaderImage::ValidMinMax() const {
if (!has_valid)
return {};
return std::make_pair(valid_min, valid_max);
}
const std::vector<std::pair<int32_t, int32_t>> &JFJochReaderImage::GetTopPixels() const {
return top_pixels;
}
const DataMessage &JFJochReaderImage::ImageData() const {
@@ -144,10 +175,6 @@ const std::unordered_set<int64_t> &JFJochReaderImage::ErrorPixels() const {
return error_pixel;
}
const std::vector<std::pair<int32_t, int32_t>> &JFJochReaderImage::ValidPixels() const {
return valid_pixel;
}
const JFJochReaderDataset &JFJochReaderImage::Dataset() const {
if (!dataset)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
@@ -167,37 +194,56 @@ void JFJochReaderImage::AddImage(const JFJochReaderImage &other) {
error_pixel.clear();
saturated_pixel.clear();
valid_pixel.clear();
valid_pixel.reserve(image.size());
for (int i = 0; i < image.size(); i++) {
if (image[i] == GAP_PXL_VALUE || other.image[i] == GAP_PXL_VALUE)
valid_count = 0;
has_valid = false;
top_pixels_acc.Clear();
top_pixels.clear();
top_pixels.reserve(top_pixels_acc.Capacity());
for (size_t i = 0; i < image.size(); i++) {
if (image[i] == GAP_PXL_VALUE || other.image[i] == GAP_PXL_VALUE) {
image[i] = GAP_PXL_VALUE;
else if (image[i] == ERROR_PXL_VALUE || other.image[i] == ERROR_PXL_VALUE) {
} else if (image[i] == ERROR_PXL_VALUE || other.image[i] == ERROR_PXL_VALUE) {
image[i] = ERROR_PXL_VALUE;
error_pixel.emplace(i);
error_pixel.emplace(static_cast<int64_t>(i));
} else if (image[i] == SATURATED_PXL_VALUE || other.image[i] == SATURATED_PXL_VALUE) {
image[i] = SATURATED_PXL_VALUE;
saturated_pixel.emplace(i);
saturated_pixel.emplace(static_cast<int64_t>(i));
} else {
int64_t sum = static_cast<int64_t>(image[i]) + static_cast<int64_t>(other.image[i]);
if (sum <= INT32_MIN + 5) [[unlikely]] {
image[i] = ERROR_PXL_VALUE;
error_pixel.emplace(i);
error_pixel.emplace(static_cast<int64_t>(i));
} else if (sum > dataset->experiment.GetSaturationLimit()) [[unlikely]] {
image[i] = SATURATED_PXL_VALUE;
saturated_pixel.emplace(i);
saturated_pixel.emplace(static_cast<int64_t>(i));
} else {
image[i] = static_cast<int32_t>(sum);
valid_pixel.emplace_back(std::make_pair(sum, i));
const int32_t val = static_cast<int32_t>(sum);
image[i] = val;
if (!has_valid) {
has_valid = true;
valid_min = val;
valid_max = val;
} else {
valid_min = std::min(valid_min, val);
valid_max = std::max(valid_max, val);
}
valid_count++;
top_pixels_acc.Add(val, static_cast<int32_t>(i));
}
}
}
// Sort based on the first element only
std::sort(valid_pixel.begin(), valid_pixel.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
// For now: auto-foreground based purely on max valid element
auto_foreground = has_valid ? std::max(1, valid_max) : 10;
CalcAutoContrast();
for (int i = 0; i < top_pixels_acc.Size(); i++) {
const auto &e = top_pixels_acc[i];
top_pixels.emplace_back(e.value, e.index);
}
}
std::vector<float> JFJochReaderImage::GetAzInt1D() const {