Files
Jungfraujoch/preview/PreviewImage.cpp
2025-05-12 14:17:24 +02:00

286 lines
11 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "PreviewImage.h"
#include <cmath>
#include "JFJochJPEG.h"
#include "JFJochTIFF.h"
#include "../common/JFJochException.h"
#include "../common/DiffractionGeometry.h"
#include "../frame_serialize/CBORStream2Deserializer.h"
#include "../compression/JFJochDecompress.h"
constexpr const static rgb lime = {.r = 0xcd, .g = 0xdc, .b = 0x39};
constexpr const static rgb pink = {.r = 0xe9, .g = 0x1e, .b = 0x63};
constexpr const static rgb purple = {.r = 0x7b, .g = 0x1f, .b = 0xA2};
constexpr const static rgb orange = {.r = 0xff, .g = 0x57, .b = 0x22};
constexpr const static rgb amber = {.r =0xff, .g = 0xc1, .b = 0x07};
constexpr const static rgb blue = {.r = 0x0d, .g = 0x47, .b = 0xa1};
constexpr const static rgb plotly[] = {{0x1f, 0x77, 0xb4},
{0xff, 0x7f, 0x0e},
{0x2c, 0xa0, 0x2c},
{0xd6, 0x27, 0x28},
{0x94, 0x67, 0xbd},
{0x8c, 0x56, 0x4b},
{0xe3, 0x77, 0xc2},
{0x7f, 0x7f, 0x7f},
{0xbd, 0xbd, 0x22},
{0x17, 0xbe, 0xcf}};
constexpr const static rgb gray = {.r = 0xbe, .g = 0xbe, .b = 0xbe};
void PreviewImage::color_pixel(std::vector<rgb> &ret, int64_t in_xpixel, int64_t in_ypixel,const rgb &color) const {
if ((in_xpixel >= 0) && (in_xpixel < xpixel) && (in_ypixel >= 0) && (in_ypixel < ypixel))
ret[(in_ypixel * xpixel + in_xpixel)] = color;
}
void PreviewImage::spot(std::vector<rgb> &ret, int64_t in_xpixel, int64_t in_ypixel, bool indexed) const {
if (indexed)
color_pixel(ret, in_xpixel, in_ypixel, pink);
else
color_pixel(ret, in_xpixel, in_ypixel, amber);
}
void PreviewImage::roi(std::vector<rgb> &ret, int64_t in_xpixel, int64_t in_ypixel, int64_t roi_number) const {
color_pixel(ret, in_xpixel, in_ypixel, plotly[roi_number % 10]);
}
template<class T>
std::vector<rgb> PreviewImage::GenerateRGB(const T* value,
T special_value,
const ColorScale &scale,
const PreviewJPEGSettings& settings) const {
std::vector<rgb> ret(xpixel*ypixel);
for (int i = 0; i < xpixel*ypixel; i++) {
if (mask[i] == MaskGap)
ret[i] = scale.Apply(ColorScaleSpecial::Gap);
else if ((value[i] == special_value)
|| (mask[i] == MaskDet)
|| (settings.show_user_mask && (mask[i] == MaskUsr)))
ret[i] = scale.Apply(ColorScaleSpecial::BadPixel);
else if (value[i] >= static_cast<T>(settings.saturation_value))
ret[i] = scale.Apply(1.0);
else if (value[i] <= 0)
ret[i] = scale.Apply(0.0);
else
ret[i] = scale.Apply(value[i] / static_cast<float>(settings.saturation_value));
}
return ret;
}
void PreviewImage::AddBeamCenter(std::vector<rgb> &rgb_image) const {
size_t beam_x_int = std::lround(beam_x);
size_t beam_y_int = std::lround(beam_y);
int crosshair_size = 30;
int crosshair_width = 3;
for (int w = -crosshair_width; w <= crosshair_width; w++) {
for (int i = -crosshair_size; i <= crosshair_size; i++) {
color_pixel(rgb_image, beam_x_int + i, beam_y_int + w, lime);
color_pixel(rgb_image, beam_x_int + w, beam_y_int + i, lime);
}
}
}
void PreviewImage::AddSpots(std::vector<rgb> &rgb_image,
const std::vector<SpotToSave>& in_spots) const {
for (const auto &s: in_spots) {
int64_t spot_x_int = std::lround(s.x);
int64_t spot_y_int = std::lround(s.y);
int rectangle_size = 4;
int rectangle_width = 3;
for (int z = rectangle_size; z < rectangle_size + rectangle_width; z++) {
for (int w = -z; w <= z; w++) {
spot(rgb_image, spot_x_int + z, spot_y_int + w, s.indexed);
spot(rgb_image, spot_x_int - z, spot_y_int + w, s.indexed);
spot(rgb_image, spot_x_int + w, spot_y_int + z, s.indexed);
spot(rgb_image, spot_x_int + w, spot_y_int - z, s.indexed);
}
}
}
}
void PreviewImage::AddROI(std::vector<rgb> &rgb_image) const {
int64_t roi_counter = 0;
for (const auto &box: experiment.ROI().GetROIDefinition().boxes) {
int rectangle_width = 5;
for (auto x = box.GetXMin() - rectangle_width; x <= box.GetXMax() + rectangle_width; x++) {
for (auto w = 1; w <= rectangle_width; w++) {
roi(rgb_image, x, box.GetYMax() + w, roi_counter);
roi(rgb_image, x, box.GetYMin() - w, roi_counter);
}
}
for (auto y = box.GetYMin() - rectangle_width; y <= box.GetYMax() + rectangle_width; y++) {
for (auto w = 1; w <= rectangle_width; w++) {
roi(rgb_image, box.GetXMax() + w, y, roi_counter);
roi(rgb_image, box.GetXMin() - w, y, roi_counter);
}
}
roi_counter++;
}
for (const auto &circle: experiment.ROI().GetROIDefinition().circles) {
int width = 5;
for (int64_t y = std::floor(circle.GetY() - circle.GetRadius_pxl() - width);
y <= std::ceil(circle.GetY() + circle.GetRadius_pxl() + width);
y++) {
for (int64_t x = std::floor(circle.GetX() - circle.GetRadius_pxl() - width);
x <= std::ceil(circle.GetX() + circle.GetRadius_pxl() + width);
x++) {
float dist = sqrtf((x - circle.GetX()) * (x - circle.GetX())
+ (y - circle.GetY()) * (y - circle.GetY()));
if ((dist > circle.GetRadius_pxl()) && (dist < circle.GetRadius_pxl() + width))
roi(rgb_image, x, y, roi_counter);
}
}
roi_counter++;
}
}
void PreviewImage::AddResolutionRing(std::vector<rgb> &rgb_image, float d) const {
DiffractionGeometry geom = experiment.GetDiffractionGeometry();
int width = 3;
float radius = geom.ResToPxl(d);
for (int64_t y = 0; y <= ypixel; y++) {
for (int64_t x = 0; x <= xpixel; x++) {
float dist = sqrtf((x - beam_x) * (x - beam_x) + (y - beam_y) * (y - beam_y));
if ((dist > radius) && (dist < radius + width))
color_pixel(rgb_image, x, y, orange);
}
}
}
void PreviewImage::Configure(const DiffractionExperiment &in_experiment, const PixelMask& pixel_mask) {
std::unique_lock ul(m);
experiment = in_experiment;
xpixel = experiment.GetXPixelsNum();
ypixel = experiment.GetYPixelsNum();
beam_x = experiment.GetBeamX_pxl();
beam_y = experiment.GetBeamY_pxl();
pixel_depth_bytes = experiment.GetByteDepthImage();
pixel_is_signed = experiment.IsPixelSigned();
mask = std::vector<uint8_t>(experiment.GetPixelsNum(), 0);
std::vector<uint32_t> mask_tmp = pixel_mask.GetMask(experiment);
for (int i = 0; i < experiment.GetPixelsNum(); i++) {
if (((mask_tmp[i] & (1 << PixelMask::ModuleGapPixelBit)) != 0)
|| ((mask_tmp[i] & (1 << PixelMask::ChipGapPixelBit)) != 0)
|| ((mask_tmp[i] & (1 << PixelMask::ModuleEdgePixelBit)) != 0))
mask[i] = MaskGap;
else if (mask_tmp[i] & 0xFEFE) // bits 1-7 and 9-15
mask[i] = MaskDet;
else if ((mask_tmp[i] & (1 << PixelMask::UserMaskedPixelBit)) != 0)
mask[i] = MaskUsr;
}
}
void PreviewImage::Configure() {
std::unique_lock ul(m);
xpixel = 0;
ypixel = 0;
}
std::string PreviewImage::GenerateJPEG(const PreviewJPEGSettings& settings,
const std::vector<uint8_t>& xray_image,
const std::vector<SpotToSave>& in_spots,
std::optional<size_t> in_pixel_depth_bytes,
std::optional<bool> in_pixel_is_signed) const {
std::vector<rgb> v;
size_t local_xpixel;
size_t local_ypixel;
{
// JPEG compression is outside the critical loop protected by m
std::unique_lock ul(m);
local_xpixel = xpixel;
local_ypixel = ypixel;
size_t depth = in_pixel_depth_bytes.value_or(pixel_depth_bytes);
if ( depth * xpixel * ypixel == 0)
return {};
if (xray_image.size() != depth * xpixel * ypixel)
return {};
ColorScale scale;
scale.Select(settings.scale);
if (!in_pixel_is_signed.value_or(pixel_is_signed)) {
if (depth == 1)
v = GenerateRGB<uint8_t>((uint8_t *) xray_image.data(),UINT8_MAX, scale, settings);
else if (depth == 2)
v = GenerateRGB<uint16_t>((uint16_t *) xray_image.data(), UINT16_MAX, scale, settings);
else if (depth == 4)
v = GenerateRGB<uint32_t>((uint32_t *) xray_image.data(), UINT32_MAX, scale, settings);
else
return {};
} else {
if (depth == 1)
v = GenerateRGB<int8_t>((int8_t *) xray_image.data(), INT8_MIN, scale, settings);
else if (depth == 2)
v = GenerateRGB<int16_t>((int16_t *) xray_image.data(), INT16_MIN, scale, settings);
else if (depth == 4)
v = GenerateRGB<int32_t>((int32_t *) xray_image.data(), INT32_MIN, scale, settings);
else
return {};
}
if (settings.show_spots)
AddSpots(v, in_spots);
if (settings.show_roi)
AddROI(v);
if (settings.resolution_ring)
AddResolutionRing(v, settings.resolution_ring.value());
AddBeamCenter(v);
}
return WriteJPEGToMem(v, local_xpixel, local_ypixel, settings.jpeg_quality);
}
std::string PreviewImage::GenerateJPEG(const PreviewJPEGSettings &settings, const std::vector<uint8_t> &cbor_format) {
auto cbor = CBORStream2Deserialize(cbor_format);
if (!cbor || !cbor->data_message)
return {};
std::vector<uint8_t> decompressed;
JFJochDecompress(decompressed, cbor->data_message->image );
return GenerateJPEG(settings,
decompressed,
cbor->data_message->spots,
cbor->data_message->image.pixel_depth_bytes,
cbor->data_message->image.pixel_is_signed);
}
std::string PreviewImage::GenerateTIFF(const std::vector<uint8_t>& cbor_format) {
auto cbor = CBORStream2Deserialize(cbor_format);
if (!cbor || !cbor->data_message)
return {};
std::vector<uint8_t> decompressed;
JFJochDecompress(decompressed, cbor->data_message->image );
return WriteTIFFToString(decompressed.data(),
cbor->data_message->image.xpixel,
cbor->data_message->image.ypixel,
cbor->data_message->image.pixel_depth_bytes,
cbor->data_message->image.pixel_is_signed);
}