From bbd0b6ef360bc1d9d0c9a860680db3428970ef0b Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Fri, 23 Jun 2023 12:38:34 +0200 Subject: [PATCH] ROIFilter: Add filter to only preserve ROI regions --- common/CMakeLists.txt | 3 +- common/FrameTransformation.cpp | 7 ++ common/FrameTransformation.h | 2 + common/ROIFilter.h | 63 ++++++++++++++++ tests/CMakeLists.txt | 2 +- tests/FrameTransformationTest.cpp | 116 ++++++++++++++++++++++++++++++ tests/ROIFilterTest.cpp | 29 ++++++++ 7 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 common/ROIFilter.h create mode 100644 tests/ROIFilterTest.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a60406f2..c7272c3c 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -43,7 +43,8 @@ ADD_LIBRARY( CommonFunctions STATIC grpcToJson.h jsonToGrpc.h to_fixed.h DetectorGeometry.cpp DetectorGeometry.h DetectorModuleGeometry.cpp DetectorModuleGeometry.h - DetectorSetup.h DetectorSetup.cpp ZeroCopyReturnValue.h Histogram.h DiffractionGeometry.h) + DetectorSetup.h DetectorSetup.cpp ZeroCopyReturnValue.h Histogram.h DiffractionGeometry.h + ROIFilter.h) TARGET_LINK_LIBRARIES(CommonFunctions Compression FrameSerialize libzmq JFCalibration JFJochProtoBuf -lrt) diff --git a/common/FrameTransformation.cpp b/common/FrameTransformation.cpp index 14db8989..25ac9afe 100644 --- a/common/FrameTransformation.cpp +++ b/common/FrameTransformation.cpp @@ -143,6 +143,13 @@ void FrameTransformation::ProcessModule(const int16_t *input, uint16_t module_nu } } +void FrameTransformation::ApplyROI(const ROIFilter &filter) { + if (pixel_depth == 2) + filter.Apply((int16_t *) precompression_buffer.data(), static_cast(INT16_MIN)); + else + filter.Apply((int32_t *) precompression_buffer.data(), static_cast(INT32_MIN)); +} + int16_t *FrameTransformation::GetPreview16BitImage() { if (pixel_depth == 2) return (int16_t *) precompression_buffer.data(); diff --git a/common/FrameTransformation.h b/common/FrameTransformation.h index 99ace14f..e86eb18e 100644 --- a/common/FrameTransformation.h +++ b/common/FrameTransformation.h @@ -7,6 +7,7 @@ #include "DiffractionExperiment.h" #include "../compression/JFJochCompressor.h" #include "../jungfrau/JFConversion.h" +#include "ROIFilter.h" class FrameTransformation { const DiffractionExperiment& experiment; @@ -31,6 +32,7 @@ public: int data_stream); void Pack(); // transfer summed image to converted coordinates, clear summation buffer size_t SaveCompressedImage(void *output); + void ApplyROI(const ROIFilter &filter); int16_t *GetPreview16BitImage(); }; diff --git a/common/ROIFilter.h b/common/ROIFilter.h new file mode 100644 index 00000000..eab8fefe --- /dev/null +++ b/common/ROIFilter.h @@ -0,0 +1,63 @@ +// Copyright (2019-2023) Paul Scherrer Institute +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef JUNGFRAUJOCH_ROIFILTER_H +#define JUNGFRAUJOCH_ROIFILTER_H + +#include +#include +#include +#include "JFJochException.h" + +class ROIFilter { + size_t width, height; + std::vector mask; +public: + ROIFilter(size_t in_width, size_t in_height, uint8_t fill_value = 0) + : width(in_width), height(in_height), mask (in_width * in_height, fill_value) {} + + void SetRectangle(size_t x0, size_t y0, size_t in_width, size_t in_height, uint8_t fill_value = 1) { + if (y0 + in_height > height) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Mismatch in array size"); + if (x0 + in_width > width) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Mismatch in array size"); + + for (size_t y = y0; y < y0 + in_height; y++) { + for (size_t x = x0; x < x0 + in_width; x++) { + mask[y * width + x] |= fill_value; + } + } + } + + + void ClearRectangle(size_t x0, size_t y0, size_t in_width, size_t in_height, uint8_t fill_value = 1) { + if (y0 + in_height > height) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Mismatch in array size"); + if (x0 + in_width > width) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Mismatch in array size"); + + for (size_t y = y0; y < y0 + in_height; y++) { + for (size_t x = x0; x < x0 + in_width; x++) { + mask[y * width + x] &= ~fill_value; + } + } + } + + template + void Apply(T* data, T fill_value) const { + for (size_t i = 0; i < mask.size(); i++) { + if (mask[i] == 0) + data[i] = fill_value; + } + } + + template + void Apply(std::vector &data, T fill_value) const { + if (data.size() != mask.size()) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Mismatch in array size"); + Apply(data.data(), fill_value); + } +}; + + +#endif //JUNGFRAUJOCH_ROIFILTER_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ecf9ba06..e589acd7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,7 +25,7 @@ ADD_EXECUTABLE(CatchTest RadialIntegrationTest.cpp StatusVectorTest.cpp ProcessRawPacketTest.cpp CBORTest.cpp ../tests/stream2.h ../tests/stream2.c - JFConversionTest.cpp DetectorGeometryTest.cpp JFJochBrokerParserTest.cpp DetectorSetupTest.cpp DiffractionGeometryTest.cpp) + JFConversionTest.cpp DetectorGeometryTest.cpp JFJochBrokerParserTest.cpp DetectorSetupTest.cpp DiffractionGeometryTest.cpp ROIFilterTest.cpp) target_link_libraries(CatchTest JFJochBroker JFJochReceiver JFJochWriter ImageAnalysis CommonFunctions HLSSimulation) target_include_directories(CatchTest PRIVATE .) diff --git a/tests/FrameTransformationTest.cpp b/tests/FrameTransformationTest.cpp index 5b02bc04..bf7c94f9 100644 --- a/tests/FrameTransformationTest.cpp +++ b/tests/FrameTransformationTest.cpp @@ -168,6 +168,122 @@ TEST_CASE("FrameTransformation_Converted_bshuf_lz4" ,"") { REQUIRE(input_1[(511+512)*1024 + 800] == output[CONVERTED_MODULE_SIZE * (nmodules - 2) + 1030 + 800 + 6]); } + +TEST_CASE("FrameTransformation_Converted_bshuf_lz4_roi" ,"") { + const uint16_t nmodules = 4; + const uint16_t ndatastreams = 2; + DiffractionExperiment experiment(DetectorGeometry(ndatastreams * nmodules, 2)); + experiment.DataStreams(ndatastreams); + + experiment.Mode(DetectorMode::Conversion).Compression(JFJochProtoBuf::BSHUF_LZ4); + + FrameTransformation transformation(experiment); + + ROIFilter filter(experiment.GetXPixelsNum(), experiment.GetYPixelsNum(), 0); + filter.SetRectangle(100, 20, 10, 10); + + std::vector input_0(nmodules*RAW_MODULE_SIZE); + for (int i = 0; i < nmodules*RAW_MODULE_SIZE; i++) + input_0[i] = 50; + + std::vector input_1(nmodules*RAW_MODULE_SIZE); + for (int i = 0; i < nmodules*RAW_MODULE_SIZE; i++) + input_1[i] = 50; + + std::vector output_compressed(experiment.GetMaxCompressedSize()); + + for (int i = 0; i < nmodules; i++) { + REQUIRE_NOTHROW(transformation.ProcessModule(input_0.data() + i * RAW_MODULE_SIZE, i, 0)); + REQUIRE_NOTHROW(transformation.ProcessModule(input_1.data() + i * RAW_MODULE_SIZE, i, 1)); + } + + size_t compressed_size; + transformation.Pack(); + transformation.ApplyROI(filter); + REQUIRE_NOTHROW(compressed_size = transformation.SaveCompressedImage(output_compressed.data())); + output_compressed.resize(compressed_size); + + REQUIRE(bshuf_read_uint64_BE(output_compressed.data()) == experiment.GetPixelsNum() * experiment.GetPixelDepth()); + REQUIRE(bshuf_read_uint32_BE(output_compressed.data()+8) == JFJochBitShuffleCompressor::DefaultBlockSize * experiment.GetPixelDepth()); + + std::vector output; + REQUIRE_NOTHROW(JFJochDecompress(output, experiment.GetCompressionAlgorithmEnum(), output_compressed, + experiment.GetPixelsNum())); + + size_t diff = 0; + for (int y = 0; y < experiment.GetYPixelsNum(); y++) { + for (int x = 0; x < experiment.GetXPixelsNum(); x++) { + if ((y >= 20 ) && (y < 30) && (x >= 100) && (x < 110)) { + if (output[y * experiment.GetXPixelsNum() + x] != 50) + diff++; + } else { + if (output[y * experiment.GetXPixelsNum() + x] != INT16_MIN) + diff++; + } + } + } + REQUIRE(diff == 0); +} + +TEST_CASE("FrameTransformation_Converted_bshuf_lz4_roi_summation" ,"") { + const uint16_t nmodules = 4; + const uint16_t ndatastreams = 2; + const uint16_t nframes = 4; + DiffractionExperiment experiment(DetectorGeometry(ndatastreams * nmodules, 2)); + experiment.DataStreams(ndatastreams); + + experiment.Mode(DetectorMode::Conversion).Compression(JFJochProtoBuf::BSHUF_LZ4).Summation(nframes); + + FrameTransformation transformation(experiment); + + ROIFilter filter(experiment.GetXPixelsNum(), experiment.GetYPixelsNum(), 0); + filter.SetRectangle(100, 20, 10, 10); + + std::vector input_0(nmodules*RAW_MODULE_SIZE); + for (int i = 0; i < nmodules*RAW_MODULE_SIZE; i++) + input_0[i] = 50; + + std::vector input_1(nmodules*RAW_MODULE_SIZE); + for (int i = 0; i < nmodules*RAW_MODULE_SIZE; i++) + input_1[i] = 50; + + std::vector output_compressed(experiment.GetMaxCompressedSize()); + + for (int f = 0; f < nframes; f++) { + for (int i = 0; i < nmodules; i++) { + REQUIRE_NOTHROW(transformation.ProcessModule(input_0.data() + i * RAW_MODULE_SIZE, i, 0)); + REQUIRE_NOTHROW(transformation.ProcessModule(input_1.data() + i * RAW_MODULE_SIZE, i, 1)); + } + } + + size_t compressed_size; + transformation.Pack(); + transformation.ApplyROI(filter); + REQUIRE_NOTHROW(compressed_size = transformation.SaveCompressedImage(output_compressed.data())); + output_compressed.resize(compressed_size); + + REQUIRE(bshuf_read_uint64_BE(output_compressed.data()) == experiment.GetPixelsNum() * experiment.GetPixelDepth()); + REQUIRE(bshuf_read_uint32_BE(output_compressed.data()+8) == JFJochBitShuffleCompressor::DefaultBlockSize * experiment.GetPixelDepth()); + + std::vector output; + REQUIRE_NOTHROW(JFJochDecompress(output, experiment.GetCompressionAlgorithmEnum(), output_compressed, + experiment.GetPixelsNum())); + + size_t diff = 0; + for (int y = 0; y < experiment.GetYPixelsNum(); y++) { + for (int x = 0; x < experiment.GetXPixelsNum(); x++) { + if ((y >= 20 ) && (y < 30) && (x >= 100) && (x < 110)) { + if (output[y * experiment.GetXPixelsNum() + x] != nframes * 50) + diff++; + } else { + if (output[y * experiment.GetXPixelsNum() + x] != INT32_MIN) + diff++; + } + } + } + REQUIRE(diff == 0); +} + TEST_CASE("FrameTransformation_Converted_bshuf_zstd" ,"") { const uint16_t nmodules = 4; const uint16_t ndatastreams = 2; diff --git a/tests/ROIFilterTest.cpp b/tests/ROIFilterTest.cpp new file mode 100644 index 00000000..4d9738ed --- /dev/null +++ b/tests/ROIFilterTest.cpp @@ -0,0 +1,29 @@ +// Copyright (2019-2022) Paul Scherrer Institute +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "../common/ROIFilter.h" + +TEST_CASE("ROIFilter") { + size_t width = 4; + size_t height = 5; + std::vector v(width * height, 1); + + ROIFilter filter(width, height); + + filter.SetRectangle(1, 1, 2, 3); + + filter.Apply(v, (uint32_t) 55); + + REQUIRE(v[0] == 55); + REQUIRE(v[2] == 55); + + REQUIRE(v[width * 1 + 0] == 55); + REQUIRE(v[width * 1 + 1] == 1); + REQUIRE(v[width * 1 + 2] == 1); + REQUIRE(v[width * 1 + 3] == 55); + + REQUIRE(v[width * 3 + 2] == 1); + REQUIRE(v[width * 4 + 2] == 55); +}