Blender V4.3
COM_DilateErodeOperation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7namespace blender::compositor {
8
10{
14 inset_ = 0.0f;
15 switch_ = 0.5f;
16 distance_ = 0.0f;
17}
18
20{
21 if (distance_ < 0.0f) {
22 scope_ = -distance_ + inset_;
23 }
24 else {
25 if (inset_ * 2 > distance_) {
26 scope_ = std::max(inset_ * 2 - distance_, distance_);
27 }
28 else {
29 scope_ = distance_;
30 }
31 }
32 if (scope_ < 3) {
33 scope_ = 3;
34 }
35}
36
38 const rcti &output_area,
39 rcti &r_input_area)
40{
41 BLI_assert(input_idx == 0);
42 UNUSED_VARS_NDEBUG(input_idx);
43 r_input_area.xmin = output_area.xmin - scope_;
44 r_input_area.xmax = output_area.xmax + scope_;
45 r_input_area.ymin = output_area.ymin - scope_;
46 r_input_area.ymax = output_area.ymax + scope_;
47}
48
50 int x;
51 int y;
52 int xmin;
53 int xmax;
54 int ymin;
55 int ymax;
56 const float *elem;
57 float distance;
61 float sw;
62};
63
64template<template<typename> typename TCompare>
66{
67 /* TODO(manzanilla): bad performance, generate a table with relative offsets on operation
68 * initialization to loop from less to greater distance and break as soon as #compare is
69 * true. */
70 const TCompare compare;
71 float min_dist = p.distance;
72 const float *row = p.elem + (intptr_t(p.ymin) - p.y) * p.row_stride +
73 (intptr_t(p.xmin) - p.x) * p.elem_stride;
74 for (int yi = p.ymin; yi < p.ymax; yi++) {
75 const float dy = yi - p.y;
76 const float dist_y = dy * dy;
77 const float *elem = row;
78 for (int xi = p.xmin; xi < p.xmax; xi++) {
79 if (compare(*elem, p.sw)) {
80 const float dx = xi - p.x;
81 const float dist = dx * dx + dist_y;
82 min_dist = std::min(min_dist, dist);
83 }
84 elem += p.elem_stride;
85 }
86 row += p.row_stride;
87 }
88 return min_dist;
89}
90
92 const rcti &area,
94{
95 const MemoryBuffer *input = inputs[0];
96 const rcti &input_rect = input->get_rect();
97 const float rd = scope_ * scope_;
98 const float inset = inset_;
99
100 PixelData p;
101 p.sw = switch_;
102 p.distance = rd * 2;
103 p.elem_stride = input->elem_stride;
104 p.row_stride = input->row_stride;
105 for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
106 p.x = it.x;
107 p.y = it.y;
108 p.xmin = std::max(p.x - scope_, input_rect.xmin);
109 p.ymin = std::max(p.y - scope_, input_rect.ymin);
110 p.xmax = std::min(p.x + scope_, input_rect.xmax);
111 p.ymax = std::min(p.y + scope_, input_rect.ymax);
112 p.elem = it.in(0);
113
114 float pixel_value;
115 if (*p.elem > p.sw) {
116 pixel_value = -sqrtf(get_min_distance<std::less>(p));
117 }
118 else {
119 pixel_value = sqrtf(get_min_distance<std::greater>(p));
120 }
121
122 if (distance_ > 0.0f) {
123 const float delta = distance_ - pixel_value;
124 if (delta >= 0.0f) {
125 *it.out = delta >= inset ? 1.0f : delta / inset;
126 }
127 else {
128 *it.out = 0.0f;
129 }
130 }
131 else {
132 const float delta = -distance_ + pixel_value;
133 if (delta < 0.0f) {
134 *it.out = delta < -inset ? 1.0f : (-delta) / inset;
135 }
136 else {
137 *it.out = 0.0f;
138 }
139 }
140 }
141}
142
150
152{
154 if (scope_ < 3) {
155 scope_ = 3;
156 }
157}
158
160 const rcti &output_area,
161 rcti &r_input_area)
162{
163 BLI_assert(input_idx == 0);
164 UNUSED_VARS_NDEBUG(input_idx);
165 r_input_area.xmin = output_area.xmin - scope_;
166 r_input_area.xmax = output_area.xmax + scope_;
167 r_input_area.ymin = output_area.ymin - scope_;
168 r_input_area.ymax = output_area.ymax + scope_;
169}
170
172 int x;
173 int y;
174 int xmin;
175 int xmax;
176 int ymin;
177 int ymax;
178 const float *elem;
180 int scope;
184
185 PixelData(MemoryBuffer *input, const int distance, const int scope)
186 : min_distance(distance * distance),
187 scope(scope),
188 elem_stride(input->elem_stride),
189 row_stride(input->row_stride),
190 input_rect(input->get_rect())
191 {
192 }
193
195 {
196 x = it.x;
197 y = it.y;
198 xmin = std::max(x - scope, input_rect.xmin);
199 ymin = std::max(y - scope, input_rect.ymin);
200 xmax = std::min(x + scope, input_rect.xmax);
201 ymax = std::min(y + scope, input_rect.ymax);
202 elem = it.in(0);
203 }
204};
205
206template<template<typename> typename TCompare>
207static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value)
208{
209 /* TODO(manzanilla): bad performance, only loop elements within minimum distance removing
210 * coordinates and conditional if `dist <= min_dist`. May need to generate a table of offsets. */
211 const TCompare compare;
212 const float min_dist = p.min_distance;
213 float value = start_value;
214 const float *row = p.elem + (intptr_t(p.ymin) - p.y) * p.row_stride +
215 (intptr_t(p.xmin) - p.x) * p.elem_stride;
216 for (int yi = p.ymin; yi < p.ymax; yi++) {
217 const float dy = yi - p.y;
218 const float dist_y = dy * dy;
219 const float *elem = row;
220 for (int xi = p.xmin; xi < p.xmax; xi++) {
221 const float dx = xi - p.x;
222 const float dist = dx * dx + dist_y;
223 if (dist <= min_dist) {
224 value = compare(*elem, value) ? *elem : value;
225 }
226 elem += p.elem_stride;
227 }
228 row += p.row_stride;
229 }
230
231 return value;
232}
233
235 const rcti &area,
237{
238 PixelData p(inputs[0], distance_, scope_);
239 for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
240 p.update(it);
241 *it.out = get_distance_value<std::greater>(p, 0.0f);
242 }
243}
244
249
251 const rcti &area,
253{
254 PixelData p(inputs[0], distance_, scope_);
255 for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
256 p.update(it);
257 *it.out = get_distance_value<std::less>(p, 1.0f);
258 }
259}
260
266
268 const rcti &output_area,
269 rcti &r_input_area)
270{
271 BLI_assert(input_idx == 0);
272 UNUSED_VARS_NDEBUG(input_idx);
273 r_input_area.xmin = output_area.xmin - iterations_;
274 r_input_area.xmax = output_area.xmax + iterations_;
275 r_input_area.ymin = output_area.ymin - iterations_;
276 r_input_area.ymax = output_area.ymax + iterations_;
277}
278
279template<typename TCompareSelector>
281 const MemoryBuffer *input,
282 const rcti &area,
283 const int num_iterations,
284 const float compare_min_value)
285{
286 TCompareSelector selector;
287
288 const int width = output->get_width();
289 const int height = output->get_height();
290
291 const int half_window = num_iterations;
292 const int window = half_window * 2 + 1;
293
294 const int xmin = std::max(0, area.xmin - half_window);
295 const int ymin = std::max(0, area.ymin - half_window);
296 const int xmax = std::min(width, area.xmax + half_window);
297 const int ymax = std::min(height, area.ymax + half_window);
298
299 const int bwidth = area.xmax - area.xmin;
300 const int bheight = area.ymax - area.ymin;
301
302 /* NOTE: #result has area width, but new height.
303 * We have to calculate the additional rows in the first pass,
304 * to have valid data available for the second pass. */
305 rcti result_area;
306 BLI_rcti_init(&result_area, area.xmin, area.xmax, ymin, ymax);
307 MemoryBuffer result(DataType::Value, result_area);
308
309 /* #temp holds maxima for every step in the algorithm, #buf holds a
310 * single row or column of input values, padded with #limit values to
311 * simplify the logic. */
312 float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
313 float *buf = (float *)MEM_mallocN(sizeof(float) * (std::max(bwidth, bheight) + 5 * half_window),
314 "dilate erode buf");
315
316 /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. */
317 /* First pass, horizontal dilate/erode. */
318 for (int y = ymin; y < ymax; y++) {
319 for (int x = 0; x < bwidth + 5 * half_window; x++) {
320 buf[x] = compare_min_value;
321 }
322 for (int x = xmin; x < xmax; x++) {
323 buf[x - area.xmin + window - 1] = input->get_value(x, y, 0);
324 }
325
326 for (int i = 0; i < (bwidth + 3 * half_window) / window; i++) {
327 int start = (i + 1) * window - 1;
328
329 temp[window - 1] = buf[start];
330 for (int x = 1; x < window; x++) {
331 temp[window - 1 - x] = selector(temp[window - x], buf[start - x]);
332 temp[window - 1 + x] = selector(temp[window + x - 2], buf[start + x]);
333 }
334
335 start = half_window + (i - 1) * window + 1;
336 for (int x = -std::min(0, start); x < window - std::max(0, start + window - bwidth); x++) {
337 result.get_value(start + x + area.xmin, y, 0) = selector(temp[x], temp[x + window - 1]);
338 }
339 }
340 }
341
342 /* Second pass, vertical dilate/erode. */
343 for (int x = 0; x < bwidth; x++) {
344 for (int y = 0; y < bheight + 5 * half_window; y++) {
345 buf[y] = compare_min_value;
346 }
347 for (int y = ymin; y < ymax; y++) {
348 buf[y - area.ymin + window - 1] = result.get_value(x + area.xmin, y, 0);
349 }
350
351 for (int i = 0; i < (bheight + 3 * half_window) / window; i++) {
352 int start = (i + 1) * window - 1;
353
354 temp[window - 1] = buf[start];
355 for (int y = 1; y < window; y++) {
356 temp[window - 1 - y] = selector(temp[window - y], buf[start - y]);
357 temp[window - 1 + y] = selector(temp[window + y - 2], buf[start + y]);
358 }
359
360 start = half_window + (i - 1) * window + 1;
361 for (int y = -std::min(0, start); y < window - std::max(0, start + window - bheight); y++) {
362 result.get_value(x + area.xmin, y + start + area.ymin, 0) = selector(temp[y],
363 temp[y + window - 1]);
364 }
365 }
366 }
367
368 MEM_freeN(temp);
369 MEM_freeN(buf);
370
371 output->copy_from(&result, area);
372}
373
375 float operator()(float f1, float f2) const
376 {
377 return std::max(f1, f2);
378 }
379};
380
387
392
394 float operator()(float f1, float f2) const
395 {
396 return std::min(f1, f2);
397 }
398};
399
406
407} // namespace blender::compositor
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.c:418
#define UNUSED_VARS_NDEBUG(...)
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final
Get input operation area being read by this operation on rendering given output area.
virtual void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override
Get input operation area being read by this operation on rendering given output area.
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
virtual void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final
Get input operation area being read by this operation on rendering given output area.
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
a MemoryBuffer contains access to the data
void add_output_socket(DataType datatype)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
#define sqrtf(x)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static void step_update_memory_buffer(MemoryBuffer *output, const MemoryBuffer *input, const rcti &area, const int num_iterations, const float compare_min_value)
static float get_min_distance(DilateErodeThresholdOperation::PixelData &p)
static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value)
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
#define FLT_MAX
Definition stdcycles.h:14
_W64 int intptr_t
Definition stdint.h:118
PixelData(MemoryBuffer *input, const int distance, const int scope)
float operator()(float f1, float f2) const
float operator()(float f1, float f2) const
int ymin
int ymax
int xmin
int xmax