Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 00017 00018 /* 00019 * readbarcode.c 00020 * 00021 * Basic operations to locate and identify the line widths 00022 * in 1D barcodes. 00023 * 00024 * Top level 00025 * SARRAY *pixProcessBarcodes() 00026 * 00027 * Next levels 00028 * PIXA *pixExtractBarcodes() 00029 * SARRAY *pixReadBarcodes() 00030 * l_int32 pixReadBarcodeWidths() 00031 * 00032 * Location 00033 * BOXA *pixLocateBarcodes() 00034 * static PIX *pixGenerateBarcodeMask() 00035 * 00036 * Extraction and deskew 00037 * PIXA *pixDeskewBarcodes() 00038 * 00039 * Process to get line widths 00040 * NUMA *pixExtractBarcodeWidths1() 00041 * NUMA *pixExtractBarcodeWidths2() 00042 * NUMA *pixExtractBarcodeCrossings() 00043 * 00044 * Average adjacent rasters 00045 * static NUMA *pixAverageRasterScans() 00046 * 00047 * Signal processing for barcode widths 00048 * NUMA *numaQuantizeCrossingsByWidth() 00049 * static l_int32 numaGetCrossingDistances() 00050 * static NUMA *numaLocatePeakRanges() 00051 * static NUMA *numaGetPeakCentroids() 00052 * static NUMA *numaGetPeakWidthLUT() 00053 * NUMA *numaQuantizeCrossingsByWindow() 00054 * static l_int32 numaEvalBestWidthAndShift() 00055 * static l_int32 numaEvalSyncError() 00056 * 00057 * 00058 * NOTE CAREFULLY: This is "early beta" code. It has not been tuned 00059 * to work robustly on a large database of barcode images. I'm putting 00060 * it out so that people can play with it, find out how it breaks, and 00061 * contribute decoders for other barcode formats. Both the functional 00062 * interfaces and ABI will almost certainly change in the coming 00063 * few months. The actual decoder, in bardecode.c, at present only 00064 * works on the following codes: Code I2of5, Code 2of5, Code 39, Code 93 00065 * Codabar and UPCA. To add another barcode format, it is necessary 00066 * to make changes in readbarcode.h and bardecode.c. 00067 * The program prog/barcodetest shows how to run from the top level 00068 * (image --> decoded data). 00069 */ 00070 00071 #include <stdio.h> 00072 #include <stdlib.h> 00073 #include <string.h> 00074 #include "allheaders.h" 00075 #include "readbarcode.h" 00076 00077 /* Parameters for pixGenerateBarcodeMask() */ 00078 static const l_int32 MAX_SPACE_WIDTH = 19; /* was 15 */ 00079 static const l_int32 MAX_NOISE_WIDTH = 50; /* smaller than barcode width */ 00080 static const l_int32 MAX_NOISE_HEIGHT = 30; /* smaller than barcode height */ 00081 00082 /* Static functions */ 00083 static PIX *pixGenerateBarcodeMask(PIX *pixs, l_int32 maxspace, 00084 l_int32 nwidth, l_int32 nheight); 00085 static NUMA *pixAverageRasterScans(PIX *pixs, l_int32 nscans); 00086 static l_int32 numaGetCrossingDistances(NUMA *nas, NUMA **pnaedist, 00087 NUMA **pnaodist, l_float32 *pmindist, 00088 l_float32 *pmaxdist); 00089 static NUMA *numaLocatePeakRanges(NUMA *nas, l_float32 minfirst, 00090 l_float32 minsep, l_float32 maxmin); 00091 static NUMA *numaGetPeakCentroids(NUMA *nahist, NUMA *narange); 00092 static NUMA *numaGetPeakWidthLUT(NUMA *narange, NUMA *nacent); 00093 static l_int32 numaEvalBestWidthAndShift(NUMA *nas, l_int32 nwidth, 00094 l_int32 nshift, l_float32 minwidth, 00095 l_float32 maxwidth, 00096 l_float32 *pbestwidth, 00097 l_float32 *pbestshift, 00098 l_float32 *pbestscore); 00099 static l_int32 numaEvalSyncError(NUMA *nas, l_int32 ifirst, l_int32 ilast, 00100 l_float32 width, l_float32 shift, 00101 l_float32 *pscore, NUMA **pnad); 00102 00103 00104 #ifndef NO_CONSOLE_IO 00105 #define DEBUG_DESKEW 1 00106 #define DEBUG_WIDTHS 0 00107 #endif /* ~NO_CONSOLE_IO */ 00108 00109 00110 /*------------------------------------------------------------------------* 00111 * Top level * 00112 *------------------------------------------------------------------------*/ 00113 /*! 00114 * pixProcessBarcodes() 00115 * 00116 * Input: pixs (any depth) 00117 * format (L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ...) 00118 * method (L_USE_WIDTHS, L_USE_WINDOWS) 00119 * &saw (<optional return> sarray of bar widths) 00120 * debugflag (use 1 to generate debug output) 00121 * Return: sarray (text of barcodes), or null if none found or on error 00122 */ 00123 SARRAY * 00124 pixProcessBarcodes(PIX *pixs, 00125 l_int32 format, 00126 l_int32 method, 00127 SARRAY **psaw, 00128 l_int32 debugflag) 00129 { 00130 PIX *pixg; 00131 PIXA *pixa; 00132 SARRAY *sad; 00133 00134 PROCNAME("pixProcessBarcodes"); 00135 00136 if (psaw) *psaw = NULL; 00137 if (!pixs) 00138 return (SARRAY *)ERROR_PTR("pixs not defined", procName, NULL); 00139 if (format != L_BF_ANY && !barcodeFormatIsSupported(format)) 00140 return (SARRAY *)ERROR_PTR("unsupported format", procName, NULL); 00141 if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) 00142 return (SARRAY *)ERROR_PTR("invalid method", procName, NULL); 00143 00144 /* Get an 8 bpp image, no cmap */ 00145 if (pixGetDepth(pixs) == 8 && !pixGetColormap(pixs)) 00146 pixg = pixClone(pixs); 00147 else 00148 pixg = pixConvertTo8(pixs, 0); 00149 00150 if ((pixa = pixExtractBarcodes(pixg, debugflag)) == NULL) { 00151 pixDestroy(&pixg); 00152 return (SARRAY *)ERROR_PTR("no barcode(s) found", procName, NULL); 00153 } 00154 00155 sad = pixReadBarcodes(pixa, format, method, psaw, debugflag); 00156 00157 pixDestroy(&pixg); 00158 pixaDestroy(&pixa); 00159 return sad; 00160 } 00161 00162 00163 /*! 00164 * pixExtractBarcodes() 00165 * 00166 * Input: pixs (8 bpp, no colormap) 00167 * debugflag (use 1 to generate debug output) 00168 * Return: pixa (deskewed and cropped barcodes), or null if 00169 * none found or on error 00170 */ 00171 PIXA * 00172 pixExtractBarcodes(PIX *pixs, 00173 l_int32 debugflag) 00174 { 00175 l_int32 i, n; 00176 l_float32 angle, conf; 00177 BOX *box; 00178 BOXA *boxa; 00179 PIX *pixb, *pixm, *pixt; 00180 PIXA *pixa; 00181 00182 PROCNAME("pixExtractBarcodes"); 00183 00184 if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) 00185 return (PIXA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 00186 00187 /* Locate them; use small threshold for edges. */ 00188 boxa = pixLocateBarcodes(pixs, 20, &pixb, &pixm); 00189 n = boxaGetCount(boxa); 00190 L_INFO_INT("%d possible barcode(s) found", procName, n); 00191 if (n == 0) { 00192 boxaDestroy(&boxa); 00193 pixDestroy(&pixb); 00194 pixDestroy(&pixm); 00195 return NULL; 00196 } 00197 00198 if (debugflag) { 00199 boxaWriteStream(stderr, boxa); 00200 pixDisplay(pixb, 100, 100); 00201 pixDisplay(pixm, 800, 100); 00202 } 00203 00204 /* Deskew each barcode individually */ 00205 pixa = pixaCreate(n); 00206 for (i = 0; i < n; i++) { 00207 box = boxaGetBox(boxa, i, L_CLONE); 00208 pixt = pixDeskewBarcode(pixs, pixb, box, 15, 20, &angle, &conf); 00209 L_INFO_FLOAT2("angle = %6.2f, conf = %6.2f", procName, angle, conf); 00210 if (conf > 5.0) { 00211 pixaAddPix(pixa, pixt, L_INSERT); 00212 pixaAddBox(pixa, box, L_INSERT); 00213 } 00214 else { 00215 pixDestroy(&pixt); 00216 boxDestroy(&box); 00217 } 00218 } 00219 00220 #if DEBUG_DESKEW 00221 pixt = pixaDisplayTiledInRows(pixa, 8, 1000, 1.0, 0, 30, 2); 00222 pixWrite("junkpixt", pixt, IFF_PNG); 00223 pixDestroy(&pixt); 00224 #endif /* DEBUG_DESKEW */ 00225 00226 pixDestroy(&pixb); 00227 pixDestroy(&pixm); 00228 boxaDestroy(&boxa); 00229 return pixa; 00230 } 00231 00232 00233 /*! 00234 * pixReadBarcodes() 00235 * 00236 * Input: pixa (of 8 bpp deskewed and cropped barcodes) 00237 * format (L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ...) 00238 * method (L_USE_WIDTHS, L_USE_WINDOWS); 00239 * &saw (<optional return> sarray of bar widths) 00240 * debugflag (use 1 to generate debug output) 00241 * Return: sa (sarray of widths, one string for each barcode found), 00242 * or null on error 00243 */ 00244 SARRAY * 00245 pixReadBarcodes(PIXA *pixa, 00246 l_int32 format, 00247 l_int32 method, 00248 SARRAY **psaw, 00249 l_int32 debugflag) 00250 { 00251 char *barstr, *data; 00252 char emptystring[] = ""; 00253 l_int32 i, j, n, nbars, ival; 00254 NUMA *na; 00255 PIX *pixt; 00256 SARRAY *saw, *sad; 00257 00258 PROCNAME("pixReadBarcodes"); 00259 00260 if (psaw) *psaw = NULL; 00261 if (!pixa) 00262 return (SARRAY *)ERROR_PTR("pixa not defined", procName, NULL); 00263 if (format != L_BF_ANY && !barcodeFormatIsSupported(format)) 00264 return (SARRAY *)ERROR_PTR("unsupported format", procName, NULL); 00265 if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) 00266 return (SARRAY *)ERROR_PTR("invalid method", procName, NULL); 00267 00268 n = pixaGetCount(pixa); 00269 saw = sarrayCreate(n); 00270 sad = sarrayCreate(n); 00271 for (i = 0; i < n; i++) { 00272 /* Extract the widths of the lines in each barcode */ 00273 pixt = pixaGetPix(pixa, i, L_CLONE); 00274 na = pixReadBarcodeWidths(pixt, method, debugflag); 00275 pixDestroy(&pixt); 00276 if (!na) { 00277 ERROR_INT("valid barcode widths not returned", procName, 1); 00278 continue; 00279 } 00280 00281 /* Save the widths as a string */ 00282 nbars = numaGetCount(na); 00283 barstr = (char *)CALLOC(nbars + 1, sizeof(char)); 00284 for (j = 0; j < nbars; j++) { 00285 numaGetIValue(na, j, &ival); 00286 barstr[j] = 0x30 + ival; 00287 } 00288 sarrayAddString(saw, barstr, L_INSERT); 00289 numaDestroy(&na); 00290 00291 /* Decode the width strings */ 00292 data = barcodeDispatchDecoder(barstr, format, debugflag); 00293 if (!data) { 00294 ERROR_INT("barcode not decoded", procName, 1); 00295 sarrayAddString(sad, emptystring, L_COPY); 00296 continue; 00297 } 00298 sarrayAddString(sad, data, L_INSERT); 00299 } 00300 00301 /* If nothing found, clean up */ 00302 if (sarrayGetCount(saw) == 0) { 00303 sarrayDestroy(&saw); 00304 sarrayDestroy(&sad); 00305 return (SARRAY *)ERROR_PTR("no valid barcode data", procName, NULL); 00306 } 00307 00308 if (psaw) 00309 *psaw = saw; 00310 else 00311 sarrayDestroy(&saw); 00312 00313 return sad; 00314 } 00315 00316 00317 /*! 00318 * pixReadBarcodeWidths() 00319 * 00320 * Input: pixs (of 8 bpp deskewed and cropped barcode) 00321 * method (L_USE_WIDTHS, L_USE_WINDOWS); 00322 * debugflag (use 1 to generate debug output) 00323 * Return: na (numa of widths (each in set {1,2,3,4}), or null on error 00324 */ 00325 NUMA * 00326 pixReadBarcodeWidths(PIX *pixs, 00327 l_int32 method, 00328 l_int32 debugflag) 00329 { 00330 l_float32 winwidth; 00331 NUMA *na; 00332 00333 PROCNAME("pixReadBarcodeWidths"); 00334 00335 if (!pixs) 00336 return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); 00337 if (pixGetDepth(pixs) != 8) 00338 return (NUMA *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00339 if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) 00340 return (NUMA *)ERROR_PTR("invalid method", procName, NULL); 00341 00342 /* Extract the widths of the lines in each barcode */ 00343 if (method == L_USE_WIDTHS) 00344 na = pixExtractBarcodeWidths1(pixs, 120, 0.25, NULL, NULL, 00345 debugflag); 00346 else /* method == L_USE_WINDOWS */ 00347 na = pixExtractBarcodeWidths2(pixs, 120, &winwidth, 00348 NULL, debugflag); 00349 #if DEBUG_WIDTHS 00350 if (method == L_USE_WINDOWS) 00351 fprintf(stderr, "Window width for barcode: %7.3f\n", winwidth); 00352 numaWriteStream(stderr, na); 00353 #endif /* DEBUG_WIDTHS */ 00354 00355 if (!na) 00356 return (NUMA *)ERROR_PTR("barcode widths invalid", procName, NULL); 00357 00358 return na; 00359 } 00360 00361 00362 /*------------------------------------------------------------------------* 00363 * Locate barcode in image * 00364 *------------------------------------------------------------------------*/ 00365 /*! 00366 * pixLocateBarcodes() 00367 * 00368 * Input: pixs (any depth) 00369 * thresh (for binarization of edge filter output; typ. 20) 00370 * &pixb (<optional return> binarized edge filtered input image) 00371 * &pixm (<optional return> mask over barcodes) 00372 * Return: boxa (location of barcodes), or null if none found or on error 00373 */ 00374 BOXA * 00375 pixLocateBarcodes(PIX *pixs, 00376 l_int32 thresh, 00377 PIX **ppixb, 00378 PIX **ppixm) 00379 { 00380 BOXA *boxa; 00381 PIX *pix8, *pixe, *pixb, *pixm; 00382 00383 PROCNAME("pixLocateBarcodes"); 00384 00385 if (!pixs) 00386 return (BOXA *)ERROR_PTR("pixs not defined", procName, NULL); 00387 00388 /* Get an 8 bpp image, no cmap */ 00389 if (pixGetDepth(pixs) == 8 && !pixGetColormap(pixs)) 00390 pix8 = pixClone(pixs); 00391 else 00392 pix8 = pixConvertTo8(pixs, 0); 00393 00394 /* Get a 1 bpp image of the edges */ 00395 pixe = pixSobelEdgeFilter(pix8, L_ALL_EDGES); 00396 pixb = pixThresholdToBinary(pixe, thresh); 00397 pixInvert(pixb, pixb); 00398 pixDestroy(&pix8); 00399 pixDestroy(&pixe); 00400 00401 pixm = pixGenerateBarcodeMask(pixb, MAX_SPACE_WIDTH, MAX_NOISE_WIDTH, 00402 MAX_NOISE_HEIGHT); 00403 boxa = pixConnComp(pixm, NULL, 8); 00404 00405 if (ppixb) 00406 *ppixb = pixb; 00407 else 00408 pixDestroy(&pixb); 00409 if (ppixm) 00410 *ppixm = pixm; 00411 else 00412 pixDestroy(&pixm); 00413 00414 return boxa; 00415 } 00416 00417 00418 /*! 00419 * pixGenerateBarcodeMask() 00420 * 00421 * Input: pixs (1 bpp) 00422 * maxspace (largest space in the barcode, in pixels) 00423 * nwidth (opening 'width' to remove noise) 00424 * nheight (opening 'height' to remove noise) 00425 * Return: pixm (mask over barcodes), or null if none found or on error 00426 * 00427 * Notes: 00428 * (1) For noise removal, 'width' and 'height' are referred to the 00429 * barcode orientation. 00430 * (2) If there is skew, the mask will not cover the barcode corners. 00431 */ 00432 static PIX * 00433 pixGenerateBarcodeMask(PIX *pixs, 00434 l_int32 maxspace, 00435 l_int32 nwidth, 00436 l_int32 nheight) 00437 { 00438 PIX *pixt1, *pixt2, *pixd; 00439 00440 PROCNAME("pixGenerateBarcodeMask"); 00441 00442 if (!pixs || pixGetDepth(pixs) != 1) 00443 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00444 00445 /* Identify horizontal barcodes */ 00446 pixt1 = pixCloseBrick(NULL, pixs, maxspace + 1, 1); 00447 pixt2 = pixOpenBrick(NULL, pixs, maxspace + 1, 1); 00448 pixXor(pixt2, pixt2, pixt1); 00449 pixOpenBrick(pixt2, pixt2, nwidth, nheight); 00450 pixDestroy(&pixt1); 00451 00452 /* Identify vertical barcodes */ 00453 pixt1 = pixCloseBrick(NULL, pixs, 1, maxspace + 1); 00454 pixd = pixOpenBrick(NULL, pixs, 1, maxspace + 1); 00455 pixXor(pixd, pixd, pixt1); 00456 pixOpenBrick(pixd, pixd, nheight, nwidth); 00457 pixDestroy(&pixt1); 00458 00459 /* Combine to get all barcodes */ 00460 pixOr(pixd, pixd, pixt2); 00461 pixDestroy(&pixt2); 00462 00463 return pixd; 00464 } 00465 00466 00467 /*------------------------------------------------------------------------* 00468 * Extract and deskew barcode * 00469 *------------------------------------------------------------------------*/ 00470 /*! 00471 * pixDeskewBarcode() 00472 * 00473 * Input: pixs (input image; 8 bpp) 00474 * pixb (binarized edge-filtered input image) 00475 * box (identified region containing barcode) 00476 * margin (of extra pixels around box to extract) 00477 * threshold (for binarization; ~20) 00478 * &angle (<optional return> in degrees, clockwise is positive) 00479 * &conf (<optional return> confidence) 00480 * Return: pixd (deskewed barcode), or null on error 00481 * 00482 * Note: 00483 * (1) The (optional) angle returned is the angle in degrees (cw positive) 00484 * necessary to rotate the image so that it is deskewed. 00485 */ 00486 PIX * 00487 pixDeskewBarcode(PIX *pixs, 00488 PIX *pixb, 00489 BOX *box, 00490 l_int32 margin, 00491 l_int32 threshold, 00492 l_float32 *pangle, 00493 l_float32 *pconf) 00494 { 00495 l_int32 x, y, w, h, n; 00496 l_float32 angle, angle1, angle2, conf, conf1, conf2, score1, score2, deg2rad; 00497 BOX *boxe, *boxt; 00498 BOXA *boxa, *boxat; 00499 PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6, *pixd; 00500 00501 PROCNAME("pixDeskewBarcode"); 00502 00503 if (!pixs || pixGetDepth(pixs) != 8) 00504 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 00505 if (!pixb || pixGetDepth(pixb) != 1) 00506 return (PIX *)ERROR_PTR("pixb undefined or not 1 bpp", procName, NULL); 00507 if (!box) 00508 return (PIX *)ERROR_PTR("box not defined or 1 bpp", procName, NULL); 00509 00510 /* Clip out */ 00511 deg2rad = 3.1415926535 / 180.; 00512 boxGetGeometry(box, &x, &y, &w, &h); 00513 boxe = boxCreate(x - 25, y - 25, w + 51, h + 51); 00514 pixt1 = pixClipRectangle(pixb, boxe, NULL); 00515 pixt2 = pixClipRectangle(pixs, boxe, NULL); 00516 boxDestroy(&boxe); 00517 00518 /* Deskew, looking at all possible orientations over 180 degrees */ 00519 pixt3 = pixRotateOrth(pixt1, 1); /* look for vertical bar lines */ 00520 pixt4 = pixClone(pixt1); /* look for horizontal bar lines */ 00521 pixFindSkewSweepAndSearchScore(pixt3, &angle1, &conf1, &score1, 00522 1, 1, 0.0, 45.0, 2.5, 0.01); 00523 pixFindSkewSweepAndSearchScore(pixt4, &angle2, &conf2, &score2, 00524 1, 1, 0.0, 45.0, 2.5, 0.01); 00525 00526 /* Because we're using the boundary pixels of the barcodes, 00527 * the peak can be sharper (and the confidence ratio higher) 00528 * from the signal across the top and bottom of the barcode. 00529 * However, the max score, which is the magnitude of the signal 00530 * at the optimum skew angle, will be smaller, so we use the 00531 * max score as the primary indicator of orientation. */ 00532 if (score1 >= score2) { 00533 conf = conf1; 00534 if (conf1 > 6.0 && L_ABS(angle1) > 0.1) { 00535 angle = angle1; 00536 pixt5 = pixRotate(pixt2, deg2rad * angle1, L_ROTATE_AREA_MAP, 00537 L_BRING_IN_WHITE, 0, 0); 00538 } 00539 else { 00540 angle = 0.0; 00541 pixt5 = pixClone(pixt2); 00542 } 00543 } 00544 else { /* score2 > score1 */ 00545 conf = conf2; 00546 pixt6 = pixRotateOrth(pixt2, 1); 00547 if (conf2 > 6.0 && L_ABS(angle2) > 0.1) { 00548 angle = 90.0 + angle2; 00549 pixt5 = pixRotate(pixt6, deg2rad * angle2, L_ROTATE_AREA_MAP, 00550 L_BRING_IN_WHITE, 0, 0); 00551 } 00552 else { 00553 angle = 90.0; 00554 pixt5 = pixClone(pixt6); 00555 } 00556 pixDestroy(&pixt6); 00557 } 00558 pixDestroy(&pixt3); 00559 pixDestroy(&pixt4); 00560 00561 /* Extract barcode plus a margin around it */ 00562 boxa = pixLocateBarcodes(pixt5, threshold, 0, 0); 00563 if ((n = boxaGetCount(boxa)) != 1) { 00564 L_WARNING_INT("barcode mask in %d components", procName, n); 00565 boxat = boxaSort(boxa, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); 00566 } 00567 else { 00568 boxat = boxaCopy(boxa, L_CLONE); 00569 } 00570 boxt = boxaGetBox(boxat, 0, L_CLONE); 00571 boxGetGeometry(boxt, &x, &y, &w, &h); 00572 boxe = boxCreate(x - margin, y - margin, w + 2 * margin, 00573 h + 2 * margin); 00574 pixd = pixClipRectangle(pixt5, boxe, NULL); 00575 boxDestroy(&boxt); 00576 boxDestroy(&boxe); 00577 boxaDestroy(&boxa); 00578 boxaDestroy(&boxat); 00579 00580 if (pangle) *pangle = angle; 00581 if (pconf) *pconf = conf; 00582 00583 pixDestroy(&pixt1); 00584 pixDestroy(&pixt2); 00585 pixDestroy(&pixt5); 00586 return pixd; 00587 } 00588 00589 00590 /*------------------------------------------------------------------------* 00591 * Process to get line widths * 00592 *------------------------------------------------------------------------*/ 00593 /*! 00594 * pixExtractBarcodeWidths1() 00595 * 00596 * Input: pixs (input image; 8 bpp) 00597 * thresh (estimated pixel threshold for crossing 00598 * white <--> black; typ. ~120) 00599 * binfract (histo binsize as a fraction of minsize; e.g., 0.25) 00600 * &naehist (<optional return> histogram of black widths; NULL ok) 00601 * &naohist (<optional return> histogram of white widths; NULL ok) 00602 * debugflag (use 1 to generate debug output) 00603 * Return: nad (numa of barcode widths in encoded integer units), 00604 * or null on error 00605 * 00606 * Note: 00607 * (1) The widths are alternating black/white, starting with black 00608 * and ending with black. 00609 * (2) This method uses the widths of the bars directly, in terms 00610 * of the (float) number of pixels between transitions. 00611 * The histograms of these widths for black and white bars is 00612 * generated and interpreted. 00613 */ 00614 NUMA * 00615 pixExtractBarcodeWidths1(PIX *pixs, 00616 l_float32 thresh, 00617 l_float32 binfract, 00618 NUMA **pnaehist, 00619 NUMA **pnaohist, 00620 l_int32 debugflag) 00621 { 00622 NUMA *nac, *nad; 00623 00624 PROCNAME("pixExtractBarcodeWidths1"); 00625 00626 if (!pixs || pixGetDepth(pixs) != 8) 00627 return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 00628 00629 /* Get the best estimate of the crossings, in pixel units */ 00630 nac = pixExtractBarcodeCrossings(pixs, thresh, debugflag); 00631 00632 /* Get the array of bar widths, starting with a black bar */ 00633 nad = numaQuantizeCrossingsByWidth(nac, binfract, pnaehist, 00634 pnaohist, debugflag); 00635 00636 numaDestroy(&nac); 00637 return nad; 00638 } 00639 00640 00641 /*! 00642 * pixExtractBarcodeWidths2() 00643 * 00644 * Input: pixs (input image; 8 bpp) 00645 * thresh (estimated pixel threshold for crossing 00646 * white <--> black; typ. ~120) 00647 * &width (<optional return> best decoding window width, in pixels) 00648 * &nac (<optional return> number of transitions in each window) 00649 * debugflag (use 1 to generate debug output) 00650 * Return: nad (numa of barcode widths in encoded integer units), 00651 * or null on error 00652 * 00653 * Notes: 00654 * (1) The widths are alternating black/white, starting with black 00655 * and ending with black. 00656 * (2) The optional best decoding window width is the width of the window 00657 * that is used to make a decision about whether a transition occurs. 00658 * It is approximately the average width in pixels of the narrowest 00659 * white and black bars (i.e., those corresponding to unit width). 00660 * (3) The optional return signal @nac is a sequence of 0s, 1s, 00661 * and perhaps a few 2s, giving the number of crossings in each window. 00662 * On the occasion where there is a '2', it is interpreted as 00663 * as ending two runs: the previous one and another one that has length 1. 00664 */ 00665 NUMA * 00666 pixExtractBarcodeWidths2(PIX *pixs, 00667 l_float32 thresh, 00668 l_float32 *pwidth, 00669 NUMA **pnac, 00670 l_int32 debugflag) 00671 { 00672 NUMA *nacp, *nad; 00673 00674 PROCNAME("pixExtractBarcodeWidths2"); 00675 00676 if (!pixs || pixGetDepth(pixs) != 8) 00677 return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 00678 00679 /* Get the best estimate of the crossings, in pixel units */ 00680 nacp = pixExtractBarcodeCrossings(pixs, thresh, debugflag); 00681 00682 /* Quantize the crossings to get actual windowed data */ 00683 nad = numaQuantizeCrossingsByWindow(nacp, 2.0, pwidth, NULL, pnac, debugflag); 00684 00685 numaDestroy(&nacp); 00686 return nad; 00687 } 00688 00689 00690 /*! 00691 * pixExtractBarcodeCrossings() 00692 * 00693 * Input: pixs (input image; 8 bpp) 00694 * thresh (estimated pixel threshold for crossing 00695 * white <--> black; typ. ~120) 00696 * debugflag (use 1 to generate debug output) 00697 * Return: numa (of crossings, in pixel units), or null on error 00698 */ 00699 NUMA * 00700 pixExtractBarcodeCrossings(PIX *pixs, 00701 l_float32 thresh, 00702 l_int32 debugflag) 00703 { 00704 l_int32 w; 00705 l_float32 bestthresh; 00706 NUMA *nas, *nax, *nay, *nad; 00707 00708 PROCNAME("pixExtractBarcodeCrossings"); 00709 00710 if (!pixs || pixGetDepth(pixs) != 8) 00711 return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 00712 00713 /* Scan pixels horizontally and average results */ 00714 nas = pixAverageRasterScans(pixs, 51); 00715 00716 /* Interpolate to get 4x the number of values */ 00717 w = pixGetWidth(pixs); 00718 numaInterpolateEqxInterval(0.0, 1.0, nas, L_QUADRATIC_INTERP, 0.0, 00719 (l_float32)(w - 1), 4 * w + 1, &nax, &nay); 00720 00721 if (debugflag) { 00722 GPLOT *gplot = gplotCreate("junksignal", GPLOT_X11, "Pixel values", 00723 "dist in pixels", "value"); 00724 gplotAddPlot(gplot, nax, nay, GPLOT_LINES, "plot 1"); 00725 gplotMakeOutput(gplot); 00726 gplotDestroy(&gplot); 00727 } 00728 00729 /* Locate the crossings. Run multiple times with different 00730 * thresholds, and choose a threshold in the center of the 00731 * run of thresholds that all give the maximum number of crossings. */ 00732 numaSelectCrossingThreshold(nax, nay, thresh, &bestthresh); 00733 00734 /* Get the crossings with the best threshold. */ 00735 nad = numaCrossingsByThreshold(nax, nay, bestthresh); 00736 00737 numaDestroy(&nas); 00738 numaDestroy(&nax); 00739 numaDestroy(&nay); 00740 return nad; 00741 } 00742 00743 00744 /*------------------------------------------------------------------------* 00745 * Average adjacent rasters * 00746 *------------------------------------------------------------------------*/ 00747 /*! 00748 * pixAverageRasterScans() 00749 * 00750 * Input: pixs (input image; 8 bpp) 00751 * nscans (number of adjacent scans, about the center vertically) 00752 * Return: numa (of average pixel values across image), or null on error 00753 */ 00754 static NUMA * 00755 pixAverageRasterScans(PIX *pixs, 00756 l_int32 nscans) 00757 { 00758 l_int32 w, h, first, last, i, j, wpl, val; 00759 l_uint32 *line, *data; 00760 l_float32 *array; 00761 NUMA *nad; 00762 00763 PROCNAME("pixAverageRasterScans"); 00764 00765 if (!pixs || pixGetDepth(pixs) != 8) 00766 return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 00767 00768 pixGetDimensions(pixs, &w, &h, NULL); 00769 if (nscans <= h) { 00770 first = 0; 00771 last = h - 1; 00772 nscans = h; 00773 } 00774 else { 00775 first = (h - nscans) / 2; 00776 last = first + nscans - 1; 00777 } 00778 00779 nad = numaCreate(w); 00780 numaSetCount(nad, w); 00781 array = numaGetFArray(nad, L_NOCOPY); 00782 wpl = pixGetWpl(pixs); 00783 data = pixGetData(pixs); 00784 for (j = 0; j < w; j++) { 00785 for (i = first; i <= last; i++) { 00786 line = data + i * wpl; 00787 val = GET_DATA_BYTE(line, j); 00788 array[j] += val; 00789 } 00790 array[j] = array[j] / (l_float32)nscans; 00791 } 00792 00793 return nad; 00794 } 00795 00796 00797 /*------------------------------------------------------------------------* 00798 * Signal processing for barcode widths * 00799 *------------------------------------------------------------------------*/ 00800 /*! 00801 * numaQuantizeCrossingsByWidth() 00802 * 00803 * Input: nas (numa of crossing locations, in pixel units) 00804 * binfract (histo binsize as a fraction of minsize; e.g., 0.25) 00805 * &naehist (<optional return> histo of even (black) bar widths) 00806 * &naohist (<optional return> histo of odd (white) bar widths) 00807 * debugflag (1 to generate plots of histograms of bar widths) 00808 * Return: nad (sequence of widths, in unit sizes), or null on error 00809 * 00810 * Notes: 00811 * (1) This first computes the histogram of black and white bar widths, 00812 * binned in appropriate units. There should be well-defined 00813 * peaks, each corresponding to a specific width. The sequence 00814 * of barcode widths (namely, the integers from the set {1,2,3,4}) 00815 * is returned. 00816 * (2) The optional returned histograms are binned in width units 00817 * that are inversely proportional to @binfract. For example, 00818 * if @binfract = 0.25, there are 4.0 bins in the distance of 00819 * the width of the narrowest bar. 00820 */ 00821 NUMA * 00822 numaQuantizeCrossingsByWidth(NUMA *nas, 00823 l_float32 binfract, 00824 NUMA **pnaehist, 00825 NUMA **pnaohist, 00826 l_int32 debugflag) 00827 { 00828 l_int32 i, n, ned, nod, iw, width; 00829 l_float32 val, minsize, maxsize, factor; 00830 GPLOT *gplot; 00831 NUMA *naedist, *naodist, *naehist, *naohist, *naecent, *naocent; 00832 NUMA *naerange, *naorange, *naelut, *naolut, *nad; 00833 00834 PROCNAME("numaQuantizeCrossingsByWidth"); 00835 00836 if (!nas) 00837 return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); 00838 n = numaGetCount(nas); 00839 if (n < 2) 00840 return (NUMA *)ERROR_PTR("n < 2", procName, NULL); 00841 if (binfract <= 0.0) 00842 return (NUMA *)ERROR_PTR("binfract <= 0.0", procName, NULL); 00843 00844 /* Get even and odd crossing distances */ 00845 numaGetCrossingDistances(nas, &naedist, &naodist, &minsize, &maxsize); 00846 00847 /* Bin the spans in units of binfract * minsize. These 00848 * units are convenient because they scale to make at least 00849 * 1/binfract bins in the smallest span (width). We want this 00850 * number to be large enough to clearly separate the 00851 * widths, but small enough so that the histogram peaks 00852 * have very few if any holes (zeroes) within them. */ 00853 naehist = numaMakeHistogramClipped(naedist, binfract * minsize, 00854 (1.25 / binfract) * maxsize); 00855 naohist = numaMakeHistogramClipped(naodist, binfract * minsize, 00856 (1.25 / binfract) * maxsize); 00857 00858 if (debugflag) { 00859 gplot = gplotCreate("junkhistw", GPLOT_X11, 00860 "Raw width histogram", "Width", "Number"); 00861 gplotAddPlot(gplot, NULL, naehist, GPLOT_LINES, "plot black"); 00862 gplotAddPlot(gplot, NULL, naohist, GPLOT_LINES, "plot white"); 00863 gplotMakeOutput(gplot); 00864 gplotDestroy(&gplot); 00865 } 00866 00867 /* Compute the peak ranges, still in units of binfract * minsize. */ 00868 naerange = numaLocatePeakRanges(naehist, 1.0 / binfract, 00869 1.0 / binfract, 0.0); 00870 naorange = numaLocatePeakRanges(naohist, 1.0 / binfract, 00871 1.0 / binfract, 0.0); 00872 00873 /* Find the centroid values of each peak */ 00874 naecent = numaGetPeakCentroids(naehist, naerange); 00875 naocent = numaGetPeakCentroids(naohist, naorange); 00876 00877 /* Generate the lookup tables that map from the bar width, in 00878 * units of (binfract * minsize), to the integerized barcode 00879 * units (1, 2, 3, 4), which are the output integer widths 00880 * between transitions. */ 00881 naelut = numaGetPeakWidthLUT(naerange, naecent); 00882 naolut = numaGetPeakWidthLUT(naorange, naocent); 00883 00884 /* Get the widths. Because the LUT accepts our funny units, 00885 * we first must convert the pixel widths to these units, 00886 * which is what 'factor' does. */ 00887 nad = numaCreate(0); 00888 ned = numaGetCount(naedist); 00889 nod = numaGetCount(naodist); 00890 if (nod != ned - 1) 00891 L_WARNING("ned != nod + 1", procName); 00892 factor = 1.0 / (binfract * minsize); /* for converting units */ 00893 for (i = 0; i < ned - 1; i++) { 00894 numaGetFValue(naedist, i, &val); 00895 width = (l_int32)(factor * val); 00896 numaGetIValue(naelut, width, &iw); 00897 numaAddNumber(nad, iw); 00898 /* fprintf(stderr, "even: val = %7.3f, width = %d, iw = %d\n", 00899 val, width, iw); */ 00900 numaGetFValue(naodist, i, &val); 00901 width = (l_int32)(factor * val); 00902 numaGetIValue(naolut, width, &iw); 00903 numaAddNumber(nad, iw); 00904 /* fprintf(stderr, "odd: val = %7.3f, width = %d, iw = %d\n", 00905 val, width, iw); */ 00906 } 00907 numaGetFValue(naedist, ned - 1, &val); 00908 width = (l_int32)(factor * val); 00909 numaGetIValue(naelut, width, &iw); 00910 numaAddNumber(nad, iw); 00911 00912 if (debugflag) { 00913 fprintf(stderr, " ---- Black bar widths (pixels) ------ \n"); 00914 numaWriteStream(stderr, naedist); 00915 } 00916 if (debugflag) { 00917 fprintf(stderr, " ---- Histogram of black bar widths ------ \n"); 00918 numaWriteStream(stderr, naehist); 00919 } 00920 if (debugflag) { 00921 fprintf(stderr, " ---- Peak ranges in black bar histogram bins ------ \n"); 00922 numaWriteStream(stderr, naerange); 00923 } 00924 if (debugflag) { 00925 fprintf(stderr, " ---- Peak black bar centroid width values ------ \n"); 00926 numaWriteStream(stderr, naecent); 00927 } 00928 if (debugflag) { 00929 fprintf(stderr, " ---- Black bar lookup table ------ \n"); 00930 numaWriteStream(stderr, naelut); 00931 } 00932 if (debugflag) { 00933 fprintf(stderr, " ---- White bar widths (pixels) ------ \n"); 00934 numaWriteStream(stderr, naodist); 00935 } 00936 if (debugflag) { 00937 fprintf(stderr, " ---- Histogram of white bar widths ------ \n"); 00938 numaWriteStream(stderr, naohist); 00939 } 00940 if (debugflag) { 00941 fprintf(stderr, " ---- Peak ranges in white bar histogram bins ------ \n"); 00942 numaWriteStream(stderr, naorange); 00943 } 00944 if (debugflag) { 00945 fprintf(stderr, " ---- Peak white bar centroid width values ------ \n"); 00946 numaWriteStream(stderr, naocent); 00947 } 00948 if (debugflag) { 00949 fprintf(stderr, " ---- White bar lookup table ------ \n"); 00950 numaWriteStream(stderr, naolut); 00951 } 00952 00953 numaDestroy(&naedist); 00954 numaDestroy(&naodist); 00955 numaDestroy(&naerange); 00956 numaDestroy(&naorange); 00957 numaDestroy(&naecent); 00958 numaDestroy(&naocent); 00959 numaDestroy(&naelut); 00960 numaDestroy(&naolut); 00961 if (pnaehist) 00962 *pnaehist = naehist; 00963 else 00964 numaDestroy(&naehist); 00965 if (pnaohist) 00966 *pnaohist = naohist; 00967 else 00968 numaDestroy(&naohist); 00969 return nad; 00970 } 00971 00972 00973 /*! 00974 * numaGetCrossingDistances() 00975 * 00976 * Input: nas (numa of crossing locations) 00977 * &naedist (<optional return> even distances between crossings) 00978 * &naodist (<optional return> odd distances between crossings) 00979 * &mindist (<optional return> min distance between crossings) 00980 * &maxdist (<optional return> max distance between crossings) 00981 * Return: 0 if OK, 1 on error 00982 */ 00983 static l_int32 00984 numaGetCrossingDistances(NUMA *nas, 00985 NUMA **pnaedist, 00986 NUMA **pnaodist, 00987 l_float32 *pmindist, 00988 l_float32 *pmaxdist) 00989 { 00990 l_int32 i, n; 00991 l_float32 val, newval, mindist, maxdist, dist; 00992 NUMA *naedist, *naodist; 00993 00994 PROCNAME("numaGetCrossingDistances"); 00995 00996 if (pnaedist) *pnaedist = NULL; 00997 if (pnaodist) *pnaodist = NULL; 00998 if (pmindist) *pmindist = 0.0; 00999 if (pmaxdist) *pmaxdist = 0.0; 01000 if (!nas) 01001 return ERROR_INT("nas not defined", procName, 1); 01002 if ((n = numaGetCount(nas)) < 2) 01003 return ERROR_INT("n < 2", procName, 1); 01004 01005 /* Get numas of distances between crossings. Separate these 01006 * into even (e.g., black) and odd (e.g., white) spans. 01007 * For barcodes, the black spans are 0, 2, etc. These 01008 * distances are in pixel units. */ 01009 naedist = numaCreate(n / 2 + 1); 01010 naodist = numaCreate(n / 2); 01011 numaGetFValue(nas, 0, &val); 01012 for (i = 1; i < n; i++) { 01013 numaGetFValue(nas, i, &newval); 01014 if (i % 2) 01015 numaAddNumber(naedist, newval - val); 01016 else 01017 numaAddNumber(naodist, newval - val); 01018 val = newval; 01019 } 01020 01021 /* The mindist and maxdist of the spans are in pixel units. */ 01022 numaGetMin(naedist, &mindist, NULL); 01023 numaGetMin(naodist, &dist, NULL); 01024 mindist = L_MIN(dist, mindist); 01025 numaGetMax(naedist, &maxdist, NULL); 01026 numaGetMax(naodist, &dist, NULL); 01027 maxdist = L_MAX(dist, maxdist); 01028 L_INFO_FLOAT2("mindist = %7.3f, maxdist = %7.3f\n", 01029 procName, mindist, maxdist); 01030 01031 if (pnaedist) 01032 *pnaedist = naedist; 01033 else 01034 numaDestroy(&naedist); 01035 if (pnaodist) 01036 *pnaodist = naodist; 01037 else 01038 numaDestroy(&naodist); 01039 if (pmindist) *pmindist = mindist; 01040 if (pmaxdist) *pmaxdist = maxdist; 01041 return 0; 01042 } 01043 01044 01045 /*! 01046 * numaLocatePeakRanges() 01047 * 01048 * Input: nas (numa of histogram of crossing widths) 01049 * minfirst (min location of center of first peak) 01050 * minsep (min separation between peak range centers) 01051 * maxmin (max allowed value for min histo value between peaks) 01052 * Return: nad (ranges for each peak found, in pairs), or null on error 01053 * 01054 * Notes: 01055 * (1) Units of @minsep are the index into nas. 01056 * This puts useful constraints on peak-finding. 01057 * (2) If maxmin == 0.0, the value of nas[i] must go to 0.0 (or less) 01058 * between peaks. 01059 * (3) All calculations are done in units of the index into nas. 01060 * The resulting ranges are therefore integers. 01061 * (4) The output nad gives pairs of range values for successive peaks. 01062 * Any location [i] for which maxmin = nas[i] = 0.0 will NOT be 01063 * included in a peak range. This works fine for histograms where 01064 * if nas[i] == 0.0, it means that there are no samples at [i]. 01065 * (5) For barcodes, when this is used on a histogram of barcode 01066 * widths, use maxmin = 0.0. This requires that there is at 01067 * least one histogram bin corresponding to a width value between 01068 * adjacent peak ranges that is unpopulated, making the separation 01069 * of the histogram peaks unambiguous. 01070 */ 01071 static NUMA * 01072 numaLocatePeakRanges(NUMA *nas, 01073 l_float32 minfirst, 01074 l_float32 minsep, 01075 l_float32 maxmin) 01076 { 01077 l_int32 i, n, inpeak, left; 01078 l_float32 center, prevcenter, val; 01079 NUMA *nad; 01080 01081 PROCNAME("numaLocatePeakRanges"); 01082 01083 if (!nas) 01084 return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); 01085 n = numaGetCount(nas); 01086 nad = numaCreate(0); 01087 01088 inpeak = FALSE; 01089 prevcenter = minfirst - minsep - 1.0; 01090 for (i = 0; i < n; i++) { 01091 numaGetFValue(nas, i, &val); 01092 if (inpeak == FALSE && val > maxmin) { 01093 inpeak = TRUE; 01094 left = i; 01095 } 01096 else if (inpeak == TRUE && val <= maxmin) { /* end peak */ 01097 center = (left + i - 1.0) / 2.0; 01098 if (center - prevcenter >= minsep) { /* save new peak */ 01099 inpeak = FALSE; 01100 numaAddNumber(nad, left); 01101 numaAddNumber(nad, i - 1); 01102 prevcenter = center; 01103 } 01104 else { /* attach to previous peak; revise the right edge */ 01105 numaSetValue(nad, numaGetCount(nad) - 1, i - 1); 01106 } 01107 } 01108 } 01109 if (inpeak == TRUE) { /* save the last peak */ 01110 numaAddNumber(nad, left); 01111 numaAddNumber(nad, n - 1); 01112 } 01113 01114 return nad; 01115 } 01116 01117 01118 /*! 01119 * numaGetPeakCentroids() 01120 * 01121 * Input: nahist (numa of histogram of crossing widths) 01122 * narange (numa of ranges of x-values for the peaks in @nahist) 01123 * Return: nad (centroids for each peak found; max of 4, corresponding 01124 * to 4 different barcode line widths), or null on error 01125 */ 01126 static NUMA * 01127 numaGetPeakCentroids(NUMA *nahist, 01128 NUMA *narange) 01129 { 01130 l_int32 i, j, nh, nr, low, high; 01131 l_float32 cent, sum, val; 01132 NUMA *nad; 01133 01134 PROCNAME("numaGetPeakCentroids"); 01135 01136 if (!nahist) 01137 return (NUMA *)ERROR_PTR("nahist not defined", procName, NULL); 01138 if (!narange) 01139 return (NUMA *)ERROR_PTR("narange not defined", procName, NULL); 01140 nh = numaGetCount(nahist); 01141 nr = numaGetCount(narange) / 2; 01142 01143 nad = numaCreate(4); 01144 for (i = 0; i < nr; i++) { 01145 numaGetIValue(narange, 2 * i, &low); 01146 numaGetIValue(narange, 2 * i + 1, &high); 01147 cent = 0.0; 01148 sum = 0.0; 01149 for (j = low; j <= high; j++) { 01150 numaGetFValue(nahist, j, &val); 01151 cent += j * val; 01152 sum += val; 01153 } 01154 numaAddNumber(nad, cent / sum); 01155 } 01156 01157 return nad; 01158 } 01159 01160 01161 /*! 01162 * numaGetPeakWidthLUT() 01163 * 01164 * Input: narange (numa of x-val ranges for the histogram width peaks) 01165 * nacent (numa of centroids of each peak -- up to 4) 01166 * Return: nalut (lookup table from the width of a bar to one of the four 01167 * integerized barcode units), or null on error 01168 * 01169 * Notes: 01170 * (1) This generates the lookup table that maps from a sequence of widths 01171 * (in some units) to the integerized barcode units (1, 2, 3, 4), 01172 * which are the output integer widths between transitions. 01173 * (2) The smallest width can be lost in float roundoff. To avoid 01174 * losing it, we expand the peak range of the smallest width. 01175 */ 01176 static NUMA * 01177 numaGetPeakWidthLUT(NUMA *narange, 01178 NUMA *nacent) 01179 { 01180 l_int32 i, j, nc, low, high, imax; 01181 l_int32 assign[4]; 01182 l_float32 *warray; 01183 l_float32 max, rat21, rat32, rat42; 01184 NUMA *nalut; 01185 01186 PROCNAME("numaGetPeakWidthLUT"); 01187 01188 if (!narange) 01189 return (NUMA *)ERROR_PTR("narange not defined", procName, NULL); 01190 if (!nacent) 01191 return (NUMA *)ERROR_PTR("nacent not defined", procName, NULL); 01192 nc = numaGetCount(nacent); /* half the size of narange */ 01193 if (nc < 1 || nc > 4) 01194 return (NUMA *)ERROR_PTR("nc must be 1, 2, 3, or 4", procName, NULL); 01195 01196 /* Check the peak centroids for consistency with bar widths. 01197 * The third peak can correspond to a width of either 3 or 4. 01198 * Use ratios 3/2 and 4/2 instead of 3/1 and 4/1 because the 01199 * former are more stable and closer to the expected ratio. */ 01200 if (nc > 1) { 01201 warray = numaGetFArray(nacent, L_NOCOPY); 01202 if (warray[0] == 0) 01203 return (NUMA *)ERROR_PTR("first peak has width 0.0", 01204 procName, NULL); 01205 rat21 = warray[1] / warray[0]; 01206 if (rat21 < 1.5 || rat21 > 2.6) 01207 L_WARNING_FLOAT("width ratio 2/1 = %f", procName, rat21); 01208 if (nc > 2) { 01209 rat32 = warray[2] / warray[1]; 01210 if (rat32 < 1.3 || rat32 > 2.25) 01211 L_WARNING_FLOAT("width ratio 3/2 = %f", procName, rat32); 01212 } 01213 if (nc == 4) { 01214 rat42 = warray[3] / warray[1]; 01215 if (rat42 < 1.7 || rat42 > 2.3) 01216 L_WARNING_FLOAT("width ratio 4/2 = %f", procName, rat42); 01217 } 01218 } 01219 01220 /* Set width assignments. 01221 * The only possible ambiguity is with nc = 3 */ 01222 for (i = 0; i < 4; i++) 01223 assign[i] = i + 1; 01224 if (nc == 3) { 01225 if (rat32 > 1.75) 01226 assign[2] = 4; 01227 } 01228 01229 /* Put widths into the LUT */ 01230 numaGetMax(narange, &max, NULL); 01231 imax = (l_int32)max; 01232 nalut = numaCreate(imax + 1); 01233 numaSetCount(nalut, imax + 1); /* fill the array with zeroes */ 01234 for (i = 0; i < nc; i++) { 01235 numaGetIValue(narange, 2 * i, &low); 01236 if (i == 0) low--; /* catch smallest width */ 01237 numaGetIValue(narange, 2 * i + 1, &high); 01238 for (j = low; j <= high; j++) 01239 numaSetValue(nalut, j, assign[i]); 01240 } 01241 01242 return nalut; 01243 } 01244 01245 01246 /*! 01247 * numaQuantizeCrossingsByWindow() 01248 * 01249 * Input: nas (numa of crossing locations) 01250 * ratio (of max window size over min window size in search; 01251 * typ. 2.0) 01252 * &width (<optional return> best window width) 01253 * &firstloc (<optional return> center of window for first xing) 01254 * &nac (<optional return> array of window crossings (0, 1, 2)) 01255 * debugflag (1 to generate various plots of intermediate results) 01256 * Return: nad (sequence of widths, in unit sizes), or null on error 01257 * 01258 * Notes: 01259 * (1) The minimum size of the window is set by the minimum 01260 * distance between zero crossings. 01261 * (2) The optional return signal @nac is a sequence of 0s, 1s, 01262 * and perhaps a few 2s, giving the number of crossings in each window. 01263 * On the occasion where there is a '2', it is interpreted as 01264 * ending two runs: the previous one and another one that has length 1. 01265 */ 01266 NUMA * 01267 numaQuantizeCrossingsByWindow(NUMA *nas, 01268 l_float32 ratio, 01269 l_float32 *pwidth, 01270 l_float32 *pfirstloc, 01271 NUMA **pnac, 01272 l_int32 debugflag) 01273 { 01274 l_int32 i, nw, started, count, trans; 01275 l_float32 minsize, minwidth, minshift, xfirst; 01276 NUMA *nac, *nad; 01277 01278 PROCNAME("numaQuantizeCrossingsByWindow"); 01279 01280 if (!nas) 01281 return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); 01282 if (numaGetCount(nas) < 2) 01283 return (NUMA *)ERROR_PTR("nas size < 2", procName, NULL); 01284 01285 /* Get the minsize, which is needed for the search for 01286 * the window width (ultimately found as 'minwidth') */ 01287 numaGetCrossingDistances(nas, NULL, NULL, &minsize, NULL); 01288 01289 /* Compute the width and shift increments; start at minsize 01290 * and go up to ratio * minsize */ 01291 numaEvalBestWidthAndShift(nas, 100, 10, minsize, ratio * minsize, 01292 &minwidth, &minshift, NULL); 01293 01294 /* Refine width and shift calculation */ 01295 numaEvalBestWidthAndShift(nas, 100, 10, 0.98 * minwidth, 1.02 * minwidth, 01296 &minwidth, &minshift, NULL); 01297 01298 L_INFO_FLOAT2("best width = %7.3f, best shift = %7.3f\n", 01299 procName, minwidth, minshift); 01300 01301 /* Get the crossing array (0,1,2) for the best window width and shift */ 01302 numaEvalSyncError(nas, 0, 0, minwidth, minshift, NULL, &nac); 01303 if (pwidth) *pwidth = minwidth; 01304 if (pfirstloc) { 01305 numaGetFValue(nas, 0, &xfirst); 01306 *pfirstloc = xfirst + minshift; 01307 } 01308 01309 /* Get the array of bar widths, starting with a black bar */ 01310 nad = numaCreate(0); 01311 nw = numaGetCount(nac); /* number of window measurements */ 01312 started = FALSE; 01313 count = 0; /* unnecessary init */ 01314 for (i = 0; i < nw; i++) { 01315 numaGetIValue(nac, i, &trans); 01316 if (trans > 2) 01317 L_WARNING_INT("trans = %d > 2 !!!", procName, trans); 01318 if (started) { 01319 if (trans > 1) { /* i.e., when trans == 2 */ 01320 numaAddNumber(nad, count); 01321 trans--; 01322 count = 1; 01323 } 01324 if (trans == 1) { 01325 numaAddNumber(nad, count); 01326 count = 1; 01327 } 01328 else 01329 count++; 01330 } 01331 if (!started && trans) { 01332 started = TRUE; 01333 if (trans == 2) /* a whole bar in this window */ 01334 numaAddNumber(nad, 1); 01335 count = 1; 01336 } 01337 } 01338 01339 if (pnac) 01340 *pnac = nac; 01341 else 01342 numaDestroy(&nac); 01343 return nad; 01344 } 01345 01346 01347 /*! 01348 * numaEvalBestWidthAndShift() 01349 * 01350 * Input: nas (numa of crossing locations) 01351 * nwidth (number of widths to consider) 01352 * nshift (number of shifts to consider for each width) 01353 * minwidth (smallest width to consider) 01354 * maxwidth (largest width to consider) 01355 * &bestwidth (<return> best size of window) 01356 * &bestshift (<return> best shift for the window) 01357 * &bestscore (<optional return> average squared error of dist 01358 * of crossing signal from the center of the window) 01359 * Return: 0 if OK, 1 on error 01360 * 01361 * Notes: 01362 * (1) This does a linear sweep of widths, evaluating at @nshift 01363 * shifts for each width, finding the (width, shift) pair that 01364 * gives the minimum score. 01365 */ 01366 static l_int32 01367 numaEvalBestWidthAndShift(NUMA *nas, 01368 l_int32 nwidth, 01369 l_int32 nshift, 01370 l_float32 minwidth, 01371 l_float32 maxwidth, 01372 l_float32 *pbestwidth, 01373 l_float32 *pbestshift, 01374 l_float32 *pbestscore) 01375 { 01376 l_int32 i, j; 01377 l_float32 delwidth, delshift, width, shift, score; 01378 l_float32 bestwidth, bestshift, bestscore; 01379 01380 PROCNAME("numaEvalBestWidthAndShift"); 01381 01382 if (!nas) 01383 return ERROR_INT("nas not defined", procName, 1); 01384 if (!pbestwidth || !pbestshift) 01385 return ERROR_INT("&bestwidth and &bestshift not defined", procName, 1); 01386 01387 bestscore = 1.0; 01388 delwidth = (maxwidth - minwidth) / (nwidth - 1.0); 01389 for (i = 0; i < nwidth; i++) { 01390 width = minwidth + delwidth * i; 01391 delshift = width / (l_float32)(nshift); 01392 for (j = 0; j < nshift; j++) { 01393 shift = -0.5 * (width - delshift) + j * delshift; 01394 numaEvalSyncError(nas, 0, 0, width, shift, &score, NULL); 01395 if (score < bestscore) { 01396 bestscore = score; 01397 bestwidth = width; 01398 bestshift = shift; 01399 #if DEBUG_FREQUENCY 01400 fprintf(stderr, "width = %7.3f, shift = %7.3f, score = %7.3f\n", 01401 width, shift, score); 01402 #endif /* DEBUG_FREQUENCY */ 01403 } 01404 } 01405 } 01406 01407 *pbestwidth = bestwidth; 01408 *pbestshift = bestshift; 01409 if (pbestscore) 01410 *pbestscore = bestscore; 01411 return 0; 01412 } 01413 01414 01415 /*! 01416 * numaEvalSyncError() 01417 * 01418 * Input: nas (numa of crossing locations) 01419 * ifirst (first crossing to use) 01420 * ilast (last crossing to use; use 0 for all crossings) 01421 * width (size of window) 01422 * shift (of center of window w/rt first crossing) 01423 * &score (<optional return> average squared error of dist 01424 * of crossing signal from the center of the window) 01425 * &nad (<optional return> numa of 1s and 0s for crossings) 01426 * Return: 0 if OK, 1 on error 01427 * 01428 * Notes: 01429 * (1) The score is computed only on the part of the signal from the 01430 * @ifirst to @ilast crossings. Use 0 for both of these to 01431 * use all the crossings. The score is normalized for 01432 * the number of crossings and with half-width of the window. 01433 * (2) The optional return @nad is a sequence of 0s and 1s, where a '1' 01434 * indicates a crossing in the window. 01435 */ 01436 static l_int32 01437 numaEvalSyncError(NUMA *nas, 01438 l_int32 ifirst, 01439 l_int32 ilast, 01440 l_float32 width, 01441 l_float32 shift, 01442 l_float32 *pscore, 01443 NUMA **pnad) 01444 { 01445 l_int32 i, n, nc, nw, ival; 01446 l_int32 iw; /* cell in which transition occurs */ 01447 l_float32 score, xfirst, xlast, xleft, xc, xwc; 01448 NUMA *nad; 01449 01450 PROCNAME("numaEvalSyncError"); 01451 01452 if (!nas) 01453 return ERROR_INT("nas not defined", procName, 1); 01454 if ((n = numaGetCount(nas)) < 2) 01455 return ERROR_INT("nas size < 2", procName, 1); 01456 if (ifirst < 0) ifirst = 0; 01457 if (ilast <= 0) ilast = n - 1; 01458 if (ifirst >= ilast) 01459 return ERROR_INT("ifirst not < ilast", procName, 1); 01460 nc = ilast - ifirst + 1; 01461 01462 /* Set up an array corresponding to the (shifted) windows, 01463 * and fill in the crossings. */ 01464 score = 0.0; 01465 numaGetFValue(nas, ifirst, &xfirst); 01466 numaGetFValue(nas, ilast, &xlast); 01467 nw = (l_int32) ((xlast - xfirst + 2.0 * width) / width); 01468 nad = numaCreate(nw); 01469 numaSetCount(nad, nw); /* init to all 0.0 */ 01470 xleft = xfirst - width / 2.0 + shift; /* left edge of first window */ 01471 for (i = ifirst; i <= ilast; i++) { 01472 numaGetFValue(nas, i, &xc); 01473 iw = (l_int32)((xc - xleft) / width); 01474 xwc = xleft + (iw + 0.5) * width; /* center of cell iw */ 01475 score += (xwc - xc) * (xwc - xc); 01476 numaGetIValue(nad, iw, &ival); 01477 numaSetValue(nad, iw, ival + 1); 01478 } 01479 01480 if (pscore) 01481 *pscore = 4.0 * score / (width * width * (l_float32)nc); 01482 if (pnad) 01483 *pnad = nad; 01484 else 01485 numaDestroy(&nad); 01486 01487 return 0; 01488 } 01489