Leptonica 1.68
C Image Processing Library

readbarcode.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines