comparison mupdf-source/thirdparty/leptonica/src/pixafunc1.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 pixafunc1.c
29 * <pre>
30 *
31 * Filters
32 * PIX *pixSelectBySize()
33 * PIXA *pixaSelectBySize()
34 * NUMA *pixaMakeSizeIndicator()
35 *
36 * PIX *pixSelectByPerimToAreaRatio()
37 * PIXA *pixaSelectByPerimToAreaRatio()
38 * PIX *pixSelectByPerimSizeRatio()
39 * PIXA *pixaSelectByPerimSizeRatio()
40 * PIX *pixSelectByAreaFraction()
41 * PIXA *pixaSelectByAreaFraction()
42 * PIX *pixSelectByArea()
43 * PIXA *pixaSelectByArea()
44 * PIX *pixSelectByWidthHeightRatio()
45 * PIXA *pixaSelectByWidthHeightRatio()
46 * PIXA *pixaSelectByNumConnComp()
47 *
48 * PIXA *pixaSelectWithIndicator()
49 * l_int32 pixRemoveWithIndicator()
50 * l_int32 pixAddWithIndicator()
51 * PIXA *pixaSelectWithString()
52 * PIX *pixaRenderComponent()
53 *
54 * Sort functions
55 * PIXA *pixaSort()
56 * PIXA *pixaBinSort()
57 * PIXA *pixaSortByIndex()
58 * PIXAA *pixaSort2dByIndex()
59 *
60 * Pixa and Pixaa range selection
61 * PIXA *pixaSelectRange()
62 * PIXAA *pixaaSelectRange()
63 *
64 * Pixa and Pixaa scaling
65 * PIXAA *pixaaScaleToSize()
66 * PIXAA *pixaaScaleToSizeVar()
67 * PIXA *pixaScaleToSize()
68 * PIXA *pixaScaleToSizeRel()
69 * PIXA *pixaScale()
70 * PIXA *pixaScaleBySampling()
71 *
72 * Pixa rotation and translation
73 * PIXA *pixaRotate()
74 * PIXA *pixaRotateOrth()
75 * PIXA *pixaTranslate()
76 *
77 * Miscellaneous
78 * PIXA *pixaAddBorderGeneral()
79 * PIXA *pixaaFlattenToPixa()
80 * l_int32 pixaaSizeRange()
81 * l_int32 pixaSizeRange()
82 * PIXA *pixaClipToPix()
83 * PIXA *pixaClipToForeground()
84 * l_int32 pixaGetRenderingDepth()
85 * l_int32 pixaHasColor()
86 * l_int32 pixaAnyColormaps()
87 * l_int32 pixaGetDepthInfo()
88 * PIXA *pixaConvertToSameDepth()
89 * PIXA *pixaConvertToGivenDepth()
90 * l_int32 pixaEqual()
91 * l_int32 pixaSetFullSizeBoxa()
92 * </pre>
93 */
94
95 #ifdef HAVE_CONFIG_H
96 #include <config_auto.h>
97 #endif /* HAVE_CONFIG_H */
98
99 #include <string.h>
100 #include "allheaders.h"
101 #include "pix_internal.h"
102
103 /* For more than this number of c.c. in a binarized image of
104 * semi-perimeter (w + h) about 5000 or less, the O(n) binsort
105 * is faster than the O(nlogn) shellsort. */
106 static const l_int32 MinCompsForBinSort = 200;
107
108 /* Don't rotate any angle smaller than this */
109 static const l_float32 MinAngleToRotate = 0.001f; /* radians; ~0.06 deg */
110
111 /*---------------------------------------------------------------------*
112 * Filters *
113 *---------------------------------------------------------------------*/
114 /*
115 * These filters work on the connected components of 1 bpp images.
116 * They are typically used on pixa that have been generated from a Pix
117 * using pixConnComp(), so that the corresponding Boxa is available.
118 *
119 * The filters remove or retain c.c. based on these properties:
120 * (a) size [pixaFindDimensions()]
121 * (b) area-to-perimeter ratio [pixaFindAreaPerimRatio()]
122 * (c) foreground area as a fraction of bounding box area (w * h)
123 * [pixaFindForegroundArea()]
124 * (d) number of foreground pixels [pixaCountPixels()]
125 * (e) width/height aspect ratio [pixFindWidthHeightRatio()]
126 *
127 * We provide two different high-level interfaces:
128 * (1) Functions that use one of the filters on either
129 * a pix or the pixa of components.
130 * (2) A general method that generates numas of indicator functions,
131 * logically combines them, and efficiently removes or adds
132 * the selected components.
133 *
134 * For interface (1), the filtering is performed with a single function call.
135 * This is the easiest way to do simple filtering. These functions
136 * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of:
137 * Size
138 * PerimToAreaRatio
139 * PerimSizeRatio
140 * Area
141 * AreaFraction
142 * WidthHeightRatio
143 *
144 * For more complicated filtering, use the general method (2).
145 * The numa indicator functions for a pixa are generated by these functions:
146 * pixaFindDimensions()
147 * pixaFindPerimToAreaRatio()
148 * pixaFindPerimSizeRatio()
149 * pixaFindAreaFraction()
150 * pixaCountPixels()
151 * pixaFindWidthHeightRatio()
152 * pixaFindWidthHeightProduct()
153 *
154 * Here is an illustration using the general method. Suppose you want
155 * all 8-connected components that have a height greater than 40 pixels,
156 * a width not more than 30 pixels, between 150 and 300 fg pixels,
157 * and a perimeter-to-size ratio between 1.2 and 2.0.
158 *
159 * // Generate the pixa of 8 cc pieces.
160 * boxa = pixConnComp(pixs, &pixa, 8);
161 *
162 * // Extract the data we need about each component.
163 * pixaFindDimensions(pixa, &naw, &nah);
164 * nas = pixaCountPixels(pixa);
165 * nar = pixaFindPerimSizeRatio(pixa);
166 *
167 * // Build the indicator arrays for the set of components,
168 * // based on thresholds and selection criteria.
169 * na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT);
170 * na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE);
171 * na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE);
172 * na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE);
173 * na5 = numaMakeThresholdIndicator(nar, 1.2, L_SELECT_IF_GTE);
174 * na6 = numaMakeThresholdIndicator(nar, 2.0, L_SELECT_IF_LTE);
175 *
176 * // Combine the indicator arrays logically to find
177 * // the components that will be retained.
178 * nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION);
179 * numaLogicalOp(nad, nad, na3, L_INTERSECTION);
180 * numaLogicalOp(nad, nad, na4, L_INTERSECTION);
181 * numaLogicalOp(nad, nad, na5, L_INTERSECTION);
182 * numaLogicalOp(nad, nad, na6, L_INTERSECTION);
183 *
184 * // Invert to get the components that will be removed.
185 * numaInvert(nad, nad);
186 *
187 * // Remove the components, in-place.
188 * pixRemoveWithIndicator(pixs, pixa, nad);
189 */
190
191
192 /*!
193 * \brief pixSelectBySize()
194 *
195 * \param[in] pixs 1 bpp
196 * \param[in] width, height threshold dimensions
197 * \param[in] connectivity 4 or 8
198 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT,
199 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
200 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
201 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
202 * \param[out] pchanged [optional] 1 if changed; 0 otherwise
203 * \return filtered pixd, or NULL on error
204 *
205 * <pre>
206 * Notes:
207 * (1) The args specify constraints on the size of the
208 * components that are kept.
209 * (2) If unchanged, returns a copy of pixs. Otherwise,
210 * returns a new pix with the filtered components.
211 * (3) If the selection type is L_SELECT_WIDTH, the input
212 * height is ignored, and v.v.
213 * (4) To keep small components, use relation = L_SELECT_IF_LT or
214 * L_SELECT_IF_LTE.
215 * To keep large components, use relation = L_SELECT_IF_GT or
216 * L_SELECT_IF_GTE.
217 * </pre>
218 */
219 PIX *
220 pixSelectBySize(PIX *pixs,
221 l_int32 width,
222 l_int32 height,
223 l_int32 connectivity,
224 l_int32 type,
225 l_int32 relation,
226 l_int32 *pchanged)
227 {
228 l_int32 w, h, empty, changed, count;
229 BOXA *boxa;
230 PIX *pixd;
231 PIXA *pixas, *pixad;
232
233 if (!pixs)
234 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
235 if (connectivity != 4 && connectivity != 8)
236 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
237 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
238 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
239 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
240 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
241 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
242 return (PIX *)ERROR_PTR("invalid relation", __func__, NULL);
243 if (pchanged) *pchanged = FALSE;
244
245 /* Check if any components exist */
246 pixZero(pixs, &empty);
247 if (empty)
248 return pixCopy(NULL, pixs);
249
250 /* Identify and select the components */
251 boxa = pixConnComp(pixs, &pixas, connectivity);
252 pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed);
253 boxaDestroy(&boxa);
254 pixaDestroy(&pixas);
255
256 if (!changed) {
257 pixaDestroy(&pixad);
258 return pixCopy(NULL, pixs);
259 }
260
261 /* Render the result */
262 if (pchanged) *pchanged = TRUE;
263 pixGetDimensions(pixs, &w, &h, NULL);
264 count = pixaGetCount(pixad);
265 if (count == 0) { /* return empty pix */
266 pixd = pixCreateTemplate(pixs);
267 } else {
268 pixd = pixaDisplay(pixad, w, h);
269 pixCopyResolution(pixd, pixs);
270 pixCopyColormap(pixd, pixs);
271 pixCopyText(pixd, pixs);
272 pixCopyInputFormat(pixd, pixs);
273 }
274 pixaDestroy(&pixad);
275 return pixd;
276 }
277
278
279 /*!
280 * \brief pixaSelectBySize()
281 *
282 * \param[in] pixas
283 * \param[in] width, height threshold dimensions
284 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT,
285 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
286 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
287 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
288 * \param[out] pchanged [optional] 1 if changed; 0 otherwise
289 * \return pixad, or NULL on error
290 *
291 * <pre>
292 * Notes:
293 * (1) The args specify constraints on the size of the
294 * components that are kept.
295 * (2) Uses pix and box clones in the new pixa.
296 * (3) If the selection type is L_SELECT_WIDTH, the input
297 * height is ignored, and v.v.
298 * (4) To keep small components, use relation = L_SELECT_IF_LT or
299 * L_SELECT_IF_LTE.
300 * To keep large components, use relation = L_SELECT_IF_GT or
301 * L_SELECT_IF_GTE.
302 * </pre>
303 */
304 PIXA *
305 pixaSelectBySize(PIXA *pixas,
306 l_int32 width,
307 l_int32 height,
308 l_int32 type,
309 l_int32 relation,
310 l_int32 *pchanged)
311 {
312 NUMA *na;
313 PIXA *pixad;
314
315 if (!pixas)
316 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
317 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
318 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
319 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
320 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
321 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
322 return (PIXA *)ERROR_PTR("invalid relation", __func__, NULL);
323
324 /* Compute the indicator array for saving components */
325 na = pixaMakeSizeIndicator(pixas, width, height, type, relation);
326
327 /* Filter to get output */
328 pixad = pixaSelectWithIndicator(pixas, na, pchanged);
329
330 numaDestroy(&na);
331 return pixad;
332 }
333
334
335 /*!
336 * \brief pixaMakeSizeIndicator()
337 *
338 * \param[in] pixa
339 * \param[in] width, height threshold dimensions
340 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT,
341 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
342 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT,
343 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
344 * \return na indicator array, or NULL on error
345 *
346 * <pre>
347 * Notes:
348 * (1) The args specify constraints on the size of the
349 * components that are kept.
350 * (2) If the selection type is L_SELECT_WIDTH, the input
351 * height is ignored, and v.v.
352 * (3) To keep small components, use relation = L_SELECT_IF_LT or
353 * L_SELECT_IF_LTE.
354 * To keep large components, use relation = L_SELECT_IF_GT or
355 * L_SELECT_IF_GTE.
356 * </pre>
357 */
358 NUMA *
359 pixaMakeSizeIndicator(PIXA *pixa,
360 l_int32 width,
361 l_int32 height,
362 l_int32 type,
363 l_int32 relation)
364 {
365 l_int32 i, n, w, h, ival;
366 NUMA *na;
367
368 if (!pixa)
369 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
370 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
371 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
372 return (NUMA *)ERROR_PTR("invalid type", __func__, NULL);
373 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
374 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
375 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
376
377 n = pixaGetCount(pixa);
378 na = numaCreate(n);
379 for (i = 0; i < n; i++) {
380 ival = 0;
381 pixaGetPixDimensions(pixa, i, &w, &h, NULL);
382 switch (type)
383 {
384 case L_SELECT_WIDTH:
385 if ((relation == L_SELECT_IF_LT && w < width) ||
386 (relation == L_SELECT_IF_GT && w > width) ||
387 (relation == L_SELECT_IF_LTE && w <= width) ||
388 (relation == L_SELECT_IF_GTE && w >= width))
389 ival = 1;
390 break;
391 case L_SELECT_HEIGHT:
392 if ((relation == L_SELECT_IF_LT && h < height) ||
393 (relation == L_SELECT_IF_GT && h > height) ||
394 (relation == L_SELECT_IF_LTE && h <= height) ||
395 (relation == L_SELECT_IF_GTE && h >= height))
396 ival = 1;
397 break;
398 case L_SELECT_IF_EITHER:
399 if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) ||
400 ((relation == L_SELECT_IF_GT) && (w > width || h > height)) ||
401 ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) ||
402 ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height)))
403 ival = 1;
404 break;
405 case L_SELECT_IF_BOTH:
406 if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) ||
407 ((relation == L_SELECT_IF_GT) && (w > width && h > height)) ||
408 ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) ||
409 ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height)))
410 ival = 1;
411 break;
412 default:
413 L_WARNING("can't get here!\n", __func__);
414 break;
415 }
416 numaAddNumber(na, ival);
417 }
418
419 return na;
420 }
421
422
423 /*!
424 * \brief pixSelectByPerimToAreaRatio()
425 *
426 * \param[in] pixs 1 bpp
427 * \param[in] thresh threshold ratio of fg boundary to fg pixels
428 * \param[in] connectivity 4 or 8
429 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
430 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
431 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
432 * \return pixd, or NULL on error
433 *
434 * <pre>
435 * Notes:
436 * (1) The args specify constraints on the size of the
437 * components that are kept.
438 * (2) If unchanged, returns a copy of pixs. Otherwise,
439 * returns a new pix with the filtered components.
440 * (3) This filters "thick" components, where a thick component
441 * is defined to have a ratio of boundary to interior pixels
442 * that is smaller than a given threshold value.
443 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thicker
444 * components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
445 * </pre>
446 */
447 PIX *
448 pixSelectByPerimToAreaRatio(PIX *pixs,
449 l_float32 thresh,
450 l_int32 connectivity,
451 l_int32 type,
452 l_int32 *pchanged)
453 {
454 l_int32 w, h, empty, changed, count;
455 BOXA *boxa;
456 PIX *pixd;
457 PIXA *pixas, *pixad;
458
459 if (!pixs)
460 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
461 if (connectivity != 4 && connectivity != 8)
462 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
463 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
464 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
465 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
466 if (pchanged) *pchanged = FALSE;
467
468 /* Check if any components exist */
469 pixZero(pixs, &empty);
470 if (empty)
471 return pixCopy(NULL, pixs);
472
473 /* Filter thin components */
474 boxa = pixConnComp(pixs, &pixas, connectivity);
475 pixad = pixaSelectByPerimToAreaRatio(pixas, thresh, type, &changed);
476 boxaDestroy(&boxa);
477 pixaDestroy(&pixas);
478
479 if (!changed) {
480 pixaDestroy(&pixad);
481 return pixCopy(NULL, pixs);
482 }
483
484 /* Render the result */
485 if (pchanged) *pchanged = TRUE;
486 pixGetDimensions(pixs, &w, &h, NULL);
487 count = pixaGetCount(pixad);
488 if (count == 0) { /* return empty pix */
489 pixd = pixCreateTemplate(pixs);
490 } else {
491 pixd = pixaDisplay(pixad, w, h);
492 pixCopyResolution(pixd, pixs);
493 pixCopyColormap(pixd, pixs);
494 pixCopyText(pixd, pixs);
495 pixCopyInputFormat(pixd, pixs);
496 }
497 pixaDestroy(&pixad);
498 return pixd;
499 }
500
501
502 /*!
503 * \brief pixaSelectByPerimToAreaRatio()
504 *
505 * \param[in] pixas
506 * \param[in] thresh threshold ratio of fg boundary to fg pixels
507 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
508 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
509 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
510 * \return pixad, or NULL on error
511 *
512 * <pre>
513 * Notes:
514 * (1) Returns a pixa clone if no components are removed.
515 * (2) Uses pix and box clones in the new pixa.
516 * (3) See pixSelectByPerimToAreaRatio().
517 * </pre>
518 */
519 PIXA *
520 pixaSelectByPerimToAreaRatio(PIXA *pixas,
521 l_float32 thresh,
522 l_int32 type,
523 l_int32 *pchanged)
524 {
525 NUMA *na, *nai;
526 PIXA *pixad;
527
528 if (!pixas)
529 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
530 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
531 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
532 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
533
534 /* Compute component ratios. */
535 na = pixaFindPerimToAreaRatio(pixas);
536
537 /* Generate indicator array for elements to be saved. */
538 nai = numaMakeThresholdIndicator(na, thresh, type);
539 numaDestroy(&na);
540
541 /* Filter to get output */
542 pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
543
544 numaDestroy(&nai);
545 return pixad;
546 }
547
548
549 /*!
550 * \brief pixSelectByPerimSizeRatio()
551 *
552 * \param[in] pixs 1 bpp
553 * \param[in] thresh threshold ratio of fg boundary to fg pixels
554 * \param[in] connectivity 4 or 8
555 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
556 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
557 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
558 * \return pixd, or NULL on error
559 *
560 * <pre>
561 * Notes:
562 * (1) The args specify constraints on the size of the
563 * components that are kept.
564 * (2) If unchanged, returns a copy of pixs. Otherwise,
565 * returns a new pix with the filtered components.
566 * (3) This filters components with smooth vs. dendritic shape, using
567 * the ratio of the fg boundary pixels to the circumference of
568 * the bounding box, and comparing it to a threshold value.
569 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the smooth
570 * boundary components, and L_SELECT_IF_GT or L_SELECT_IF_GTE
571 * to remove them.
572 * </pre>
573 */
574 PIX *
575 pixSelectByPerimSizeRatio(PIX *pixs,
576 l_float32 thresh,
577 l_int32 connectivity,
578 l_int32 type,
579 l_int32 *pchanged)
580 {
581 l_int32 w, h, empty, changed, count;
582 BOXA *boxa;
583 PIX *pixd;
584 PIXA *pixas, *pixad;
585
586 if (!pixs)
587 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
588 if (connectivity != 4 && connectivity != 8)
589 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
590 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
591 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
592 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
593 if (pchanged) *pchanged = FALSE;
594
595 /* Check if any components exist */
596 pixZero(pixs, &empty);
597 if (empty)
598 return pixCopy(NULL, pixs);
599
600 /* Filter thin components */
601 boxa = pixConnComp(pixs, &pixas, connectivity);
602 pixad = pixaSelectByPerimSizeRatio(pixas, thresh, type, &changed);
603 boxaDestroy(&boxa);
604 pixaDestroy(&pixas);
605
606 if (!changed) {
607 pixaDestroy(&pixad);
608 return pixCopy(NULL, pixs);
609 }
610
611 /* Render the result */
612 if (pchanged) *pchanged = TRUE;
613 pixGetDimensions(pixs, &w, &h, NULL);
614 count = pixaGetCount(pixad);
615 if (count == 0) { /* return empty pix */
616 pixd = pixCreateTemplate(pixs);
617 } else {
618 pixd = pixaDisplay(pixad, w, h);
619 pixCopyResolution(pixd, pixs);
620 pixCopyColormap(pixd, pixs);
621 pixCopyText(pixd, pixs);
622 pixCopyInputFormat(pixd, pixs);
623 }
624 pixaDestroy(&pixad);
625 return pixd;
626 }
627
628
629 /*!
630 * \brief pixaSelectByPerimSizeRatio()
631 *
632 * \param[in] pixas
633 * \param[in] thresh threshold ratio of fg boundary to b.b. circumference
634 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
635 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
636 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
637 * \return pixad, or NULL on error
638 *
639 * <pre>
640 * Notes:
641 * (1) Returns a pixa clone if no components are removed.
642 * (2) Uses pix and box clones in the new pixa.
643 * (3) See pixSelectByPerimSizeRatio().
644 * </pre>
645 */
646 PIXA *
647 pixaSelectByPerimSizeRatio(PIXA *pixas,
648 l_float32 thresh,
649 l_int32 type,
650 l_int32 *pchanged)
651 {
652 NUMA *na, *nai;
653 PIXA *pixad;
654
655 if (!pixas)
656 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
657 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
658 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
659 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
660
661 /* Compute component ratios. */
662 na = pixaFindPerimSizeRatio(pixas);
663
664 /* Generate indicator array for elements to be saved. */
665 nai = numaMakeThresholdIndicator(na, thresh, type);
666 numaDestroy(&na);
667
668 /* Filter to get output */
669 pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
670
671 numaDestroy(&nai);
672 return pixad;
673 }
674
675
676 /*!
677 * \brief pixSelectByAreaFraction()
678 *
679 * \param[in] pixs 1 bpp
680 * \param[in] thresh threshold ratio of fg pixels to (w * h)
681 * \param[in] connectivity 4 or 8
682 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
683 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
684 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
685 * \return pixd, or NULL on error
686 *
687 * <pre>
688 * Notes:
689 * (1) The args specify constraints on the amount of foreground
690 * coverage of the components that are kept.
691 * (2) If unchanged, returns a copy of pixs. Otherwise,
692 * returns a new pix with the filtered components.
693 * (3) This filters components based on the fraction of fg pixels
694 * of the component in its bounding box.
695 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
696 * with less than the threshold fraction of foreground, and
697 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
698 * </pre>
699 */
700 PIX *
701 pixSelectByAreaFraction(PIX *pixs,
702 l_float32 thresh,
703 l_int32 connectivity,
704 l_int32 type,
705 l_int32 *pchanged)
706 {
707 l_int32 w, h, empty, changed, count;
708 BOXA *boxa;
709 PIX *pixd;
710 PIXA *pixas, *pixad;
711
712 if (!pixs)
713 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
714 if (connectivity != 4 && connectivity != 8)
715 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
716 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
717 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
718 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
719 if (pchanged) *pchanged = FALSE;
720
721 /* Check if any components exist */
722 pixZero(pixs, &empty);
723 if (empty)
724 return pixCopy(NULL, pixs);
725
726 /* Filter components */
727 boxa = pixConnComp(pixs, &pixas, connectivity);
728 pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed);
729 boxaDestroy(&boxa);
730 pixaDestroy(&pixas);
731
732 if (!changed) {
733 pixaDestroy(&pixad);
734 return pixCopy(NULL, pixs);
735 }
736
737 /* Render the result */
738 if (pchanged) *pchanged = TRUE;
739 pixGetDimensions(pixs, &w, &h, NULL);
740 count = pixaGetCount(pixad);
741 if (count == 0) { /* return empty pix */
742 pixd = pixCreateTemplate(pixs);
743 } else {
744 pixd = pixaDisplay(pixad, w, h);
745 pixCopyResolution(pixd, pixs);
746 pixCopyColormap(pixd, pixs);
747 pixCopyText(pixd, pixs);
748 pixCopyInputFormat(pixd, pixs);
749 }
750 pixaDestroy(&pixad);
751 return pixd;
752 }
753
754
755 /*!
756 * \brief pixaSelectByAreaFraction()
757 *
758 * \param[in] pixas
759 * \param[in] thresh threshold ratio of fg pixels to (w * h)
760 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
761 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
762 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
763 * \return pixad, or NULL on error
764 *
765 * <pre>
766 * Notes:
767 * (1) Returns a pixa clone if no components are removed.
768 * (2) Uses pix and box clones in the new pixa.
769 * (3) This filters components based on the fraction of fg pixels
770 * of the component in its bounding box.
771 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
772 * with less than the threshold fraction of foreground, and
773 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
774 * </pre>
775 */
776 PIXA *
777 pixaSelectByAreaFraction(PIXA *pixas,
778 l_float32 thresh,
779 l_int32 type,
780 l_int32 *pchanged)
781 {
782 NUMA *na, *nai;
783 PIXA *pixad;
784
785 if (!pixas)
786 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
787 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
788 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
789 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
790
791 /* Compute component ratios. */
792 na = pixaFindAreaFraction(pixas);
793
794 /* Generate indicator array for elements to be saved. */
795 nai = numaMakeThresholdIndicator(na, thresh, type);
796 numaDestroy(&na);
797
798 /* Filter to get output */
799 pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
800
801 numaDestroy(&nai);
802 return pixad;
803 }
804
805
806 /*!
807 * \brief pixSelectByArea()
808 *
809 * \param[in] pixs 1 bpp
810 * \param[in] thresh threshold number of FG pixels
811 * \param[in] connectivity 4 or 8
812 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
813 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
814 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
815 * \return pixd, or NULL on error
816 *
817 * <pre>
818 * Notes:
819 * (1) The args specify constraints on the number of foreground
820 * pixels in the components that are kept.
821 * (2) If unchanged, returns a copy of pixs. Otherwise,
822 * returns a new pix with the filtered components.
823 * (3) This filters components based on the number of fg pixels
824 * in each component.
825 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
826 * with less than the threshold number of fg pixels, and
827 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
828 * </pre>
829 */
830 PIX *
831 pixSelectByArea(PIX *pixs,
832 l_float32 thresh,
833 l_int32 connectivity,
834 l_int32 type,
835 l_int32 *pchanged)
836 {
837 l_int32 w, h, empty, changed, count;
838 BOXA *boxa;
839 PIX *pixd;
840 PIXA *pixas, *pixad;
841
842 if (!pixs)
843 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
844 if (connectivity != 4 && connectivity != 8)
845 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
846 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
847 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
848 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
849 if (pchanged) *pchanged = FALSE;
850
851 /* Check if any components exist */
852 pixZero(pixs, &empty);
853 if (empty)
854 return pixCopy(NULL, pixs);
855
856 /* Filter components */
857 boxa = pixConnComp(pixs, &pixas, connectivity);
858 pixad = pixaSelectByArea(pixas, thresh, type, &changed);
859 boxaDestroy(&boxa);
860 pixaDestroy(&pixas);
861
862 if (!changed) {
863 pixaDestroy(&pixad);
864 return pixCopy(NULL, pixs);
865 }
866
867 /* Render the result */
868 if (pchanged) *pchanged = TRUE;
869 pixGetDimensions(pixs, &w, &h, NULL);
870 count = pixaGetCount(pixad);
871 if (count == 0) { /* return empty pix */
872 pixd = pixCreateTemplate(pixs);
873 } else {
874 pixd = pixaDisplay(pixad, w, h);
875 pixCopyResolution(pixd, pixs);
876 pixCopyColormap(pixd, pixs);
877 pixCopyText(pixd, pixs);
878 pixCopyInputFormat(pixd, pixs);
879 }
880 pixaDestroy(&pixad);
881 return pixd;
882 }
883
884
885 /*!
886 * \brief pixaSelectByArea()
887 *
888 * \param[in] pixas
889 * \param[in] thresh threshold number of fg pixels
890 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
891 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
892 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
893 * \return pixad, or NULL on error
894 *
895 * <pre>
896 * Notes:
897 * (1) Returns a pixa clone if no components are removed.
898 * (2) Uses pix and box clones in the new pixa.
899 * (3) This filters components based on the number of fg pixels
900 * in the component.
901 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
902 * with less than the threshold number of fg pixels, and
903 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
904 * </pre>
905 */
906 PIXA *
907 pixaSelectByArea(PIXA *pixas,
908 l_float32 thresh,
909 l_int32 type,
910 l_int32 *pchanged)
911 {
912 NUMA *na, *nai;
913 PIXA *pixad;
914
915 if (!pixas)
916 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
917 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
918 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
919 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
920
921 /* Compute area of each component */
922 na = pixaCountPixels(pixas);
923
924 /* Generate indicator array for elements to be saved. */
925 nai = numaMakeThresholdIndicator(na, thresh, type);
926 numaDestroy(&na);
927
928 /* Filter to get output */
929 pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
930
931 numaDestroy(&nai);
932 return pixad;
933 }
934
935
936 /*!
937 * \brief pixSelectByWidthHeightRatio()
938 *
939 * \param[in] pixs 1 bpp
940 * \param[in] thresh threshold ratio of width/height
941 * \param[in] connectivity 4 or 8
942 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
943 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
944 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
945 * \return pixd, or NULL on error
946 *
947 * <pre>
948 * Notes:
949 * (1) The args specify constraints on the width-to-height ratio
950 * for components that are kept.
951 * (2) If unchanged, returns a copy of pixs. Otherwise,
952 * returns a new pix with the filtered components.
953 * (3) This filters components based on the width-to-height ratios.
954 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
955 * with less than the threshold ratio, and
956 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
957 * </pre>
958 */
959 PIX *
960 pixSelectByWidthHeightRatio(PIX *pixs,
961 l_float32 thresh,
962 l_int32 connectivity,
963 l_int32 type,
964 l_int32 *pchanged)
965 {
966 l_int32 w, h, empty, changed, count;
967 BOXA *boxa;
968 PIX *pixd;
969 PIXA *pixas, *pixad;
970
971 if (!pixs)
972 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
973 if (connectivity != 4 && connectivity != 8)
974 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
975 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
976 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
977 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
978 if (pchanged) *pchanged = FALSE;
979
980 /* Check if any components exist */
981 pixZero(pixs, &empty);
982 if (empty)
983 return pixCopy(NULL, pixs);
984
985 /* Filter components */
986 boxa = pixConnComp(pixs, &pixas, connectivity);
987 pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed);
988 boxaDestroy(&boxa);
989 pixaDestroy(&pixas);
990
991 if (!changed) {
992 pixaDestroy(&pixad);
993 return pixCopy(NULL, pixs);
994 }
995
996 /* Render the result */
997 if (pchanged) *pchanged = TRUE;
998 pixGetDimensions(pixs, &w, &h, NULL);
999 count = pixaGetCount(pixad);
1000 if (count == 0) { /* return empty pix */
1001 pixd = pixCreateTemplate(pixs);
1002 } else {
1003 pixd = pixaDisplay(pixad, w, h);
1004 pixCopyResolution(pixd, pixs);
1005 pixCopyColormap(pixd, pixs);
1006 pixCopyText(pixd, pixs);
1007 pixCopyInputFormat(pixd, pixs);
1008 }
1009 pixaDestroy(&pixad);
1010 return pixd;
1011 }
1012
1013
1014 /*!
1015 * \brief pixaSelectByWidthHeightRatio()
1016 *
1017 * \param[in] pixas
1018 * \param[in] thresh threshold ratio of width/height
1019 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT,
1020 * L_SELECT_IF_LTE, L_SELECT_IF_GTE
1021 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
1022 * \return pixad, or NULL on error
1023 *
1024 * <pre>
1025 * Notes:
1026 * (1) Returns a pixa clone if no components are removed.
1027 * (2) Uses pix and box clones in the new pixa.
1028 * (3) This filters components based on the width-to-height ratio
1029 * of each pix.
1030 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
1031 * with less than the threshold ratio, and
1032 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
1033 * </pre>
1034 */
1035 PIXA *
1036 pixaSelectByWidthHeightRatio(PIXA *pixas,
1037 l_float32 thresh,
1038 l_int32 type,
1039 l_int32 *pchanged)
1040 {
1041 NUMA *na, *nai;
1042 PIXA *pixad;
1043
1044 if (!pixas)
1045 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1046 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
1047 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
1048 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
1049
1050 /* Compute component ratios. */
1051 na = pixaFindWidthHeightRatio(pixas);
1052
1053 /* Generate indicator array for elements to be saved. */
1054 nai = numaMakeThresholdIndicator(na, thresh, type);
1055 numaDestroy(&na);
1056
1057 /* Filter to get output */
1058 pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
1059
1060 numaDestroy(&nai);
1061 return pixad;
1062 }
1063
1064
1065 /*!
1066 * \brief pixaSelectByNumConnComp()
1067 *
1068 * \param[in] pixas
1069 * \param[in] nmin minimum number of components
1070 * \param[in] nmax maximum number of components
1071 * \param[in] connectivity 4 or 8
1072 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
1073 * \return pixad, or NULL on error
1074 *
1075 * <pre>
1076 * Notes:
1077 * (1) Returns a pixa clone if no components are removed.
1078 * (2) Uses pix and box clones in the new pixa.
1079 * (3) This filters by the number of connected components in
1080 * a given range.
1081 * </pre>
1082 */
1083 PIXA *
1084 pixaSelectByNumConnComp(PIXA *pixas,
1085 l_int32 nmin,
1086 l_int32 nmax,
1087 l_int32 connectivity,
1088 l_int32 *pchanged)
1089 {
1090 l_int32 n, i, count;
1091 NUMA *na;
1092 PIX *pix;
1093 PIXA *pixad;
1094
1095 if (pchanged) *pchanged = 0;
1096 if (!pixas)
1097 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1098 if (nmin > nmax)
1099 return (PIXA *)ERROR_PTR("nmin > nmax", __func__, NULL);
1100 if (connectivity != 4 && connectivity != 8)
1101 return (PIXA *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
1102
1103 /* Get indicator array based on number of c.c. */
1104 n = pixaGetCount(pixas);
1105 na = numaCreate(n);
1106 for (i = 0; i < n; i++) {
1107 pix = pixaGetPix(pixas, i, L_CLONE);
1108 pixCountConnComp(pix, connectivity, &count);
1109 if (count >= nmin && count <= nmax)
1110 numaAddNumber(na, 1);
1111 else
1112 numaAddNumber(na, 0);
1113 pixDestroy(&pix);
1114 }
1115
1116 /* Filter to get output */
1117 pixad = pixaSelectWithIndicator(pixas, na, pchanged);
1118 numaDestroy(&na);
1119 return pixad;
1120 }
1121
1122
1123 /*!
1124 * \brief pixaSelectWithIndicator()
1125 *
1126 * \param[in] pixas
1127 * \param[in] na indicator numa
1128 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned
1129 * \return pixad, or NULL on error
1130 *
1131 * <pre>
1132 * Notes:
1133 * (1) Returns a pixa clone if no components are removed.
1134 * (2) Uses pix and box clones in the new pixa.
1135 * (3) The indicator numa has values 0 (ignore) and 1 (accept).
1136 * (4) If the source boxa is not fully populated, it is left
1137 * empty in the dest pixa.
1138 * </pre>
1139 */
1140 PIXA *
1141 pixaSelectWithIndicator(PIXA *pixas,
1142 NUMA *na,
1143 l_int32 *pchanged)
1144 {
1145 l_int32 i, n, nbox, ival, nsave;
1146 BOX *box;
1147 PIX *pix1;
1148 PIXA *pixad;
1149
1150 if (!pixas)
1151 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1152 if (!na)
1153 return (PIXA *)ERROR_PTR("na not defined", __func__, NULL);
1154
1155 nsave = 0;
1156 n = numaGetCount(na);
1157 for (i = 0; i < n; i++) {
1158 numaGetIValue(na, i, &ival);
1159 if (ival == 1) nsave++;
1160 }
1161
1162 if (nsave == n) {
1163 if (pchanged) *pchanged = FALSE;
1164 return pixaCopy(pixas, L_CLONE);
1165 }
1166 if (pchanged) *pchanged = TRUE;
1167 pixad = pixaCreate(nsave);
1168 nbox = pixaGetBoxaCount(pixas);
1169 for (i = 0; i < n; i++) {
1170 numaGetIValue(na, i, &ival);
1171 if (ival == 0) continue;
1172 pix1 = pixaGetPix(pixas, i, L_CLONE);
1173 pixaAddPix(pixad, pix1, L_INSERT);
1174 if (nbox == n) { /* fully populated boxa */
1175 box = pixaGetBox(pixas, i, L_CLONE);
1176 pixaAddBox(pixad, box, L_INSERT);
1177 }
1178 }
1179
1180 return pixad;
1181 }
1182
1183
1184 /*!
1185 * \brief pixRemoveWithIndicator()
1186 *
1187 * \param[in] pixs 1 bpp pix from which components are removed; in-place
1188 * \param[in] pixa of connected components in pixs
1189 * \param[in] na numa indicator: remove components corresponding to 1s
1190 * \return 0 if OK, 1 on error
1191 *
1192 * <pre>
1193 * Notes:
1194 * (1) This complements pixAddWithIndicator(). Here, the selected
1195 * components are set subtracted from pixs.
1196 * </pre>
1197 */
1198 l_ok
1199 pixRemoveWithIndicator(PIX *pixs,
1200 PIXA *pixa,
1201 NUMA *na)
1202 {
1203 l_int32 i, n, ival, x, y, w, h;
1204 BOX *box;
1205 PIX *pix;
1206
1207 if (!pixs)
1208 return ERROR_INT("pixs not defined", __func__, 1);
1209 if (!pixa)
1210 return ERROR_INT("pixa not defined", __func__, 1);
1211 if (!na)
1212 return ERROR_INT("na not defined", __func__, 1);
1213 n = pixaGetCount(pixa);
1214 if (n != numaGetCount(na))
1215 return ERROR_INT("pixa and na sizes not equal", __func__, 1);
1216
1217 for (i = 0; i < n; i++) {
1218 numaGetIValue(na, i, &ival);
1219 if (ival == 1) {
1220 pix = pixaGetPix(pixa, i, L_CLONE);
1221 box = pixaGetBox(pixa, i, L_CLONE);
1222 boxGetGeometry(box, &x, &y, &w, &h);
1223 pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1224 pix, 0, 0);
1225 boxDestroy(&box);
1226 pixDestroy(&pix);
1227 }
1228 }
1229
1230 return 0;
1231 }
1232
1233
1234 /*!
1235 * \brief pixAddWithIndicator()
1236 *
1237 * \param[in] pixs 1 bpp pix from which components are added; in-place
1238 * \param[in] pixa of connected components, some of which will be put
1239 * into pixs
1240 * \param[in] na numa indicator: add components corresponding to 1s
1241 * \return 0 if OK, 1 on error
1242 *
1243 * <pre>
1244 * Notes:
1245 * (1) This complements pixRemoveWithIndicator(). Here, the selected
1246 * components are added to pixs.
1247 * </pre>
1248 */
1249 l_ok
1250 pixAddWithIndicator(PIX *pixs,
1251 PIXA *pixa,
1252 NUMA *na)
1253 {
1254 l_int32 i, n, ival, x, y, w, h;
1255 BOX *box;
1256 PIX *pix;
1257
1258 if (!pixs)
1259 return ERROR_INT("pixs not defined", __func__, 1);
1260 if (!pixa)
1261 return ERROR_INT("pixa not defined", __func__, 1);
1262 if (!na)
1263 return ERROR_INT("na not defined", __func__, 1);
1264 n = pixaGetCount(pixa);
1265 if (n != numaGetCount(na))
1266 return ERROR_INT("pixa and na sizes not equal", __func__, 1);
1267
1268 for (i = 0; i < n; i++) {
1269 numaGetIValue(na, i, &ival);
1270 if (ival == 1) {
1271 pix = pixaGetPix(pixa, i, L_CLONE);
1272 box = pixaGetBox(pixa, i, L_CLONE);
1273 boxGetGeometry(box, &x, &y, &w, &h);
1274 pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0);
1275 boxDestroy(&box);
1276 pixDestroy(&pix);
1277 }
1278 }
1279
1280 return 0;
1281 }
1282
1283
1284 /*!
1285 * \brief pixaSelectWithString()
1286 *
1287 * \param[in] pixas
1288 * \param[in] str string of indices into pixa, giving the pix to
1289 * be selected
1290 * \param[out] perror [optional] 1 if any indices are invalid;
1291 * 0 if all indices are valid
1292 * \return pixad, or NULL on error
1293 *
1294 * <pre>
1295 * Notes:
1296 * (1) Returns a pixa with copies of selected pix.
1297 * (2) Associated boxes are also copied, if fully populated.
1298 * </pre>
1299 */
1300 PIXA *
1301 pixaSelectWithString(PIXA *pixas,
1302 const char *str,
1303 l_int32 *perror)
1304 {
1305 l_int32 i, nval, npix, nbox, val, imaxval;
1306 l_float32 maxval;
1307 BOX *box;
1308 NUMA *na;
1309 PIX *pix1;
1310 PIXA *pixad;
1311
1312 if (perror) *perror = 0;
1313 if (!pixas)
1314 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1315 if (!str)
1316 return (PIXA *)ERROR_PTR("str not defined", __func__, NULL);
1317
1318 if ((na = numaCreateFromString(str)) == NULL)
1319 return (PIXA *)ERROR_PTR("na not made", __func__, NULL);
1320 if ((nval = numaGetCount(na)) == 0) {
1321 numaDestroy(&na);
1322 return (PIXA *)ERROR_PTR("no indices found", __func__, NULL);
1323 }
1324 numaGetMax(na, &maxval, NULL);
1325 imaxval = (l_int32)(maxval + 0.1);
1326 nbox = pixaGetBoxaCount(pixas);
1327 npix = pixaGetCount(pixas);
1328 if (imaxval >= npix) {
1329 if (perror) *perror = 1;
1330 L_ERROR("max index = %d, size of pixa = %d\n", __func__, imaxval, npix);
1331 }
1332
1333 pixad = pixaCreate(nval);
1334 for (i = 0; i < nval; i++) {
1335 numaGetIValue(na, i, &val);
1336 if (val < 0 || val >= npix) {
1337 L_ERROR("index %d out of range of pix\n", __func__, val);
1338 continue;
1339 }
1340 pix1 = pixaGetPix(pixas, val, L_COPY);
1341 pixaAddPix(pixad, pix1, L_INSERT);
1342 if (nbox == npix) { /* fully populated boxa */
1343 box = pixaGetBox(pixas, val, L_COPY);
1344 pixaAddBox(pixad, box, L_INSERT);
1345 }
1346 }
1347 numaDestroy(&na);
1348 return pixad;
1349 }
1350
1351
1352 /*!
1353 * \brief pixaRenderComponent()
1354 *
1355 * \param[in] pixs [optional] 1 bpp pix
1356 * \param[in] pixa of 1 bpp connected components, one of which will
1357 * be rendered in pixs, with its origin determined
1358 * by the associated box.
1359 * \param[in] index of component to be rendered
1360 * \return pixd, or NULL on error
1361 *
1362 * <pre>
1363 * Notes:
1364 * (1) If pixs is null, this generates an empty pix of a size determined
1365 * by union of the component bounding boxes, and including the origin.
1366 * (2) The selected component is blitted into pixs.
1367 * </pre>
1368 */
1369 PIX *
1370 pixaRenderComponent(PIX *pixs,
1371 PIXA *pixa,
1372 l_int32 index)
1373 {
1374 l_int32 n, x, y, w, h, same, maxd;
1375 BOX *box;
1376 BOXA *boxa;
1377 PIX *pix;
1378
1379 if (!pixa)
1380 return (PIX *)ERROR_PTR("pixa not defined", __func__, pixs);
1381 n = pixaGetCount(pixa);
1382 if (index < 0 || index >= n)
1383 return (PIX *)ERROR_PTR("invalid index", __func__, pixs);
1384 if (pixs && (pixGetDepth(pixs) != 1))
1385 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixs);
1386 pixaVerifyDepth(pixa, &same, &maxd);
1387 if (maxd > 1)
1388 return (PIX *)ERROR_PTR("not all pix with d == 1", __func__, pixs);
1389
1390 boxa = pixaGetBoxa(pixa, L_CLONE);
1391 if (!pixs) {
1392 boxaGetExtent(boxa, &w, &h, NULL);
1393 pixs = pixCreate(w, h, 1);
1394 }
1395
1396 pix = pixaGetPix(pixa, index, L_CLONE);
1397 box = boxaGetBox(boxa, index, L_CLONE);
1398 boxGetGeometry(box, &x, &y, &w, &h);
1399 pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0);
1400 boxDestroy(&box);
1401 pixDestroy(&pix);
1402 boxaDestroy(&boxa);
1403
1404 return pixs;
1405 }
1406
1407
1408 /*---------------------------------------------------------------------*
1409 * Sort functions *
1410 *---------------------------------------------------------------------*/
1411 /*!
1412 * \brief pixaSort()
1413 *
1414 * \param[in] pixas
1415 * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
1416 * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION,
1417 * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER,
1418 * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO
1419 * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING
1420 * \param[out] pnaindex [optional] index of sorted order into
1421 * original array
1422 * \param[in] copyflag L_COPY, L_CLONE
1423 * \return pixad sorted version of pixas, or NULL on error
1424 *
1425 * <pre>
1426 * Notes:
1427 * (1) This sorts based on the data in the boxa. If the boxa
1428 * count is not the same as the pixa count, this returns an error.
1429 * (2) If the boxa is empty, it makes one corresponding to the
1430 * dimensions of each pix, which allows meaningful sorting on
1431 * all types except x and y.
1432 * (3) The copyflag refers to the pix and box copies that are
1433 * inserted into the sorted pixa. These are either L_COPY
1434 * or L_CLONE.
1435 * </pre>
1436 */
1437 PIXA *
1438 pixaSort(PIXA *pixas,
1439 l_int32 sorttype,
1440 l_int32 sortorder,
1441 NUMA **pnaindex,
1442 l_int32 copyflag)
1443 {
1444 l_int32 i, n, nb, x, y, w, h;
1445 BOXA *boxa;
1446 NUMA *na, *naindex;
1447 PIXA *pixad;
1448
1449 if (pnaindex) *pnaindex = NULL;
1450 if (!pixas)
1451 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1452 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y &&
1453 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
1454 sorttype != L_SORT_BY_MIN_DIMENSION &&
1455 sorttype != L_SORT_BY_MAX_DIMENSION &&
1456 sorttype != L_SORT_BY_PERIMETER &&
1457 sorttype != L_SORT_BY_AREA &&
1458 sorttype != L_SORT_BY_ASPECT_RATIO)
1459 return (PIXA *)ERROR_PTR("invalid sort type", __func__, NULL);
1460 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
1461 return (PIXA *)ERROR_PTR("invalid sort order", __func__, NULL);
1462 if (copyflag != L_COPY && copyflag != L_CLONE)
1463 return (PIXA *)ERROR_PTR("invalid copy flag", __func__, NULL);
1464
1465 /* Check the pixa and boxa counts. Make a boxa if required. */
1466 if ((n = pixaGetCount(pixas)) == 0) {
1467 L_INFO("no pix in pixa\n", __func__);
1468 return pixaCopy(pixas, copyflag);
1469 }
1470 if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */
1471 return (PIXA *)ERROR_PTR("boxa not found!", __func__, NULL);
1472 nb = boxaGetCount(boxa);
1473 if (nb == 0) {
1474 pixaSetFullSizeBoxa(pixas);
1475 nb = n;
1476 boxa = pixas->boxa; /* not owned */
1477 if (sorttype == L_SORT_BY_X || sorttype == L_SORT_BY_Y)
1478 L_WARNING("sort by x or y where all values are 0\n", __func__);
1479 }
1480 if (nb != n)
1481 return (PIXA *)ERROR_PTR("boxa and pixa counts differ", __func__, NULL);
1482
1483 /* Use O(n) binsort if possible */
1484 if (n > MinCompsForBinSort &&
1485 ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) ||
1486 (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) ||
1487 (sorttype == L_SORT_BY_PERIMETER)))
1488 return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag);
1489
1490 /* Build up numa of specific data */
1491 if ((na = numaCreate(n)) == NULL)
1492 return (PIXA *)ERROR_PTR("na not made", __func__, NULL);
1493 for (i = 0; i < n; i++) {
1494 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
1495 switch (sorttype)
1496 {
1497 case L_SORT_BY_X:
1498 numaAddNumber(na, x);
1499 break;
1500 case L_SORT_BY_Y:
1501 numaAddNumber(na, y);
1502 break;
1503 case L_SORT_BY_WIDTH:
1504 numaAddNumber(na, w);
1505 break;
1506 case L_SORT_BY_HEIGHT:
1507 numaAddNumber(na, h);
1508 break;
1509 case L_SORT_BY_MIN_DIMENSION:
1510 numaAddNumber(na, L_MIN(w, h));
1511 break;
1512 case L_SORT_BY_MAX_DIMENSION:
1513 numaAddNumber(na, L_MAX(w, h));
1514 break;
1515 case L_SORT_BY_PERIMETER:
1516 numaAddNumber(na, w + h);
1517 break;
1518 case L_SORT_BY_AREA:
1519 numaAddNumber(na, w * h);
1520 break;
1521 case L_SORT_BY_ASPECT_RATIO:
1522 numaAddNumber(na, (l_float32)w / (l_float32)h);
1523 break;
1524 default:
1525 L_WARNING("invalid sort type\n", __func__);
1526 }
1527 }
1528
1529 /* Get the sort index for data array */
1530 naindex = numaGetSortIndex(na, sortorder);
1531 numaDestroy(&na);
1532 if (!naindex)
1533 return (PIXA *)ERROR_PTR("naindex not made", __func__, NULL);
1534
1535 /* Build up sorted pixa using sort index */
1536 if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) {
1537 numaDestroy(&naindex);
1538 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
1539 }
1540
1541 if (pnaindex)
1542 *pnaindex = naindex;
1543 else
1544 numaDestroy(&naindex);
1545 return pixad;
1546 }
1547
1548
1549 /*!
1550 * \brief pixaBinSort()
1551 *
1552 * \param[in] pixas
1553 * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
1554 * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER
1555 * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING
1556 * \param[out] pnaindex [optional] index of sorted order into
1557 * original array
1558 * \param[in] copyflag L_COPY, L_CLONE
1559 * \return pixad sorted version of pixas, or NULL on error
1560 *
1561 * <pre>
1562 * Notes:
1563 * (1) This sorts based on the data in the boxa. If the boxa
1564 * count is not the same as the pixa count, this returns an error.
1565 * (2) The copyflag refers to the pix and box copies that are
1566 * inserted into the sorted pixa. These are either L_COPY
1567 * or L_CLONE.
1568 * (3) For a large number of boxes (say, greater than 1000), this
1569 * O(n) binsort is much faster than the O(nlogn) shellsort.
1570 * For 5000 components, this is over 20x faster than boxaSort().
1571 * (4) Consequently, pixaSort() calls this function if it will
1572 * likely go much faster.
1573 * </pre>
1574 */
1575 PIXA *
1576 pixaBinSort(PIXA *pixas,
1577 l_int32 sorttype,
1578 l_int32 sortorder,
1579 NUMA **pnaindex,
1580 l_int32 copyflag)
1581 {
1582 l_int32 i, n, x, y, w, h;
1583 BOXA *boxa;
1584 NUMA *na, *naindex;
1585 PIXA *pixad;
1586
1587 if (pnaindex) *pnaindex = NULL;
1588 if (!pixas)
1589 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1590 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y &&
1591 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
1592 sorttype != L_SORT_BY_PERIMETER)
1593 return (PIXA *)ERROR_PTR("invalid sort type", __func__, NULL);
1594 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
1595 return (PIXA *)ERROR_PTR("invalid sort order", __func__, NULL);
1596 if (copyflag != L_COPY && copyflag != L_CLONE)
1597 return (PIXA *)ERROR_PTR("invalid copy flag", __func__, NULL);
1598
1599 /* Verify that the pixa and its boxa have the same count */
1600 if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */
1601 return (PIXA *)ERROR_PTR("boxa not found", __func__, NULL);
1602 n = pixaGetCount(pixas);
1603 if (boxaGetCount(boxa) != n)
1604 return (PIXA *)ERROR_PTR("boxa and pixa counts differ", __func__, NULL);
1605
1606 /* Generate Numa of appropriate box dimensions */
1607 if ((na = numaCreate(n)) == NULL)
1608 return (PIXA *)ERROR_PTR("na not made", __func__, NULL);
1609 for (i = 0; i < n; i++) {
1610 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
1611 switch (sorttype)
1612 {
1613 case L_SORT_BY_X:
1614 numaAddNumber(na, x);
1615 break;
1616 case L_SORT_BY_Y:
1617 numaAddNumber(na, y);
1618 break;
1619 case L_SORT_BY_WIDTH:
1620 numaAddNumber(na, w);
1621 break;
1622 case L_SORT_BY_HEIGHT:
1623 numaAddNumber(na, h);
1624 break;
1625 case L_SORT_BY_PERIMETER:
1626 numaAddNumber(na, w + h);
1627 break;
1628 default:
1629 L_WARNING("invalid sort type\n", __func__);
1630 }
1631 }
1632
1633 /* Get the sort index for data array */
1634 naindex = numaGetBinSortIndex(na, sortorder);
1635 numaDestroy(&na);
1636 if (!naindex)
1637 return (PIXA *)ERROR_PTR("naindex not made", __func__, NULL);
1638
1639 /* Build up sorted pixa using sort index */
1640 if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) {
1641 numaDestroy(&naindex);
1642 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
1643 }
1644
1645 if (pnaindex)
1646 *pnaindex = naindex;
1647 else
1648 numaDestroy(&naindex);
1649 return pixad;
1650 }
1651
1652
1653 /*!
1654 * \brief pixaSortByIndex()
1655 *
1656 * \param[in] pixas
1657 * \param[in] naindex na that maps from the new pixa to the input pixa
1658 * \param[in] copyflag L_COPY, L_CLONE
1659 * \return pixad sorted, or NULL on error
1660 */
1661 PIXA *
1662 pixaSortByIndex(PIXA *pixas,
1663 NUMA *naindex,
1664 l_int32 copyflag)
1665 {
1666 l_int32 i, n, index;
1667 BOX *box;
1668 PIX *pix;
1669 PIXA *pixad;
1670
1671 if (!pixas)
1672 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1673 if (!naindex)
1674 return (PIXA *)ERROR_PTR("naindex not defined", __func__, NULL);
1675 if (copyflag != L_CLONE && copyflag != L_COPY)
1676 return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
1677
1678 n = pixaGetCount(pixas);
1679 pixad = pixaCreate(n);
1680 for (i = 0; i < n; i++) {
1681 numaGetIValue(naindex, i, &index);
1682 pix = pixaGetPix(pixas, index, copyflag);
1683 box = pixaGetBox(pixas, index, copyflag);
1684 pixaAddPix(pixad, pix, L_INSERT);
1685 pixaAddBox(pixad, box, L_INSERT);
1686 }
1687
1688 return pixad;
1689 }
1690
1691
1692 /*!
1693 * \brief pixaSort2dByIndex()
1694 *
1695 * \param[in] pixas
1696 * \param[in] naa numaa that maps from the new pixaa to the input pixas
1697 * \param[in] copyflag L_CLONE or L_COPY
1698 * \return paa sorted, or NULL on error
1699 */
1700 PIXAA *
1701 pixaSort2dByIndex(PIXA *pixas,
1702 NUMAA *naa,
1703 l_int32 copyflag)
1704 {
1705 l_int32 pixtot, ntot, i, j, n, nn, index;
1706 BOX *box;
1707 NUMA *na;
1708 PIX *pix;
1709 PIXA *pixa;
1710 PIXAA *paa;
1711
1712 if (!pixas)
1713 return (PIXAA *)ERROR_PTR("pixas not defined", __func__, NULL);
1714 if (!naa)
1715 return (PIXAA *)ERROR_PTR("naindex not defined", __func__, NULL);
1716
1717 /* Check counts */
1718 ntot = numaaGetNumberCount(naa);
1719 pixtot = pixaGetCount(pixas);
1720 if (ntot != pixtot)
1721 return (PIXAA *)ERROR_PTR("element count mismatch", __func__, NULL);
1722
1723 n = numaaGetCount(naa);
1724 paa = pixaaCreate(n);
1725 for (i = 0; i < n; i++) {
1726 na = numaaGetNuma(naa, i, L_CLONE);
1727 nn = numaGetCount(na);
1728 pixa = pixaCreate(nn);
1729 for (j = 0; j < nn; j++) {
1730 numaGetIValue(na, j, &index);
1731 pix = pixaGetPix(pixas, index, copyflag);
1732 box = pixaGetBox(pixas, index, copyflag);
1733 pixaAddPix(pixa, pix, L_INSERT);
1734 pixaAddBox(pixa, box, L_INSERT);
1735 }
1736 pixaaAddPixa(paa, pixa, L_INSERT);
1737 numaDestroy(&na);
1738 }
1739
1740 return paa;
1741 }
1742
1743
1744 /*---------------------------------------------------------------------*
1745 * Pixa and Pixaa range selection *
1746 *---------------------------------------------------------------------*/
1747 /*!
1748 * \brief pixaSelectRange()
1749 *
1750 * \param[in] pixas
1751 * \param[in] first use 0 to select from the beginning
1752 * \param[in] last use -1 to select to the end
1753 * \param[in] copyflag L_COPY, L_CLONE
1754 * \return pixad, or NULL on error
1755 *
1756 * <pre>
1757 * Notes:
1758 * (1) The copyflag specifies what we do with each pix from pixas.
1759 * Specifically, L_CLONE inserts a clone into pixad of each
1760 * selected pix from pixas.
1761 * </pre>
1762 */
1763 PIXA *
1764 pixaSelectRange(PIXA *pixas,
1765 l_int32 first,
1766 l_int32 last,
1767 l_int32 copyflag)
1768 {
1769 l_int32 n, npix, i;
1770 PIX *pix;
1771 PIXA *pixad;
1772
1773 if (!pixas)
1774 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1775 if (copyflag != L_COPY && copyflag != L_CLONE)
1776 return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
1777 n = pixaGetCount(pixas);
1778 first = L_MAX(0, first);
1779 if (last < 0) last = n - 1;
1780 if (first >= n)
1781 return (PIXA *)ERROR_PTR("invalid first", __func__, NULL);
1782 if (last >= n) {
1783 L_WARNING("last = %d is beyond max index = %d; adjusting\n",
1784 __func__, last, n - 1);
1785 last = n - 1;
1786 }
1787 if (first > last)
1788 return (PIXA *)ERROR_PTR("first > last", __func__, NULL);
1789
1790 npix = last - first + 1;
1791 pixad = pixaCreate(npix);
1792 for (i = first; i <= last; i++) {
1793 pix = pixaGetPix(pixas, i, copyflag);
1794 pixaAddPix(pixad, pix, L_INSERT);
1795 }
1796 return pixad;
1797 }
1798
1799
1800 /*!
1801 * \brief pixaaSelectRange()
1802 *
1803 * \param[in] paas
1804 * \param[in] first use 0 to select from the beginning
1805 * \param[in] last use -1 to select to the end
1806 * \param[in] copyflag L_COPY, L_CLONE
1807 * \return paad, or NULL on error
1808 *
1809 * <pre>
1810 * Notes:
1811 * (1) The copyflag specifies what we do with each pixa from paas.
1812 * Specifically, L_CLONE inserts a clone into paad of each
1813 * selected pixa from paas.
1814 * </pre>
1815 */
1816 PIXAA *
1817 pixaaSelectRange(PIXAA *paas,
1818 l_int32 first,
1819 l_int32 last,
1820 l_int32 copyflag)
1821 {
1822 l_int32 n, npixa, i;
1823 PIXA *pixa;
1824 PIXAA *paad;
1825
1826 if (!paas)
1827 return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL);
1828 if (copyflag != L_COPY && copyflag != L_CLONE)
1829 return (PIXAA *)ERROR_PTR("invalid copyflag", __func__, NULL);
1830 n = pixaaGetCount(paas, NULL);
1831 first = L_MAX(0, first);
1832 if (last < 0) last = n - 1;
1833 if (first >= n)
1834 return (PIXAA *)ERROR_PTR("invalid first", __func__, NULL);
1835 if (last >= n) {
1836 L_WARNING("last = %d is beyond max index = %d; adjusting\n",
1837 __func__, last, n - 1);
1838 last = n - 1;
1839 }
1840 if (first > last)
1841 return (PIXAA *)ERROR_PTR("first > last", __func__, NULL);
1842
1843 npixa = last - first + 1;
1844 paad = pixaaCreate(npixa);
1845 for (i = first; i <= last; i++) {
1846 pixa = pixaaGetPixa(paas, i, copyflag);
1847 pixaaAddPixa(paad, pixa, L_INSERT);
1848 }
1849 return paad;
1850 }
1851
1852
1853 /*---------------------------------------------------------------------*
1854 * Pixa and Pixaa scaling *
1855 *---------------------------------------------------------------------*/
1856 /*!
1857 * \brief pixaaScaleToSize()
1858 *
1859 * \param[in] paas
1860 * \param[in] wd target width; use 0 if using height as target
1861 * \param[in] hd target height; use 0 if using width as target
1862 * \return paad, or NULL on error
1863 *
1864 * <pre>
1865 * Notes:
1866 * (1) This guarantees that each output scaled image has the
1867 * dimension(s) you specify.
1868 * ~ To specify the width with isotropic scaling, set %hd = 0.
1869 * ~ To specify the height with isotropic scaling, set %wd = 0.
1870 * ~ If both %wd and %hd are specified, the image is scaled
1871 * (in general, anisotropically) to that size.
1872 * ~ It is an error to set both %wd and %hd to 0.
1873 * </pre>
1874 */
1875 PIXAA *
1876 pixaaScaleToSize(PIXAA *paas,
1877 l_int32 wd,
1878 l_int32 hd)
1879 {
1880 l_int32 n, i;
1881 PIXA *pixa1, *pixa2;
1882 PIXAA *paad;
1883
1884 if (!paas)
1885 return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL);
1886 if (wd <= 0 && hd <= 0)
1887 return (PIXAA *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL);
1888
1889 n = pixaaGetCount(paas, NULL);
1890 paad = pixaaCreate(n);
1891 for (i = 0; i < n; i++) {
1892 pixa1 = pixaaGetPixa(paas, i, L_CLONE);
1893 pixa2 = pixaScaleToSize(pixa1, wd, hd);
1894 pixaaAddPixa(paad, pixa2, L_INSERT);
1895 pixaDestroy(&pixa1);
1896 }
1897 return paad;
1898 }
1899
1900
1901 /*!
1902 * \brief pixaaScaleToSizeVar()
1903 *
1904 * \param[in] paas
1905 * \param[in] nawd [optional] target widths; use NULL if using height
1906 * \param[in] nahd [optional] target height; use NULL if using width
1907 * \return paad, or NULL on error
1908 *
1909 * <pre>
1910 * Notes:
1911 * (1) This guarantees that the scaled images in each pixa have the
1912 * dimension(s) you specify in the numas.
1913 * ~ To specify the width with isotropic scaling, set %nahd = NULL.
1914 * ~ To specify the height with isotropic scaling, set %nawd = NULL.
1915 * ~ If both %nawd and %nahd are specified, the image is scaled
1916 * (in general, anisotropically) to that size.
1917 * ~ It is an error to set both %nawd and %nahd to NULL.
1918 * (2) If either nawd and/or nahd is defined, it must have the same
1919 * count as the number of pixa in paas.
1920 * </pre>
1921 */
1922 PIXAA *
1923 pixaaScaleToSizeVar(PIXAA *paas,
1924 NUMA *nawd,
1925 NUMA *nahd)
1926 {
1927 l_int32 n, i, wd, hd;
1928 PIXA *pixa1, *pixa2;
1929 PIXAA *paad;
1930
1931 if (!paas)
1932 return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL);
1933 if (!nawd && !nahd)
1934 return (PIXAA *)ERROR_PTR("!nawd && !nahd", __func__, NULL);
1935
1936 n = pixaaGetCount(paas, NULL);
1937 if (nawd && (n != numaGetCount(nawd)))
1938 return (PIXAA *)ERROR_PTR("nawd wrong size", __func__, NULL);
1939 if (nahd && (n != numaGetCount(nahd)))
1940 return (PIXAA *)ERROR_PTR("nahd wrong size", __func__, NULL);
1941 paad = pixaaCreate(n);
1942 for (i = 0; i < n; i++) {
1943 wd = hd = 0;
1944 if (nawd) numaGetIValue(nawd, i, &wd);
1945 if (nahd) numaGetIValue(nahd, i, &hd);
1946 pixa1 = pixaaGetPixa(paas, i, L_CLONE);
1947 pixa2 = pixaScaleToSize(pixa1, wd, hd);
1948 pixaaAddPixa(paad, pixa2, L_INSERT);
1949 pixaDestroy(&pixa1);
1950 }
1951 return paad;
1952 }
1953
1954
1955 /*!
1956 * \brief pixaScaleToSize()
1957 *
1958 * \param[in] pixas
1959 * \param[in] wd target width; use 0 if using height as target
1960 * \param[in] hd target height; use 0 if using width as target
1961 * \return pixad, or NULL on error
1962 *
1963 * <pre>
1964 * Notes:
1965 * (1) See pixaaScaleToSize()
1966 * </pre>
1967 */
1968 PIXA *
1969 pixaScaleToSize(PIXA *pixas,
1970 l_int32 wd,
1971 l_int32 hd)
1972 {
1973 l_int32 n, i;
1974 PIX *pix1, *pix2;
1975 PIXA *pixad;
1976
1977 if (!pixas)
1978 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1979
1980 if (wd <= 0 && hd <= 0) /* no scaling requested */
1981 return pixaCopy(pixas, L_CLONE);
1982
1983 n = pixaGetCount(pixas);
1984 pixad = pixaCreate(n);
1985 for (i = 0; i < n; i++) {
1986 pix1 = pixaGetPix(pixas, i, L_CLONE);
1987 pix2 = pixScaleToSize(pix1, wd, hd);
1988 pixCopyText(pix2, pix1);
1989 pixaAddPix(pixad, pix2, L_INSERT);
1990 pixDestroy(&pix1);
1991 }
1992 return pixad;
1993 }
1994
1995
1996 /*!
1997 * \brief pixaScaleToSizeRel()
1998 *
1999 * \param[in] pixas
2000 * \param[in] delw change in width, in pixels; 0 means no change
2001 * \param[in] delh change in height, in pixels; 0 means no change
2002 * return pixad, or NULL on error
2003 *
2004 * <pre>
2005 * Notes:
2006 * (1) If a requested change in a pix is not possible because
2007 * either the requested width or height is <= 0, issue a
2008 * warning and return a copy.
2009 * </pre>
2010 */
2011 PIXA *
2012 pixaScaleToSizeRel(PIXA *pixas,
2013 l_int32 delw,
2014 l_int32 delh)
2015 {
2016 l_int32 n, i;
2017 PIX *pix1, *pix2;
2018 PIXA *pixad;
2019
2020 if (!pixas)
2021 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2022
2023 n = pixaGetCount(pixas);
2024 pixad = pixaCreate(n);
2025 for (i = 0; i < n; i++) {
2026 pix1 = pixaGetPix(pixas, i, L_CLONE);
2027 pix2 = pixScaleToSizeRel(pix1, delw, delh);
2028 if (pix2) {
2029 pixaAddPix(pixad, pix2, L_INSERT);
2030 } else {
2031 L_WARNING("relative scale to size failed; use a copy\n", __func__);
2032 pixaAddPix(pixad, pix1, L_COPY);
2033 }
2034 pixDestroy(&pix1);
2035 }
2036 return pixad;
2037 }
2038
2039
2040 /*!
2041 * \brief pixaScale()
2042 *
2043 * \param[in] pixas
2044 * \param[in] scalex
2045 * \param[in] scaley
2046 * \return pixad, or NULL on error
2047 *
2048 * <pre>
2049 * Notes:
2050 * (1) If pixas has a full boxes, it is scaled as well.
2051 * </pre>
2052 */
2053 PIXA *
2054 pixaScale(PIXA *pixas,
2055 l_float32 scalex,
2056 l_float32 scaley)
2057 {
2058 l_int32 i, n, nb;
2059 BOXA *boxa1, *boxa2;
2060 PIX *pix1, *pix2;
2061 PIXA *pixad;
2062
2063 if (!pixas)
2064 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2065 if (scalex <= 0.0 || scaley <= 0.0)
2066 return (PIXA *)ERROR_PTR("invalid scaling parameters", __func__, NULL);
2067
2068 n = pixaGetCount(pixas);
2069 pixad = pixaCreate(n);
2070 for (i = 0; i < n; i++) {
2071 pix1 = pixaGetPix(pixas, i, L_CLONE);
2072 pix2 = pixScale(pix1, scalex, scaley);
2073 pixCopyText(pix2, pix1);
2074 pixaAddPix(pixad, pix2, L_INSERT);
2075 pixDestroy(&pix1);
2076 }
2077
2078 boxa1 = pixaGetBoxa(pixas, L_CLONE);
2079 nb = boxaGetCount(boxa1);
2080 if (nb == n) {
2081 boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley);
2082 pixaSetBoxa(pixad, boxa2, L_INSERT);
2083 }
2084 boxaDestroy(&boxa1);
2085 return pixad;
2086 }
2087
2088
2089 /*!
2090 * \brief pixaScaleBySampling()
2091 *
2092 * \param[in] pixas
2093 * \param[in] scalex
2094 * \param[in] scaley
2095 * \return pixad, or NULL on error
2096 *
2097 * <pre>
2098 * Notes:
2099 * (1) If pixas has a full boxes, it is scaled as well.
2100 * </pre>
2101 */
2102 PIXA *
2103 pixaScaleBySampling(PIXA *pixas,
2104 l_float32 scalex,
2105 l_float32 scaley)
2106 {
2107 l_int32 i, n, nb;
2108 BOXA *boxa1, *boxa2;
2109 PIX *pix1, *pix2;
2110 PIXA *pixad;
2111
2112 if (!pixas)
2113 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2114 if (scalex <= 0.0 || scaley <= 0.0)
2115 return (PIXA *)ERROR_PTR("invalid scaling parameters", __func__, NULL);
2116
2117 n = pixaGetCount(pixas);
2118 pixad = pixaCreate(n);
2119 for (i = 0; i < n; i++) {
2120 pix1 = pixaGetPix(pixas, i, L_CLONE);
2121 pix2 = pixScaleBySampling(pix1, scalex, scaley);
2122 pixCopyText(pix2, pix1);
2123 pixaAddPix(pixad, pix2, L_INSERT);
2124 pixDestroy(&pix1);
2125 }
2126
2127 boxa1 = pixaGetBoxa(pixas, L_CLONE);
2128 nb = boxaGetCount(boxa1);
2129 if (nb == n) {
2130 boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley);
2131 pixaSetBoxa(pixad, boxa2, L_INSERT);
2132 }
2133 boxaDestroy(&boxa1);
2134 return pixad;
2135 }
2136
2137
2138 /*---------------------------------------------------------------------*
2139 * Pixa rotation and translation *
2140 *---------------------------------------------------------------------*/
2141 /*!
2142 * \brief pixaRotate()
2143 *
2144 * \param[in] pixas 1, 2, 4, 8, 32 bpp rgb
2145 * \param[in] angle rotation angle in radians; clockwise is positive
2146 * \param[in] type L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING
2147 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
2148 * \param[in] width original width; use 0 to avoid embedding
2149 * \param[in] height original height; use 0 to avoid embedding
2150 * \return pixad, or NULL on error
2151 *
2152 * <pre>
2153 * Notes:
2154 * (1) Each pix is rotated about its center. See pixRotate() for details.
2155 * (2) The boxa array is copied. Why is it not rotated?
2156 * If a boxa exists, the array of boxes is in 1-to-1
2157 * correspondence with the array of pix, and each box typically
2158 * represents the location of the pix relative to an image from
2159 * which it has been extracted. Like the pix, we could rotate
2160 * each box around its center, and then generate a box that
2161 * contains all four corners, as is done in boxaRotate(), but
2162 * this seems unnecessary.
2163 * </pre>
2164 */
2165 PIXA *
2166 pixaRotate(PIXA *pixas,
2167 l_float32 angle,
2168 l_int32 type,
2169 l_int32 incolor,
2170 l_int32 width,
2171 l_int32 height)
2172 {
2173 l_int32 i, n;
2174 BOXA *boxa;
2175 PIX *pixs, *pixd;
2176 PIXA *pixad;
2177
2178 if (!pixas)
2179 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2180 if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP &&
2181 type != L_ROTATE_SAMPLING)
2182 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
2183 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
2184 return (PIXA *)ERROR_PTR("invalid incolor", __func__, NULL);
2185 if (L_ABS(angle) < MinAngleToRotate)
2186 return pixaCopy(pixas, L_COPY);
2187
2188 n = pixaGetCount(pixas);
2189 if ((pixad = pixaCreate(n)) == NULL)
2190 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2191 boxa = pixaGetBoxa(pixad, L_COPY);
2192 pixaSetBoxa(pixad, boxa, L_INSERT);
2193 for (i = 0; i < n; i++) {
2194 if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) {
2195 pixaDestroy(&pixad);
2196 return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL);
2197 }
2198 pixd = pixRotate(pixs, angle, type, incolor, width, height);
2199 pixaAddPix(pixad, pixd, L_INSERT);
2200 pixDestroy(&pixs);
2201 }
2202
2203 return pixad;
2204 }
2205
2206
2207 /*!
2208 * \brief pixaRotateOrth()
2209 *
2210 * \param[in] pixas
2211 * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg;
2212 * all rotations are clockwise
2213 * \return pixad, or NULL on error
2214 *
2215 * <pre>
2216 * Notes:
2217 * (1) Rotates each pix in the pixa. Rotates and saves the boxes in
2218 * the boxa if the boxa is full.
2219 * </pre>
2220 */
2221 PIXA *
2222 pixaRotateOrth(PIXA *pixas,
2223 l_int32 rotation)
2224 {
2225 l_int32 i, n, nb, w, h;
2226 BOX *boxs, *boxd;
2227 PIX *pixs, *pixd;
2228 PIXA *pixad;
2229
2230 if (!pixas)
2231 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2232 if (rotation < 0 || rotation > 3)
2233 return (PIXA *)ERROR_PTR("rotation not in {0,1,2,3}", __func__, NULL);
2234 if (rotation == 0)
2235 return pixaCopy(pixas, L_COPY);
2236
2237 n = pixaGetCount(pixas);
2238 nb = pixaGetBoxaCount(pixas);
2239 if ((pixad = pixaCreate(n)) == NULL)
2240 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2241 for (i = 0; i < n; i++) {
2242 if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) {
2243 pixaDestroy(&pixad);
2244 return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL);
2245 }
2246 pixd = pixRotateOrth(pixs, rotation);
2247 pixaAddPix(pixad, pixd, L_INSERT);
2248 if (n == nb) {
2249 boxs = pixaGetBox(pixas, i, L_COPY);
2250 pixGetDimensions(pixs, &w, &h, NULL);
2251 boxd = boxRotateOrth(boxs, w, h, rotation);
2252 pixaAddBox(pixad, boxd, L_INSERT);
2253 boxDestroy(&boxs);
2254 }
2255 pixDestroy(&pixs);
2256 }
2257
2258 return pixad;
2259 }
2260
2261
2262 /*!
2263 * \brief pixaTranslate()
2264 *
2265 * \param[in] pixas
2266 * \param[in] hshift horizontal shift; hshift > 0 is to right
2267 * \param[in] vshift vertical shift; vshift > 0 is down
2268 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
2269 * \return pixad, or NULL on error.
2270 */
2271 PIXA *
2272 pixaTranslate(PIXA *pixas,
2273 l_int32 hshift,
2274 l_int32 vshift,
2275 l_int32 incolor)
2276 {
2277 l_int32 i, n, nb;
2278 BOXA *boxas, *boxad;
2279 PIX *pixs, *pixd;
2280 PIXA *pixad;
2281
2282 if (!pixas)
2283 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2284 if (hshift == 0 && vshift == 0)
2285 return pixaCopy(pixas, L_COPY);
2286
2287 n = pixaGetCount(pixas);
2288 nb = pixaGetBoxaCount(pixas);
2289 if ((pixad = pixaCreate(n)) == NULL)
2290 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2291 for (i = 0; i < n; i++) {
2292 if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) {
2293 pixaDestroy(&pixad);
2294 return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL);
2295 }
2296 pixd = pixTranslate(NULL, pixs, hshift, vshift, incolor);
2297 pixaAddPix(pixad, pixd, L_INSERT);
2298 pixDestroy(&pixs);
2299 }
2300 if (n == nb) {
2301 boxas = pixaGetBoxa(pixas, L_CLONE);
2302 boxad = boxaTransform(boxas, hshift, vshift, 1.0, 1.0);
2303 pixaSetBoxa(pixad, boxad, L_INSERT);
2304 boxaDestroy(&boxas);
2305 }
2306
2307 return pixad;
2308 }
2309
2310
2311 /*---------------------------------------------------------------------*
2312 * Miscellaneous functions *
2313 *---------------------------------------------------------------------*/
2314 /*!
2315 * \brief pixaAddBorderGeneral()
2316 *
2317 * \param[in] pixad can be null or equal to pixas
2318 * \param[in] pixas containing pix of all depths; colormap ok
2319 * \param[in] left, right, top, bot number of pixels added
2320 * \param[in] val value of added border pixels
2321 * \return pixad with border added to each pix, including on error
2322 *
2323 * <pre>
2324 * Notes:
2325 * (1) For binary images:
2326 * white: val = 0
2327 * black: val = 1
2328 * For grayscale images:
2329 * white: val = 2 ** d - 1
2330 * black: val = 0
2331 * For rgb color images:
2332 * white: val = 0xffffff00
2333 * black: val = 0
2334 * For colormapped images, use 'index' found this way:
2335 * white: pixcmapGetRankIntensity(cmap, 1.0, &index);
2336 * black: pixcmapGetRankIntensity(cmap, 0.0, &index);
2337 * (2) For in-place replacement of each pix with a bordered version,
2338 * use %pixad = %pixas. To make a new pixa, use %pixad = NULL.
2339 * (3) In both cases, the boxa has sides adjusted as if it were
2340 * expanded by the border.
2341 * </pre>
2342 */
2343 PIXA *
2344 pixaAddBorderGeneral(PIXA *pixad,
2345 PIXA *pixas,
2346 l_int32 left,
2347 l_int32 right,
2348 l_int32 top,
2349 l_int32 bot,
2350 l_uint32 val)
2351 {
2352 l_int32 i, n, nbox;
2353 BOX *box;
2354 BOXA *boxad;
2355 PIX *pixs, *pixd;
2356
2357 if (!pixas)
2358 return (PIXA *)ERROR_PTR("pixas not defined", __func__, pixad);
2359 if (left < 0 || right < 0 || top < 0 || bot < 0)
2360 return (PIXA *)ERROR_PTR("negative border added!", __func__, pixad);
2361 if (pixad && (pixad != pixas))
2362 return (PIXA *)ERROR_PTR("pixad defined but != pixas", __func__, pixad);
2363
2364 n = pixaGetCount(pixas);
2365 if (!pixad)
2366 pixad = pixaCreate(n);
2367 for (i = 0; i < n; i++) {
2368 pixs = pixaGetPix(pixas, i, L_CLONE);
2369 pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val);
2370 if (pixad == pixas) /* replace */
2371 pixaReplacePix(pixad, i, pixd, NULL);
2372 else
2373 pixaAddPix(pixad, pixd, L_INSERT);
2374 pixDestroy(&pixs);
2375 }
2376
2377 nbox = pixaGetBoxaCount(pixas);
2378 boxad = pixaGetBoxa(pixad, L_CLONE);
2379 for (i = 0; i < nbox; i++) {
2380 if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) {
2381 L_WARNING("box %d not found\n", __func__, i);
2382 break;
2383 }
2384 boxAdjustSides(box, box, -left, right, -top, bot);
2385 if (pixad == pixas) /* replace */
2386 boxaReplaceBox(boxad, i, box);
2387 else
2388 boxaAddBox(boxad, box, L_INSERT);
2389 }
2390 boxaDestroy(&boxad);
2391
2392 return pixad;
2393 }
2394
2395
2396 /*!
2397 * \brief pixaaFlattenToPixa()
2398 *
2399 * \param[in] paa
2400 * \param[out] pnaindex [optional] the pixa index in the pixaa
2401 * \param[in] copyflag L_COPY or L_CLONE
2402 * \return pixa, or NULL on error
2403 *
2404 * <pre>
2405 * Notes:
2406 * (1) This 'flattens' the pixaa to a pixa, taking the pix in
2407 * order in the first pixa, then the second, etc.
2408 * (2) If &naindex is defined, we generate a Numa that gives, for
2409 * each pix in the pixaa, the index of the pixa to which it belongs.
2410 * </pre>
2411 */
2412 PIXA *
2413 pixaaFlattenToPixa(PIXAA *paa,
2414 NUMA **pnaindex,
2415 l_int32 copyflag)
2416 {
2417 l_int32 i, j, m, mb, n;
2418 BOX *box;
2419 NUMA *naindex = NULL;
2420 PIX *pix;
2421 PIXA *pixa, *pixat;
2422
2423 if (pnaindex) *pnaindex = NULL;
2424 if (!paa)
2425 return (PIXA *)ERROR_PTR("paa not defined", __func__, NULL);
2426 if (copyflag != L_COPY && copyflag != L_CLONE)
2427 return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
2428
2429 if (pnaindex) {
2430 naindex = numaCreate(0);
2431 *pnaindex = naindex;
2432 }
2433
2434 n = pixaaGetCount(paa, NULL);
2435 pixa = pixaCreate(n);
2436 for (i = 0; i < n; i++) {
2437 pixat = pixaaGetPixa(paa, i, L_CLONE);
2438 m = pixaGetCount(pixat);
2439 mb = pixaGetBoxaCount(pixat);
2440 for (j = 0; j < m; j++) {
2441 pix = pixaGetPix(pixat, j, copyflag);
2442 pixaAddPix(pixa, pix, L_INSERT);
2443 if (j < mb) {
2444 box = pixaGetBox(pixat, j, copyflag);
2445 pixaAddBox(pixa, box, L_INSERT);
2446 }
2447 if (pnaindex)
2448 numaAddNumber(naindex, i); /* save 'row' number */
2449 }
2450 pixaDestroy(&pixat);
2451 }
2452
2453 return pixa;
2454 }
2455
2456
2457 /*!
2458 * \brief pixaaSizeRange()
2459 *
2460 * \param[in] paa
2461 * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of
2462 * dimensions of all boxes
2463 * \return 0 if OK, 1 on error
2464 */
2465 l_ok
2466 pixaaSizeRange(PIXAA *paa,
2467 l_int32 *pminw,
2468 l_int32 *pminh,
2469 l_int32 *pmaxw,
2470 l_int32 *pmaxh)
2471 {
2472 l_int32 minw, minh, maxw, maxh, minpw, minph, maxpw, maxph, i, n;
2473 PIXA *pixa;
2474
2475 if (pminw) *pminw = 0;
2476 if (pminh) *pminh = 0;
2477 if (pmaxw) *pmaxw = 0;
2478 if (pmaxh) *pmaxh = 0;
2479 if (!paa)
2480 return ERROR_INT("paa not defined", __func__, 1);
2481 if (!pminw && !pmaxw && !pminh && !pmaxh)
2482 return ERROR_INT("no data can be returned", __func__, 1);
2483
2484 minw = minh = 100000000;
2485 maxw = maxh = 0;
2486 n = pixaaGetCount(paa, NULL);
2487 for (i = 0; i < n; i++) {
2488 pixa = pixaaGetPixa(paa, i, L_CLONE);
2489 pixaSizeRange(pixa, &minpw, &minph, &maxpw, &maxph);
2490 if (minpw < minw)
2491 minw = minpw;
2492 if (minph < minh)
2493 minh = minph;
2494 if (maxpw > maxw)
2495 maxw = maxpw;
2496 if (maxph > maxh)
2497 maxh = maxph;
2498 pixaDestroy(&pixa);
2499 }
2500
2501 if (pminw) *pminw = minw;
2502 if (pminh) *pminh = minh;
2503 if (pmaxw) *pmaxw = maxw;
2504 if (pmaxh) *pmaxh = maxh;
2505 return 0;
2506 }
2507
2508
2509 /*!
2510 * \brief pixaSizeRange()
2511 *
2512 * \param[in] pixa
2513 * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of
2514 * dimensions of pix in the array
2515 * \return 0 if OK, 1 on error
2516 */
2517 l_ok
2518 pixaSizeRange(PIXA *pixa,
2519 l_int32 *pminw,
2520 l_int32 *pminh,
2521 l_int32 *pmaxw,
2522 l_int32 *pmaxh)
2523 {
2524 l_int32 minw, minh, maxw, maxh, i, n, w, h;
2525 PIX *pix;
2526
2527 if (pminw) *pminw = 0;
2528 if (pminh) *pminh = 0;
2529 if (pmaxw) *pmaxw = 0;
2530 if (pmaxh) *pmaxh = 0;
2531 if (!pixa)
2532 return ERROR_INT("pixa not defined", __func__, 1);
2533 if (!pminw && !pmaxw && !pminh && !pmaxh)
2534 return ERROR_INT("no data can be returned", __func__, 1);
2535
2536 minw = minh = 1000000;
2537 maxw = maxh = 0;
2538 n = pixaGetCount(pixa);
2539 for (i = 0; i < n; i++) {
2540 pix = pixaGetPix(pixa, i, L_CLONE);
2541 w = pixGetWidth(pix);
2542 h = pixGetHeight(pix);
2543 if (w < minw)
2544 minw = w;
2545 if (h < minh)
2546 minh = h;
2547 if (w > maxw)
2548 maxw = w;
2549 if (h > maxh)
2550 maxh = h;
2551 pixDestroy(&pix);
2552 }
2553
2554 if (pminw) *pminw = minw;
2555 if (pminh) *pminh = minh;
2556 if (pmaxw) *pmaxw = maxw;
2557 if (pmaxh) *pmaxh = maxh;
2558
2559 return 0;
2560 }
2561
2562
2563 /*!
2564 * \brief pixaClipToPix()
2565 *
2566 * \param[in] pixas
2567 * \param[in] pixs
2568 * \return pixad, or NULL on error
2569 *
2570 * <pre>
2571 * Notes:
2572 * (1) This is intended for use in situations where pixas
2573 * was originally generated from the input pixs.
2574 * (2) Returns a pixad where each pix in pixas is ANDed
2575 * with its associated region of the input pixs. This
2576 * region is specified by the the box that is associated
2577 * with the pix.
2578 * (3) In a typical application of this function, pixas has
2579 * a set of region masks, so this generates a pixa of
2580 * the parts of pixs that correspond to each region
2581 * mask component, along with the bounding box for
2582 * the region.
2583 * </pre>
2584 */
2585 PIXA *
2586 pixaClipToPix(PIXA *pixas,
2587 PIX *pixs)
2588 {
2589 l_int32 i, n;
2590 BOX *box;
2591 PIX *pix, *pixc;
2592 PIXA *pixad;
2593
2594 if (!pixas)
2595 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2596 if (!pixs)
2597 return (PIXA *)ERROR_PTR("pixs not defined", __func__, NULL);
2598
2599 n = pixaGetCount(pixas);
2600 if ((pixad = pixaCreate(n)) == NULL)
2601 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2602
2603 for (i = 0; i < n; i++) {
2604 pix = pixaGetPix(pixas, i, L_CLONE);
2605 box = pixaGetBox(pixas, i, L_COPY);
2606 pixc = pixClipRectangle(pixs, box, NULL);
2607 pixAnd(pixc, pixc, pix);
2608 pixaAddPix(pixad, pixc, L_INSERT);
2609 pixaAddBox(pixad, box, L_INSERT);
2610 pixDestroy(&pix);
2611 }
2612
2613 return pixad;
2614 }
2615
2616
2617 /*!
2618 * \brief pixaClipToForeground()
2619 *
2620 * \param[in] pixas
2621 * \param[out] ppixad [optional] pixa of clipped pix returned
2622 * \param[out] pboxa [optional] clipping boxes returned
2623 * \return 0 if OK, 1 on error
2624 *
2625 * <pre>
2626 * Notes:
2627 * (1) At least one of [&pixd, &boxa] must be specified.
2628 * (2) Any pix with no fg pixels is skipped.
2629 * (3) See pixClipToForeground().
2630 * </pre>
2631 */
2632 l_ok
2633 pixaClipToForeground(PIXA *pixas,
2634 PIXA **ppixad,
2635 BOXA **pboxa)
2636 {
2637 l_int32 i, n;
2638 BOX *box1;
2639 PIX *pix1, *pix2;
2640
2641 if (ppixad) *ppixad = NULL;
2642 if (pboxa) *pboxa = NULL;
2643 if (!pixas)
2644 return ERROR_INT("pixas not defined", __func__, 1);
2645 if (!ppixad && !pboxa)
2646 return ERROR_INT("no output requested", __func__, 1);
2647
2648 n = pixaGetCount(pixas);
2649 if (ppixad) *ppixad = pixaCreate(n);
2650 if (pboxa) *pboxa = boxaCreate(n);
2651 for (i = 0; i < n; i++) {
2652 pix1 = pixaGetPix(pixas, i, L_CLONE);
2653 pixClipToForeground(pix1, &pix2, &box1);
2654 pixDestroy(&pix1);
2655 if (ppixad)
2656 pixaAddPix(*ppixad, pix2, L_INSERT);
2657 else
2658 pixDestroy(&pix2);
2659 if (pboxa)
2660 boxaAddBox(*pboxa, box1, L_INSERT);
2661 else
2662 boxDestroy(&box1);
2663 }
2664
2665 return 0;
2666 }
2667
2668
2669 /*!
2670 * \brief pixaGetRenderingDepth()
2671 *
2672 * \param[in] pixa
2673 * \param[out] pdepth depth required to render if all colormaps are removed
2674 * \return 0 if OK; 1 on error
2675 *
2676 * <pre>
2677 * Notes:
2678 * (1) Any pix with 16 bpp will be considered as having 8 bpp.
2679 * If all pix have bpp = 1, this returns 1.
2680 * If any pix has color (either rgb or a colormap with color),
2681 * this return 32.
2682 * Otherwise, this returns the maximum of 8 and the max depth
2683 * of all the pix.
2684 * (2) This can be used to allow lossless rendering onto a single pix.
2685 * </pre>
2686 */
2687 l_ok
2688 pixaGetRenderingDepth(PIXA *pixa,
2689 l_int32 *pdepth)
2690 {
2691 l_int32 hascolor, maxdepth;
2692
2693 if (!pdepth)
2694 return ERROR_INT("&depth not defined", __func__, 1);
2695 *pdepth = 0;
2696 if (!pixa)
2697 return ERROR_INT("pixa not defined", __func__, 1);
2698
2699 pixaHasColor(pixa, &hascolor);
2700 if (hascolor) {
2701 *pdepth = 32;
2702 return 0;
2703 }
2704
2705 pixaGetDepthInfo(pixa, &maxdepth, NULL);
2706 if (maxdepth == 1)
2707 *pdepth = 1;
2708 else /* 2, 4, 8 or 16 */
2709 *pdepth = 8;
2710 return 0;
2711 }
2712
2713
2714 /*!
2715 * \brief pixaHasColor()
2716 *
2717 * \param[in] pixa
2718 * \param[out] phascolor 1 if any pix is rgb or has a colormap with color;
2719 * 0 otherwise
2720 * \return 0 if OK; 1 on error
2721 */
2722 l_ok
2723 pixaHasColor(PIXA *pixa,
2724 l_int32 *phascolor)
2725 {
2726 l_int32 i, n, hascolor, d;
2727 PIX *pix;
2728 PIXCMAP *cmap;
2729
2730 if (!phascolor)
2731 return ERROR_INT("&hascolor not defined", __func__, 1);
2732 *phascolor = 0;
2733 if (!pixa)
2734 return ERROR_INT("pixa not defined", __func__, 1);
2735
2736 n = pixaGetCount(pixa);
2737 hascolor = 0;
2738 for (i = 0; i < n; i++) {
2739 pix = pixaGetPix(pixa, i, L_CLONE);
2740 if ((cmap = pixGetColormap(pix)) != NULL)
2741 pixcmapHasColor(cmap, &hascolor);
2742 d = pixGetDepth(pix);
2743 pixDestroy(&pix);
2744 if (d == 32 || hascolor == 1) {
2745 *phascolor = 1;
2746 break;
2747 }
2748 }
2749
2750 return 0;
2751 }
2752
2753
2754 /*!
2755 * \brief pixaAnyColormaps()
2756 *
2757 * \param[in] pixa
2758 * \param[out] phascmap 1 if any pix has a colormap; 0 otherwise
2759 * \return 0 if OK; 1 on error
2760 */
2761 l_ok
2762 pixaAnyColormaps(PIXA *pixa,
2763 l_int32 *phascmap)
2764 {
2765 l_int32 i, n;
2766 PIX *pix;
2767 PIXCMAP *cmap;
2768
2769 if (!phascmap)
2770 return ERROR_INT("&hascmap not defined", __func__, 1);
2771 *phascmap = 0;
2772 if (!pixa)
2773 return ERROR_INT("pixa not defined", __func__, 1);
2774
2775 n = pixaGetCount(pixa);
2776 for (i = 0; i < n; i++) {
2777 pix = pixaGetPix(pixa, i, L_CLONE);
2778 cmap = pixGetColormap(pix);
2779 pixDestroy(&pix);
2780 if (cmap) {
2781 *phascmap = 1;
2782 return 0;
2783 }
2784 }
2785
2786 return 0;
2787 }
2788
2789
2790 /*!
2791 * \brief pixaGetDepthInfo()
2792 *
2793 * \param[in] pixa
2794 * \param[out] pmaxdepth [optional] max pixel depth of pix in pixa
2795 * \param[out] psame [optional] true if all depths are equal
2796 * \return 0 if OK; 1 on error
2797 */
2798 l_ok
2799 pixaGetDepthInfo(PIXA *pixa,
2800 l_int32 *pmaxdepth,
2801 l_int32 *psame)
2802 {
2803 l_int32 i, n, d, d0;
2804 l_int32 maxd, same; /* depth info */
2805
2806 if (pmaxdepth) *pmaxdepth = 0;
2807 if (psame) *psame = TRUE;
2808 if (!pmaxdepth && !psame) return 0;
2809 if (!pixa)
2810 return ERROR_INT("pixa not defined", __func__, 1);
2811 if ((n = pixaGetCount(pixa)) == 0)
2812 return ERROR_INT("pixa is empty", __func__, 1);
2813
2814 same = TRUE;
2815 maxd = 0;
2816 for (i = 0; i < n; i++) {
2817 pixaGetPixDimensions(pixa, i, NULL, NULL, &d);
2818 if (i == 0)
2819 d0 = d;
2820 else if (d != d0)
2821 same = FALSE;
2822 if (d > maxd) maxd = d;
2823 }
2824
2825 if (pmaxdepth) *pmaxdepth = maxd;
2826 if (psame) *psame = same;
2827 return 0;
2828 }
2829
2830
2831 /*!
2832 * \brief pixaConvertToSameDepth()
2833 *
2834 * \param[in] pixas
2835 * \return pixad, or NULL on error
2836 *
2837 * <pre>
2838 * Notes:
2839 * (1) Any pix with 16 bpp will be converted to 8 bpp.
2840 * If all pix have bpp = 1, the output depth will be 1.
2841 * If any pix has color (either rgb or a colormap with color),
2842 * the output depth will be 32.
2843 * Otherwise, the output depth is the maximum of 8 and the
2844 * the max depth of all the pix.
2845 * (2) This can be used to allow lossless rendering onto
2846 * a single pix. (Except: 16 bpp gets converted to 8.)
2847 * </pre>
2848 */
2849 PIXA *
2850 pixaConvertToSameDepth(PIXA *pixas)
2851 {
2852 l_int32 i, n, depth, same, hascmap, maxdepth;
2853 BOXA *boxa;
2854 PIX *pix1, *pix2;
2855 PIXA *pixa1, *pixad;
2856
2857 if (!pixas)
2858 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2859 if ((n = pixaGetCount(pixas)) == 0)
2860 return (PIXA *)ERROR_PTR("no components", __func__, NULL);
2861
2862
2863 /* Remove colormaps if necessary */
2864 pixaGetRenderingDepth(pixas, &depth);
2865 pixaAnyColormaps(pixas, &hascmap);
2866 if (hascmap) {
2867 pixa1 = pixaCreate(n);
2868 for (i = 0; i < n; i++) {
2869 pix1 = pixaGetPix(pixas, i, L_CLONE);
2870 if (depth == 32)
2871 pix2 = pixConvertTo32(pix1);
2872 else /* depth = 8 */
2873 pix2 = pixConvertTo8(pix1, 0);
2874 pixaAddPix(pixa1, pix2, L_INSERT);
2875 pixDestroy(&pix1);
2876 }
2877 } else {
2878 pixa1 = pixaCopy(pixas, L_CLONE);
2879 }
2880
2881 pixaGetDepthInfo(pixa1, &maxdepth, &same);
2882 if (!same) { /* at least one pix has depth < maxdepth */
2883 pixad = pixaCreate(n);
2884 for (i = 0; i < n; i++) {
2885 pix1 = pixaGetPix(pixa1, i, L_CLONE);
2886 if (maxdepth <= 16)
2887 pix2 = pixConvertTo8(pix1, 0);
2888 else
2889 pix2 = pixConvertTo32(pix1);
2890 pixaAddPix(pixad, pix2, L_INSERT);
2891 pixDestroy(&pix1);
2892 }
2893 } else {
2894 pixad = pixaCopy(pixa1, L_CLONE);
2895 }
2896
2897 boxa = pixaGetBoxa(pixas, L_COPY);
2898 pixaSetBoxa(pixad, boxa, L_INSERT);
2899 pixaDestroy(&pixa1);
2900 return pixad;
2901 }
2902
2903
2904 /*!
2905 * \brief pixaConvertToGivenDepth()
2906 *
2907 * \param[in] pixas
2908 * \param[in] depth specify either 8 or 32 bpp
2909 * \return pixad, or NULL on error
2910 *
2911 * <pre>
2912 * Notes:
2913 * (1) Use this to remove any colormaps and convert all pix to either
2914 * 8 or 32 bpp.
2915 * (2) To convert losslessly, get %depth from pixaGetRenderingDepth().
2916 * (3) Clone pix may be in the returned pixa if conversion is to 32 bpp.
2917 * </pre>
2918 */
2919 PIXA *
2920 pixaConvertToGivenDepth(PIXA *pixas,
2921 l_int32 depth)
2922 {
2923 l_int32 i, n, maxd;
2924 BOXA *boxa;
2925 PIX *pix1, *pix2;
2926 PIXA *pixad;
2927
2928 if (!pixas)
2929 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2930 if ((n = pixaGetCount(pixas)) == 0)
2931 return (PIXA *)ERROR_PTR("no components", __func__, NULL);
2932 if (depth != 8 && depth != 32)
2933 return (PIXA *)ERROR_PTR("depth not 8 or 32", __func__, NULL);
2934
2935 /* Warn with 1 --> {8,32} or lossy conversions */
2936 pixaGetRenderingDepth(pixas, &maxd);
2937 if (maxd == 1)
2938 L_WARNING("All pix are 1 bpp; converting to %d bpp\n", __func__, depth);
2939 if (maxd > depth)
2940 L_WARNING("Lossy conversion: max rendering depth %d > input %d\n",
2941 __func__, maxd, depth);
2942
2943 pixad = pixaCreate(n);
2944 for (i = 0; i < n; i++) {
2945 pix1 = pixaGetPix(pixas, i, L_CLONE);
2946 if (depth == 32) {
2947 pix2 = (pixGetDepth(pix1) == 32) ? pixClone(pix1) :
2948 pixConvertTo32(pix1);
2949 } else { /* depth = 8 */
2950 pix2 = pixConvertTo8(pix1, 0);
2951 }
2952 pixaAddPix(pixad, pix2, L_INSERT);
2953 pixDestroy(&pix1);
2954 }
2955
2956 boxa = pixaGetBoxa(pixas, L_COPY);
2957 pixaSetBoxa(pixad, boxa, L_INSERT);
2958 return pixad;
2959 }
2960
2961
2962 /*!
2963 * \brief pixaEqual()
2964 *
2965 * \param[in] pixa1
2966 * \param[in] pixa2
2967 * \param[in] maxdist
2968 * \param[out] pnaindex [optional] index array of correspondences
2969 * \param[out] psame 1 if equal; 0 otherwise
2970 * \return 0 if OK, 1 on error
2971 *
2972 * <pre>
2973 * Notes:
2974 * (1) The two pixa are the "same" if they contain the same
2975 * boxa and the same ordered set of pix. However, if they
2976 * have boxa, the pix in each pixa can differ in ordering
2977 * by an amount given by the parameter %maxdist. If they
2978 * don't have a boxa, the %maxdist parameter is ignored,
2979 * and the ordering of the pix must be identical.
2980 * (2) This applies only to boxa geometry, pixels and ordering;
2981 * other fields in the pix are ignored.
2982 * (3) naindex[i] gives the position of the box in pixa2 that
2983 * corresponds to box i in pixa1. It is only returned if the
2984 * pixa have boxa and the boxa are equal.
2985 * (4) In situations where the ordering is very different, so that
2986 * a large %maxdist is required for "equality", this should be
2987 * implemented with a hash function for efficiency.
2988 * </pre>
2989 */
2990 l_ok
2991 pixaEqual(PIXA *pixa1,
2992 PIXA *pixa2,
2993 l_int32 maxdist,
2994 NUMA **pnaindex,
2995 l_int32 *psame)
2996 {
2997 l_int32 i, j, n, empty1, empty2, same, sameboxa;
2998 BOXA *boxa1, *boxa2;
2999 NUMA *na;
3000 PIX *pix1, *pix2;
3001
3002 if (pnaindex) *pnaindex = NULL;
3003 if (!psame)
3004 return ERROR_INT("&same not defined", __func__, 1);
3005 *psame = 0;
3006 sameboxa = 0;
3007 na = NULL;
3008 if (!pixa1 || !pixa2)
3009 return ERROR_INT("pixa1 and pixa2 not both defined", __func__, 1);
3010 n = pixaGetCount(pixa1);
3011 if (n != pixaGetCount(pixa2))
3012 return 0;
3013
3014 /* If there are no boxes, strict ordering of the pix in each
3015 * pixa is required. */
3016 boxa1 = pixaGetBoxa(pixa1, L_CLONE);
3017 boxa2 = pixaGetBoxa(pixa2, L_CLONE);
3018 empty1 = (boxaGetCount(boxa1) == 0) ? 1 : 0;
3019 empty2 = (boxaGetCount(boxa2) == 0) ? 1 : 0;
3020 if (!empty1 && !empty2) {
3021 boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa);
3022 if (!sameboxa) {
3023 boxaDestroy(&boxa1);
3024 boxaDestroy(&boxa2);
3025 numaDestroy(&na);
3026 return 0;
3027 }
3028 }
3029 boxaDestroy(&boxa1);
3030 boxaDestroy(&boxa2);
3031 if ((!empty1 && empty2) || (empty1 && !empty2))
3032 return 0;
3033
3034 for (i = 0; i < n; i++) {
3035 pix1 = pixaGetPix(pixa1, i, L_CLONE);
3036 if (na)
3037 numaGetIValue(na, i, &j);
3038 else
3039 j = i;
3040 pix2 = pixaGetPix(pixa2, j, L_CLONE);
3041 pixEqual(pix1, pix2, &same);
3042 pixDestroy(&pix1);
3043 pixDestroy(&pix2);
3044 if (!same) {
3045 numaDestroy(&na);
3046 return 0;
3047 }
3048 }
3049
3050 *psame = 1;
3051 if (pnaindex)
3052 *pnaindex = na;
3053 else
3054 numaDestroy(&na);
3055 return 0;
3056 }
3057
3058
3059 /*!
3060 * \brief pixaSetFullSizeBoxa()
3061 *
3062 * \param[in] pixa
3063 * \return 0 if OK, 1 on error
3064 *
3065 * <pre>
3066 * Notes:
3067 * (1) Replaces the existing boxa. Each box gives the dimensions
3068 * of the corresponding pix. This is needed for functions
3069 * like pixaSort() that sort based on the boxes.
3070 * </pre>
3071 */
3072 l_ok
3073 pixaSetFullSizeBoxa(PIXA *pixa)
3074 {
3075 l_int32 i, n, w, h;
3076 BOX *box;
3077 BOXA *boxa;
3078 PIX *pix;
3079
3080 if (!pixa)
3081 return ERROR_INT("pixa not defined", __func__, 1);
3082 if ((n = pixaGetCount(pixa)) == 0) {
3083 L_INFO("pixa contains no pix\n", __func__);
3084 return 0;
3085 }
3086
3087 boxa = boxaCreate(n);
3088 pixaSetBoxa(pixa, boxa, L_INSERT);
3089 for (i = 0; i < n; i++) {
3090 pix = pixaGetPix(pixa, i, L_CLONE);
3091 pixGetDimensions(pix, &w, &h, NULL);
3092 box = boxCreate(0, 0, w, h);
3093 boxaAddBox(boxa, box, L_INSERT);
3094 pixDestroy(&pix);
3095 }
3096 return 0;
3097 }
3098