From 1fa04a8e8f5ef0fa9d2e30eaea0d94224300b5fb Mon Sep 17 00:00:00 2001 From: Alice Date: Wed, 5 Aug 2026 18:31:52 +0200 Subject: [PATCH] added functionality to merge rois --- CMakeLists.txt | 4 +- include/aare/defs.hpp | 5 ++ include/aare/utils/utility_functions.hpp | 92 ++++++++++++++++++++++++ src/utils/utility_functions.test.cpp | 57 +++++++++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 include/aare/utils/utility_functions.hpp create mode 100644 src/utils/utility_functions.test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f923dfc..cef492f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -414,7 +414,8 @@ set(PUBLICHEADERS include/aare/RawMasterFile.hpp include/aare/RawSubFile.hpp include/aare/VarClusterFinder.hpp - include/aare/utils/task.hpp) + include/aare/utils/task.hpp + include/aare/utils/utility_functions.hpp) set(SourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/src/calibration.cpp @@ -507,6 +508,7 @@ if(AARE_TESTS) ${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/utility_functions.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.test.cpp) target_sources(tests PRIVATE ${TestSources}) endif() diff --git a/include/aare/defs.hpp b/include/aare/defs.hpp index af73a84..668331f 100644 --- a/include/aare/defs.hpp +++ b/include/aare/defs.hpp @@ -186,6 +186,11 @@ struct ROI { bool contains(ssize_t x, ssize_t y) const { return x >= xmin && x < xmax && y >= ymin && y < ymax; } + + bool operator==(const ROI &other) const { + return xmin == other.xmin && xmax == other.xmax && ymin == other.ymin && + ymax == other.ymax; + } }; /// @brief Chip specifications for Matterhorn1 diff --git a/include/aare/utils/utility_functions.hpp b/include/aare/utils/utility_functions.hpp new file mode 100644 index 0000000..8b325c7 --- /dev/null +++ b/include/aare/utils/utility_functions.hpp @@ -0,0 +1,92 @@ +#pragma once +#include "aare/defs.hpp" +#include +#include +#include +#include + +namespace aare { + +/** + * @brief Merge all consecutive ROIs in a vector into a single ROI. + * @param rois vector of ROIs to merge + * @return vector of merged ROIs + * @tparam horizontally_aligned true if the ROIs are horizontally aligned, false + * otherwise + * @tparam vertically_aligned true if the ROIs are vertically aligned, false + * otherwise + */ +template +std::vector merge_consecutive_rois(std::vector &rois) { + + if constexpr (horizontally_aligned && vertically_aligned) { + throw std::runtime_error( + LOCATION + "Vector of the same ROI? Cannot merge ROIs both " + "horizontally and vertically at the same time."); + } + + if (rois.empty()) { + return {}; + } + if (rois.size() == 1) { + return rois; + } + + auto merge_along_x = [](std::vector in_rois) { + std::sort(in_rois.begin(), in_rois.end(), + [](const ROI &a, const ROI &b) { + return (a.ymin != b.ymin) ? (a.ymin < b.ymin) + : (a.xmin < b.xmin); + }); // N log (N) + + std::vector merged_rois; + merged_rois.reserve(in_rois.size()); + merged_rois.push_back(in_rois[0]); + + for (size_t i = 1; i < in_rois.size(); ++i) { + auto &last = merged_rois.back(); + const auto ¤t = in_rois[i]; + if (last.ymin == current.ymin && last.ymax == current.ymax && + last.xmax == current.xmin) { + // merge + last.xmax = current.xmax; + } else { + merged_rois.push_back(current); + } + } + return merged_rois; + }; + + auto merge_along_y = [](std::vector in_rois) { + std::sort(in_rois.begin(), in_rois.end(), + [](const ROI &a, const ROI &b) { + return (a.xmin != b.xmin) ? (a.xmin < b.xmin) + : (a.ymin < b.ymin); + }); + + std::vector merged_rois; + merged_rois.reserve(in_rois.size()); + merged_rois.push_back(in_rois[0]); + + for (size_t i = 1; i < in_rois.size(); ++i) { + auto &last = merged_rois.back(); + const auto ¤t = in_rois[i]; + if (last.xmin == current.xmin && last.xmax == current.xmax && + last.ymax == current.ymin) { + last.ymax = current.ymax; + } else { + merged_rois.push_back(current); + } + } + return merged_rois; + }; + + if constexpr (horizontally_aligned) { + return merge_along_y(rois); // one sort + one pass + } else if constexpr (vertically_aligned) { + return merge_along_x(rois); // one sort + one pass + } else { + return merge_along_y(merge_along_x(rois)); // generic case: two passes + } +} +} // namespace aare \ No newline at end of file diff --git a/src/utils/utility_functions.test.cpp b/src/utils/utility_functions.test.cpp new file mode 100644 index 0000000..a98fe6e --- /dev/null +++ b/src/utils/utility_functions.test.cpp @@ -0,0 +1,57 @@ +#include "aare/utils/utility_functions.hpp" +#include + +namespace aare { + +TEST_CASE("merge ROIs", "[utility_functions]") { + + SECTION("not fully contiguous") { + + std::vector rois = {ROI{20, 30, 50, 60}, ROI{20, 30, 40, 50}, + ROI{10, 20, 40, 50}}; + + auto merged_rois = merge_consecutive_rois(rois); + + REQUIRE(merged_rois.size() == 2); + + REQUIRE(merged_rois[0] == ROI{10, 30, 40, 50}); + REQUIRE(merged_rois[1] == ROI{20, 30, 50, 60}); + } + SECTION("complex merge") { + + std::vector rois = {ROI{40, 50, 20, 30}, ROI{10, 20, 30, 40}, + ROI{10, 20, 50, 60}, ROI{20, 30, 30, 40}, + ROI{60, 70, 30, 40}, ROI{60, 70, 20, 30}, + ROI{20, 30, 20, 30}, ROI{10, 20, 20, 30}}; + + auto merged_rois = merge_consecutive_rois(rois); + + REQUIRE(merged_rois.size() == 4); + + REQUIRE(merged_rois[0] == ROI{10, 30, 20, 40}); + REQUIRE(merged_rois[1] == ROI{10, 20, 50, 60}); + REQUIRE(merged_rois[2] == ROI{40, 50, 20, 30}); + REQUIRE(merged_rois[3] == ROI{60, 70, 20, 40}); + } + SECTION("horizontally aligned") { + std::vector rois = {ROI{10, 20, 30, 40}, ROI{10, 20, 50, 60}, + ROI{10, 20, 20, 30}}; + + auto merged_rois = merge_consecutive_rois(rois); + + REQUIRE(merged_rois.size() == 2); + REQUIRE(merged_rois[0] == ROI{10, 20, 20, 40}); + REQUIRE(merged_rois[1] == ROI{10, 20, 50, 60}); + } + SECTION("vertically aligned") { + std::vector rois = {ROI{10, 20, 30, 40}, ROI{30, 40, 30, 40}, + ROI{20, 30, 30, 40}}; + + auto merged_rois = merge_consecutive_rois(rois); + + REQUIRE(merged_rois.size() == 1); + REQUIRE(merged_rois[0] == ROI{10, 40, 30, 40}); + } +} + +} // namespace aare