comparison mupdf-source/thirdparty/leptonica/src/pixlabel.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 pixlabel.c
29 * <pre>
30 *
31 * Label pixels by an index for connected component membership
32 * PIX *pixConnCompTransform()
33 *
34 * Label pixels by the area of their connected component
35 * PIX *pixConnCompAreaTransform()
36 *
37 * Label pixels to allow incremental computation of connected components
38 * l_int32 pixConnCompIncrInit()
39 * l_int32 pixConnCompIncrAdd()
40 * l_int32 pixGetSortedNeighborValues()
41 *
42 * Label pixels with spatially-dependent color coding
43 * PIX *pixLocToColorTransform()
44 *
45 * Pixels get labelled in various ways throughout the leptonica library,
46 * but most of the labelling is implicit, where the new value isn't
47 * even considered to be a label -- it is just a transformed pixel value
48 * that may be transformed again by another operation. Quantization
49 * by thresholding, and dilation by a structuring element, are examples
50 * of these typical image processing operations.
51 *
52 * However, there are some explicit labelling procedures that are useful
53 * as end-points of analysis, where it typically would not make sense
54 * to do further image processing on the result. Assigning false color
55 * based on pixel properties is an example of such labelling operations.
56 * Such operations typically have 1 bpp input images, and result
57 * in grayscale or color images.
58 *
59 * The procedures in this file are concerned with such explicit labelling.
60 * Some of these labelling procedures are also in other places in leptonica:
61 *
62 * runlength.c:
63 * This file has two labelling transforms based on runlengths:
64 * pixStrokeWidthTransform() and pixvRunlengthTransform().
65 * The pixels are labelled based on the width of the "stroke" to
66 * which they belong, or on the length of the horizontal or
67 * vertical run in which they are a member. Runlengths can easily
68 * be filtered using a threshold.
69 *
70 * pixafunc2.c:
71 * This file has an operation, pixaDisplayRandomCmap(), that
72 * randomly labels pix in a pixa (that are typically found using
73 * pixConnComp) with up to 256 values, and assigns each value to
74 * a random colormap color.
75 *
76 * seedfill.c:
77 * This file has pixDistanceFunction(), that labels each pixel with
78 * its distance from either the foreground or the background.
79 * </pre>
80 */
81
82 #ifdef HAVE_CONFIG_H
83 #include <config_auto.h>
84 #endif /* HAVE_CONFIG_H */
85
86 #include <string.h>
87 #include <math.h>
88 #include "allheaders.h"
89 #include "pix_internal.h"
90
91 /*-----------------------------------------------------------------------*
92 * Label pixels by an index for connected component membership *
93 *-----------------------------------------------------------------------*/
94 /*!
95 * \brief pixConnCompTransform()
96 *
97 * \param[in] pixs 1 bpp
98 * \param[in] connect connectivity: 4 or 8
99 * \param[in] depth of pixd: 8 or 16 bpp; use 0 for auto determination
100 * \return pixd 8, 16 or 32 bpp, or NULL on error
101 *
102 * <pre>
103 * Notes:
104 * (1) pixd is 8, 16 or 32 bpp, and the pixel values label the
105 * fg component, starting with 1. Pixels in the bg are labelled 0.
106 * (2) If %depth = 0, the depth of pixd is 8 if the number of c.c.
107 * is less than 254, 16 if the number of c.c is less than 0xfffe,
108 * and 32 otherwise.
109 * (3) If %depth = 8, the assigned label for the n-th component is
110 * 1 + n % 254. We use mod 254 because 0 is uniquely assigned
111 * to black: e.g., see pixcmapCreateRandom(). Likewise,
112 * if %depth = 16, the assigned label uses mod(2^16 - 2), and
113 * if %depth = 32, no mod is taken.
114 * </pre>
115 */
116 PIX *
117 pixConnCompTransform(PIX *pixs,
118 l_int32 connect,
119 l_int32 depth)
120 {
121 l_int32 i, n, index, w, h, xb, yb, wb, hb;
122 BOXA *boxa;
123 PIX *pix1, *pix2, *pixd;
124 PIXA *pixa;
125
126 if (!pixs || pixGetDepth(pixs) != 1)
127 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
128 if (connect != 4 && connect != 8)
129 return (PIX *)ERROR_PTR("connectivity must be 4 or 8", __func__, NULL);
130 if (depth != 0 && depth != 8 && depth != 16 && depth != 32)
131 return (PIX *)ERROR_PTR("depth must be 0, 8, 16 or 32", __func__, NULL);
132
133 boxa = pixConnComp(pixs, &pixa, connect);
134 n = pixaGetCount(pixa);
135 boxaDestroy(&boxa);
136 pixGetDimensions(pixs, &w, &h, NULL);
137 if (depth == 0) {
138 if (n < 254)
139 depth = 8;
140 else if (n < 0xfffe)
141 depth = 16;
142 else
143 depth = 32;
144 }
145 pixd = pixCreate(w, h, depth);
146 pixSetSpp(pixd, 1);
147 if (n == 0) { /* no fg */
148 pixaDestroy(&pixa);
149 return pixd;
150 }
151
152 /* Label each component and blit it in */
153 for (i = 0; i < n; i++) {
154 pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb);
155 pix1 = pixaGetPix(pixa, i, L_CLONE);
156 if (depth == 8) {
157 index = 1 + (i % 254);
158 pix2 = pixConvert1To8(NULL, pix1, 0, index);
159 } else if (depth == 16) {
160 index = 1 + (i % 0xfffe);
161 pix2 = pixConvert1To16(NULL, pix1, 0, index);
162 } else { /* depth == 32 */
163 index = 1 + i;
164 pix2 = pixConvert1To32(NULL, pix1, 0, index);
165 }
166 pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0);
167 pixDestroy(&pix1);
168 pixDestroy(&pix2);
169 }
170
171 pixaDestroy(&pixa);
172 return pixd;
173 }
174
175
176 /*-----------------------------------------------------------------------*
177 * Label pixels by the area of their connected component *
178 *-----------------------------------------------------------------------*/
179 /*!
180 * \brief pixConnCompAreaTransform()
181 *
182 * \param[in] pixs 1 bpp
183 * \param[in] connect connectivity: 4 or 8
184 * \return pixd 32 bpp, 1 spp, or NULL on error
185 *
186 * <pre>
187 * Notes:
188 * (1) The pixel values in pixd label the area of the fg component
189 * to which the pixel belongs. Pixels in the bg are labelled 0.
190 * (2) For purposes of visualization, the output can be converted
191 * to 8 bpp, using pixConvert32To8() or pixMaxDynamicRange().
192 * </pre>
193 */
194 PIX *
195 pixConnCompAreaTransform(PIX *pixs,
196 l_int32 connect)
197 {
198 l_int32 i, n, npix, w, h, xb, yb, wb, hb;
199 l_int32 *tab8;
200 BOXA *boxa;
201 PIX *pix1, *pix2, *pixd;
202 PIXA *pixa;
203
204 if (!pixs || pixGetDepth(pixs) != 1)
205 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
206 if (connect != 4 && connect != 8)
207 return (PIX *)ERROR_PTR("connectivity must be 4 or 8", __func__, NULL);
208
209 boxa = pixConnComp(pixs, &pixa, connect);
210 n = pixaGetCount(pixa);
211 boxaDestroy(&boxa);
212 pixGetDimensions(pixs, &w, &h, NULL);
213 pixd = pixCreate(w, h, 32);
214 pixSetSpp(pixd, 1);
215 if (n == 0) { /* no fg */
216 pixaDestroy(&pixa);
217 return pixd;
218 }
219
220 /* Label each component and blit it in */
221 tab8 = makePixelSumTab8();
222 for (i = 0; i < n; i++) {
223 pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb);
224 pix1 = pixaGetPix(pixa, i, L_CLONE);
225 pixCountPixels(pix1, &npix, tab8);
226 pix2 = pixConvert1To32(NULL, pix1, 0, npix);
227 pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0);
228 pixDestroy(&pix1);
229 pixDestroy(&pix2);
230 }
231
232 pixaDestroy(&pixa);
233 LEPT_FREE(tab8);
234 return pixd;
235 }
236
237
238 /*-------------------------------------------------------------------------*
239 * Label pixels to allow incremental computation of connected components *
240 *-------------------------------------------------------------------------*/
241 /*!
242 * \brief pixConnCompIncrInit()
243 *
244 * \param[in] pixs 1 bpp
245 * \param[in] conn connectivity: 4 or 8
246 * \param[out] ppixd 32 bpp, with c.c. labelled
247 * \param[out] pptaa with pixel locations indexed by c.c.
248 * \param[out] pncc initial number of c.c.
249 * \return 0 if OK, 1 on error
250 *
251 * <pre>
252 * Notes:
253 * (1) This labels the connected components in a 1 bpp pix, and
254 * additionally sets up a ptaa that lists the locations of pixels
255 * in each of the components.
256 * (2) It can be used to initialize the output image and arrays for
257 * an application that maintains information about connected
258 * components incrementally as pixels are added.
259 * (3) pixs can be empty or have some foreground pixels.
260 * (4) The connectivity is stored in pixd->special.
261 * (5) Always initialize with the first pta in ptaa being empty
262 * and representing the background value (index 0) in the pix.
263 * </pre>
264 */
265 l_ok
266 pixConnCompIncrInit(PIX *pixs,
267 l_int32 conn,
268 PIX **ppixd,
269 PTAA **pptaa,
270 l_int32 *pncc)
271 {
272 l_int32 empty, w, h, ncc;
273 PIX *pixd;
274 PTA *pta;
275 PTAA *ptaa;
276
277 if (ppixd) *ppixd = NULL;
278 if (pptaa) *pptaa = NULL;
279 if (pncc) *pncc = 0;
280 if (!ppixd || !pptaa || !pncc)
281 return ERROR_INT("&pixd, &ptaa, &ncc not all defined", __func__, 1);
282 if (!pixs || pixGetDepth(pixs) != 1)
283 return ERROR_INT("pixs undefined or not 1 bpp", __func__, 1);
284 if (conn != 4 && conn != 8)
285 return ERROR_INT("connectivity must be 4 or 8", __func__, 1);
286
287 pixGetDimensions(pixs, &w, &h, NULL);
288 pixZero(pixs, &empty);
289 if (empty) {
290 *ppixd = pixCreate(w, h, 32);
291 pixSetSpp(*ppixd, 1);
292 pixSetSpecial(*ppixd, conn);
293 *pptaa = ptaaCreate(0);
294 pta = ptaCreate(1);
295 ptaaAddPta(*pptaa, pta, L_INSERT); /* reserve index 0 for background */
296 return 0;
297 }
298
299 /* Set up the initial labeled image and indexed pixel arrays */
300 if ((pixd = pixConnCompTransform(pixs, conn, 32)) == NULL)
301 return ERROR_INT("pixd not made", __func__, 1);
302 pixSetSpecial(pixd, conn);
303 *ppixd = pixd;
304 if ((ptaa = ptaaIndexLabeledPixels(pixd, &ncc)) == NULL)
305 return ERROR_INT("ptaa not made", __func__, 1);
306 *pptaa = ptaa;
307 *pncc = ncc;
308 return 0;
309 }
310
311
312 /*!
313 * \brief pixConnCompIncrAdd()
314 *
315 * \param[in] pixs 32 bpp, with pixels labeled by c.c.
316 * \param[in] ptaa with each pta of pixel locations indexed by c.c.
317 * \param[out] pncc number of c.c
318 * \param[in] x,y location of added pixel
319 * \param[in] debug 0 for no output; otherwise output whenever
320 * debug <= nvals, up to debug == 3
321 * \return -1 if nothing happens; 0 if a pixel is added; 1 on error
322 *
323 * <pre>
324 * Notes:
325 * (1) This adds a pixel and updates the labeled connected components.
326 * Before calling this function, initialize the process using
327 * pixConnCompIncrInit().
328 * (2) As a result of adding a pixel, one of the following can happen,
329 * depending on the number of neighbors with non-zero value:
330 * (a) nothing: the pixel is already a member of a c.c.
331 * (b) no neighbors: a new component is added, increasing the
332 * number of c.c.
333 * (c) one neighbor: the pixel is added to an existing c.c.
334 * (d) more than one neighbor: the added pixel causes joining of
335 * two or more c.c., reducing the number of c.c. A maximum
336 * of 4 c.c. can be joined.
337 * (3) When two c.c. are joined, the pixels in the larger index are
338 * relabeled to those of the smaller in pixs, and their locations
339 * are transferred to the pta with the smaller index in the ptaa.
340 * The pta corresponding to the larger index is then deleted.
341 * (4) This is an efficient implementation of a "union-find" operation,
342 * which supports the generation and merging of disjoint sets
343 * of pixels. This function can be called about 1.3 million times
344 * per second.
345 * </pre>
346 */
347 l_int32
348 pixConnCompIncrAdd(PIX *pixs,
349 PTAA *ptaa,
350 l_int32 *pncc,
351 l_float32 x,
352 l_float32 y,
353 l_int32 debug)
354 {
355 l_int32 conn, i, j, w, h, count, nvals, ns, firstindex;
356 l_uint32 val;
357 l_int32 *neigh;
358 PTA *ptas, *ptad;
359
360 if (!pixs || pixGetDepth(pixs) != 32)
361 return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
362 if (!ptaa)
363 return ERROR_INT("ptaa not defined", __func__, 1);
364 if (!pncc)
365 return ERROR_INT("&ncc not defined", __func__, 1);
366 conn = pixs->special;
367 if (conn != 4 && conn != 8)
368 return ERROR_INT("connectivity must be 4 or 8", __func__, 1);
369 pixGetDimensions(pixs, &w, &h, NULL);
370 if (x < 0 || x >= w)
371 return ERROR_INT("invalid x pixel location", __func__, 1);
372 if (y < 0 || y >= h)
373 return ERROR_INT("invalid y pixel location", __func__, 1);
374
375 pixGetPixel(pixs, x, y, &val);
376 if (val > 0) /* already belongs to a set */
377 return -1;
378
379 /* Find unique neighbor pixel values in increasing order of value.
380 * If %nvals > 0, these are returned in the %neigh array, which
381 * is of size %nvals. Note that the pixel values in each
382 * connected component are used as the index into the pta
383 * array of the ptaa, giving the pixel locations. */
384 pixGetSortedNeighborValues(pixs, x, y, conn, &neigh, &nvals);
385
386 /* If there are no neighbors, just add a new component */
387 if (nvals == 0) {
388 count = ptaaGetCount(ptaa);
389 pixSetPixel(pixs, x, y, count);
390 ptas = ptaCreate(1);
391 ptaAddPt(ptas, x, y);
392 ptaaAddPta(ptaa, ptas, L_INSERT);
393 *pncc += 1;
394 LEPT_FREE(neigh);
395 return 0;
396 }
397
398 /* Otherwise, there is at least one neighbor. Add the pixel
399 * to the first neighbor c.c. */
400 firstindex = neigh[0];
401 pixSetPixel(pixs, x, y, firstindex);
402 ptaaAddPt(ptaa, neigh[0], x, y);
403 if (nvals == 1) {
404 if (debug == 1)
405 lept_stderr("nvals = %d: neigh = (%d)\n", nvals, neigh[0]);
406 LEPT_FREE(neigh);
407 return 0;
408 }
409
410 /* If nvals > 1, there are at least 2 neighbors, so this pixel
411 * joins at least one pair of existing c.c. Join each component
412 * to the first component in the list, which is the one with
413 * the smallest integer label. This is done in two steps:
414 * (a) re-label the pixels in the component to the label of the
415 * first component, and
416 * (b) save the pixel locations in the pta for the first component. */
417 if (nvals == 2) {
418 if (debug >= 1 && debug <= 2) {
419 lept_stderr("nvals = %d: neigh = (%d,%d)\n", nvals,
420 neigh[0], neigh[1]);
421 }
422 } else if (nvals == 3) {
423 if (debug >= 1 && debug <= 3) {
424 lept_stderr("nvals = %d: neigh = (%d,%d,%d)\n", nvals,
425 neigh[0], neigh[1], neigh[2]);
426 }
427 } else { /* nvals == 4 */
428 if (debug >= 1 && debug <= 4) {
429 lept_stderr("nvals = %d: neigh = (%d,%d,%d,%d)\n", nvals,
430 neigh[0], neigh[1], neigh[2], neigh[3]);
431 }
432 }
433 ptad = ptaaGetPta(ptaa, firstindex, L_CLONE);
434 for (i = 1; i < nvals; i++) {
435 ptas = ptaaGetPta(ptaa, neigh[i], L_CLONE);
436 ns = ptaGetCount(ptas);
437 for (j = 0; j < ns; j++) { /* relabel pixels */
438 ptaGetPt(ptas, j, &x, &y);
439 pixSetPixel(pixs, x, y, firstindex);
440 }
441 ptaJoin(ptad, ptas, 0, -1); /* add relabeled pixel locations */
442 *pncc -= 1;
443 ptaDestroy(&ptaa->pta[neigh[i]]);
444 ptaDestroy(&ptas); /* the clone */
445 }
446 ptaDestroy(&ptad); /* the clone */
447 LEPT_FREE(neigh);
448 return 0;
449 }
450
451
452 /*!
453 * \brief pixGetSortedNeighborValues()
454 *
455 * \param[in] pixs 8, 16 or 32 bpp, with pixels labeled by c.c.
456 * \param[in] x, y location of pixel
457 * \param[in] conn 4 or 8 connected neighbors
458 * \param[out] pneigh array of integers, to be filled with
459 * the values of the neighbors, if any
460 * \param[out] pnvals the number of unique neighbor values found
461 * \return 0 if OK, 1 on error
462 *
463 * <pre>
464 * Notes:
465 * (1) The returned %neigh array is the unique set of neighboring
466 * pixel values, of size nvals, sorted from smallest to largest.
467 * The value 0, which represents background pixels that do
468 * not belong to any set of connected components, is discarded.
469 * (2) If there are no neighbors, this returns %neigh = NULL; otherwise,
470 * the caller must free the array.
471 * (3) For either 4 or 8 connectivity, the maximum number of unique
472 * neighbor values is 4.
473 * </pre>
474 */
475 l_ok
476 pixGetSortedNeighborValues(PIX *pixs,
477 l_int32 x,
478 l_int32 y,
479 l_int32 conn,
480 l_int32 **pneigh,
481 l_int32 *pnvals)
482 {
483 l_int32 i, npt, index;
484 l_int32 neigh[4];
485 l_uint32 val;
486 l_float32 fx, fy;
487 L_ASET *aset;
488 L_ASET_NODE *node;
489 PTA *pta;
490 RB_TYPE key;
491
492 if (pneigh) *pneigh = NULL;
493 if (pnvals) *pnvals = 0;
494 if (!pneigh || !pnvals)
495 return ERROR_INT("&neigh and &nvals not both defined", __func__, 1);
496 if (!pixs || pixGetDepth(pixs) < 8)
497 return ERROR_INT("pixs not defined or depth < 8", __func__, 1);
498
499 /* Identify the locations of nearest neighbor pixels */
500 if ((pta = ptaGetNeighborPixLocs(pixs, x, y, conn)) == NULL)
501 return ERROR_INT("pta of neighbors not made", __func__, 1);
502
503 /* Find the pixel values and insert into a set as keys */
504 aset = l_asetCreate(L_UINT_TYPE);
505 npt = ptaGetCount(pta);
506 for (i = 0; i < npt; i++) {
507 ptaGetPt(pta, i, &fx, &fy);
508 pixGetPixel(pixs, (l_int32)fx, (l_int32)fy, &val);
509 key.utype = val;
510 l_asetInsert(aset, key);
511 }
512
513 /* Extract the set keys and put them into the %neigh array.
514 * Omit the value 0, which indicates the pixel doesn't
515 * belong to one of the sets of connected components. */
516 node = l_asetGetFirst(aset);
517 index = 0;
518 while (node) {
519 val = node->key.utype;
520 if (val > 0)
521 neigh[index++] = (l_int32)val;
522 node = l_asetGetNext(node);
523 }
524 *pnvals = index;
525 if (index > 0) {
526 *pneigh = (l_int32 *)LEPT_CALLOC(index, sizeof(l_int32));
527 for (i = 0; i < index; i++)
528 (*pneigh)[i] = neigh[i];
529 }
530
531 ptaDestroy(&pta);
532 l_asetDestroy(&aset);
533 return 0;
534 }
535
536
537 /*-----------------------------------------------------------------------*
538 * Label pixels with spatially-dependent color coding *
539 *-----------------------------------------------------------------------*/
540 /*!
541 * \brief pixLocToColorTransform()
542 *
543 * \param[in] pixs 1 bpp
544 * \return pixd 32 bpp rgb, or NULL on error
545 *
546 * <pre>
547 * Notes:
548 * (1) This generates an RGB image where each component value
549 * is coded depending on the (x.y) location and the size
550 * of the fg connected component that the pixel in pixs belongs to.
551 * It is independent of the 4-fold orthogonal orientation, and
552 * only weakly depends on translations and small angle rotations.
553 * Background pixels are black.
554 * (2) Such encodings can be compared between two 1 bpp images
555 * by performing this transform and calculating the
556 * "earth-mover" distance on the resulting R,G,B histograms.
557 * </pre>
558 */
559 PIX *
560 pixLocToColorTransform(PIX *pixs)
561 {
562 l_int32 w, h, w2, h2, wpls, wplr, wplg, wplb, wplcc, i, j, rval, gval, bval;
563 l_float32 invw2, invh2;
564 l_uint32 *datas, *datar, *datag, *datab, *datacc;
565 l_uint32 *lines, *liner, *lineg, *lineb, *linecc;
566 PIX *pix1, *pixcc, *pixr, *pixg, *pixb, *pixd;
567
568 if (!pixs || pixGetDepth(pixs) != 1)
569 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
570
571 /* Label each pixel with the area of the c.c. to which it belongs.
572 * Clip the result to 255 in an 8 bpp pix. This is used for
573 * the blue component of pixd. */
574 pixGetDimensions(pixs, &w, &h, NULL);
575 w2 = w / 2;
576 h2 = h / 2;
577 invw2 = 255.0f / (l_float32)w2;
578 invh2 = 255.0f / (l_float32)h2;
579 pix1 = pixConnCompAreaTransform(pixs, 8);
580 pixcc = pixConvert32To8(pix1, L_LS_TWO_BYTES, L_CLIP_TO_FF);
581 pixDestroy(&pix1);
582
583 /* Label the red and green components depending on the location
584 * of the fg pixels, in a way that is 4-fold rotationally invariant. */
585 pixr = pixCreate(w, h, 8);
586 pixg = pixCreate(w, h, 8);
587 pixb = pixCreate(w, h, 8);
588 wpls = pixGetWpl(pixs);
589 wplr = pixGetWpl(pixr);
590 wplg = pixGetWpl(pixg);
591 wplb = pixGetWpl(pixb);
592 wplcc = pixGetWpl(pixcc);
593 datas = pixGetData(pixs);
594 datar = pixGetData(pixr);
595 datag = pixGetData(pixg);
596 datab = pixGetData(pixb);
597 datacc = pixGetData(pixcc);
598 for (i = 0; i < h; i++) {
599 lines = datas + i * wpls;
600 liner = datar + i * wplr;
601 lineg = datag + i * wplg;
602 lineb = datab + i * wplb;
603 linecc = datacc+ i * wplcc;
604 for (j = 0; j < w; j++) {
605 if (GET_DATA_BIT(lines, j) == 0) continue;
606 if (w < h) {
607 rval = invh2 * L_ABS((l_float32)(i - h2));
608 gval = invw2 * L_ABS((l_float32)(j - w2));
609 } else {
610 rval = invw2 * L_ABS((l_float32)(j - w2));
611 gval = invh2 * L_ABS((l_float32)(i - h2));
612 }
613 bval = GET_DATA_BYTE(linecc, j);
614 SET_DATA_BYTE(liner, j, rval);
615 SET_DATA_BYTE(lineg, j, gval);
616 SET_DATA_BYTE(lineb, j, bval);
617 }
618 }
619 pixd = pixCreateRGBImage(pixr, pixg, pixb);
620
621 pixDestroy(&pixcc);
622 pixDestroy(&pixr);
623 pixDestroy(&pixg);
624 pixDestroy(&pixb);
625 return pixd;
626 }