/src/leptonica/src/morphdwa.c
Line | Count | Source |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file morphdwa.c |
29 | | * <pre> |
30 | | * |
31 | | * Binary morphological (dwa) ops with brick Sels |
32 | | * PIX *pixDilateBrickDwa() |
33 | | * PIX *pixErodeBrickDwa() |
34 | | * PIX *pixOpenBrickDwa() |
35 | | * PIX *pixCloseBrickDwa() |
36 | | * |
37 | | * Binary composite morphological (dwa) ops with brick Sels |
38 | | * PIX *pixDilateCompBrickDwa() |
39 | | * PIX *pixErodeCompBrickDwa() |
40 | | * PIX *pixOpenCompBrickDwa() |
41 | | * PIX *pixCloseCompBrickDwa() |
42 | | * |
43 | | * Binary extended composite morphological (dwa) ops with brick Sels |
44 | | * PIX *pixDilateCompBrickExtendDwa() |
45 | | * PIX *pixErodeCompBrickExtendDwa() |
46 | | * PIX *pixOpenCompBrickExtendDwa() |
47 | | * PIX *pixCloseCompBrickExtendDwa() |
48 | | * l_int32 getExtendedCompositeParameters() |
49 | | * |
50 | | * These are higher-level interfaces for dwa morphology with brick Sels. |
51 | | * Because many morphological operations are performed using |
52 | | * separable brick Sels, it is useful to have a simple interface |
53 | | * for this. |
54 | | * |
55 | | * We have included all 58 of the brick Sels that are generated |
56 | | * by selaAddBasic(). These are sufficient for all the decomposable |
57 | | * bricks up to size 63, which is the limit for dwa Sels with |
58 | | * origins at the center of the Sel. |
59 | | * |
60 | | * All three sets can be used as the basic interface for general |
61 | | * brick operations. Here are the internal calling sequences: |
62 | | * |
63 | | * (1) If you try to apply a non-decomposable operation, such as |
64 | | * pixErodeBrickDwa(), with a Sel size that doesn't exist, |
65 | | * this calls a decomposable operation, pixErodeCompBrickDwa(), |
66 | | * instead. This can differ in linear Sel size by up to |
67 | | * 2 pixels from the request. |
68 | | * |
69 | | * (2) If either Sel brick dimension is greater than 63, the extended |
70 | | * composite function is called. |
71 | | * |
72 | | * (3) The extended composite function calls the composite function |
73 | | * a number of times with size 63, and once with size < 63. |
74 | | * Because each operation with a size of 63 is done compositely |
75 | | * with 7 x 9 (exactly 63), the net result is correct in |
76 | | * length to within 2 pixels. |
77 | | * |
78 | | * For composite operations, both using a comb and extended (beyond 63), |
79 | | * horizontal and vertical operations are composed separately |
80 | | * and sequentially. |
81 | | * |
82 | | * We have also included use of all the 76 comb Sels that are generated |
83 | | * by selaAddDwaCombs(). The generated code is in dwacomb.2.c |
84 | | * and dwacomblow.2.c. These are used for the composite dwa |
85 | | * brick operations. |
86 | | * |
87 | | * The non-composite brick operations, such as pixDilateBrickDwa(), |
88 | | * will call the associated composite operation in situations where |
89 | | * the requisite brick Sel has not been compiled into fmorphgen*.1.c. |
90 | | * |
91 | | * If you want to use brick Sels that are not represented in the |
92 | | * basic set of 58, you must generate the dwa code to implement them. |
93 | | * You have three choices for how to use these: |
94 | | * |
95 | | * (1) Add both the new Sels and the dwa code to the library: |
96 | | * ~ For simplicity, add your new brick Sels to those defined |
97 | | * in selaAddBasic(). |
98 | | * ~ Recompile the library. |
99 | | * ~ Make prog/fmorphautogen. |
100 | | * ~ Run prog/fmorphautogen, to generate new versions of the |
101 | | * dwa code in fmorphgen.1.c and fmorphgenlow.1.c. |
102 | | * ~ Copy these two files to src. |
103 | | * ~ Recompile the library again. |
104 | | * ~ Use the new brick Sels in your program and compile it. |
105 | | * |
106 | | * (2) Make both the new Sels and dwa code outside the library, |
107 | | * and link it directly to an executable: |
108 | | * ~ Write a function to generate the new Sels in a Sela, and call |
109 | | * fmorphautogen(sela, <N>, filename) to generate the code. |
110 | | * ~ Compile your program that uses the newly generated function |
111 | | * pixMorphDwa_<N>(), and link to the two new C files. |
112 | | * |
113 | | * (3) Make the new Sels in the library and use the dwa code outside it: |
114 | | * ~ Add code in the library to generate your new brick Sels. |
115 | | * (It is suggested that you NOT add these Sels to the |
116 | | * selaAddBasic() function; write a new function that generates |
117 | | * a new Sela.) |
118 | | * ~ Recompile the library. |
119 | | * ~ Write a small program that generates the Sela and calls |
120 | | * fmorphautogen(sela, <N>, filename) to generate the code. |
121 | | * ~ Compile your program that uses the newly generated function |
122 | | * pixMorphDwa_<N>(), and link to the two new C files. |
123 | | * As an example of this approach, see prog/dwamorph*_reg.c: |
124 | | * ~ added selaAddDwaLinear() to sel2.c |
125 | | * ~ wrote dwamorph1_reg.c, to generate the dwa code. |
126 | | * ~ compiled and linked the generated code with the application, |
127 | | * dwamorph2_reg.c. (Note: because this was a regression test, |
128 | | * dwamorph1_reg also builds and runs the application program.) |
129 | | * </pre> |
130 | | */ |
131 | | |
132 | | #ifdef HAVE_CONFIG_H |
133 | | #include <config_auto.h> |
134 | | #endif /* HAVE_CONFIG_H */ |
135 | | |
136 | | #include "allheaders.h" |
137 | | |
138 | | #ifndef NO_CONSOLE_IO |
139 | | #define DEBUG_SEL_LOOKUP 0 |
140 | | #endif /* ~NO_CONSOLE_IO */ |
141 | | |
142 | | /*-----------------------------------------------------------------* |
143 | | * Binary morphological (dwa) ops with brick Sels * |
144 | | *-----------------------------------------------------------------*/ |
145 | | /*! |
146 | | * \brief pixDilateBrickDwa() |
147 | | * |
148 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
149 | | * or different from pixs |
150 | | * \param[in] pixs 1 bpp |
151 | | * \param[in] hsize width of brick Sel |
152 | | * \param[in] vsize height of brick Sel |
153 | | * \return pixd |
154 | | * |
155 | | * <pre> |
156 | | * Notes: |
157 | | * (1) These implement 2D brick Sels, using linear Sels generated |
158 | | * with selaAddBasic(). |
159 | | * (2) A brick Sel has hits for all elements. |
160 | | * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
161 | | * (4) Do separably if both hsize and vsize are > 1. |
162 | | * (5) It is necessary that both horizontal and vertical Sels |
163 | | * of the input size are defined in the basic sela. |
164 | | * (6) There are three cases: |
165 | | * (a) pixd == null (result into new pixd) |
166 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
167 | | * (c) pixd != pixs (puts result into existing pixd) |
168 | | * (7) For clarity, if the case is known, use these patterns: |
169 | | * (a) pixd = pixDilateBrickDwa(NULL, pixs, ...); |
170 | | * (b) pixDilateBrickDwa(pixs, pixs, ...); |
171 | | * (c) pixDilateBrickDwa(pixd, pixs, ...); |
172 | | * (8) The size of pixd is determined by pixs. |
173 | | * (9) If either linear Sel is not found, this calls |
174 | | * the appropriate decomposible function. |
175 | | * </pre> |
176 | | */ |
177 | | PIX * |
178 | | pixDilateBrickDwa(PIX *pixd, |
179 | | PIX *pixs, |
180 | | l_int32 hsize, |
181 | | l_int32 vsize) |
182 | 0 | { |
183 | 0 | l_int32 found; |
184 | 0 | char *selnameh, *selnamev; |
185 | 0 | SELA *sela; |
186 | 0 | PIX *pixt1, *pixt2, *pixt3; |
187 | |
|
188 | 0 | if (!pixs) |
189 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
190 | 0 | if (pixGetDepth(pixs) != 1) |
191 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
192 | 0 | if (hsize < 1 || vsize < 1) |
193 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
194 | | |
195 | 0 | if (hsize == 1 && vsize == 1) |
196 | 0 | return pixCopy(pixd, pixs); |
197 | | |
198 | 0 | sela = selaAddBasic(NULL); |
199 | 0 | found = TRUE; |
200 | 0 | selnameh = selnamev = NULL; |
201 | 0 | if (hsize > 1) { |
202 | 0 | selnameh = selaGetBrickName(sela, hsize, 1); |
203 | 0 | if (!selnameh) found = FALSE; |
204 | 0 | } |
205 | 0 | if (vsize > 1) { |
206 | 0 | selnamev = selaGetBrickName(sela, 1, vsize); |
207 | 0 | if (!selnamev) found = FALSE; |
208 | 0 | } |
209 | 0 | selaDestroy(&sela); |
210 | 0 | if (!found) { |
211 | 0 | L_INFO("Calling the decomposable dwa function\n", __func__); |
212 | 0 | if (selnameh) LEPT_FREE(selnameh); |
213 | 0 | if (selnamev) LEPT_FREE(selnamev); |
214 | 0 | return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); |
215 | 0 | } |
216 | | |
217 | 0 | if (vsize == 1) { |
218 | 0 | pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh); |
219 | 0 | LEPT_FREE(selnameh); |
220 | 0 | } else if (hsize == 1) { |
221 | 0 | pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev); |
222 | 0 | LEPT_FREE(selnamev); |
223 | 0 | } else { |
224 | 0 | pixt1 = pixAddBorder(pixs, 32, 0); |
225 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); |
226 | 0 | pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev); |
227 | 0 | pixt2 = pixRemoveBorder(pixt1, 32); |
228 | 0 | pixDestroy(&pixt1); |
229 | 0 | pixDestroy(&pixt3); |
230 | 0 | LEPT_FREE(selnameh); |
231 | 0 | LEPT_FREE(selnamev); |
232 | 0 | } |
233 | |
|
234 | 0 | if (!pixd) |
235 | 0 | return pixt2; |
236 | | |
237 | 0 | pixTransferAllData(pixd, &pixt2, 0, 0); |
238 | 0 | return pixd; |
239 | 0 | } |
240 | | |
241 | | |
242 | | /*! |
243 | | * \brief pixErodeBrickDwa() |
244 | | * |
245 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
246 | | * or different from pixs |
247 | | * \param[in] pixs 1 bpp |
248 | | * \param[in] hsize width of brick Sel |
249 | | * \param[in] vsize height of brick Sel |
250 | | * \return pixd |
251 | | * |
252 | | * <pre> |
253 | | * Notes: |
254 | | * (1) These implement 2D brick Sels, using linear Sels generated |
255 | | * with selaAddBasic(). |
256 | | * (2) A brick Sel has hits for all elements. |
257 | | * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
258 | | * (4) Do separably if both hsize and vsize are > 1. |
259 | | * (5) It is necessary that both horizontal and vertical Sels |
260 | | * of the input size are defined in the basic sela. |
261 | | * (6) Note that we must always set or clear the border pixels |
262 | | * before each operation, depending on the the b.c. |
263 | | * (symmetric or asymmetric). |
264 | | * (7) There are three cases: |
265 | | * (a) pixd == null (result into new pixd) |
266 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
267 | | * (c) pixd != pixs (puts result into existing pixd) |
268 | | * (8) For clarity, if the case is known, use these patterns: |
269 | | * (a) pixd = pixErodeBrickDwa(NULL, pixs, ...); |
270 | | * (b) pixErodeBrickDwa(pixs, pixs, ...); |
271 | | * (c) pixErodeBrickDwa(pixd, pixs, ...); |
272 | | * (9) The size of the result is determined by pixs. |
273 | | * (10) If either linear Sel is not found, this calls |
274 | | * the appropriate decomposible function. |
275 | | * </pre> |
276 | | */ |
277 | | PIX * |
278 | | pixErodeBrickDwa(PIX *pixd, |
279 | | PIX *pixs, |
280 | | l_int32 hsize, |
281 | | l_int32 vsize) |
282 | 0 | { |
283 | 0 | l_int32 found; |
284 | 0 | char *selnameh, *selnamev; |
285 | 0 | SELA *sela; |
286 | 0 | PIX *pixt1, *pixt2, *pixt3; |
287 | |
|
288 | 0 | if (!pixs) |
289 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
290 | 0 | if (pixGetDepth(pixs) != 1) |
291 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
292 | 0 | if (hsize < 1 || vsize < 1) |
293 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
294 | | |
295 | 0 | if (hsize == 1 && vsize == 1) |
296 | 0 | return pixCopy(pixd, pixs); |
297 | | |
298 | 0 | sela = selaAddBasic(NULL); |
299 | 0 | found = TRUE; |
300 | 0 | selnameh = selnamev = NULL; |
301 | 0 | if (hsize > 1) { |
302 | 0 | selnameh = selaGetBrickName(sela, hsize, 1); |
303 | 0 | if (!selnameh) found = FALSE; |
304 | 0 | } |
305 | 0 | if (vsize > 1) { |
306 | 0 | selnamev = selaGetBrickName(sela, 1, vsize); |
307 | 0 | if (!selnamev) found = FALSE; |
308 | 0 | } |
309 | 0 | selaDestroy(&sela); |
310 | 0 | if (!found) { |
311 | 0 | L_INFO("Calling the decomposable dwa function\n", __func__); |
312 | 0 | if (selnameh) LEPT_FREE(selnameh); |
313 | 0 | if (selnamev) LEPT_FREE(selnamev); |
314 | 0 | return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); |
315 | 0 | } |
316 | | |
317 | 0 | if (vsize == 1) { |
318 | 0 | pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh); |
319 | 0 | LEPT_FREE(selnameh); |
320 | 0 | } else if (hsize == 1) { |
321 | 0 | pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev); |
322 | 0 | LEPT_FREE(selnamev); |
323 | 0 | } else { |
324 | 0 | pixt1 = pixAddBorder(pixs, 32, 0); |
325 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); |
326 | 0 | pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev); |
327 | 0 | pixt2 = pixRemoveBorder(pixt1, 32); |
328 | 0 | pixDestroy(&pixt1); |
329 | 0 | pixDestroy(&pixt3); |
330 | 0 | LEPT_FREE(selnameh); |
331 | 0 | LEPT_FREE(selnamev); |
332 | 0 | } |
333 | |
|
334 | 0 | if (!pixd) |
335 | 0 | return pixt2; |
336 | | |
337 | 0 | pixTransferAllData(pixd, &pixt2, 0, 0); |
338 | 0 | return pixd; |
339 | 0 | } |
340 | | |
341 | | |
342 | | /*! |
343 | | * \brief pixOpenBrickDwa() |
344 | | * |
345 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
346 | | * or different from pixs |
347 | | * \param[in] pixs 1 bpp |
348 | | * \param[in] hsize width of brick Sel |
349 | | * \param[in] vsize height of brick Sel |
350 | | * \return pixd |
351 | | * |
352 | | * <pre> |
353 | | * Notes: |
354 | | * (1) These implement 2D brick Sels, using linear Sels generated |
355 | | * with selaAddBasic(). |
356 | | * (2) A brick Sel has hits for all elements. |
357 | | * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
358 | | * (4) Do separably if both hsize and vsize are > 1. |
359 | | * (5) It is necessary that both horizontal and vertical Sels |
360 | | * of the input size are defined in the basic sela. |
361 | | * (6) Note that we must always set or clear the border pixels |
362 | | * before each operation, depending on the the b.c. |
363 | | * (symmetric or asymmetric). |
364 | | * (7) There are three cases: |
365 | | * (a) pixd == null (result into new pixd) |
366 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
367 | | * (c) pixd != pixs (puts result into existing pixd) |
368 | | * (8) For clarity, if the case is known, use these patterns: |
369 | | * (a) pixd = pixOpenBrickDwa(NULL, pixs, ...); |
370 | | * (b) pixOpenBrickDwa(pixs, pixs, ...); |
371 | | * (c) pixOpenBrickDwa(pixd, pixs, ...); |
372 | | * (9) The size of the result is determined by pixs. |
373 | | * (10) If either linear Sel is not found, this calls |
374 | | * the appropriate decomposible function. |
375 | | * </pre> |
376 | | */ |
377 | | PIX * |
378 | | pixOpenBrickDwa(PIX *pixd, |
379 | | PIX *pixs, |
380 | | l_int32 hsize, |
381 | | l_int32 vsize) |
382 | 0 | { |
383 | 0 | l_int32 found; |
384 | 0 | char *selnameh, *selnamev; |
385 | 0 | SELA *sela; |
386 | 0 | PIX *pixt1, *pixt2, *pixt3; |
387 | |
|
388 | 0 | if (!pixs) |
389 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
390 | 0 | if (pixGetDepth(pixs) != 1) |
391 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
392 | 0 | if (hsize < 1 || vsize < 1) |
393 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
394 | | |
395 | 0 | if (hsize == 1 && vsize == 1) |
396 | 0 | return pixCopy(pixd, pixs); |
397 | | |
398 | 0 | sela = selaAddBasic(NULL); |
399 | 0 | found = TRUE; |
400 | 0 | selnameh = selnamev = NULL; |
401 | 0 | if (hsize > 1) { |
402 | 0 | selnameh = selaGetBrickName(sela, hsize, 1); |
403 | 0 | if (!selnameh) found = FALSE; |
404 | 0 | } |
405 | 0 | if (vsize > 1) { |
406 | 0 | selnamev = selaGetBrickName(sela, 1, vsize); |
407 | 0 | if (!selnamev) found = FALSE; |
408 | 0 | } |
409 | 0 | selaDestroy(&sela); |
410 | 0 | if (!found) { |
411 | 0 | L_INFO("Calling the decomposable dwa function\n", __func__); |
412 | 0 | if (selnameh) LEPT_FREE(selnameh); |
413 | 0 | if (selnamev) LEPT_FREE(selnamev); |
414 | 0 | return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize); |
415 | 0 | } |
416 | | |
417 | 0 | pixt1 = pixAddBorder(pixs, 32, 0); |
418 | 0 | if (vsize == 1) { /* horizontal only */ |
419 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh); |
420 | 0 | LEPT_FREE(selnameh); |
421 | 0 | } else if (hsize == 1) { /* vertical only */ |
422 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev); |
423 | 0 | LEPT_FREE(selnamev); |
424 | 0 | } else { /* do separable */ |
425 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); |
426 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev); |
427 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh); |
428 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev); |
429 | 0 | LEPT_FREE(selnameh); |
430 | 0 | LEPT_FREE(selnamev); |
431 | 0 | pixDestroy(&pixt3); |
432 | 0 | } |
433 | 0 | pixt3 = pixRemoveBorder(pixt2, 32); |
434 | 0 | pixDestroy(&pixt1); |
435 | 0 | pixDestroy(&pixt2); |
436 | |
|
437 | 0 | if (!pixd) |
438 | 0 | return pixt3; |
439 | | |
440 | 0 | pixTransferAllData(pixd, &pixt3, 0, 0); |
441 | 0 | return pixd; |
442 | 0 | } |
443 | | |
444 | | |
445 | | /*! |
446 | | * \brief pixCloseBrickDwa() |
447 | | * |
448 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
449 | | * or different from pixs |
450 | | * \param[in] pixs 1 bpp |
451 | | * \param[in] hsize width of brick Sel |
452 | | * \param[in] vsize height of brick Sel |
453 | | * \return pixd |
454 | | * |
455 | | * <pre> |
456 | | * Notes: |
457 | | * (1) This is a 'safe' closing; we add an extra border of 32 OFF |
458 | | * pixels for the standard asymmetric b.c. |
459 | | * (2) These implement 2D brick Sels, using linear Sels generated |
460 | | * with selaAddBasic(). |
461 | | * (3) A brick Sel has hits for all elements. |
462 | | * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
463 | | * (5) Do separably if both hsize and vsize are > 1. |
464 | | * (6) It is necessary that both horizontal and vertical Sels |
465 | | * of the input size are defined in the basic sela. |
466 | | * (7) Note that we must always set or clear the border pixels |
467 | | * before each operation, depending on the the b.c. |
468 | | * (symmetric or asymmetric). |
469 | | * (8) There are three cases: |
470 | | * (a) pixd == null (result into new pixd) |
471 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
472 | | * (c) pixd != pixs (puts result into existing pixd) |
473 | | * (9) For clarity, if the case is known, use these patterns: |
474 | | * (a) pixd = pixCloseBrickDwa(NULL, pixs, ...); |
475 | | * (b) pixCloseBrickDwa(pixs, pixs, ...); |
476 | | * (c) pixCloseBrickDwa(pixd, pixs, ...); |
477 | | * (10) The size of the result is determined by pixs. |
478 | | * (11) If either linear Sel is not found, this calls |
479 | | * the appropriate decomposible function. |
480 | | * </pre> |
481 | | */ |
482 | | PIX * |
483 | | pixCloseBrickDwa(PIX *pixd, |
484 | | PIX *pixs, |
485 | | l_int32 hsize, |
486 | | l_int32 vsize) |
487 | 0 | { |
488 | 0 | l_int32 bordercolor, bordersize, found; |
489 | 0 | char *selnameh, *selnamev; |
490 | 0 | SELA *sela; |
491 | 0 | PIX *pixt1, *pixt2, *pixt3; |
492 | |
|
493 | 0 | if (!pixs) |
494 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
495 | 0 | if (pixGetDepth(pixs) != 1) |
496 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
497 | 0 | if (hsize < 1 || vsize < 1) |
498 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
499 | | |
500 | 0 | if (hsize == 1 && vsize == 1) |
501 | 0 | return pixCopy(pixd, pixs); |
502 | | |
503 | 0 | sela = selaAddBasic(NULL); |
504 | 0 | found = TRUE; |
505 | 0 | selnameh = selnamev = NULL; |
506 | 0 | if (hsize > 1) { |
507 | 0 | selnameh = selaGetBrickName(sela, hsize, 1); |
508 | 0 | if (!selnameh) found = FALSE; |
509 | 0 | } |
510 | 0 | if (vsize > 1) { |
511 | 0 | selnamev = selaGetBrickName(sela, 1, vsize); |
512 | 0 | if (!selnamev) found = FALSE; |
513 | 0 | } |
514 | 0 | selaDestroy(&sela); |
515 | 0 | if (!found) { |
516 | 0 | L_INFO("Calling the decomposable dwa function\n", __func__); |
517 | 0 | if (selnameh) LEPT_FREE(selnameh); |
518 | 0 | if (selnamev) LEPT_FREE(selnamev); |
519 | 0 | return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize); |
520 | 0 | } |
521 | | |
522 | | /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need |
523 | | * an extra 32 OFF pixels around the image (in addition to |
524 | | * the 32 added pixels for all dwa operations), whereas with |
525 | | * SYMMETRIC_MORPH_BC this is not necessary. */ |
526 | 0 | bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); |
527 | 0 | if (bordercolor == 0) /* asymmetric b.c. */ |
528 | 0 | bordersize = 64; |
529 | 0 | else /* symmetric b.c. */ |
530 | 0 | bordersize = 32; |
531 | 0 | pixt1 = pixAddBorder(pixs, bordersize, 0); |
532 | |
|
533 | 0 | if (vsize == 1) { /* horizontal only */ |
534 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh); |
535 | 0 | LEPT_FREE(selnameh); |
536 | 0 | } else if (hsize == 1) { /* vertical only */ |
537 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev); |
538 | 0 | LEPT_FREE(selnamev); |
539 | 0 | } else { /* do separable */ |
540 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); |
541 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev); |
542 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh); |
543 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev); |
544 | 0 | LEPT_FREE(selnameh); |
545 | 0 | LEPT_FREE(selnamev); |
546 | 0 | pixDestroy(&pixt3); |
547 | 0 | } |
548 | 0 | pixt3 = pixRemoveBorder(pixt2, bordersize); |
549 | 0 | pixDestroy(&pixt1); |
550 | 0 | pixDestroy(&pixt2); |
551 | |
|
552 | 0 | if (!pixd) |
553 | 0 | return pixt3; |
554 | | |
555 | 0 | pixTransferAllData(pixd, &pixt3, 0, 0); |
556 | 0 | return pixd; |
557 | 0 | } |
558 | | |
559 | | |
560 | | /*-----------------------------------------------------------------* |
561 | | * Binary composite morphological (dwa) ops with brick Sels * |
562 | | *-----------------------------------------------------------------*/ |
563 | | /*! |
564 | | * \brief pixDilateCompBrickDwa() |
565 | | * |
566 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
567 | | * or different from pixs |
568 | | * \param[in] pixs 1 bpp |
569 | | * \param[in] hsize width of brick Sel |
570 | | * \param[in] vsize height of brick Sel |
571 | | * \return pixd |
572 | | * |
573 | | * <pre> |
574 | | * Notes: |
575 | | * (1) These implement a separable composite dilation with 2D brick Sels. |
576 | | * (2) For efficiency, it may decompose each linear morphological |
577 | | * operation into two (brick + comb). |
578 | | * (3) A brick Sel has hits for all elements. |
579 | | * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
580 | | * (5) Do separably if both hsize and vsize are > 1. |
581 | | * (6) It is necessary that both horizontal and vertical Sels |
582 | | * of the input size are defined in the basic sela. |
583 | | * (7) There are three cases: |
584 | | * (a) pixd == null (result into new pixd) |
585 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
586 | | * (c) pixd != pixs (puts result into existing pixd) |
587 | | * (8) For clarity, if the case is known, use these patterns: |
588 | | * (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...); |
589 | | * (b) pixDilateCompBrickDwa(pixs, pixs, ...); |
590 | | * (c) pixDilateCompBrickDwa(pixd, pixs, ...); |
591 | | * (9) The size of pixd is determined by pixs. |
592 | | * (10) CAUTION: both hsize and vsize are being decomposed. |
593 | | * The decomposer chooses a product of sizes (call them |
594 | | * 'terms') for each that is close to the input size, |
595 | | * but not necessarily equal to it. It attempts to optimize: |
596 | | * (a) for consistency with the input values: the product |
597 | | * of terms is close to the input size |
598 | | * (b) for efficiency of the operation: the sum of the |
599 | | * terms is small; ideally about twice the square |
600 | | * root of the input size. |
601 | | * So, for example, if the input hsize = 37, which is |
602 | | * a prime number, the decomposer will break this into two |
603 | | * terms, 6 and 6, so that the net result is a dilation |
604 | | * with hsize = 36. |
605 | | * </pre> |
606 | | */ |
607 | | PIX * |
608 | | pixDilateCompBrickDwa(PIX *pixd, |
609 | | PIX *pixs, |
610 | | l_int32 hsize, |
611 | | l_int32 vsize) |
612 | 0 | { |
613 | 0 | char *selnameh1, *selnameh2, *selnamev1, *selnamev2; |
614 | 0 | l_int32 hsize1, hsize2, vsize1, vsize2; |
615 | 0 | PIX *pixt1, *pixt2, *pixt3; |
616 | |
|
617 | 0 | if (!pixs) |
618 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
619 | 0 | if (pixGetDepth(pixs) != 1) |
620 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
621 | 0 | if (hsize < 1 || vsize < 1) |
622 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
623 | 0 | if (hsize > 63 || vsize > 63) |
624 | 0 | return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize); |
625 | | |
626 | 0 | if (hsize == 1 && vsize == 1) |
627 | 0 | return pixCopy(pixd, pixs); |
628 | | |
629 | 0 | hsize1 = hsize2 = vsize1 = vsize2 = 1; |
630 | 0 | selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; |
631 | 0 | if (hsize > 1) |
632 | 0 | getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, |
633 | 0 | &selnameh2, NULL, NULL); |
634 | 0 | if (vsize > 1) |
635 | 0 | getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, |
636 | 0 | &selnamev1, &selnamev2); |
637 | |
|
638 | | #if DEBUG_SEL_LOOKUP |
639 | | lept_stderr("nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n", |
640 | | selnameh1, selnameh2, selnamev1, selnamev2); |
641 | | lept_stderr("hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n", |
642 | | hsize1, hsize2, vsize1, vsize2); |
643 | | #endif /* DEBUG_SEL_LOOKUP */ |
644 | |
|
645 | 0 | pixt1 = pixAddBorder(pixs, 64, 0); |
646 | 0 | if (vsize == 1) { |
647 | 0 | if (hsize2 == 1) { |
648 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
649 | 0 | } else { |
650 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
651 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); |
652 | 0 | pixDestroy(&pixt3); |
653 | 0 | } |
654 | 0 | } else if (hsize == 1) { |
655 | 0 | if (vsize2 == 1) { |
656 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); |
657 | 0 | } else { |
658 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); |
659 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); |
660 | 0 | pixDestroy(&pixt3); |
661 | 0 | } |
662 | 0 | } else { /* vsize and hsize both > 1 */ |
663 | 0 | if (hsize2 == 1) { |
664 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
665 | 0 | } else { |
666 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
667 | 0 | pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2); |
668 | 0 | pixDestroy(&pixt2); |
669 | 0 | } |
670 | 0 | if (vsize2 == 1) { |
671 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); |
672 | 0 | } else { |
673 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); |
674 | 0 | pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2); |
675 | 0 | } |
676 | 0 | pixDestroy(&pixt3); |
677 | 0 | } |
678 | 0 | pixDestroy(&pixt1); |
679 | 0 | pixt1 = pixRemoveBorder(pixt2, 64); |
680 | 0 | pixDestroy(&pixt2); |
681 | 0 | if (selnameh1) LEPT_FREE(selnameh1); |
682 | 0 | if (selnameh2) LEPT_FREE(selnameh2); |
683 | 0 | if (selnamev1) LEPT_FREE(selnamev1); |
684 | 0 | if (selnamev2) LEPT_FREE(selnamev2); |
685 | |
|
686 | 0 | if (!pixd) |
687 | 0 | return pixt1; |
688 | | |
689 | 0 | pixTransferAllData(pixd, &pixt1, 0, 0); |
690 | 0 | return pixd; |
691 | 0 | } |
692 | | |
693 | | |
694 | | /*! |
695 | | * \brief pixErodeCompBrickDwa() |
696 | | * |
697 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
698 | | * or different from pixs |
699 | | * \param[in] pixs 1 bpp |
700 | | * \param[in] hsize width of brick Sel |
701 | | * \param[in] vsize height of brick Sel |
702 | | * \return pixd |
703 | | * |
704 | | * <pre> |
705 | | * Notes: |
706 | | * (1) These implement a separable composite erosion with 2D brick Sels. |
707 | | * (2) For efficiency, it may decompose each linear morphological |
708 | | * operation into two (brick + comb). |
709 | | * (3) A brick Sel has hits for all elements. |
710 | | * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
711 | | * (5) Do separably if both hsize and vsize are > 1. |
712 | | * (6) It is necessary that both horizontal and vertical Sels |
713 | | * of the input size are defined in the basic sela. |
714 | | * (7) There are three cases: |
715 | | * (a) pixd == null (result into new pixd) |
716 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
717 | | * (c) pixd != pixs (puts result into existing pixd) |
718 | | * (8) For clarity, if the case is known, use these patterns: |
719 | | * (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...); |
720 | | * (b) pixErodeCompBrickDwa(pixs, pixs, ...); |
721 | | * (c) pixErodeCompBrickDwa(pixd, pixs, ...); |
722 | | * (9) The size of pixd is determined by pixs. |
723 | | * (10) CAUTION: both hsize and vsize are being decomposed. |
724 | | * The decomposer chooses a product of sizes (call them |
725 | | * 'terms') for each that is close to the input size, |
726 | | * but not necessarily equal to it. It attempts to optimize: |
727 | | * (a) for consistency with the input values: the product |
728 | | * of terms is close to the input size |
729 | | * (b) for efficiency of the operation: the sum of the |
730 | | * terms is small; ideally about twice the square |
731 | | * root of the input size. |
732 | | * So, for example, if the input hsize = 37, which is |
733 | | * a prime number, the decomposer will break this into two |
734 | | * terms, 6 and 6, so that the net result is a dilation |
735 | | * with hsize = 36. |
736 | | * </pre> |
737 | | */ |
738 | | PIX * |
739 | | pixErodeCompBrickDwa(PIX *pixd, |
740 | | PIX *pixs, |
741 | | l_int32 hsize, |
742 | | l_int32 vsize) |
743 | 0 | { |
744 | 0 | char *selnameh1, *selnameh2, *selnamev1, *selnamev2; |
745 | 0 | l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; |
746 | 0 | PIX *pixt1, *pixt2, *pixt3; |
747 | |
|
748 | 0 | if (!pixs) |
749 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
750 | 0 | if (pixGetDepth(pixs) != 1) |
751 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
752 | 0 | if (hsize < 1 || vsize < 1) |
753 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
754 | 0 | if (hsize > 63 || vsize > 63) |
755 | 0 | return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize); |
756 | | |
757 | 0 | if (hsize == 1 && vsize == 1) |
758 | 0 | return pixCopy(pixd, pixs); |
759 | | |
760 | 0 | hsize1 = hsize2 = vsize1 = vsize2 = 1; |
761 | 0 | selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; |
762 | 0 | if (hsize > 1) |
763 | 0 | getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, |
764 | 0 | &selnameh2, NULL, NULL); |
765 | 0 | if (vsize > 1) |
766 | 0 | getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, |
767 | 0 | &selnamev1, &selnamev2); |
768 | | |
769 | | /* For symmetric b.c., bordercolor == 1 for erosion */ |
770 | 0 | bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); |
771 | 0 | pixt1 = pixAddBorder(pixs, 64, bordercolor); |
772 | |
|
773 | 0 | if (vsize == 1) { |
774 | 0 | if (hsize2 == 1) { |
775 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
776 | 0 | } else { |
777 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
778 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); |
779 | 0 | pixDestroy(&pixt3); |
780 | 0 | } |
781 | 0 | } else if (hsize == 1) { |
782 | 0 | if (vsize2 == 1) { |
783 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); |
784 | 0 | } else { |
785 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); |
786 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); |
787 | 0 | pixDestroy(&pixt3); |
788 | 0 | } |
789 | 0 | } else { /* vsize and hsize both > 1 */ |
790 | 0 | if (hsize2 == 1) { |
791 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
792 | 0 | } else { |
793 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
794 | 0 | pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2); |
795 | 0 | pixDestroy(&pixt2); |
796 | 0 | } |
797 | 0 | if (vsize2 == 1) { |
798 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); |
799 | 0 | } else { |
800 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); |
801 | 0 | pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2); |
802 | 0 | } |
803 | 0 | pixDestroy(&pixt3); |
804 | 0 | } |
805 | 0 | pixDestroy(&pixt1); |
806 | 0 | pixt1 = pixRemoveBorder(pixt2, 64); |
807 | 0 | pixDestroy(&pixt2); |
808 | 0 | if (selnameh1) LEPT_FREE(selnameh1); |
809 | 0 | if (selnameh2) LEPT_FREE(selnameh2); |
810 | 0 | if (selnamev1) LEPT_FREE(selnamev1); |
811 | 0 | if (selnamev2) LEPT_FREE(selnamev2); |
812 | |
|
813 | 0 | if (!pixd) |
814 | 0 | return pixt1; |
815 | | |
816 | 0 | pixTransferAllData(pixd, &pixt1, 0, 0); |
817 | 0 | return pixd; |
818 | 0 | } |
819 | | |
820 | | |
821 | | /*! |
822 | | * \brief pixOpenCompBrickDwa() |
823 | | * |
824 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
825 | | * or different from pixs |
826 | | * \param[in] pixs 1 bpp |
827 | | * \param[in] hsize width of brick Sel |
828 | | * \param[in] vsize height of brick Sel |
829 | | * \return pixd |
830 | | * |
831 | | * <pre> |
832 | | * Notes: |
833 | | * (1) These implement a separable composite opening with 2D brick Sels. |
834 | | * (2) For efficiency, it may decompose each linear morphological |
835 | | * operation into two (brick + comb). |
836 | | * (3) A brick Sel has hits for all elements. |
837 | | * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
838 | | * (5) Do separably if both hsize and vsize are > 1. |
839 | | * (6) It is necessary that both horizontal and vertical Sels |
840 | | * of the input size are defined in the basic sela. |
841 | | * (7) There are three cases: |
842 | | * (a) pixd == null (result into new pixd) |
843 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
844 | | * (c) pixd != pixs (puts result into existing pixd) |
845 | | * (8) For clarity, if the case is known, use these patterns: |
846 | | * (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...); |
847 | | * (b) pixOpenCompBrickDwa(pixs, pixs, ...); |
848 | | * (c) pixOpenCompBrickDwa(pixd, pixs, ...); |
849 | | * (9) The size of pixd is determined by pixs. |
850 | | * (10) CAUTION: both hsize and vsize are being decomposed. |
851 | | * The decomposer chooses a product of sizes (call them |
852 | | * 'terms') for each that is close to the input size, |
853 | | * but not necessarily equal to it. It attempts to optimize: |
854 | | * (a) for consistency with the input values: the product |
855 | | * of terms is close to the input size |
856 | | * (b) for efficiency of the operation: the sum of the |
857 | | * terms is small; ideally about twice the square |
858 | | * root of the input size. |
859 | | * So, for example, if the input hsize = 37, which is |
860 | | * a prime number, the decomposer will break this into two |
861 | | * terms, 6 and 6, so that the net result is a dilation |
862 | | * with hsize = 36. |
863 | | * </pre> |
864 | | */ |
865 | | PIX * |
866 | | pixOpenCompBrickDwa(PIX *pixd, |
867 | | PIX *pixs, |
868 | | l_int32 hsize, |
869 | | l_int32 vsize) |
870 | 0 | { |
871 | 0 | char *selnameh1, *selnameh2, *selnamev1, *selnamev2; |
872 | 0 | l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; |
873 | 0 | PIX *pixt1, *pixt2, *pixt3; |
874 | |
|
875 | 0 | if (!pixs) |
876 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
877 | 0 | if (pixGetDepth(pixs) != 1) |
878 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
879 | 0 | if (hsize < 1 || vsize < 1) |
880 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
881 | 0 | if (hsize > 63 || vsize > 63) |
882 | 0 | return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize); |
883 | | |
884 | 0 | if (hsize == 1 && vsize == 1) |
885 | 0 | return pixCopy(pixd, pixs); |
886 | | |
887 | 0 | hsize1 = hsize2 = vsize1 = vsize2 = 1; |
888 | 0 | selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; |
889 | 0 | if (hsize > 1) |
890 | 0 | getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, |
891 | 0 | &selnameh2, NULL, NULL); |
892 | 0 | if (vsize > 1) |
893 | 0 | getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, |
894 | 0 | &selnamev1, &selnamev2); |
895 | | |
896 | | /* For symmetric b.c., initialize erosion with bordercolor == 1 */ |
897 | 0 | bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); |
898 | 0 | pixt1 = pixAddBorder(pixs, 64, bordercolor); |
899 | |
|
900 | 0 | if (vsize == 1) { |
901 | 0 | if (hsize2 == 1) { |
902 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
903 | 0 | if (bordercolor == 1) |
904 | 0 | pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); |
905 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1); |
906 | 0 | } else { |
907 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
908 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); |
909 | 0 | if (bordercolor == 1) |
910 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); |
911 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); |
912 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); |
913 | 0 | } |
914 | 0 | } else if (hsize == 1) { |
915 | 0 | if (vsize2 == 1) { |
916 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); |
917 | 0 | if (bordercolor == 1) |
918 | 0 | pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); |
919 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); |
920 | 0 | } else { |
921 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); |
922 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); |
923 | 0 | if (bordercolor == 1) |
924 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); |
925 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); |
926 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); |
927 | 0 | } |
928 | 0 | } else { /* vsize and hsize both > 1 */ |
929 | 0 | if (hsize2 == 1 && vsize2 == 1) { |
930 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
931 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); |
932 | 0 | if (bordercolor == 1) |
933 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); |
934 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); |
935 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); |
936 | 0 | } else if (vsize2 == 1) { |
937 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
938 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); |
939 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); |
940 | 0 | if (bordercolor == 1) |
941 | 0 | pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); |
942 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); |
943 | 0 | pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2); |
944 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); |
945 | 0 | } else if (hsize2 == 1) { |
946 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
947 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); |
948 | 0 | pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2); |
949 | 0 | if (bordercolor == 1) |
950 | 0 | pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); |
951 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); |
952 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); |
953 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); |
954 | 0 | } else { /* both directions are combed */ |
955 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); |
956 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); |
957 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); |
958 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); |
959 | 0 | if (bordercolor == 1) |
960 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); |
961 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); |
962 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); |
963 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); |
964 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); |
965 | 0 | } |
966 | 0 | } |
967 | 0 | pixDestroy(&pixt3); |
968 | |
|
969 | 0 | pixDestroy(&pixt1); |
970 | 0 | pixt1 = pixRemoveBorder(pixt2, 64); |
971 | 0 | pixDestroy(&pixt2); |
972 | 0 | if (selnameh1) LEPT_FREE(selnameh1); |
973 | 0 | if (selnameh2) LEPT_FREE(selnameh2); |
974 | 0 | if (selnamev1) LEPT_FREE(selnamev1); |
975 | 0 | if (selnamev2) LEPT_FREE(selnamev2); |
976 | |
|
977 | 0 | if (!pixd) |
978 | 0 | return pixt1; |
979 | | |
980 | 0 | pixTransferAllData(pixd, &pixt1, 0, 0); |
981 | 0 | return pixd; |
982 | 0 | } |
983 | | |
984 | | |
985 | | /*! |
986 | | * \brief pixCloseCompBrickDwa() |
987 | | * |
988 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
989 | | * or different from pixs |
990 | | * \param[in] pixs 1 bpp |
991 | | * \param[in] hsize width of brick Sel |
992 | | * \param[in] vsize height of brick Sel |
993 | | * \return pixd |
994 | | * |
995 | | * <pre> |
996 | | * Notes: |
997 | | * (1) This implements a separable composite safe closing with 2D |
998 | | * brick Sels. |
999 | | * (2) For efficiency, it may decompose each linear morphological |
1000 | | * operation into two (brick + comb). |
1001 | | * (3) A brick Sel has hits for all elements. |
1002 | | * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) |
1003 | | * (5) Do separably if both hsize and vsize are > 1. |
1004 | | * (6) It is necessary that both horizontal and vertical Sels |
1005 | | * of the input size are defined in the basic sela. |
1006 | | * (7) There are three cases: |
1007 | | * (a) pixd == null (result into new pixd) |
1008 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
1009 | | * (c) pixd != pixs (puts result into existing pixd) |
1010 | | * (8) For clarity, if the case is known, use these patterns: |
1011 | | * (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...); |
1012 | | * (b) pixCloseCompBrickDwa(pixs, pixs, ...); |
1013 | | * (c) pixCloseCompBrickDwa(pixd, pixs, ...); |
1014 | | * (9) The size of pixd is determined by pixs. |
1015 | | * (10) CAUTION: both hsize and vsize are being decomposed. |
1016 | | * The decomposer chooses a product of sizes (call them |
1017 | | * 'terms') for each that is close to the input size, |
1018 | | * but not necessarily equal to it. It attempts to optimize: |
1019 | | * (a) for consistency with the input values: the product |
1020 | | * of terms is close to the input size |
1021 | | * (b) for efficiency of the operation: the sum of the |
1022 | | * terms is small; ideally about twice the square |
1023 | | * root of the input size. |
1024 | | * So, for example, if the input hsize = 37, which is |
1025 | | * a prime number, the decomposer will break this into two |
1026 | | * terms, 6 and 6, so that the net result is a dilation |
1027 | | * with hsize = 36. |
1028 | | * </pre> |
1029 | | */ |
1030 | | PIX * |
1031 | | pixCloseCompBrickDwa(PIX *pixd, |
1032 | | PIX *pixs, |
1033 | | l_int32 hsize, |
1034 | | l_int32 vsize) |
1035 | 0 | { |
1036 | 0 | char *selnameh1, *selnameh2, *selnamev1, *selnamev2; |
1037 | 0 | l_int32 hsize1, hsize2, vsize1, vsize2, setborder; |
1038 | 0 | PIX *pixt1, *pixt2, *pixt3; |
1039 | |
|
1040 | 0 | if (!pixs) |
1041 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
1042 | 0 | if (pixGetDepth(pixs) != 1) |
1043 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
1044 | 0 | if (hsize < 1 || vsize < 1) |
1045 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
1046 | 0 | if (hsize > 63 || vsize > 63) |
1047 | 0 | return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize); |
1048 | | |
1049 | 0 | if (hsize == 1 && vsize == 1) |
1050 | 0 | return pixCopy(pixd, pixs); |
1051 | | |
1052 | 0 | hsize1 = hsize2 = vsize1 = vsize2 = 1; |
1053 | 0 | selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; |
1054 | 0 | if (hsize > 1) |
1055 | 0 | getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, |
1056 | 0 | &selnameh2, NULL, NULL); |
1057 | 0 | if (vsize > 1) |
1058 | 0 | getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, |
1059 | 0 | &selnamev1, &selnamev2); |
1060 | |
|
1061 | 0 | pixt3 = NULL; |
1062 | | /* For symmetric b.c., PIX_SET border for erosions */ |
1063 | 0 | setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1); |
1064 | 0 | pixt1 = pixAddBorder(pixs, 64, 0); |
1065 | |
|
1066 | 0 | if (vsize == 1) { |
1067 | 0 | if (hsize2 == 1) { |
1068 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1); |
1069 | 0 | } else { |
1070 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
1071 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); |
1072 | 0 | if (setborder == 1) |
1073 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); |
1074 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); |
1075 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); |
1076 | 0 | } |
1077 | 0 | } else if (hsize == 1) { |
1078 | 0 | if (vsize2 == 1) { |
1079 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1); |
1080 | 0 | } else { |
1081 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); |
1082 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); |
1083 | 0 | if (setborder == 1) |
1084 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); |
1085 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); |
1086 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); |
1087 | 0 | } |
1088 | 0 | } else { /* vsize and hsize both > 1 */ |
1089 | 0 | if (hsize2 == 1 && vsize2 == 1) { |
1090 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
1091 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); |
1092 | 0 | if (setborder == 1) |
1093 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); |
1094 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); |
1095 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); |
1096 | 0 | } else if (vsize2 == 1) { |
1097 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
1098 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); |
1099 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); |
1100 | 0 | if (setborder == 1) |
1101 | 0 | pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); |
1102 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); |
1103 | 0 | pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2); |
1104 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); |
1105 | 0 | } else if (hsize2 == 1) { |
1106 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
1107 | 0 | pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); |
1108 | 0 | pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2); |
1109 | 0 | if (setborder == 1) |
1110 | 0 | pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); |
1111 | 0 | pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); |
1112 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); |
1113 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); |
1114 | 0 | } else { /* both directions are combed */ |
1115 | 0 | pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); |
1116 | 0 | pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); |
1117 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); |
1118 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); |
1119 | 0 | if (setborder == 1) |
1120 | 0 | pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); |
1121 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); |
1122 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); |
1123 | 0 | pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); |
1124 | 0 | pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); |
1125 | 0 | } |
1126 | 0 | } |
1127 | 0 | pixDestroy(&pixt3); |
1128 | |
|
1129 | 0 | pixDestroy(&pixt1); |
1130 | 0 | pixt1 = pixRemoveBorder(pixt2, 64); |
1131 | 0 | pixDestroy(&pixt2); |
1132 | 0 | if (selnameh1) LEPT_FREE(selnameh1); |
1133 | 0 | if (selnameh2) LEPT_FREE(selnameh2); |
1134 | 0 | if (selnamev1) LEPT_FREE(selnamev1); |
1135 | 0 | if (selnamev2) LEPT_FREE(selnamev2); |
1136 | |
|
1137 | 0 | if (!pixd) |
1138 | 0 | return pixt1; |
1139 | | |
1140 | 0 | pixTransferAllData(pixd, &pixt1, 0, 0); |
1141 | 0 | return pixd; |
1142 | 0 | } |
1143 | | |
1144 | | |
1145 | | /*--------------------------------------------------------------------------* |
1146 | | * Binary expanded composite morphological (dwa) ops with brick Sels * |
1147 | | *--------------------------------------------------------------------------*/ |
1148 | | /*! |
1149 | | * \brief pixDilateCompBrickExtendDwa() |
1150 | | * |
1151 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
1152 | | * or different from pixs |
1153 | | * \param[in] pixs 1 bpp |
1154 | | * \param[in] hsize width of brick Sel |
1155 | | * \param[in] vsize height of brick Sel |
1156 | | * \return pixd |
1157 | | * |
1158 | | * <pre> |
1159 | | * Notes: |
1160 | | * (1) Ankur Jain suggested and implemented extending the composite |
1161 | | * DWA operations beyond the 63 pixel limit. This is a |
1162 | | * simplified and approximate implementation of the extension. |
1163 | | * This allows arbitrary Dwa morph operations using brick Sels, |
1164 | | * by decomposing the horizontal and vertical dilations into |
1165 | | * a sequence of 63-element dilations plus a dilation of size |
1166 | | * between 3 and 62. |
1167 | | * (2) The 63-element dilations are exact, whereas the extra dilation |
1168 | | * is approximate, because the underlying decomposition is |
1169 | | * in pixDilateCompBrickDwa(). See there for further details. |
1170 | | * (3) There are three cases: |
1171 | | * (a) pixd == null (result into new pixd) |
1172 | | * (b) pixd == pixs (in-place; writes result back to pixs) |
1173 | | * (c) pixd != pixs (puts result into existing pixd) |
1174 | | * (4) There is no need to call this directly: pixDilateCompBrickDwa() |
1175 | | * calls this function if either brick dimension exceeds 63. |
1176 | | * </pre> |
1177 | | */ |
1178 | | PIX * |
1179 | | pixDilateCompBrickExtendDwa(PIX *pixd, |
1180 | | PIX *pixs, |
1181 | | l_int32 hsize, |
1182 | | l_int32 vsize) |
1183 | 0 | { |
1184 | 0 | l_int32 i, nops, nh, extrah, nv, extrav; |
1185 | 0 | PIX *pixt1, *pixt2, *pixt3; |
1186 | |
|
1187 | 0 | if (!pixs) |
1188 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
1189 | 0 | if (pixGetDepth(pixs) != 1) |
1190 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
1191 | 0 | if (hsize < 1 || vsize < 1) |
1192 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
1193 | | |
1194 | 0 | if (hsize < 64 && vsize < 64) |
1195 | 0 | return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); |
1196 | | |
1197 | 0 | if (hsize > 63) |
1198 | 0 | getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); |
1199 | 0 | if (vsize > 63) |
1200 | 0 | getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); |
1201 | | |
1202 | | /* Horizontal dilation first: pixs --> pixt2. Do not alter pixs. */ |
1203 | 0 | pixt1 = pixCreateTemplate(pixs); /* temp image */ |
1204 | 0 | if (hsize == 1) { |
1205 | 0 | pixt2 = pixClone(pixs); |
1206 | 0 | } else if (hsize < 64) { |
1207 | 0 | pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1); |
1208 | 0 | } else if (hsize == 64) { /* approximate */ |
1209 | 0 | pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); |
1210 | 0 | } else { |
1211 | 0 | nops = (extrah < 3) ? nh : nh + 1; |
1212 | 0 | if (nops & 1) { /* odd */ |
1213 | 0 | if (extrah > 2) |
1214 | 0 | pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1); |
1215 | 0 | else |
1216 | 0 | pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); |
1217 | 0 | for (i = 0; i < nops / 2; i++) { |
1218 | 0 | pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); |
1219 | 0 | pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); |
1220 | 0 | } |
1221 | 0 | } else { /* nops even */ |
1222 | 0 | if (extrah > 2) { |
1223 | 0 | pixDilateCompBrickDwa(pixt1, pixs, extrah, 1); |
1224 | 0 | pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); |
1225 | 0 | } else { /* they're all 63s */ |
1226 | 0 | pixDilateCompBrickDwa(pixt1, pixs, 63, 1); |
1227 | 0 | pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); |
1228 | 0 | } |
1229 | 0 | for (i = 0; i < nops / 2 - 1; i++) { |
1230 | 0 | pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); |
1231 | 0 | pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); |
1232 | 0 | } |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | | /* Vertical dilation: pixt2 --> pixt3. */ |
1237 | 0 | if (vsize == 1) { |
1238 | 0 | pixt3 = pixClone(pixt2); |
1239 | 0 | } else if (vsize < 64) { |
1240 | 0 | pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize); |
1241 | 0 | } else if (vsize == 64) { /* approximate */ |
1242 | 0 | pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); |
1243 | 0 | } else { |
1244 | 0 | nops = (extrav < 3) ? nv : nv + 1; |
1245 | 0 | if (nops & 1) { /* odd */ |
1246 | 0 | if (extrav > 2) |
1247 | 0 | pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav); |
1248 | 0 | else |
1249 | 0 | pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); |
1250 | 0 | for (i = 0; i < nops / 2; i++) { |
1251 | 0 | pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); |
1252 | 0 | pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); |
1253 | 0 | } |
1254 | 0 | } else { /* nops even */ |
1255 | 0 | if (extrav > 2) { |
1256 | 0 | pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav); |
1257 | 0 | pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); |
1258 | 0 | } else { /* they're all 63s */ |
1259 | 0 | pixDilateCompBrickDwa(pixt1, pixt2, 1, 63); |
1260 | 0 | pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); |
1261 | 0 | } |
1262 | 0 | for (i = 0; i < nops / 2 - 1; i++) { |
1263 | 0 | pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); |
1264 | 0 | pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); |
1265 | 0 | } |
1266 | 0 | } |
1267 | 0 | } |
1268 | 0 | pixDestroy(&pixt1); |
1269 | 0 | pixDestroy(&pixt2); |
1270 | |
|
1271 | 0 | if (!pixd) |
1272 | 0 | return pixt3; |
1273 | | |
1274 | 0 | pixTransferAllData(pixd, &pixt3, 0, 0); |
1275 | 0 | return pixd; |
1276 | 0 | } |
1277 | | |
1278 | | |
1279 | | /*! |
1280 | | * \brief pixErodeCompBrickExtendDwa() |
1281 | | * |
1282 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
1283 | | * or different from pixs |
1284 | | * \param[in] pixs 1 bpp |
1285 | | * \param[in] hsize width of brick Sel |
1286 | | * \param[in] vsize height of brick Sel |
1287 | | * \return pixd |
1288 | | * |
1289 | | * <pre> |
1290 | | * Notes: |
1291 | | * (1) See pixDilateCompBrickExtendDwa() for usage. |
1292 | | * (2) There is no need to call this directly: pixErodeCompBrickDwa() |
1293 | | * calls this function if either brick dimension exceeds 63. |
1294 | | * </pre> |
1295 | | */ |
1296 | | PIX * |
1297 | | pixErodeCompBrickExtendDwa(PIX *pixd, |
1298 | | PIX *pixs, |
1299 | | l_int32 hsize, |
1300 | | l_int32 vsize) |
1301 | 0 | { |
1302 | 0 | l_int32 i, nops, nh, extrah, nv, extrav; |
1303 | 0 | PIX *pixt1, *pixt2, *pixt3; |
1304 | |
|
1305 | 0 | if (!pixs) |
1306 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
1307 | 0 | if (pixGetDepth(pixs) != 1) |
1308 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
1309 | 0 | if (hsize < 1 || vsize < 1) |
1310 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
1311 | | |
1312 | 0 | if (hsize < 64 && vsize < 64) |
1313 | 0 | return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); |
1314 | | |
1315 | 0 | if (hsize > 63) |
1316 | 0 | getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); |
1317 | 0 | if (vsize > 63) |
1318 | 0 | getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); |
1319 | | |
1320 | | /* Horizontal erosion first: pixs --> pixt2. Do not alter pixs. */ |
1321 | 0 | pixt1 = pixCreateTemplate(pixs); /* temp image */ |
1322 | 0 | if (hsize == 1) { |
1323 | 0 | pixt2 = pixClone(pixs); |
1324 | 0 | } else if (hsize < 64) { |
1325 | 0 | pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1); |
1326 | 0 | } else if (hsize == 64) { /* approximate */ |
1327 | 0 | pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); |
1328 | 0 | } else { |
1329 | 0 | nops = (extrah < 3) ? nh : nh + 1; |
1330 | 0 | if (nops & 1) { /* odd */ |
1331 | 0 | if (extrah > 2) |
1332 | 0 | pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1); |
1333 | 0 | else |
1334 | 0 | pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); |
1335 | 0 | for (i = 0; i < nops / 2; i++) { |
1336 | 0 | pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); |
1337 | 0 | pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); |
1338 | 0 | } |
1339 | 0 | } else { /* nops even */ |
1340 | 0 | if (extrah > 2) { |
1341 | 0 | pixErodeCompBrickDwa(pixt1, pixs, extrah, 1); |
1342 | 0 | pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); |
1343 | 0 | } else { /* they're all 63s */ |
1344 | 0 | pixErodeCompBrickDwa(pixt1, pixs, 63, 1); |
1345 | 0 | pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); |
1346 | 0 | } |
1347 | 0 | for (i = 0; i < nops / 2 - 1; i++) { |
1348 | 0 | pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); |
1349 | 0 | pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); |
1350 | 0 | } |
1351 | 0 | } |
1352 | 0 | } |
1353 | | |
1354 | | /* Vertical erosion: pixt2 --> pixt3. */ |
1355 | 0 | if (vsize == 1) { |
1356 | 0 | pixt3 = pixClone(pixt2); |
1357 | 0 | } else if (vsize < 64) { |
1358 | 0 | pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize); |
1359 | 0 | } else if (vsize == 64) { /* approximate */ |
1360 | 0 | pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); |
1361 | 0 | } else { |
1362 | 0 | nops = (extrav < 3) ? nv : nv + 1; |
1363 | 0 | if (nops & 1) { /* odd */ |
1364 | 0 | if (extrav > 2) |
1365 | 0 | pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav); |
1366 | 0 | else |
1367 | 0 | pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); |
1368 | 0 | for (i = 0; i < nops / 2; i++) { |
1369 | 0 | pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); |
1370 | 0 | pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); |
1371 | 0 | } |
1372 | 0 | } else { /* nops even */ |
1373 | 0 | if (extrav > 2) { |
1374 | 0 | pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav); |
1375 | 0 | pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); |
1376 | 0 | } else { /* they're all 63s */ |
1377 | 0 | pixErodeCompBrickDwa(pixt1, pixt2, 1, 63); |
1378 | 0 | pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); |
1379 | 0 | } |
1380 | 0 | for (i = 0; i < nops / 2 - 1; i++) { |
1381 | 0 | pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); |
1382 | 0 | pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); |
1383 | 0 | } |
1384 | 0 | } |
1385 | 0 | } |
1386 | 0 | pixDestroy(&pixt1); |
1387 | 0 | pixDestroy(&pixt2); |
1388 | |
|
1389 | 0 | if (!pixd) |
1390 | 0 | return pixt3; |
1391 | | |
1392 | 0 | pixTransferAllData(pixd, &pixt3, 0, 0); |
1393 | 0 | return pixd; |
1394 | 0 | } |
1395 | | |
1396 | | |
1397 | | /*! |
1398 | | * \brief pixOpenCompBrickExtendDwa() |
1399 | | * |
1400 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
1401 | | * or different from pixs |
1402 | | * \param[in] pixs 1 bpp |
1403 | | * \param[in] hsize width of brick Sel |
1404 | | * \param[in] vsize height of brick Sel |
1405 | | * \return pixd |
1406 | | * |
1407 | | * <pre> |
1408 | | * Notes: |
1409 | | * 1) There are three cases: |
1410 | | * a) pixd == null (result into new pixd |
1411 | | * b) pixd == pixs (in-place; writes result back to pixs |
1412 | | * c) pixd != pixs (puts result into existing pixd |
1413 | | * 2) There is no need to call this directly: pixOpenCompBrickDwa( |
1414 | | * calls this function if either brick dimension exceeds 63. |
1415 | | * </pre> |
1416 | | */ |
1417 | | PIX * |
1418 | | pixOpenCompBrickExtendDwa(PIX *pixd, |
1419 | | PIX *pixs, |
1420 | | l_int32 hsize, |
1421 | | l_int32 vsize) |
1422 | 0 | { |
1423 | 0 | PIX *pixt; |
1424 | |
|
1425 | 0 | if (!pixs) |
1426 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
1427 | 0 | if (pixGetDepth(pixs) != 1) |
1428 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
1429 | 0 | if (hsize < 1 || vsize < 1) |
1430 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
1431 | | |
1432 | 0 | pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize); |
1433 | 0 | pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize); |
1434 | 0 | pixDestroy(&pixt); |
1435 | 0 | return pixd; |
1436 | 0 | } |
1437 | | |
1438 | | |
1439 | | /*! |
1440 | | * \brief pixCloseCompBrickExtendDwa() |
1441 | | * |
1442 | | * \param[in] pixd [optional]; this can be null, equal to pixs, |
1443 | | * or different from pixs |
1444 | | * \param[in] pixs 1 bpp |
1445 | | * \param[in] hsize width of brick Sel |
1446 | | * \param[in] vsize height of brick Sel |
1447 | | * \return pixd |
1448 | | * |
1449 | | * <pre> |
1450 | | * Notes: |
1451 | | * 1) There are three cases: |
1452 | | * a) pixd == null (result into new pixd |
1453 | | * b) pixd == pixs (in-place; writes result back to pixs |
1454 | | * c) pixd != pixs (puts result into existing pixd |
1455 | | * 2) There is no need to call this directly: pixCloseCompBrickDwa( |
1456 | | * calls this function if either brick dimension exceeds 63. |
1457 | | * </pre> |
1458 | | */ |
1459 | | PIX * |
1460 | | pixCloseCompBrickExtendDwa(PIX *pixd, |
1461 | | PIX *pixs, |
1462 | | l_int32 hsize, |
1463 | | l_int32 vsize) |
1464 | 0 | { |
1465 | 0 | l_int32 bordercolor, borderx, bordery; |
1466 | 0 | PIX *pixt1, *pixt2, *pixt3; |
1467 | |
|
1468 | 0 | if (!pixs) |
1469 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
1470 | 0 | if (pixGetDepth(pixs) != 1) |
1471 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); |
1472 | 0 | if (hsize < 1 || vsize < 1) |
1473 | 0 | return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); |
1474 | | |
1475 | | /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need |
1476 | | * an extra 32 OFF pixels around the image (in addition to |
1477 | | * the 32 added pixels for all dwa operations), whereas with |
1478 | | * SYMMETRIC_MORPH_BC this is not necessary. */ |
1479 | 0 | bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); |
1480 | 0 | if (bordercolor == 0) { /* asymmetric b.c. */ |
1481 | 0 | borderx = 32 + (hsize / 64) * 32; |
1482 | 0 | bordery = 32 + (vsize / 64) * 32; |
1483 | 0 | } else { /* symmetric b.c. */ |
1484 | 0 | borderx = bordery = 32; |
1485 | 0 | } |
1486 | 0 | pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0); |
1487 | |
|
1488 | 0 | pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize); |
1489 | 0 | pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize); |
1490 | |
|
1491 | 0 | pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery); |
1492 | 0 | pixDestroy(&pixt1); |
1493 | 0 | pixDestroy(&pixt2); |
1494 | |
|
1495 | 0 | if (!pixd) |
1496 | 0 | return pixt3; |
1497 | | |
1498 | 0 | pixTransferAllData(pixd, &pixt3, 0, 0); |
1499 | 0 | return pixd; |
1500 | 0 | } |
1501 | | |
1502 | | |
1503 | | /*! |
1504 | | * \brief getExtendedCompositeParameters() |
1505 | | * |
1506 | | * \param[in] size of linear Sel |
1507 | | * \param[out] pn number of 63 wide convolutions |
1508 | | * \param[out] pextra size of extra Sel |
1509 | | * \param[out] pactualsize [optional] actual size used in operation |
1510 | | * \return 0 if OK, 1 on error |
1511 | | * |
1512 | | * <pre> |
1513 | | * Notes: |
1514 | | * (1) The DWA implementation allows Sels to be used with hits |
1515 | | * up to 31 pixels from the origin, either horizontally or |
1516 | | * vertically. Larger Sels can be used if decomposed into |
1517 | | * a set of operations with Sels not exceeding 63 pixels |
1518 | | * in either width or height (and with the origin as close |
1519 | | * to the center of the Sel as possible). |
1520 | | * (2) This returns the decomposition of a linear Sel of length |
1521 | | * %size into a set of %n Sels of length 63 plus an extra |
1522 | | * Sel of length %extra. |
1523 | | * (3) For notation, let w == %size, n == %n, and e == %extra. |
1524 | | * We have 1 < e < 63. |
1525 | | * |
1526 | | * Then if w < 64, we have n = 0 and e = w. |
1527 | | * The general formula for w > 63 is: |
1528 | | * w = 63 + (n - 1) * 62 + (e - 1) |
1529 | | * |
1530 | | * Where did this come from? Each successive convolution with |
1531 | | * a Sel of length L adds a total length (L - 1) to w. |
1532 | | * This accounts for using 62 for each additional Sel of size 63, |
1533 | | * and using (e - 1) for the additional Sel of size e. |
1534 | | * |
1535 | | * Solving for n and e for w > 63: |
1536 | | * n = 1 + Int((w - 63) / 62) |
1537 | | * e = w - 63 - (n - 1) * 62 + 1 |
1538 | | * |
1539 | | * The extra part is decomposed into two factors f1 and f2, |
1540 | | * and the actual size of the extra part is |
1541 | | * e' = f1 * f2 |
1542 | | * Then the actual width is: |
1543 | | * w' = 63 + (n - 1) * 62 + f1 * f2 - 1 |
1544 | | * </pre> |
1545 | | */ |
1546 | | l_ok |
1547 | | getExtendedCompositeParameters(l_int32 size, |
1548 | | l_int32 *pn, |
1549 | | l_int32 *pextra, |
1550 | | l_int32 *pactualsize) |
1551 | 0 | { |
1552 | 0 | l_int32 n, extra, fact1, fact2; |
1553 | |
|
1554 | 0 | if (!pn || !pextra) |
1555 | 0 | return ERROR_INT("&n and &extra not both defined", __func__, 1); |
1556 | | |
1557 | 0 | if (size <= 63) { |
1558 | 0 | n = 0; |
1559 | 0 | extra = L_MIN(1, size); |
1560 | 0 | } else { /* size > 63 */ |
1561 | 0 | n = 1 + (l_int32)((size - 63) / 62); |
1562 | 0 | extra = size - 63 - (n - 1) * 62 + 1; |
1563 | 0 | } |
1564 | |
|
1565 | 0 | if (pactualsize) { |
1566 | 0 | selectComposableSizes(extra, &fact1, &fact2); |
1567 | 0 | *pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1; |
1568 | 0 | } |
1569 | |
|
1570 | 0 | *pn = n; |
1571 | 0 | *pextra = extra; |
1572 | 0 | return 0; |
1573 | 0 | } |