/src/leptonica/src/ccthin.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 ccthin.c |
29 | | * <pre> |
30 | | * |
31 | | * PIXA *pixaThinConnected() |
32 | | * PIX *pixThinConnected() |
33 | | * PIX *pixThinConnectedBySet() |
34 | | * SELA *selaMakeThinSets() |
35 | | * </pre> |
36 | | */ |
37 | | |
38 | | #ifdef HAVE_CONFIG_H |
39 | | #include <config_auto.h> |
40 | | #endif /* HAVE_CONFIG_H */ |
41 | | |
42 | | #include "allheaders.h" |
43 | | |
44 | | /* ------------------------------------------------------------ |
45 | | * The sels used here (and their rotated counterparts) are the |
46 | | * useful 3x3 Sels for thinning. They are defined in sel2.c, |
47 | | * and the sets are constructed in selaMakeThinSets(). |
48 | | * The notation is based on "Connectivity-preserving morphological |
49 | | * image transformations", a version of which can be found at |
50 | | * http://www.leptonica.com/papers/conn.pdf |
51 | | * ------------------------------------------------------------ */ |
52 | | |
53 | | /*----------------------------------------------------------------* |
54 | | * CC-preserving thinning * |
55 | | *----------------------------------------------------------------*/ |
56 | | /*! |
57 | | * \brief pixaThinConnected() |
58 | | * |
59 | | * \param[in] pixas of 1 bpp pix |
60 | | * \param[in] type L_THIN_FG, L_THIN_BG |
61 | | * \param[in] connectivity 4 or 8 |
62 | | * \param[in] maxiters max number of iters allowed; |
63 | | * use 0 to iterate until completion |
64 | | * \return pixds, or NULL on error |
65 | | * |
66 | | * <pre> |
67 | | * Notes: |
68 | | * (1) See notes in pixThinConnected(). |
69 | | * </pre> |
70 | | */ |
71 | | PIXA * |
72 | | pixaThinConnected(PIXA *pixas, |
73 | | l_int32 type, |
74 | | l_int32 connectivity, |
75 | | l_int32 maxiters) |
76 | 1.70k | { |
77 | 1.70k | l_int32 i, n, d, same; |
78 | 1.70k | PIX *pix1, *pix2; |
79 | 1.70k | PIXA *pixad; |
80 | 1.70k | SELA *sela; |
81 | | |
82 | 1.70k | if (!pixas) |
83 | 1.70k | return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); |
84 | 0 | if (type != L_THIN_FG && type != L_THIN_BG) |
85 | 0 | return (PIXA *)ERROR_PTR("invalid fg/bg type", __func__, NULL); |
86 | 0 | if (connectivity != 4 && connectivity != 8) |
87 | 0 | return (PIXA *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); |
88 | 0 | if (maxiters == 0) maxiters = 10000; |
89 | |
|
90 | 0 | pixaVerifyDepth(pixas, &same, &d); |
91 | 0 | if (d != 1) |
92 | 0 | return (PIXA *)ERROR_PTR("pix are not all 1 bpp", __func__, NULL); |
93 | | |
94 | 0 | if (connectivity == 4) |
95 | 0 | sela = selaMakeThinSets(1, 0); |
96 | 0 | else /* connectivity == 8 */ |
97 | 0 | sela = selaMakeThinSets(5, 0); |
98 | |
|
99 | 0 | n = pixaGetCount(pixas); |
100 | 0 | pixad = pixaCreate(n); |
101 | 0 | for (i = 0; i < n; i++) { |
102 | 0 | pix1 = pixaGetPix(pixas, i, L_CLONE); |
103 | 0 | pix2 = pixThinConnectedBySet(pix1, type, sela, maxiters); |
104 | 0 | pixaAddPix(pixad, pix2, L_INSERT); |
105 | 0 | pixDestroy(&pix1); |
106 | 0 | } |
107 | |
|
108 | 0 | selaDestroy(&sela); |
109 | 0 | return pixad; |
110 | 0 | } |
111 | | |
112 | | |
113 | | /*! |
114 | | * \brief pixThinConnected() |
115 | | * |
116 | | * \param[in] pixs 1 bpp |
117 | | * \param[in] type L_THIN_FG, L_THIN_BG |
118 | | * \param[in] connectivity 4 or 8 |
119 | | * \param[in] maxiters max number of iters allowed; |
120 | | * use 0 to iterate until completion |
121 | | * \return pixd, or NULL on error |
122 | | * |
123 | | * <pre> |
124 | | * Notes: |
125 | | * (1) See "Connectivity-preserving morphological image transformations," |
126 | | * Dan S. Bloomberg, in SPIE Visual Communications and Image |
127 | | * Processing, Conference 1606, pp. 320-334, November 1991, |
128 | | * Boston, MA. A web version is available at |
129 | | * http://www.leptonica.com/papers/conn.pdf |
130 | | * (2) This is a simple interface for two of the best iterative |
131 | | * morphological thinning algorithms, for 4-c.c and 8-c.c. |
132 | | * Each iteration uses a mixture of parallel operations |
133 | | * (using several different 3x3 Sels) and serial operations. |
134 | | * Specifically, each thinning iteration consists of |
135 | | * four sequential thinnings from each of four directions. |
136 | | * Each of these thinnings is a parallel composite |
137 | | * operation, where the union of a set of HMTs are set |
138 | | * subtracted from the input. For 4-cc thinning, we |
139 | | * use 3 HMTs in parallel, and for 8-cc thinning we use 4 HMTs. |
140 | | * (3) A "good" thinning algorithm is one that generates a skeleton |
141 | | * that is near the medial axis and has neither pruned |
142 | | * real branches nor left extra dendritic branches. |
143 | | * (4) Duality between operations on fg and bg require switching |
144 | | * the connectivity. To thin the foreground, which is the usual |
145 | | * situation, use type == L_THIN_FG. Thickening the foreground |
146 | | * is equivalent to thinning the background (type == L_THIN_BG), |
147 | | * where the alternate connectivity gets preserved. |
148 | | * For example, to thicken the fg with 2 rounds of iterations |
149 | | * using 4-c.c., thin the bg using Sels that preserve 8-connectivity: |
150 | | * Pix *pix = pixThinConnected(pixs, L_THIN_BG, 8, 2); |
151 | | * (5) This makes and destroys the sela set each time. It's not a large |
152 | | * overhead, but if you are calling this thousands of times on |
153 | | * very small images, you can avoid the overhead; e.g. |
154 | | * Sela *sela = selaMakeThinSets(1, 0); // for 4-c.c. |
155 | | * Pix *pix = pixThinConnectedBySet(pixs, L_THIN_FG, sela, 0); |
156 | | * using set 1 for 4-c.c. and set 5 for 8-c.c operations. |
157 | | * </pre> |
158 | | */ |
159 | | PIX * |
160 | | pixThinConnected(PIX *pixs, |
161 | | l_int32 type, |
162 | | l_int32 connectivity, |
163 | | l_int32 maxiters) |
164 | 231k | { |
165 | 231k | PIX *pixd; |
166 | 231k | SELA *sela; |
167 | | |
168 | 231k | if (!pixs) |
169 | 41 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
170 | 231k | if (pixGetDepth(pixs) != 1) |
171 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); |
172 | 231k | if (type != L_THIN_FG && type != L_THIN_BG) |
173 | 0 | return (PIX *)ERROR_PTR("invalid fg/bg type", __func__, NULL); |
174 | 231k | if (connectivity != 4 && connectivity != 8) |
175 | 0 | return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); |
176 | 231k | if (maxiters == 0) maxiters = 10000; |
177 | | |
178 | 231k | if (connectivity == 4) |
179 | 0 | sela = selaMakeThinSets(1, 0); |
180 | 231k | else /* connectivity == 8 */ |
181 | 231k | sela = selaMakeThinSets(5, 0); |
182 | | |
183 | 231k | pixd = pixThinConnectedBySet(pixs, type, sela, maxiters); |
184 | | |
185 | 231k | selaDestroy(&sela); |
186 | 231k | return pixd; |
187 | 231k | } |
188 | | |
189 | | |
190 | | /*! |
191 | | * \brief pixThinConnectedBySet() |
192 | | * |
193 | | * \param[in] pixs 1 bpp |
194 | | * \param[in] type L_THIN_FG, L_THIN_BG |
195 | | * \param[in] sela of Sels for parallel composite HMTs |
196 | | * \param[in] maxiters max number of iters allowed; |
197 | | * use 0 to iterate until completion |
198 | | * \return pixd, or NULL on error |
199 | | * |
200 | | * <pre> |
201 | | * Notes: |
202 | | * (1) See notes in pixThinConnected(). |
203 | | * (2) This takes a sela representing one of 11 sets of HMT Sels. |
204 | | * The HMTs from this set are run in parallel and the result |
205 | | * is OR'd before being subtracted from the source. For each |
206 | | * iteration, this "parallel" thin is performed four times |
207 | | * sequentially, for sels rotated by 90 degrees in all four |
208 | | * directions. |
209 | | * (3) The "parallel" and "sequential" nomenclature is standard |
210 | | * in digital filtering. Here, "parallel" operations work on the |
211 | | * same source (pixd), and accumulate the results in a temp |
212 | | * image before actually applying them to the source (in this |
213 | | * case, using an in-place subtraction). "Sequential" operations |
214 | | * operate directly on the source (pixd) to produce the result |
215 | | * (in this case, with four sequential thinning operations, one |
216 | | * from each of four directions). |
217 | | * </pre> |
218 | | */ |
219 | | PIX * |
220 | | pixThinConnectedBySet(PIX *pixs, |
221 | | l_int32 type, |
222 | | SELA *sela, |
223 | | l_int32 maxiters) |
224 | 231k | { |
225 | 231k | l_int32 i, j, r, nsels, same; |
226 | 231k | PIXA *pixahmt; |
227 | 231k | PIX **pixhmt; /* array owned by pixahmt; do not destroy! */ |
228 | 231k | PIX *pix1, *pix2, *pixd; |
229 | 231k | SEL *sel, *selr; |
230 | | |
231 | 231k | if (!pixs) |
232 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
233 | 231k | if (pixGetDepth(pixs) != 1) |
234 | 0 | return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); |
235 | 231k | if (type != L_THIN_FG && type != L_THIN_BG) |
236 | 0 | return (PIX *)ERROR_PTR("invalid fg/bg type", __func__, NULL); |
237 | 231k | if (!sela) |
238 | 0 | return (PIX *)ERROR_PTR("sela not defined", __func__, NULL); |
239 | 231k | if (maxiters == 0) maxiters = 10000; |
240 | | |
241 | | /* Set up array of temp pix to hold hmts */ |
242 | 231k | nsels = selaGetCount(sela); |
243 | 231k | pixahmt = pixaCreate(nsels); |
244 | 1.15M | for (i = 0; i < nsels; i++) { |
245 | 925k | pix1 = pixCreateTemplate(pixs); |
246 | 925k | pixaAddPix(pixahmt, pix1, L_INSERT); |
247 | 925k | } |
248 | 231k | pixhmt = pixaGetPixArray(pixahmt); |
249 | 231k | if (!pixhmt) { |
250 | 0 | pixaDestroy(&pixahmt); |
251 | 0 | return (PIX *)ERROR_PTR("pixhmt array not made", __func__, NULL); |
252 | 0 | } |
253 | | |
254 | | /* Set up initial image for fg thinning */ |
255 | 231k | if (type == L_THIN_FG) |
256 | 231k | pixd = pixCopy(NULL, pixs); |
257 | 0 | else /* bg thinning */ |
258 | 0 | pixd = pixInvert(NULL, pixs); |
259 | | |
260 | | /* Thin the fg, with up to maxiters iterations */ |
261 | 624k | for (i = 0; i < maxiters; i++) { |
262 | 624k | pix1 = pixCopy(NULL, pixd); /* test for completion */ |
263 | 3.12M | for (r = 0; r < 4; r++) { /* over 90 degree rotations of Sels */ |
264 | 12.4M | for (j = 0; j < nsels; j++) { /* over individual sels in sela */ |
265 | 9.99M | sel = selaGetSel(sela, j); /* not a copy */ |
266 | 9.99M | selr = selRotateOrth(sel, r); |
267 | 9.99M | pixHMT(pixhmt[j], pixd, selr); |
268 | 9.99M | selDestroy(&selr); |
269 | 9.99M | if (j > 0) |
270 | 7.49M | pixOr(pixhmt[0], pixhmt[0], pixhmt[j]); /* accum result */ |
271 | 9.99M | } |
272 | 2.49M | pixSubtract(pixd, pixd, pixhmt[0]); /* remove result */ |
273 | 2.49M | } |
274 | 624k | pixEqual(pixd, pix1, &same); |
275 | 624k | pixDestroy(&pix1); |
276 | 624k | if (same) { |
277 | | /* L_INFO("%d iterations to completion\n", __func__, i); */ |
278 | 231k | break; |
279 | 231k | } |
280 | 624k | } |
281 | | |
282 | | /* This is a bit tricky. If we're thickening the foreground, then |
283 | | * we get a fg border of thickness equal to the number of |
284 | | * iterations. This border is connected to all components that |
285 | | * were initially touching the border, but as it grows, it does |
286 | | * not touch other growing components -- it leaves a 1 pixel wide |
287 | | * background between it and the growing components, and that |
288 | | * thin background prevents the components from growing further. |
289 | | * This border can be entirely removed as follows: |
290 | | * (1) Subtract the original (unthickened) image pixs from the |
291 | | * thickened image. This removes the pixels that were originally |
292 | | * touching the border. |
293 | | * (2) Get all remaining pixels that are connected to the border. |
294 | | * (3) Remove those pixels from the thickened image. */ |
295 | 231k | if (type == L_THIN_BG) { |
296 | 0 | pixInvert(pixd, pixd); /* finish with duality */ |
297 | 0 | pix1 = pixSubtract(NULL, pixd, pixs); |
298 | 0 | pix2 = pixExtractBorderConnComps(pix1, 4); |
299 | 0 | pixSubtract(pixd, pixd, pix2); |
300 | 0 | pixDestroy(&pix1); |
301 | 0 | pixDestroy(&pix2); |
302 | 0 | } |
303 | | |
304 | 231k | pixaDestroy(&pixahmt); |
305 | 231k | return pixd; |
306 | 231k | } |
307 | | |
308 | | |
309 | | /*! |
310 | | * \brief selaMakeThinSets() |
311 | | * |
312 | | * \param[in] index into specific sets |
313 | | * \param[in] debug 1 to output display of sela |
314 | | * \return sela, or NULL on error |
315 | | * |
316 | | * <pre> |
317 | | * Notes: |
318 | | * (1) These are specific sets of HMTs to be used in parallel for |
319 | | * for thinning from each of four directions. |
320 | | * (2) The sets are indexed as follows: |
321 | | * For thinning (e.g., run to completion): |
322 | | * index = 1 sel_4_1, sel_4_2, sel_4_3 |
323 | | * index = 2 sel_4_1, sel_4_5, sel_4_6 |
324 | | * index = 3 sel_4_1, sel_4_7, sel_4_7_rot |
325 | | * index = 4 sel_48_1, sel_48_1_rot, sel_48_2 |
326 | | * index = 5 sel_8_2, sel_8_3, sel_8_5, sel_8_6 |
327 | | * index = 6 sel_8_2, sel_8_3, sel_48_2 |
328 | | * index = 7 sel_8_1, sel_8_5, sel_8_6 |
329 | | * index = 8 sel_8_2, sel_8_3, sel_8_8, sel_8_9 |
330 | | * index = 9 sel_8_5, sel_8_6, sel_8_7, sel_8_7_rot |
331 | | * For thickening (e.g., just a few iterations): |
332 | | * index = 10 sel_4_2, sel_4_3 |
333 | | * index = 11 sel_8_4 |
334 | | * (3) For a very smooth skeleton, use set 1 for 4 connected and |
335 | | * set 5 for 8 connected thins. |
336 | | * </pre> |
337 | | */ |
338 | | SELA * |
339 | | selaMakeThinSets(l_int32 index, |
340 | | l_int32 debug) |
341 | 231k | { |
342 | 231k | SEL *sel; |
343 | 231k | SELA *sela1, *sela2, *sela3; |
344 | | |
345 | 231k | if (index < 1 || index > 11) |
346 | 0 | return (SELA *)ERROR_PTR("invalid index", __func__, NULL); |
347 | | |
348 | 231k | sela2 = selaCreate(4); |
349 | 231k | switch(index) |
350 | 231k | { |
351 | 0 | case 1: |
352 | 0 | sela1 = sela4ccThin(NULL); |
353 | 0 | selaFindSelByName(sela1, "sel_4_1", NULL, &sel); |
354 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
355 | 0 | selaFindSelByName(sela1, "sel_4_2", NULL, &sel); |
356 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
357 | 0 | selaFindSelByName(sela1, "sel_4_3", NULL, &sel); |
358 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
359 | 0 | break; |
360 | 0 | case 2: |
361 | 0 | sela1 = sela4ccThin(NULL); |
362 | 0 | selaFindSelByName(sela1, "sel_4_1", NULL, &sel); |
363 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
364 | 0 | selaFindSelByName(sela1, "sel_4_5", NULL, &sel); |
365 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
366 | 0 | selaFindSelByName(sela1, "sel_4_6", NULL, &sel); |
367 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
368 | 0 | break; |
369 | 0 | case 3: |
370 | 0 | sela1 = sela4ccThin(NULL); |
371 | 0 | selaFindSelByName(sela1, "sel_4_1", NULL, &sel); |
372 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
373 | 0 | selaFindSelByName(sela1, "sel_4_7", NULL, &sel); |
374 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
375 | 0 | sel = selRotateOrth(sel, 1); |
376 | 0 | selaAddSel(sela2, sel, "sel_4_7_rot", L_INSERT); |
377 | 0 | break; |
378 | 0 | case 4: |
379 | 0 | sela1 = sela4and8ccThin(NULL); |
380 | 0 | selaFindSelByName(sela1, "sel_48_1", NULL, &sel); |
381 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
382 | 0 | sel = selRotateOrth(sel, 1); |
383 | 0 | selaAddSel(sela2, sel, "sel_48_1_rot", L_INSERT); |
384 | 0 | selaFindSelByName(sela1, "sel_48_2", NULL, &sel); |
385 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
386 | 0 | break; |
387 | 231k | case 5: |
388 | 231k | sela1 = sela8ccThin(NULL); |
389 | 231k | selaFindSelByName(sela1, "sel_8_2", NULL, &sel); |
390 | 231k | selaAddSel(sela2, sel, NULL, L_COPY); |
391 | 231k | selaFindSelByName(sela1, "sel_8_3", NULL, &sel); |
392 | 231k | selaAddSel(sela2, sel, NULL, L_COPY); |
393 | 231k | selaFindSelByName(sela1, "sel_8_5", NULL, &sel); |
394 | 231k | selaAddSel(sela2, sel, NULL, L_COPY); |
395 | 231k | selaFindSelByName(sela1, "sel_8_6", NULL, &sel); |
396 | 231k | selaAddSel(sela2, sel, NULL, L_COPY); |
397 | 231k | break; |
398 | 0 | case 6: |
399 | 0 | sela1 = sela8ccThin(NULL); |
400 | 0 | sela3 = sela4and8ccThin(NULL); |
401 | 0 | selaFindSelByName(sela1, "sel_8_2", NULL, &sel); |
402 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
403 | 0 | selaFindSelByName(sela1, "sel_8_3", NULL, &sel); |
404 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
405 | 0 | selaFindSelByName(sela3, "sel_48_2", NULL, &sel); |
406 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
407 | 0 | selaDestroy(&sela3); |
408 | 0 | break; |
409 | 0 | case 7: |
410 | 0 | sela1 = sela8ccThin(NULL); |
411 | 0 | selaFindSelByName(sela1, "sel_8_1", NULL, &sel); |
412 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
413 | 0 | selaFindSelByName(sela1, "sel_8_5", NULL, &sel); |
414 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
415 | 0 | selaFindSelByName(sela1, "sel_8_6", NULL, &sel); |
416 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
417 | 0 | break; |
418 | 0 | case 8: |
419 | 0 | sela1 = sela8ccThin(NULL); |
420 | 0 | selaFindSelByName(sela1, "sel_8_2", NULL, &sel); |
421 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
422 | 0 | selaFindSelByName(sela1, "sel_8_3", NULL, &sel); |
423 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
424 | 0 | selaFindSelByName(sela1, "sel_8_8", NULL, &sel); |
425 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
426 | 0 | selaFindSelByName(sela1, "sel_8_9", NULL, &sel); |
427 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
428 | 0 | break; |
429 | 0 | case 9: |
430 | 0 | sela1 = sela8ccThin(NULL); |
431 | 0 | selaFindSelByName(sela1, "sel_8_5", NULL, &sel); |
432 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
433 | 0 | selaFindSelByName(sela1, "sel_8_6", NULL, &sel); |
434 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
435 | 0 | selaFindSelByName(sela1, "sel_8_7", NULL, &sel); |
436 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
437 | 0 | sel = selRotateOrth(sel, 1); |
438 | 0 | selaAddSel(sela2, sel, "sel_8_7_rot", L_INSERT); |
439 | 0 | break; |
440 | 0 | case 10: /* thicken for this one; use just a few iterations */ |
441 | 0 | sela1 = sela4ccThin(NULL); |
442 | 0 | selaFindSelByName(sela1, "sel_4_2", NULL, &sel); |
443 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
444 | 0 | selaFindSelByName(sela1, "sel_4_3", NULL, &sel); |
445 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
446 | 0 | break; |
447 | 0 | case 11: /* thicken for this one; use just a few iterations */ |
448 | 0 | sela1 = sela8ccThin(NULL); |
449 | 0 | selaFindSelByName(sela1, "sel_8_4", NULL, &sel); |
450 | 0 | selaAddSel(sela2, sel, NULL, L_COPY); |
451 | 0 | break; |
452 | 231k | } |
453 | | |
454 | | /* Optionally display the sel set */ |
455 | 231k | if (debug) { |
456 | 0 | PIX *pix1; |
457 | 0 | char buf[32]; |
458 | 0 | lept_mkdir("/lept/sels"); |
459 | 0 | pix1 = selaDisplayInPix(sela2, 35, 3, 15, 4); |
460 | 0 | snprintf(buf, sizeof(buf), "/tmp/lept/sels/set%d.png", index); |
461 | 0 | pixWrite(buf, pix1, IFF_PNG); |
462 | 0 | pixDisplay(pix1, 100, 100); |
463 | 0 | pixDestroy(&pix1); |
464 | 0 | } |
465 | | |
466 | 231k | selaDestroy(&sela1); |
467 | 231k | return sela2; |
468 | 231k | } |