MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Morphology.cpp
Go to the documentation of this file.
1#include "Morphology.hpp"
2
3#include <execution>
4#include <Eigen/Core>
5
7
8namespace {
9
10 inline bool is_fg(float v) noexcept { return v >= 0.5F; }
11
12 std::vector<float> morph_op(
13 std::span<const float> mask, uint32_t w, uint32_t h,
14 uint32_t radius, bool dilating)
15 {
16 const size_t n = static_cast<size_t>(w) * h;
17 const auto r = static_cast<int32_t>(radius);
18 std::vector<float> out(n);
19
20 std::for_each(std::execution::par_unseq,
21 std::views::iota(size_t { 0 }, n).begin(),
22 std::views::iota(size_t { 0 }, n).end(),
23 [&](size_t idx) {
24 const auto px = static_cast<int32_t>(idx % w);
25 const auto py = static_cast<int32_t>(idx / w);
26
27 bool result = !dilating;
28
29 for (int32_t dy = -r; dy <= r && result == !dilating; ++dy) {
30 const int32_t ny = std::clamp(py + dy, 0, static_cast<int32_t>(h) - 1);
31 for (int32_t dx = -r; dx <= r && result == !dilating; ++dx) {
32 const int32_t nx = std::clamp(px + dx, 0, static_cast<int32_t>(w) - 1);
33 const bool fg = is_fg(mask[static_cast<size_t>(ny) * w + nx]);
34 if (dilating && fg) {
35 result = true;
36 } else if (!dilating && !fg) {
37 result = false;
38 }
39 }
40 }
41
42 out[idx] = result ? 1.0F : 0.0F;
43 });
44
45 return out;
46 }
47
48 void morph_op_into(
49 std::span<const float> mask, std::span<float> out,
50 uint32_t w, uint32_t h, uint32_t radius, bool dilating)
51 {
52 const size_t n = static_cast<size_t>(w) * h;
53 const auto r = static_cast<int32_t>(radius);
54
55 std::for_each(std::execution::par_unseq,
56 std::views::iota(size_t { 0 }, n).begin(),
57 std::views::iota(size_t { 0 }, n).end(),
58 [&](size_t idx) {
59 const auto px = static_cast<int32_t>(idx % w);
60 const auto py = static_cast<int32_t>(idx / w);
61 bool result = !dilating;
62
63 for (int32_t dy = -r; dy <= r && result == !dilating; ++dy) {
64 const int32_t ny = std::clamp(py + dy, 0, static_cast<int32_t>(h) - 1);
65
66 for (int32_t dx = -r; dx <= r && result == !dilating; ++dx) {
67 const int32_t nx = std::clamp(px + dx, 0, static_cast<int32_t>(w) - 1);
68 const bool fg = is_fg(mask[static_cast<size_t>(ny) * w + nx]);
69
70 if (dilating && fg) {
71 result = true;
72 } else if (!dilating && !fg) {
73 result = false;
74 }
75 }
76 }
77 out[idx] = result ? 1.0F : 0.0F;
78 });
79 }
80
81} // namespace
82
83void erode(std::span<const float> mask, std::span<float> dst,
84 uint32_t w, uint32_t h, uint32_t radius)
85{
86 morph_op_into(mask, dst, w, h, radius, false);
87}
88
89std::vector<float> erode(std::span<const float> mask, uint32_t w, uint32_t h, uint32_t radius)
90{
91 std::vector<float> out(static_cast<size_t>(w) * h);
92 erode(mask, out, w, h, radius);
93 return out;
94}
95
96void dilate(std::span<const float> mask, std::span<float> dst,
97 uint32_t w, uint32_t h, uint32_t radius)
98{
99 morph_op_into(mask, dst, w, h, radius, true);
100}
101
102std::vector<float> dilate(std::span<const float> mask, uint32_t w, uint32_t h, uint32_t radius)
103{
104 std::vector<float> out(static_cast<size_t>(w) * h);
105 dilate(mask, out, w, h, radius);
106 return out;
107}
108
109void open(std::span<const float> mask, std::span<float> tmp, std::span<float> dst,
110 uint32_t w, uint32_t h, uint32_t radius)
111{
112 erode(mask, tmp, w, h, radius);
113 dilate(tmp, dst, w, h, radius);
114}
115
116std::vector<float> open(std::span<const float> mask, uint32_t w, uint32_t h, uint32_t radius)
117{
118 std::vector<float> tmp(static_cast<size_t>(w) * h);
119 std::vector<float> out(tmp.size());
120 open(mask, tmp, out, w, h, radius);
121 return out;
122}
123
124void close(std::span<const float> mask, std::span<float> tmp, std::span<float> dst,
125 uint32_t w, uint32_t h, uint32_t radius)
126{
127 dilate(mask, tmp, w, h, radius);
128 erode(tmp, dst, w, h, radius);
129}
130
131std::vector<float> close(std::span<const float> mask, uint32_t w, uint32_t h, uint32_t radius)
132{
133 std::vector<float> tmp(static_cast<size_t>(w) * h);
134 std::vector<float> out(tmp.size());
135 close(mask, tmp, out, w, h, radius);
136 return out;
137}
138
139void morph_gradient(std::span<const float> mask, std::span<float> tmp, std::span<float> dst,
140 uint32_t w, uint32_t h, uint32_t radius)
141{
142 const auto en = static_cast<Eigen::Index>(static_cast<size_t>(w) * h);
143 dilate(mask, tmp, w, h, radius);
144 erode(mask, dst, w, h, radius);
145
146 Eigen::Map<Eigen::ArrayXf>(dst.data(), en) = Eigen::Map<const Eigen::ArrayXf>(tmp.data(), en)
147 - Eigen::Map<const Eigen::ArrayXf>(dst.data(), en);
148}
149
150std::vector<float> morph_gradient(std::span<const float> mask, uint32_t w, uint32_t h, uint32_t radius)
151{
152 std::vector<float> tmp(static_cast<size_t>(w) * h);
153 std::vector<float> out(tmp.size());
154 morph_gradient(mask, tmp, out, w, h, radius);
155 return out;
156}
157
158} // namespace MayaFlux::Kinesis::Vision
float radius
uint32_t h
Definition InkPress.cpp:29
uint32_t * dst
std::vector< float > * out
uint32_t py
uint32_t px
Binary morphological operations on normalised float masks.
void erode(std::span< const float > mask, std::span< float > dst, uint32_t w, uint32_t h, uint32_t radius)
Erosion writing into dst.
void close(std::span< const float > mask, std::span< float > tmp, std::span< float > dst, uint32_t w, uint32_t h, uint32_t radius)
Morphological closing writing into dst.
void dilate(std::span< const float > mask, std::span< float > dst, uint32_t w, uint32_t h, uint32_t radius)
Dilation writing into dst.
void morph_gradient(std::span< const float > mask, std::span< float > tmp, std::span< float > dst, uint32_t w, uint32_t h, uint32_t radius)
Morphological gradient (dilate - erode) writing into dst.
void open(std::span< const float > mask, std::span< float > tmp, std::span< float > dst, uint32_t w, uint32_t h, uint32_t radius)
Morphological opening writing into dst.