Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
