3#include "../trees/MorphologicalTree.hpp"
4#include "../trees/detail/ProperPartEntryNode.hpp"
5#include "../utils/Common.hpp"
13namespace mmcfilters::local_events {
163 template<
class Policy>
166 const std::vector<WindowOffset>&
window,
168 requireValidInput(tree,
window);
170 std::vector<typename Policy::Bucket>
buckets(
172 std::vector<EntryEvent>
events;
178 for (std::size_t
i = 0;
i <
window.size(); ++
i) {
188 sortEventsBottomUp(tree,
events);
192 for (std::size_t
i = 0;
i <
events.size();) {
242 template<
class Policy>
245 const std::vector<WindowOffset>&
window,
247 std::vector<typename Policy::Bucket>
buckets =
259 throw std::invalid_argument(
"EventEngine requires a non-empty image domain.");
262 throw std::invalid_argument(
"EventEngine requires a live tree root.");
264 if (window.size() > 32) {
265 throw std::invalid_argument(
"EventEngine supports at most 32 window samples.");
278 static void sortEventsBottomUp(
const MorphologicalTree& tree, std::vector<EntryEvent>& events) {
279 std::sort(events.begin(), events.end(), [&](
const EntryEvent& lhs,
const EntryEvent& rhs) {
280 if (lhs.node == rhs.node) {
283 if (tree.isStrictAncestor(lhs.node, rhs.node)) {
286 if (tree.isStrictAncestor(rhs.node, lhs.node)) {
289 return lhs.node < rhs.node;
302 template<
class Bucket,
class Policy>
303 static void aggregateSubtreeBuckets(
304 const MorphologicalTree& tree,
305 std::vector<Bucket>& values,
306 const Policy& policy) {
307 std::vector<std::pair<NodeId, bool>> stack;
308 stack.emplace_back(tree.getRoot(),
false);
310 while (!stack.empty()) {
311 const auto [node, expanded] = stack.back();
314 stack.emplace_back(node,
true);
315 for (NodeId child : tree.getChildren(node)) {
316 stack.emplace_back(child,
false);
321 for (NodeId child : tree.getChildren(node)) {
322 policy.merge(values[
static_cast<std::size_t
>(node)], values[
static_cast<std::size_t
>(child)]);
329using WindowOffset = EventEngine::WindowOffset;
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
static std::pair< int, int > to2D(int index, int numCols) noexcept
Converts a row-major linear index to (row, col).
static int to1D(int row, int col, int numCols) noexcept
Converts (row, col) to a row-major linear index.
Mutable morphological tree built directly on proper parts and dense node ids.
int getNumInternalNodeSlots() const
Returns the size of the dense internal-node id domain.
bool isAlive(NodeId nodeId) const
Tests whether a node slot currently represents a live node.
NodeId getRoot() const
Returns the current hierarchy root.
int getNumColsOfImage() const
Returns the number of image columns in the attached 2D domain.
int getNumRowsOfImage() const
Returns the number of image rows in the attached 2D domain.
Generic event engine for finite binary local computations.
static std::vector< typename Policy::Bucket > computeDeltasWithPolicy(const MorphologicalTree &tree, const std::vector< WindowOffset > &window, const Policy &policy)
Computes local state-change deltas before subtree aggregation.
static NodeId entryNode(const MorphologicalTree &tree, int anchorPixel, int samplePixel)
Returns the first ancestor of anchorPixel that contains samplePixel.
static std::vector< typename Policy::Bucket > computeWithPolicy(const MorphologicalTree &tree, const std::vector< WindowOffset > &window, const Policy &policy)
Computes local counters with a caller-owned bucket policy.
static NodeId entryNode(const MorphologicalTree &tree, int anchorPixel, WindowOffset offset)
Returns the entry node for one translated sample around an anchor.
Owning result for one computed scalar attribute layout and buffer.
Relative integer offset of one sample in a local image window.
int colOffset
Column displacement from the anchor pixel.
int rowOffset
Row displacement from the anchor pixel.