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