/************************************************ * @file Cluster.hpp * @short definition of cluster, where CoordType (x,y) give * the cluster center coordinates and data the actual cluster data * cluster size is given as template parameters ***********************************************/ #pragma once #include "logger.hpp" #include #include #include #include #include namespace aare { // requires clause c++20 maybe update template struct Cluster { static_assert(std::is_arithmetic_v, "T needs to be an arithmetic type"); static_assert(std::is_integral_v, "CoordType needs to be an integral type"); static_assert(ClusterSizeX > 0 && ClusterSizeY > 0, "Cluster sizes must be bigger than zero"); CoordType x; CoordType y; std::array data; static constexpr uint8_t cluster_size_x = ClusterSizeX; static constexpr uint8_t cluster_size_y = ClusterSizeY; using value_type = T; using coord_type = CoordType; T sum() const { return std::accumulate(data.begin(), data.end(), T{}); } std::pair max_sum_2x2() const { if constexpr (cluster_size_x == 3 && cluster_size_y == 3) { std::array sum_2x2_subclusters; sum_2x2_subclusters[0] = data[0] + data[1] + data[3] + data[4]; sum_2x2_subclusters[1] = data[1] + data[2] + data[4] + data[5]; sum_2x2_subclusters[2] = data[3] + data[4] + data[6] + data[7]; sum_2x2_subclusters[3] = data[4] + data[5] + data[7] + data[8]; int index = std::max_element(sum_2x2_subclusters.begin(), sum_2x2_subclusters.end()) - sum_2x2_subclusters.begin(); return std::make_pair(sum_2x2_subclusters[index], index); } else if constexpr (cluster_size_x == 2 && cluster_size_y == 2) { return std::make_pair(data[0] + data[1] + data[2] + data[3], 0); } else { constexpr size_t num_2x2_subclusters = (ClusterSizeX - 1) * (ClusterSizeY - 1); std::array sum_2x2_subcluster; for (size_t i = 0; i < ClusterSizeY - 1; ++i) { for (size_t j = 0; j < ClusterSizeX - 1; ++j) sum_2x2_subcluster[i * (ClusterSizeX - 1) + j] = data[i * ClusterSizeX + j] + data[i * ClusterSizeX + j + 1] + data[(i + 1) * ClusterSizeX + j] + data[(i + 1) * ClusterSizeX + j + 1]; } int index = std::max_element(sum_2x2_subcluster.begin(), sum_2x2_subcluster.end()) - sum_2x2_subcluster.begin(); return std::make_pair(sum_2x2_subcluster[index], index); } } }; /** * @brief Reduce a cluster to a 2x2 cluster by selecting the 2x2 block with the * highest sum. * @param c Cluster to reduce * @return reduced cluster */ template Cluster reduce_to_2x2(const Cluster &c) { static_assert(ClusterSizeX >= 2 && ClusterSizeY >= 2, "Cluster sizes must be at least 2x2 for reduction to 2x2"); // TODO maybe add sanity check and check that center is in max subcluster Cluster result; auto [sum, index] = c.max_sum_2x2(); int16_t cluster_center_index = (ClusterSizeX / 2) + (ClusterSizeY / 2) * ClusterSizeX; int16_t index_bottom_left_max_2x2_subcluster = (int(index / (ClusterSizeX - 1))) * ClusterSizeX + index % (ClusterSizeX - 1); result.x = c.x + (index_bottom_left_max_2x2_subcluster - cluster_center_index) % ClusterSizeX; result.y = c.y - (index_bottom_left_max_2x2_subcluster - cluster_center_index) / ClusterSizeX; result.data = { c.data[index_bottom_left_max_2x2_subcluster], c.data[index_bottom_left_max_2x2_subcluster + 1], c.data[index_bottom_left_max_2x2_subcluster + ClusterSizeX], c.data[index_bottom_left_max_2x2_subcluster + ClusterSizeX + 1]}; return result; } template Cluster reduce_to_2x2(const Cluster &c) { Cluster result; auto [s, i] = c.max_sum_2x2(); switch (i) { case 0: result.x = c.x - 1; result.y = c.y + 1; result.data = {c.data[0], c.data[1], c.data[3], c.data[4]}; break; case 1: result.x = c.x; result.y = c.y + 1; result.data = {c.data[1], c.data[2], c.data[4], c.data[5]}; break; case 2: result.x = c.x - 1; result.y = c.y; result.data = {c.data[3], c.data[4], c.data[6], c.data[7]}; break; case 3: result.x = c.x; result.y = c.y; result.data = {c.data[4], c.data[5], c.data[7], c.data[8]}; break; } return result; } template inline std::pair max_3x3_sum(const Cluster &cluster) { if constexpr (ClusterSizeX == 3 && ClusterSizeY == 3) { return std::make_pair(cluster.sum(), 0); } else { size_t index = 0; T max_3x3_subcluster_sum = 0; for (size_t i = 0; i < ClusterSizeY - 2; ++i) { for (size_t j = 0; j < ClusterSizeX - 2; ++j) { T sum = cluster.data[i * ClusterSizeX + j] + cluster.data[i * ClusterSizeX + j + 1] + cluster.data[i * ClusterSizeX + j + 2] + cluster.data[(i + 1) * ClusterSizeX + j] + cluster.data[(i + 1) * ClusterSizeX + j + 1] + cluster.data[(i + 1) * ClusterSizeX + j + 2] + cluster.data[(i + 2) * ClusterSizeX + j] + cluster.data[(i + 2) * ClusterSizeX + j + 1] + cluster.data[(i + 2) * ClusterSizeX + j + 2]; if (sum > max_3x3_subcluster_sum) { max_3x3_subcluster_sum = sum; index = i * (ClusterSizeX - 2) + j; } } } return std::make_pair(max_3x3_subcluster_sum, index); } } /** * @brief Reduce a cluster to a 3x3 cluster by selecting the 3x3 block with the * highest sum. * @param c Cluster to reduce * @return reduced cluster */ template Cluster reduce_to_3x3(const Cluster &c) { static_assert(ClusterSizeX >= 3 && ClusterSizeY >= 3, "Cluster sizes must be at least 3x3 for reduction to 3x3"); Cluster result; // TODO maybe add sanity check and check that center is in max subcluster auto [sum, index] = max_3x3_sum(c); int16_t cluster_center_index = (ClusterSizeX / 2) + (ClusterSizeY / 2) * ClusterSizeX; int16_t index_center_max_3x3_subcluster = (int(index / (ClusterSizeX - 2))) * ClusterSizeX + ClusterSizeX + index % (ClusterSizeX - 2) + 1; int16_t index_3x3_subcluster_cluster_center = int((cluster_center_index - 1 - ClusterSizeX) / ClusterSizeX) * (ClusterSizeX - 2) + (cluster_center_index - 1 - ClusterSizeX) % ClusterSizeX; result.x = c.x + (index % (ClusterSizeX - 2) - (index_3x3_subcluster_cluster_center % (ClusterSizeX - 2))); result.y = c.y - (index / (ClusterSizeX - 2) - (index_3x3_subcluster_cluster_center / (ClusterSizeX - 2))); result.data = {c.data[index_center_max_3x3_subcluster - ClusterSizeX - 1], c.data[index_center_max_3x3_subcluster - ClusterSizeX], c.data[index_center_max_3x3_subcluster - ClusterSizeX + 1], c.data[index_center_max_3x3_subcluster - 1], c.data[index_center_max_3x3_subcluster], c.data[index_center_max_3x3_subcluster + 1], c.data[index_center_max_3x3_subcluster + ClusterSizeX - 1], c.data[index_center_max_3x3_subcluster + ClusterSizeX], c.data[index_center_max_3x3_subcluster + ClusterSizeX + 1]}; return result; } // Type Traits for is_cluster_type template struct is_cluster : std::false_type {}; // Default case: Not a Cluster template struct is_cluster> : std::true_type {}; // Cluster template constexpr bool is_cluster_v = is_cluster::value; } // namespace aare