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