1 /*
2 Scan Tailor - Interactive post-processing tool for scanned pages.
3 Copyright (C) Joseph Artsimovich <joseph.artsimovich@gmail.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifndef IMAGEPROC_SEEDFILL_GENERIC_H_
20 #define IMAGEPROC_SEEDFILL_GENERIC_H_
21
22 #include <QSize>
23 #include <cassert>
24 #include <vector>
25 #include "BinaryImage.h"
26 #include "Connectivity.h"
27 #include "FastQueue.h"
28
29 namespace imageproc {
30 namespace detail {
31 namespace seed_fill_generic {
32 struct HTransition {
33 int west_delta; // -1 or 0
34 int east_delta; // 1 or 0
35
HTransitionHTransition36 HTransition(int west_delta_, int east_delta_) : west_delta(west_delta_), east_delta(east_delta_) {}
37 };
38
39 struct VTransition {
40 int north_mask; // 0 or ~0
41 int south_mask; // 0 or ~0
42
VTransitionVTransition43 VTransition(int north_mask_, int south_mask_) : north_mask(north_mask_), south_mask(south_mask_) {}
44 };
45
46 template <typename T>
47 struct Position {
48 T* seed;
49 const T* mask;
50 int x;
51 int y;
52
PositionPosition53 Position(T* seed_, const T* mask_, int x_, int y_) : seed(seed_), mask(mask_), x(x_), y(y_) {}
54 };
55
56 void initHorTransitions(std::vector<HTransition>& transitions, int width);
57
58 void initVertTransitions(std::vector<VTransition>& transitions, int height);
59
60 template <typename T, typename SpreadOp, typename MaskOp>
seedFillSingleLine(SpreadOp spread_op,MaskOp mask_op,const int line_len,T * seed,const int seed_stride,const T * mask,const int mask_stride)61 void seedFillSingleLine(SpreadOp spread_op,
62 MaskOp mask_op,
63 const int line_len,
64 T* seed,
65 const int seed_stride,
66 const T* mask,
67 const int mask_stride) {
68 if (line_len == 0) {
69 return;
70 }
71
72 *seed = mask_op(*seed, *mask);
73
74 for (int i = 1; i < line_len; ++i) {
75 seed += seed_stride;
76 mask += mask_stride;
77 *seed = mask_op(*mask, spread_op(*seed, seed[-seed_stride]));
78 }
79
80 for (int i = 1; i < line_len; ++i) {
81 seed -= seed_stride;
82 mask -= mask_stride;
83 *seed = mask_op(*mask, spread_op(*seed, seed[seed_stride]));
84 }
85 }
86
87 template <typename T, typename SpreadOp, typename MaskOp>
processNeighbor(SpreadOp spread_op,MaskOp mask_op,FastQueue<Position<T>> & queue,uint32_t * in_queue_line,const T this_val,T * const neighbor,const T * const neighbor_mask,const Position<T> & base_pos,const int x_delta,const int y_delta)88 inline void processNeighbor(SpreadOp spread_op,
89 MaskOp mask_op,
90 FastQueue<Position<T>>& queue,
91 uint32_t* in_queue_line,
92 const T this_val,
93 T* const neighbor,
94 const T* const neighbor_mask,
95 const Position<T>& base_pos,
96 const int x_delta,
97 const int y_delta) {
98 const T new_val(mask_op(*neighbor_mask, spread_op(this_val, *neighbor)));
99 if (new_val != *neighbor) {
100 *neighbor = new_val;
101 const int x = base_pos.x + x_delta;
102 const int y = base_pos.y + y_delta;
103 uint32_t& in_queue_word = in_queue_line[x >> 5];
104 const uint32_t in_queue_mask = (uint32_t(1) << 31) >> (x & 31);
105 if (!(in_queue_word & in_queue_mask)) { // If not already in the queue.
106 queue.push(Position<T>(neighbor, neighbor_mask, x, y));
107 in_queue_word |= in_queue_mask; // Mark as already in the queue.
108 }
109 }
110 }
111
112 template <typename T, typename SpreadOp, typename MaskOp>
spread4(SpreadOp spread_op,MaskOp mask_op,FastQueue<Position<T>> & queue,uint32_t * const in_queue_data,const int in_queue_stride,const HTransition * h_transitions,const VTransition * v_transitions,const int seed_stride,const int mask_stride)113 void spread4(SpreadOp spread_op,
114 MaskOp mask_op,
115 FastQueue<Position<T>>& queue,
116 uint32_t* const in_queue_data,
117 const int in_queue_stride,
118 const HTransition* h_transitions,
119 const VTransition* v_transitions,
120 const int seed_stride,
121 const int mask_stride) {
122 while (!queue.empty()) {
123 const Position<T> pos(queue.front());
124 queue.pop();
125
126 const T this_val(*pos.seed);
127 const HTransition ht(h_transitions[pos.x]);
128 const VTransition vt(v_transitions[pos.y]);
129 uint32_t* const in_queue_line = in_queue_data + in_queue_stride * pos.y;
130 T* seed;
131 const T* mask;
132
133 // Western neighbor.
134 seed = pos.seed + ht.west_delta;
135 mask = pos.mask + ht.west_delta;
136 processNeighbor(spread_op, mask_op, queue, in_queue_line, this_val, seed, mask, pos, ht.west_delta, 0);
137
138 // Eastern neighbor.
139 seed = pos.seed + ht.east_delta;
140 mask = pos.mask + ht.east_delta;
141 processNeighbor(spread_op, mask_op, queue, in_queue_line, this_val, seed, mask, pos, ht.east_delta, 0);
142
143 // Northern neighbor.
144 seed = pos.seed - (seed_stride & vt.north_mask);
145 mask = pos.mask - (mask_stride & vt.north_mask);
146 processNeighbor(spread_op, mask_op, queue, in_queue_line - (in_queue_stride & vt.north_mask), this_val, seed, mask,
147 pos, 0, -1 & vt.north_mask);
148
149 // Southern neighbor.
150 seed = pos.seed + (seed_stride & vt.south_mask);
151 mask = pos.mask + (mask_stride & vt.south_mask);
152 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), this_val, seed, mask,
153 pos, 0, 1 & vt.south_mask);
154 }
155 } // spread4
156
157 template <typename T, typename SpreadOp, typename MaskOp>
spread8(SpreadOp spread_op,MaskOp mask_op,FastQueue<Position<T>> & queue,uint32_t * const in_queue_data,const int in_queue_stride,const HTransition * h_transitions,const VTransition * v_transitions,const int seed_stride,const int mask_stride)158 void spread8(SpreadOp spread_op,
159 MaskOp mask_op,
160 FastQueue<Position<T>>& queue,
161 uint32_t* const in_queue_data,
162 const int in_queue_stride,
163 const HTransition* h_transitions,
164 const VTransition* v_transitions,
165 const int seed_stride,
166 const int mask_stride) {
167 while (!queue.empty()) {
168 const Position<T> pos(queue.front());
169 queue.pop();
170
171 const T this_val(*pos.seed);
172 const HTransition ht(h_transitions[pos.x]);
173 const VTransition vt(v_transitions[pos.y]);
174 uint32_t* const in_queue_line = in_queue_data + in_queue_stride * pos.y;
175 T* seed;
176 const T* mask;
177
178 // Northern neighbor.
179 seed = pos.seed - (seed_stride & vt.north_mask);
180 mask = pos.mask - (mask_stride & vt.north_mask);
181 processNeighbor(spread_op, mask_op, queue, in_queue_line - (in_queue_stride & vt.north_mask), this_val, seed, mask,
182 pos, 0, -1 & vt.north_mask);
183
184 // North-Western neighbor.
185 seed = pos.seed - (seed_stride & vt.north_mask) + ht.west_delta;
186 mask = pos.mask - (mask_stride & vt.north_mask) + ht.west_delta;
187 processNeighbor(spread_op, mask_op, queue, in_queue_line - (in_queue_stride & vt.north_mask), this_val, seed, mask,
188 pos, ht.west_delta, -1 & vt.north_mask);
189
190 // North-Eastern neighbor.
191 seed = pos.seed - (seed_stride & vt.north_mask) + ht.east_delta;
192 mask = pos.mask - (mask_stride & vt.north_mask) + ht.east_delta;
193 processNeighbor(spread_op, mask_op, queue, in_queue_line - (in_queue_stride & vt.north_mask), this_val, seed, mask,
194 pos, ht.east_delta, -1 & vt.north_mask);
195
196 // Eastern neighbor.
197 seed = pos.seed + ht.east_delta;
198 mask = pos.mask + ht.east_delta;
199 processNeighbor(spread_op, mask_op, queue, in_queue_line, this_val, seed, mask, pos, ht.east_delta, 0);
200
201 // Western neighbor.
202 seed = pos.seed + ht.west_delta;
203 mask = pos.mask + ht.west_delta;
204 processNeighbor(spread_op, mask_op, queue, in_queue_line, this_val, seed, mask, pos, ht.west_delta, 0);
205
206 // Southern neighbor.
207 seed = pos.seed + (seed_stride & vt.south_mask);
208 mask = pos.mask + (mask_stride & vt.south_mask);
209 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), this_val, seed, mask,
210 pos, 0, 1 & vt.south_mask);
211
212 // South-Eastern neighbor.
213 seed = pos.seed + (seed_stride & vt.south_mask) + ht.east_delta;
214 mask = pos.mask + (mask_stride & vt.south_mask) + ht.east_delta;
215 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), this_val, seed, mask,
216 pos, ht.east_delta, 1 & vt.south_mask);
217
218 // South-Western neighbor.
219 seed = pos.seed + (seed_stride & vt.south_mask) + ht.west_delta;
220 mask = pos.mask + (seed_stride & vt.south_mask) + ht.west_delta;
221 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), this_val, seed, mask,
222 pos, ht.west_delta, 1 & vt.south_mask);
223 }
224 } // spread8
225
226 template <typename T, typename SpreadOp, typename MaskOp>
seedFill4(SpreadOp spread_op,MaskOp mask_op,T * const seed,const int seed_stride,const QSize size,const T * const mask,const int mask_stride)227 void seedFill4(SpreadOp spread_op,
228 MaskOp mask_op,
229 T* const seed,
230 const int seed_stride,
231 const QSize size,
232 const T* const mask,
233 const int mask_stride) {
234 const int w = size.width();
235 const int h = size.height();
236
237 T* seed_line = seed;
238 const T* mask_line = mask;
239 T* prev_line = seed_line;
240
241 // Top to bottom.
242 for (int y = 0; y < h; ++y) {
243 int x = 0;
244
245 // First item in line.
246 T prev(mask_op(mask_line[x], spread_op(seed_line[x], prev_line[x])));
247 seed_line[x] = prev;
248
249 // Other items, left to right.
250 while (++x < w) {
251 prev = mask_op(mask_line[x], spread_op(prev, spread_op(seed_line[x], prev_line[x])));
252 seed_line[x] = prev;
253 }
254
255 prev_line = seed_line;
256 seed_line += seed_stride;
257 mask_line += mask_stride;
258 }
259
260 seed_line -= seed_stride;
261 mask_line -= mask_stride;
262
263 FastQueue<Position<T>> queue;
264 BinaryImage in_queue(size, WHITE);
265 uint32_t* const in_queue_data = in_queue.data();
266 const int in_queue_stride = in_queue.wordsPerLine();
267 std::vector<HTransition> h_transitions;
268 std::vector<VTransition> v_transitions;
269 initHorTransitions(h_transitions, w);
270 initVertTransitions(v_transitions, h);
271
272 // Bottom to top.
273 uint32_t* in_queue_line = in_queue_data + in_queue_stride * (h - 1);
274 for (int y = h - 1; y >= 0; --y) {
275 const VTransition vt(v_transitions[y]);
276
277 // Right to left.
278 for (int x = w - 1; x >= 0; --x) {
279 const HTransition ht(h_transitions[x]);
280
281 T* const p_base_seed = seed_line + x;
282 const T* const p_base_mask = mask_line + x;
283
284 T* const p_east_seed = p_base_seed + ht.east_delta;
285 T* const p_south_seed = p_base_seed + (seed_stride & vt.south_mask);
286
287 const T new_val(mask_op(*p_base_mask, spread_op(*p_base_seed, spread_op(*p_east_seed, *p_south_seed))));
288 if (new_val == *p_base_seed) {
289 continue;
290 }
291
292 *p_base_seed = new_val;
293
294 const Position<T> pos(p_base_seed, p_base_mask, x, y);
295 const T* p_east_mask = p_base_mask + ht.east_delta;
296 const T* p_south_mask = p_base_mask + (mask_stride & vt.south_mask);
297
298 // Eastern neighbor.
299 processNeighbor(spread_op, mask_op, queue, in_queue_line, new_val, p_east_seed, p_east_mask, pos, ht.east_delta,
300 0);
301
302 // Southern neighbor.
303 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), new_val,
304 p_south_seed, p_south_mask, pos, 0, 1 & vt.south_mask);
305 }
306
307 seed_line -= seed_stride;
308 mask_line -= mask_stride;
309 in_queue_line -= in_queue_stride;
310 }
311
312 spread4(spread_op, mask_op, queue, in_queue_data, in_queue_stride, &h_transitions[0], &v_transitions[0], seed_stride,
313 mask_stride);
314 } // seedFill4
315
316 template <typename T, typename SpreadOp, typename MaskOp>
seedFill8(SpreadOp spread_op,MaskOp mask_op,T * const seed,const int seed_stride,const QSize size,const T * const mask,const int mask_stride)317 void seedFill8(SpreadOp spread_op,
318 MaskOp mask_op,
319 T* const seed,
320 const int seed_stride,
321 const QSize size,
322 const T* const mask,
323 const int mask_stride) {
324 const int w = size.width();
325 const int h = size.height();
326
327 // Some code below doesn't handle such cases.
328 if (w == 1) {
329 seedFillSingleLine(spread_op, mask_op, h, seed, seed_stride, mask, mask_stride);
330 return;
331 } else if (h == 1) {
332 seedFillSingleLine(spread_op, mask_op, w, seed, 1, mask, 1);
333 return;
334 }
335
336 T* seed_line = seed;
337 const T* mask_line = mask;
338
339 // Note: we usually process the first line by assigning
340 // prev_line = seed_line, but in this case prev_line[x + 1]
341 // won't be clipped by its mask when we use it to update seed_line[x].
342 // The wrong value may propagate further from there, so clipping
343 // we do on the anti-raster pass won't help.
344 // That's why we process the first line separately.
345 seed_line[0] = mask_op(seed_line[0], mask_line[0]);
346 for (int x = 1; x < w; ++x) {
347 seed_line[x] = mask_op(mask_line[x], spread_op(seed_line[x], seed_line[x - 1]));
348 }
349
350 T* prev_line = seed_line;
351
352 // Top to bottom.
353 for (int y = 1; y < h; ++y) {
354 seed_line += seed_stride;
355 mask_line += mask_stride;
356
357 int x = 0;
358
359 // Leftmost pixel.
360 seed_line[x] = mask_op(mask_line[x], spread_op(seed_line[x], spread_op(prev_line[x], prev_line[x + 1])));
361
362 // Left to right.
363 while (++x < w - 1) {
364 seed_line[x] = mask_op(mask_line[x], spread_op(spread_op(spread_op(seed_line[x], seed_line[x - 1]),
365 spread_op(prev_line[x], prev_line[x - 1])),
366 prev_line[x + 1]));
367 }
368
369 // Rightmost pixel.
370 seed_line[x] = mask_op(
371 mask_line[x], spread_op(spread_op(seed_line[x], seed_line[x - 1]), spread_op(prev_line[x], prev_line[x - 1])));
372
373 prev_line = seed_line;
374 }
375
376 FastQueue<Position<T>> queue;
377 BinaryImage in_queue(size, WHITE);
378 uint32_t* const in_queue_data = in_queue.data();
379 const int in_queue_stride = in_queue.wordsPerLine();
380 std::vector<HTransition> h_transitions;
381 std::vector<VTransition> v_transitions;
382 initHorTransitions(h_transitions, w);
383 initVertTransitions(v_transitions, h);
384
385 // Bottom to top.
386 uint32_t* in_queue_line = in_queue_data + in_queue_stride * (h - 1);
387 for (int y = h - 1; y >= 0; --y) {
388 const VTransition vt(v_transitions[y]);
389
390 for (int x = w - 1; x >= 0; --x) {
391 const HTransition ht(h_transitions[x]);
392
393 T* const p_base_seed = seed_line + x;
394 const T* const p_base_mask = mask_line + x;
395
396 T* const p_east_seed = p_base_seed + ht.east_delta;
397 T* const p_south_seed = p_base_seed + (seed_stride & vt.south_mask);
398 T* const p_south_west_seed = p_south_seed + ht.west_delta;
399 T* const p_south_east_seed = p_south_seed + ht.east_delta;
400
401 const T new_val
402 = mask_op(*p_base_mask, spread_op(*p_base_seed, spread_op(spread_op(*p_east_seed, *p_south_east_seed),
403 spread_op(*p_south_seed, *p_south_west_seed))));
404 if (new_val == *p_base_seed) {
405 continue;
406 }
407
408 *p_base_seed = new_val;
409
410 const Position<T> pos(p_base_seed, p_base_mask, x, y);
411 const T* p_east_mask = p_base_mask + ht.east_delta;
412 const T* p_south_mask = p_base_mask + (mask_stride & vt.south_mask);
413 const T* p_south_west_mask = p_south_mask + ht.west_delta;
414 const T* p_south_east_mask = p_south_mask + ht.east_delta;
415
416 // Eastern neighbor.
417 processNeighbor(spread_op, mask_op, queue, in_queue_line, new_val, p_east_seed, p_east_mask, pos, ht.east_delta,
418 0);
419
420 // South-eastern neighbor.
421 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), new_val,
422 p_south_east_seed, p_south_east_mask, pos, ht.east_delta, 1 & vt.south_mask);
423
424 // Southern neighbor.
425 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), new_val,
426 p_south_seed, p_south_mask, pos, 0, 1 & vt.south_mask);
427
428 // South-western neighbor.
429 processNeighbor(spread_op, mask_op, queue, in_queue_line + (in_queue_stride & vt.south_mask), new_val,
430 p_south_west_seed, p_south_west_mask, pos, ht.west_delta, 1 & vt.south_mask);
431 }
432
433 seed_line -= seed_stride;
434 mask_line -= mask_stride;
435 in_queue_line -= in_queue_stride;
436 }
437
438 spread8(spread_op, mask_op, queue, in_queue_data, in_queue_stride, &h_transitions[0], &v_transitions[0], seed_stride,
439 mask_stride);
440 } // seedFill8
441 } // namespace seed_fill_generic
442 } // namespace detail
443
444
445 /**
446 * The following pseudocode illustrates the principle of a seed-fill algorithm:
447 * [code]
448 * do {
449 * foreach (<point at x, y>) {
450 * val = mask_op(mask[x, y], seed[x, y]);
451 * foreach (<neighbor at nx, ny>) {
452 * seed[nx, ny] = mask_op(mask[nx, ny], spread_op(seed[nx, ny], val));
453 * }
454 * }
455 * } while (<changes to seed were made on this iteration>);
456 * [/code]
457 *
458 * \param spread_op A functor or a pointer to a free function that can be called with
459 * two arguments of type T and return the bigger or the smaller of the two.
460 * \param mask_op Same as spread_op, but the opposite operation.
461 * \param conn Determines whether to spread values to 4 or 8 eight immediate neighbors.
462 * \param[in,out] seed Pointer to the seed buffer.
463 * \param seed_stride The size of a row in the seed buffer, in terms of the number of T objects.
464 * \param size Dimensions of the seed and the mask buffers.
465 * \param mask Pointer to the mask data.
466 * \param mask_stride The size of a row in the mask buffer, in terms of the number of T objects.
467 *
468 * This code is an implementation of the hybrid grayscale restoration algorithm described in:
469 * Morphological Grayscale Reconstruction in Image Analysis:
470 * Applications and Efficient Algorithms, technical report 91-16, Harvard Robotics Laboratory,
471 * November 1991, IEEE Transactions on Image Processing, Vol. 2, No. 2, pp. 176-201, April 1993.\n
472 */
473 template <typename T, typename SpreadOp, typename MaskOp>
seedFillGenericInPlace(SpreadOp spread_op,MaskOp mask_op,Connectivity conn,T * seed,int seed_stride,QSize size,const T * mask,int mask_stride)474 void seedFillGenericInPlace(SpreadOp spread_op,
475 MaskOp mask_op,
476 Connectivity conn,
477 T* seed,
478 int seed_stride,
479 QSize size,
480 const T* mask,
481 int mask_stride) {
482 if (size.isEmpty()) {
483 return;
484 }
485
486 if (conn == CONN4) {
487 detail::seed_fill_generic::seedFill4(spread_op, mask_op, seed, seed_stride, size, mask, mask_stride);
488 } else {
489 assert(conn == CONN8);
490 detail::seed_fill_generic::seedFill8(spread_op, mask_op, seed, seed_stride, size, mask, mask_stride);
491 }
492 }
493 } // namespace imageproc
494
495 #endif // ifndef IMAGEPROC_SEEDFILL_GENERIC_H_
496