diff --git a/broker/JFJochServices.cpp b/broker/JFJochServices.cpp index 66019a80..4beef0ba 100644 --- a/broker/JFJochServices.cpp +++ b/broker/JFJochServices.cpp @@ -154,7 +154,7 @@ std::string JFJochServices::GetPreviewJPEG(const PreviewJPEGSettings &settings) if (receiver != nullptr) return receiver->GetJPEG(settings); else - return PreviewImage::GenerateTestJPEG(settings); + return {}; } std::string JFJochServices::GetPreviewTIFF(bool calibration) const { diff --git a/preview/PreviewImage.cpp b/preview/PreviewImage.cpp index 02d8f022..22620d7d 100644 --- a/preview/PreviewImage.cpp +++ b/preview/PreviewImage.cpp @@ -8,6 +8,28 @@ #include "WriteTIFF.h" #include "../common/JFJochException.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 indigo = {.r = 0x3f, .g = 0x51, .b = 0xb5}; +constexpr const static rgb gray = {.r = 0xbe, .g = 0xbe, .b = 0xbe}; + PreviewImage::PreviewImage(const DiffractionExperiment &experiment) : xpixel(experiment.GetXPixelsNum()), ypixel(experiment.GetYPixelsNum()), @@ -16,7 +38,7 @@ PreviewImage::PreviewImage(const DiffractionExperiment &experiment) : pixel_depth_bytes(experiment.GetPixelDepth()), pixel_is_signed(experiment.IsPixelSigned()), uncompressed_image(experiment.GetPixelsNum() * experiment.GetPixelDepth()), - roi_mask(experiment.ROI().GetROIMap()), + roi_map(experiment.ROI()), counter(experiment.GetPreviewPeriod()) {} void PreviewImage::UpdateImage(const void *in_uncompressed_image, @@ -28,46 +50,40 @@ void PreviewImage::UpdateImage(const void *in_uncompressed_image, } } -#define JFJOCH_JPEG_BASE_R (0x3f) -#define JFJOCH_JPEG_BASE_G (0x51) -#define JFJOCH_JPEG_BASE_B (0xb5) - void colormap(std::vector& ret, float v, size_t pixel) { if ((v < 0.0) || (v > 1.0)) { - ret[pixel * 3] = 0xBE; - ret[pixel * 3+1] = 0xBE; - ret[pixel * 3+2] = 0xBE; + ret[pixel * 3] = gray.r; + ret[pixel * 3+1] = gray.g; + ret[pixel * 3+2] = gray.b; } else { - ret[pixel * 3] = 255 - std::lround((255-JFJOCH_JPEG_BASE_R) * v); - ret[pixel * 3 + 1] = 255 - std::lround((255-JFJOCH_JPEG_BASE_G) * v); - ret[pixel * 3 + 2] = 255 - std::lround((255-JFJOCH_JPEG_BASE_B) * v); + ret[pixel * 3] = 255 - std::lround((255-indigo.r) * v); + ret[pixel * 3 + 1] = 255 - std::lround((255-indigo.g) * v); + ret[pixel * 3 + 2] = 255 - std::lround((255-indigo.b) * v); } } -void roi(std::vector& ret, size_t pixel) { - ret[pixel * 3] = 0x7B; - ret[pixel * 3+1] = 0x1F; - ret[pixel * 3+2] = 0xA2; -} - -void feature(std::vector& ret, size_t pixel) { - ret[pixel * 3] = 0xE9; - ret[pixel * 3+1] = 0x1E; - ret[pixel * 3+2] = 0x63; -} - -void spot(std::vector& ret, int64_t pixel, bool indexed) { - if (indexed) { - ret[pixel * 3] = 0xE9; - ret[pixel * 3 + 1] = 0x1E; - ret[pixel * 3 + 2] = 0x63; - } else { - ret[pixel * 3] = 0xCD; - ret[pixel * 3 + 1] = 0xDC; - ret[pixel * 3 + 2] = 0x39; +void PreviewImage::color_pixel(std::vector &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) * 3] = color.r; + ret[(in_ypixel * xpixel + in_xpixel) * 3 + 1] = color.g; + ret[(in_ypixel * xpixel + in_xpixel) * 3 + 2] = color.b; } } +void PreviewImage::spot(std::vector &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 &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]); +} + +void PreviewImage::beam_center_mark(std::vector &ret, int64_t in_xpixel, int64_t in_ypixel) const { + color_pixel(ret, in_xpixel, in_ypixel, lime); +} template std::vector GenerateRGB(const T* value, size_t npixel, uint32_t saturation_value, T special_value) { @@ -162,45 +178,6 @@ std::string PreviewImage::GenerateTIFFDioptas() const { return WriteTIFFToString(vec.data(), xpixel, ypixel, 2, false); } -std::string PreviewImage::GenerateTestJPEG(const PreviewJPEGSettings &settings) { - std::vector v; - size_t xpixel = 1030 * 3 + 2 * 8; - size_t ypixel = 514 * 6 + 5 * 36; - - std::vector uncompressed_image(xpixel * ypixel, 5); - - for (int y = 0; y < ypixel; y++) { - for (int gap = 1; gap <= 2; gap++) { - for (int pixel = 0; pixel < 8; pixel++) - uncompressed_image[y * xpixel + 1030 * gap + 8 * (gap - 1) + pixel] = INT16_MIN; - } - } - - for (int gap = 1; gap <= 5; gap++) { - for (int pixel = 0; pixel < 36; pixel++) { - for (int x = 0; x < xpixel; x++) - uncompressed_image[(gap * 514 + 36 * (gap - 1) + pixel) * xpixel + x] = INT16_MIN; - } - } - - v = GenerateRGB((int16_t *) uncompressed_image.data(), xpixel * ypixel, settings.saturation_value, - INT16_MIN); - - size_t beam_x_int = std::lround(1200); - size_t beam_y_int = std::lround(800); - - 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++) { - feature(v, (beam_y_int + w) * xpixel + beam_x_int + i); - feature(v, (beam_y_int + i) * xpixel + beam_x_int + w); - } - } - - return WriteJPEGToMem(v, xpixel, ypixel, settings.jpeg_quality); -} - void PreviewImage::AddBeamCenter(std::vector &rgb_image) const { size_t beam_x_int = std::lround(beam_x); size_t beam_y_int = std::lround(beam_y); @@ -209,10 +186,8 @@ void PreviewImage::AddBeamCenter(std::vector &rgb_image) const { int crosshair_width = 3; for (int w = -crosshair_width; w <= crosshair_width; w++) { for (int i = -crosshair_size; i <= crosshair_size; i++) { - if ((beam_y_int + w < ypixel) && (beam_x_int + i < xpixel)) - feature(rgb_image, (beam_y_int + w) * xpixel + beam_x_int + i); - if ((beam_y_int + i < ypixel) && (beam_x_int + w < xpixel)) - feature(rgb_image, (beam_y_int + i) * xpixel + beam_x_int + w); + beam_center_mark(rgb_image, beam_x_int + i, beam_y_int + w); + beam_center_mark(rgb_image, beam_x_int + w, beam_y_int + i); } } } @@ -225,29 +200,55 @@ void PreviewImage::AddSpots(std::vector &rgb_image) const { int rectangle_size = 4; int rectangle_width = 3; - if ((spot_x_int - rectangle_width < 0) - || (spot_x_int + rectangle_width >= xpixel) - || (spot_y_int - rectangle_width < 0) - || (spot_y_int + rectangle_width >= ypixel)) - continue; - for (int z = rectangle_size; z < rectangle_size + rectangle_width; z++) { for (int w = -z; w <= z; w++) { - spot(rgb_image, (spot_y_int + w) * xpixel + spot_x_int + z, s.indexed); - spot(rgb_image, (spot_y_int + w) * xpixel + spot_x_int - z, s.indexed); - spot(rgb_image, (spot_y_int + z) * xpixel + spot_x_int + w, s.indexed); - spot(rgb_image, (spot_y_int - z) * xpixel + spot_x_int - w, s.indexed); + 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_image) const { - if (rgb_image.size() != roi_mask.size() * 3) - throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in array size"); + int64_t roi_counter = 0; - for (int i = 0; i < roi_mask.size(); i++) { - if (roi_mask[i] != UINT16_MAX) - roi(rgb_image, i); + for (const auto &box: roi_map.GetROIBox()) { + 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: roi_map.GetROICircle()) { + 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++; } } diff --git a/preview/PreviewImage.h b/preview/PreviewImage.h index e43d83ef..e0fc9bfd 100644 --- a/preview/PreviewImage.h +++ b/preview/PreviewImage.h @@ -18,10 +18,16 @@ struct PreviewJPEGSettings { bool show_indexed = false; }; +struct rgb { + unsigned char r; + unsigned char g; + unsigned char b; +}; + class PreviewImage { mutable std::mutex m; - std::vector roi_mask; + const ROIMap roi_map; std::vector uncompressed_image; std::vector spots; size_t xpixel; @@ -35,13 +41,18 @@ class PreviewImage { void AddSpots(std::vector &rgb_image) const; void AddROI(std::vector &rgb_image) const; PreviewCounter counter; + + void color_pixel(std::vector& ret, int64_t xpixel, int64_t ypixel, const rgb &color) const; + void spot(std::vector& ret, int64_t xpixel, int64_t ypixel, bool indexed) const; + void roi(std::vector& ret, int64_t xpixel, int64_t ypixel, int64_t roi_number) const; + void beam_center_mark(std::vector& ret, int64_t xpixel, int64_t ypixel) const; + public: explicit PreviewImage(const DiffractionExperiment& experiment); void UpdateImage(const void *uncompressed_image, const std::vector &spots); [[nodiscard]] std::string GenerateJPEG(const PreviewJPEGSettings& settings) const; [[nodiscard]] std::string GenerateTIFF() const; [[nodiscard]] std::string GenerateTIFFDioptas() const; - [[nodiscard]] static std::string GenerateTestJPEG(const PreviewJPEGSettings& settings); }; #endif //JUNGFRAUJOCH_PREVIEWIMAGE_H diff --git a/tests/JPEGTest.cpp b/tests/JPEGTest.cpp index f076f747..0f580eb8 100644 --- a/tests/JPEGTest.cpp +++ b/tests/JPEGTest.cpp @@ -79,7 +79,9 @@ TEST_CASE("PreviewImage_GenerateJPEG_ROI","[JPEG]") { .DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4) .SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90}); - experiment.ROI().SetROIBox({ROIBox("roi0", 800, 1200, 800, 1200)}); + experiment.ROI().SetROIBox({ROIBox("roi1", 0, 20, 0, 20), + ROIBox("roi0", 800, 1200, 800, 1200), + ROIBox("roi3", 2000, 2067, 2000, 2163)}); experiment.ROI().SetROICircle({ROICircle("roi2", 500, 500, 50)}); // Load example image