Leptonica 1.68
C Image Processing Library

sel1.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  *  sel1.c
00020  *
00021  *      Basic ops on Sels and Selas
00022  *
00023  *         Create/destroy/copy:
00024  *            SELA      *selaCreate()
00025  *            void       selaDestroy()
00026  *            SEL       *selCreate()
00027  *            void       selDestroy()
00028  *            SEL       *selCopy()
00029  *            SEL       *selCreateBrick()
00030  *            SEL       *selCreateComb()
00031  *
00032  *         Helper proc:
00033  *            l_int32  **create2dIntArray()
00034  *
00035  *         Extension of sela:
00036  *            SELA      *selaAddSel()
00037  *            l_int32    selaExtendArray()
00038  *
00039  *         Accessors:
00040  *            l_int32    selaGetCount()
00041  *            SEL       *selaGetSel()
00042  *            char      *selGetName()
00043  *            l_int32    selSetName()
00044  *            l_int32    selaFindSelByName()
00045  *            l_int32    selGetElement()
00046  *            l_int32    selSetElement()
00047  *            l_int32    selGetParameters()
00048  *            l_int32    selSetOrigin()
00049  *            l_int32    selGetTypeAtOrigin()
00050  *            char      *selaGetBrickName()
00051  *            char      *selaGetCombName()
00052  *     static char      *selaComputeCompositeParameters()
00053  *            l_int32    getCompositeParameters()
00054  *            SARRAY    *selaGetSelnames()
00055  *
00056  *         Max translations for erosion and hmt
00057  *            l_int32    selFindMaxTranslations()
00058  *
00059  *         Rotation by multiples of 90 degrees
00060  *            SEL       *selRotateOrth()
00061  *
00062  *         Sela and Sel serialized I/O
00063  *            SELA      *selaRead()
00064  *            SELA      *selaReadStream()
00065  *            SEL       *selRead()
00066  *            SEL       *selReadStream()
00067  *            l_int32    selaWrite()
00068  *            l_int32    selaWriteStream()
00069  *            l_int32    selWrite()
00070  *            l_int32    selWriteStream()
00071  *       
00072  *         Building custom hit-miss sels from compiled strings
00073  *            SEL       *selCreateFromString()
00074  *            char      *selPrintToString()     [for debugging]
00075  *
00076  *         Building custom hit-miss sels from a simple file format
00077  *            SELA      *selaCreateFromFile()
00078  *            static SEL *selCreateFromSArray()
00079  *
00080  *         Making hit-only sels from Pta and Pix
00081  *            SEL       *selCreateFromPta()
00082  *            SEL       *selCreateFromPix()
00083  *
00084  *         Making hit-miss sels from Pix and image files
00085  *            SEL       *selReadFromColorImage()
00086  *            SEL       *selCreateFromColorPix()
00087  *
00088  *         Printable display of sel
00089  *            PIX       *selDisplayInPix()
00090  *            PIX       *selaDisplayInPix()
00091  *
00092  *     Usage notes:
00093  *        In this file we have seven functions that make sels:
00094  *          (1)  selCreate(), with input (h, w, [name])
00095  *               The generic function.  Roll your own, using selSetElement().
00096  *          (2)  selCreateBrick(), with input (h, w, cy, cx, val)
00097  *               The most popular function.  Makes a rectangular sel of
00098  *               all hits, misses or don't-cares.  We have many morphology
00099  *               operations that create a sel of all hits, use it, and
00100  *               destroy it.
00101  *          (3)  selCreateFromString() with input (text, h, w, [name])
00102  *               Adam Langley's clever function, allows you to make a hit-miss
00103  *               sel from a string in code that is geometrically laid out
00104  *               just like the actual sel.
00105  *          (4)  selaCreateFromFile() with input (filename)
00106  *               This parses a simple file format to create an array of
00107  *               hit-miss sels.  The sel data uses the same encoding
00108  *               as in (3), with geometrical layout enforced.
00109  *          (5)  selCreateFromPta() with input (pta, cy, cx, [name])
00110  *               Another way to make a sel with only hits.
00111  *          (6)  selCreateFromPix() with input (pix, cy, cx, [name])
00112  *               Yet another way to make a sel from hits.
00113  *          (7)  selCreateFromColorPix() with input (pix, name).
00114  *               Another way to make a general hit-miss sel, starting with
00115  *               an image editor.
00116  *        In addition, there are three functions in selgen.c that
00117  *        automatically generate a hit-miss sel from a pix and
00118  *        a number of parameters.  This is useful for problems like
00119  *        "find all patterns that look like this one."
00120  *
00121  *        Consistency, being the hobgoblin of small minds,
00122  *        is adhered to here in the dimensioning and accessing of sels.
00123  *        Everything is done in standard matrix (row, column) order.
00124  *        When we set specific elements in a sel, we likewise use
00125  *        (row, col) ordering:
00126  *             selSetElement(), with input (row, col, type)
00127  */
00128 
00129 #include <string.h>
00130 #include "allheaders.h"
00131 
00132     /* MS VC++ can't handle array initialization with static consts ! */
00133 #define L_BUF_SIZE      256
00134 
00135 static const l_int32  INITIAL_PTR_ARRAYSIZE = 50;  /* n'import quoi */
00136 static const l_int32  MANY_SELS = 1000;
00137 
00138 static SEL *selCreateFromSArray(SARRAY *sa, l_int32 first, l_int32 last);
00139 
00140 struct CompParameterMap
00141 {
00142     l_int32  size;
00143     l_int32  size1;
00144     l_int32  size2;
00145     char     selnameh1[20];
00146     char     selnameh2[20];
00147     char     selnamev1[20];
00148     char     selnamev2[20];
00149 };
00150 
00151 static const struct CompParameterMap  comp_parameter_map[] =
00152     { { 2, 2, 1, "sel_2h", "", "sel_2v", "" },
00153       { 3, 3, 1, "sel_3h", "", "sel_3v", "" },
00154       { 4, 2, 2, "sel_2h", "sel_comb_4h", "sel_2v", "sel_comb_4v" },
00155       { 5, 5, 1, "sel_5h", "", "sel_5v", "" },
00156       { 6, 3, 2, "sel_3h", "sel_comb_6h", "sel_3v", "sel_comb_6v" },
00157       { 7, 7, 1, "sel_7h", "", "sel_7v", "" },
00158       { 8, 4, 2, "sel_4h", "sel_comb_8h", "sel_4v", "sel_comb_8v" },
00159       { 9, 3, 3, "sel_3h", "sel_comb_9h", "sel_3v", "sel_comb_9v" },
00160       { 10, 5, 2, "sel_5h", "sel_comb_10h", "sel_5v", "sel_comb_10v" },
00161       { 11, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
00162       { 12, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
00163       { 13, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
00164       { 14, 7, 2, "sel_7h", "sel_comb_14h", "sel_7v", "sel_comb_14v" },
00165       { 15, 5, 3, "sel_5h", "sel_comb_15h", "sel_5v", "sel_comb_15v" },
00166       { 16, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" },
00167       { 17, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" },
00168       { 18, 6, 3, "sel_6h", "sel_comb_18h", "sel_6v", "sel_comb_18v" },
00169       { 19, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" },
00170       { 20, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" },
00171       { 21, 7, 3, "sel_7h", "sel_comb_21h", "sel_7v", "sel_comb_21v" },
00172       { 22, 11, 2, "sel_11h", "sel_comb_22h", "sel_11v", "sel_comb_22v" },
00173       { 23, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" },
00174       { 24, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" },
00175       { 25, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" },
00176       { 26, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" },
00177       { 27, 9, 3, "sel_9h", "sel_comb_27h", "sel_9v", "sel_comb_27v" },
00178       { 28, 7, 4, "sel_7h", "sel_comb_28h", "sel_7v", "sel_comb_28v" },
00179       { 29, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
00180       { 30, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
00181       { 31, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
00182       { 32, 8, 4, "sel_8h", "sel_comb_32h", "sel_8v", "sel_comb_32v" },
00183       { 33, 11, 3, "sel_11h", "sel_comb_33h", "sel_11v", "sel_comb_33v" },
00184       { 34, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" },
00185       { 35, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" },
00186       { 36, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
00187       { 37, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
00188       { 38, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
00189       { 39, 13, 3, "sel_13h", "sel_comb_39h", "sel_13v", "sel_comb_39v" },
00190       { 40, 8, 5, "sel_8h", "sel_comb_40h", "sel_8v", "sel_comb_40v" },
00191       { 41, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
00192       { 42, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
00193       { 43, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
00194       { 44, 11, 4, "sel_11h", "sel_comb_44h", "sel_11v", "sel_comb_44v" },
00195       { 45, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" },
00196       { 46, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" },
00197       { 47, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" },
00198       { 48, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" },
00199       { 49, 7, 7, "sel_7h", "sel_comb_49h", "sel_7v", "sel_comb_49v" },
00200       { 50, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" },
00201       { 51, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" },
00202       { 52, 13, 4, "sel_13h", "sel_comb_52h", "sel_13v", "sel_comb_52v" },
00203       { 53, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" },
00204       { 54, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" },
00205       { 55, 11, 5, "sel_11h", "sel_comb_55h", "sel_11v", "sel_comb_55v" },
00206       { 56, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
00207       { 57, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
00208       { 58, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
00209       { 59, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
00210       { 60, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
00211       { 61, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
00212       { 62, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" },
00213       { 63, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" } };
00214 
00215 
00216 
00217 /*------------------------------------------------------------------------*
00218  *                      Create / Destroy / Copy                           *
00219  *------------------------------------------------------------------------*/
00220 /*!
00221  *  selaCreate()
00222  *
00223  *      Input:  n (initial number of sel ptrs; use 0 for default)
00224  *      Return: sela, or null on error
00225  */
00226 SELA *
00227 selaCreate(l_int32  n)
00228 {
00229 SELA  *sela;
00230 
00231     PROCNAME("selaCreate");
00232 
00233     if (n <= 0)
00234         n = INITIAL_PTR_ARRAYSIZE;
00235     if (n > MANY_SELS)
00236         L_WARNING_INT("%d sels", procName, n);
00237 
00238     if ((sela = (SELA *)CALLOC(1, sizeof(SELA))) == NULL)
00239         return (SELA *)ERROR_PTR("sela not made", procName, NULL);
00240 
00241     sela->nalloc = n;
00242     sela->n = 0;
00243 
00244         /* make array of se ptrs */
00245     if ((sela->sel = (SEL **)CALLOC(n, sizeof(SEL *))) == NULL)
00246         return (SELA *)ERROR_PTR("sel ptrs not made", procName, NULL);
00247 
00248     return sela;
00249 }
00250 
00251 
00252 /*!
00253  *  selaDestroy()
00254  *
00255  *      Input:  &sela (<to be nulled>)
00256  *      Return: void
00257  */
00258 void
00259 selaDestroy(SELA  **psela)
00260 {
00261 SELA    *sela;
00262 l_int32  i;
00263 
00264     if (!psela) return;
00265     if ((sela = *psela) == NULL)
00266         return;
00267 
00268     for (i = 0; i < sela->n; i++)
00269         selDestroy(&sela->sel[i]);
00270     FREE(sela->sel);
00271     FREE(sela);
00272     *psela = NULL;
00273     return;
00274 }
00275 
00276 
00277 /*!
00278  *  selCreate()
00279  *
00280  *      Input:  height, width
00281  *              name (<optional> sel name; can be null)
00282  *      Return: sel, or null on error
00283  *
00284  *  Notes:
00285  *      (1) selCreate() initializes all values to 0.
00286  *      (2) After this call, (cy,cx) and nonzero data values must be
00287  *          assigned.  If a text name is not assigned here, it will
00288  *          be needed later when the sel is put into a sela.
00289  */
00290 SEL *
00291 selCreate(l_int32      height,
00292           l_int32      width,
00293           const char  *name)
00294 {
00295 SEL  *sel;
00296 
00297     PROCNAME("selCreate");
00298 
00299     if ((sel = (SEL *)CALLOC(1, sizeof(SEL))) == NULL)
00300         return (SEL *)ERROR_PTR("sel not made", procName, NULL);
00301     if (name)
00302         sel->name = stringNew(name);
00303     sel->sy = height;
00304     sel->sx = width;
00305     if ((sel->data = create2dIntArray(height, width)) == NULL)
00306         return (SEL *)ERROR_PTR("data not allocated", procName, NULL);
00307 
00308     return sel;
00309 }
00310 
00311 
00312 /*!
00313  *  selDestroy()
00314  *
00315  *      Input:  &sel (<to be nulled>)
00316  *      Return: void
00317  */
00318 void
00319 selDestroy(SEL  **psel)
00320 {
00321 l_int32  i;
00322 SEL     *sel;
00323 
00324     PROCNAME("selDestroy");
00325 
00326     if (psel == NULL)  {
00327         L_WARNING("ptr address is NULL!", procName);
00328         return;
00329     }
00330     if ((sel = *psel) == NULL)
00331         return;
00332 
00333     for (i = 0; i < sel->sy; i++)
00334         FREE(sel->data[i]);
00335     FREE(sel->data);
00336     if (sel->name)
00337         FREE(sel->name);
00338     FREE(sel);
00339 
00340     *psel = NULL;
00341     return;
00342 }
00343 
00344 
00345 /*!
00346  *  selCopy() 
00347  *
00348  *      Input:  sel
00349  *      Return: a copy of the sel, or null on error
00350  */
00351 SEL *
00352 selCopy(SEL  *sel)
00353 {
00354 l_int32  sx, sy, cx, cy, i, j;
00355 SEL     *csel;
00356 
00357     PROCNAME("selCopy");
00358 
00359     if (!sel)
00360         return (SEL *)ERROR_PTR("sel not defined", procName, NULL);
00361 
00362     if ((csel = (SEL *)CALLOC(1, sizeof(SEL))) == NULL)
00363         return (SEL *)ERROR_PTR("csel not made", procName, NULL);
00364     selGetParameters(sel, &sy, &sx, &cy, &cx);
00365     csel->sy = sy;
00366     csel->sx = sx;
00367     csel->cy = cy;
00368     csel->cx = cx;
00369 
00370     if ((csel->data = create2dIntArray(sy, sx)) == NULL)
00371         return (SEL *)ERROR_PTR("sel data not made", procName, NULL);
00372 
00373     for (i = 0; i < sy; i++)
00374         for (j = 0; j < sx; j++)
00375             csel->data[i][j] = sel->data[i][j];
00376 
00377     if (sel->name)
00378         csel->name = stringNew(sel->name);
00379 
00380     return csel;
00381 }
00382 
00383 
00384 /*!
00385  *  selCreateBrick()
00386  *
00387  *      Input:  height, width
00388  *              cy, cx  (origin, relative to UL corner at 0,0)
00389  *              type  (SEL_HIT, SEL_MISS, or SEL_DONT_CARE)
00390  *      Return: sel, or null on error
00391  *
00392  *  Notes:
00393  *      (1) This is a rectangular sel of all hits, misses or don't cares.
00394  */
00395 SEL *
00396 selCreateBrick(l_int32  h,
00397                l_int32  w,
00398                l_int32  cy,
00399                l_int32  cx,
00400                l_int32  type)
00401 {
00402 l_int32  i, j;
00403 SEL     *sel;
00404 
00405     PROCNAME("selCreateBrick");
00406 
00407     if (h <= 0 || w <= 0)
00408         return (SEL *)ERROR_PTR("h and w must both be > 0", procName, NULL);
00409     if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE)
00410         return (SEL *)ERROR_PTR("invalid sel element type", procName, NULL);
00411 
00412     if ((sel = selCreate(h, w, NULL)) == NULL)
00413         return (SEL *)ERROR_PTR("sel not made", procName, NULL);
00414     selSetOrigin(sel, cy, cx);
00415     for (i = 0; i < h; i++)
00416         for (j = 0; j < w; j++)
00417             sel->data[i][j] = type;
00418 
00419     return sel;
00420 }
00421 
00422 
00423 /*!
00424  *  selCreateComb()
00425  *
00426  *      Input:  factor1 (contiguous space between comb tines)
00427  *              factor2 (number of comb tines)
00428  *              direction (L_HORIZ, L_VERT)
00429  *      Return: sel, or null on error
00430  *
00431  *  Notes:
00432  *      (1) This generates a comb Sel of hits with the origin as
00433  *          near the center as possible.
00434  */
00435 SEL *
00436 selCreateComb(l_int32  factor1,
00437               l_int32  factor2,
00438               l_int32  direction)
00439 {
00440 l_int32  i, size, z;
00441 SEL     *sel;
00442 
00443     PROCNAME("selCreateComb");
00444 
00445     if (factor1 < 1 || factor2 < 1)
00446         return (SEL *)ERROR_PTR("factors must be >= 1", procName, NULL);
00447     if (direction != L_HORIZ && direction != L_VERT)
00448         return (SEL *)ERROR_PTR("invalid direction", procName, NULL);
00449 
00450     size = factor1 * factor2;
00451     if (direction == L_HORIZ) {
00452         sel = selCreate(1, size, NULL);
00453         selSetOrigin(sel, 0, size / 2);
00454     }
00455     else {
00456         sel = selCreate(size, 1, NULL);
00457         selSetOrigin(sel, size / 2, 0);
00458     }
00459 
00460     for (i = 0; i < factor2; i++) {
00461         if (factor2 & 1)  /* odd */
00462             z = factor1 / 2 + i * factor1;
00463         else
00464             z = factor1 / 2 + i * factor1;
00465 /*        fprintf(stderr, "i = %d, factor1 = %d, factor2 = %d, z = %d\n",
00466                         i, factor1, factor2, z); */
00467         if (direction == L_HORIZ)
00468             selSetElement(sel, 0, z, SEL_HIT);
00469         else
00470             selSetElement(sel, z, 0, SEL_HIT);
00471     }
00472 
00473     return sel;
00474 }
00475 
00476 
00477 /*!
00478  *  create2dIntArray()
00479  *
00480  *      Input:  sy (rows == height)
00481  *              sx (columns == width)
00482  *      Return: doubly indexed array (i.e., an array of sy row pointers,
00483  *              each of which points to an array of sx ints)
00484  *
00485  *  Notes:
00486  *      (1) The array[sy][sx] is indexed in standard "matrix notation",
00487  *          with the row index first.
00488  */
00489 l_int32 **
00490 create2dIntArray(l_int32  sy,
00491                  l_int32  sx)
00492 {
00493 l_int32    i;
00494 l_int32  **array;
00495 
00496     PROCNAME("create2dIntArray");
00497 
00498     if ((array = (l_int32 **)CALLOC(sy, sizeof(l_int32 *))) == NULL)
00499         return (l_int32 **)ERROR_PTR("ptr array not made", procName, NULL);
00500 
00501     for (i = 0; i < sy; i++) {
00502         if ((array[i] = (l_int32 *)CALLOC(sx, sizeof(l_int32))) == NULL)
00503             return (l_int32 **)ERROR_PTR("array not made", procName, NULL);
00504     }
00505 
00506     return array;
00507 }
00508 
00509 
00510 
00511 /*------------------------------------------------------------------------*
00512  *                           Extension of sela                            *
00513  *------------------------------------------------------------------------*/
00514 /*!
00515  *  selaAddSel()
00516  *
00517  *      Input:  sela
00518  *              sel to be added
00519  *              selname (ignored if already defined in sel;
00520  *                       req'd in sel when added to a sela)
00521  *              copyflag (for sel: 0 inserts, 1 copies)
00522  *      Return: 0 if OK; 1 on error
00523  *
00524  *  Notes:
00525  *      (1) This adds a sel, either inserting or making a copy.
00526  *      (2) Because every sel in a sela must have a name, it copies
00527  *          the input name if necessary.  You can input NULL for
00528  *          selname if the sel already has a name.
00529  */
00530 l_int32
00531 selaAddSel(SELA        *sela,
00532            SEL         *sel,
00533            const char  *selname,
00534            l_int32      copyflag)
00535 {
00536 l_int32  n;
00537 SEL     *csel;
00538 
00539     PROCNAME("selaAddSel");
00540 
00541     if (!sela)
00542         return ERROR_INT("sela not defined", procName, 1);
00543     if (!sel)
00544         return ERROR_INT("sel not defined", procName, 1);
00545     if (!sel->name && !selname)
00546         return ERROR_INT("added sel must have name", procName, 1);
00547 
00548     if (copyflag == TRUE) {
00549         if ((csel = selCopy(sel)) == NULL)
00550             return ERROR_INT("csel not made", procName, 1);
00551     }
00552     else   /* copyflag is false; insert directly */
00553         csel = sel;
00554     if (!csel->name)
00555         csel->name = stringNew(selname);
00556 
00557     n = selaGetCount(sela);
00558     if (n >= sela->nalloc)
00559         selaExtendArray(sela);
00560     sela->sel[n] = csel;
00561     sela->n++;
00562 
00563     return 0;
00564 }
00565     
00566 
00567 /*!
00568  *  selaExtendArray()
00569  *
00570  *      Input:  sela
00571  *      Return: 0 if OK; 1 on error
00572  */
00573 l_int32
00574 selaExtendArray(SELA  *sela)
00575 {
00576     PROCNAME("selaExtendArray");
00577 
00578     if (!sela)
00579         return ERROR_INT("sela not defined", procName, 1);
00580     
00581     if ((sela->sel = (SEL **)reallocNew((void **)&sela->sel,
00582                               sizeof(SEL *) * sela->nalloc,
00583                               2 * sizeof(SEL *) * sela->nalloc)) == NULL)
00584             return ERROR_INT("new ptr array not returned", procName, 1);
00585 
00586     sela->nalloc = 2 * sela->nalloc;
00587     return 0;
00588 }
00589 
00590 
00591 
00592 /*----------------------------------------------------------------------*
00593  *                               Accessors                              *
00594  *----------------------------------------------------------------------*/
00595 /*!
00596  *  selaGetCount()
00597  *
00598  *      Input:  sela
00599  *      Return: count, or 0 on error
00600  */
00601 l_int32
00602 selaGetCount(SELA  *sela)
00603 {
00604     PROCNAME("selaGetCount");
00605 
00606     if (!sela)
00607         return ERROR_INT("sela not defined", procName, 0);
00608 
00609     return sela->n;
00610 }
00611 
00612 
00613 /*!
00614  *  selaGetSel()
00615  *
00616  *      Input:  sela
00617  *              index of sel to be retrieved (not copied)
00618  *      Return: sel, or null on error
00619  *
00620  *  Notes:
00621  *      (1) This returns a ptr to the sel, not a copy, so the caller
00622  *          must not destroy it!
00623  */
00624 SEL *
00625 selaGetSel(SELA    *sela,
00626            l_int32  i)
00627 {
00628     PROCNAME("selaGetSel");
00629 
00630     if (!sela)
00631         return (SEL *)ERROR_PTR("sela not defined", procName, NULL);
00632 
00633     if (i < 0 || i >= sela->n)
00634         return (SEL *)ERROR_PTR("invalid index", procName, NULL);
00635     return sela->sel[i];
00636 }
00637 
00638 
00639 /*!
00640  *  selGetName()
00641  *
00642  *      Input:  sel
00643  *      Return: sel name (not copied), or null if no name or on error
00644  */
00645 char *
00646 selGetName(SEL  *sel)
00647 {
00648     PROCNAME("selGetName");
00649 
00650     if (!sel)
00651         return (char *)ERROR_PTR("sel not defined", procName, NULL);
00652 
00653     return sel->name;
00654 }
00655 
00656 
00657 /*!
00658  *  selSetName()
00659  *
00660  *      Input:  sel
00661  *              name (<optional>; can be null)
00662  *      Return: 0 if OK, 1 on error
00663  *
00664  *  Notes:
00665  *      (1) Always frees the existing sel name, if defined.
00666  *      (2) If name is not defined, just clears any existing sel name.
00667  */
00668 l_int32
00669 selSetName(SEL         *sel,
00670            const char  *name)
00671 {
00672     PROCNAME("selSetName");
00673 
00674     if (!sel)
00675         return ERROR_INT("sel not defined", procName, 1);
00676 
00677     return stringReplace(&sel->name, name);
00678 }
00679 
00680 
00681 /*!
00682  *  selaFindSelByName()
00683  *
00684  *      Input:  sela
00685  *              sel name
00686  *              &index (<optional, return>)
00687  *              &sel  (<optional, return> sel (not a copy))
00688  *      Return: 0 if OK; 1 on error
00689  */
00690 l_int32
00691 selaFindSelByName(SELA        *sela, 
00692                   const char  *name,
00693                   l_int32     *pindex,
00694                   SEL        **psel)
00695 {
00696 l_int32  i, n;
00697 char    *sname;
00698 SEL     *sel;
00699 
00700     PROCNAME("selaFindSelByName");
00701 
00702     if (pindex) *pindex = -1;
00703     if (psel) *psel = NULL;
00704 
00705     if (!sela)
00706         return ERROR_INT("sela not defined", procName, 1);
00707 
00708     n = selaGetCount(sela);
00709     for (i = 0; i < n; i++)
00710     {
00711         if ((sel = selaGetSel(sela, i)) == NULL) {
00712             L_WARNING("missing sel", procName);
00713             continue;
00714         }
00715             
00716         sname = selGetName(sel);
00717         if (sname && (!strcmp(name, sname))) {
00718             if (pindex)
00719                 *pindex = i;
00720             if (psel)
00721                 *psel = sel;
00722             return 0;
00723         }
00724     }
00725     
00726     return 1;
00727 }
00728 
00729 
00730 /*!
00731  *  selGetElement()
00732  *
00733  *      Input:  sel
00734  *              row
00735  *              col
00736  *              &type  (<return> SEL_HIT, SEL_MISS, SEL_DONT_CARE)
00737  *      Return: 0 if OK; 1 on error
00738  */
00739 l_int32
00740 selGetElement(SEL      *sel,
00741               l_int32   row,
00742               l_int32   col,
00743               l_int32  *ptype)
00744 {
00745     PROCNAME("selGetElement");
00746 
00747     if (!ptype)
00748         return ERROR_INT("&type not defined", procName, 1);
00749     *ptype = SEL_DONT_CARE;
00750     if (!sel)
00751         return ERROR_INT("sel not defined", procName, 1);
00752     if (row < 0 || row >= sel->sy)
00753         return ERROR_INT("sel row out of bounds", procName, 1);
00754     if (col < 0 || col >= sel->sx)
00755         return ERROR_INT("sel col out of bounds", procName, 1);
00756 
00757     *ptype = sel->data[row][col];
00758     return 0;
00759 }
00760 
00761 
00762 /*!
00763  *  selSetElement()
00764  *
00765  *      Input:  sel
00766  *              row
00767  *              col
00768  *              type  (SEL_HIT, SEL_MISS, SEL_DONT_CARE)
00769  *      Return: 0 if OK; 1 on error
00770  *
00771  *  Notes:
00772  *      (1) Because we use row and column to index into an array,
00773  *          they are always non-negative.  The location of the origin
00774  *          (and the type of operation) determine the actual
00775  *          direction of the rasterop.
00776  */
00777 l_int32
00778 selSetElement(SEL     *sel,
00779               l_int32  row,
00780               l_int32  col,
00781               l_int32  type)
00782 {
00783     PROCNAME("selSetElement");
00784 
00785     if (!sel)
00786         return ERROR_INT("sel not defined", procName, 1);
00787     if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE)
00788         return ERROR_INT("invalid sel element type", procName, 1);
00789     if (row < 0 || row >= sel->sy)
00790         return ERROR_INT("sel row out of bounds", procName, 1);
00791     if (col < 0 || col >= sel->sx)
00792         return ERROR_INT("sel col out of bounds", procName, 1);
00793 
00794     sel->data[row][col] = type;
00795     return 0;
00796 }
00797 
00798 
00799 /*!
00800  *  selGetParameters()
00801  *
00802  *      Input:  sel
00803  *              &sy, &sx, &cy, &cx (<optional return>; each can be null)
00804  *      Return: 0 if OK, 1 on error
00805  */
00806 l_int32
00807 selGetParameters(SEL      *sel,
00808                  l_int32  *psy,
00809                  l_int32  *psx,
00810                  l_int32  *pcy,
00811                  l_int32  *pcx)
00812 {
00813     PROCNAME("selGetParameters");
00814 
00815     if (psy) *psy = 0;
00816     if (psx) *psx = 0;
00817     if (pcy) *pcy = 0;
00818     if (pcx) *pcx = 0;
00819     if (!sel)
00820         return ERROR_INT("sel not defined", procName, 1);
00821     if (psy) *psy = sel->sy; 
00822     if (psx) *psx = sel->sx; 
00823     if (pcy) *pcy = sel->cy; 
00824     if (pcx) *pcx = sel->cx; 
00825     return 0;
00826 }
00827 
00828 
00829 /*!
00830  *  selSetOrigin()
00831  *
00832  *      Input:  sel
00833  *              cy, cx
00834  *      Return: 0 if OK; 1 on error
00835  */
00836 l_int32
00837 selSetOrigin(SEL     *sel,
00838              l_int32  cy,
00839              l_int32  cx)
00840 {
00841     PROCNAME("selSetOrigin");
00842 
00843     if (!sel)
00844         return ERROR_INT("sel not defined", procName, 1);
00845     sel->cy = cy;
00846     sel->cx = cx;
00847     return 0;
00848 }
00849 
00850 
00851 /*!
00852  *  selGetTypeAtOrigin()
00853  *
00854  *      Input:  sel
00855  *              &type  (<return> SEL_HIT, SEL_MISS, SEL_DONT_CARE)
00856  *      Return: 0 if OK; 1 on error or if origin is not found
00857  */
00858 l_int32
00859 selGetTypeAtOrigin(SEL      *sel,
00860                    l_int32  *ptype)
00861 {
00862 l_int32  sx, sy, cx, cy, i, j;
00863 
00864     PROCNAME("selGetTypeAtOrigin");
00865 
00866     if (!ptype)
00867         return ERROR_INT("&type not defined", procName, 1);
00868     *ptype = SEL_DONT_CARE;  /* init */
00869     if (!sel)
00870         return ERROR_INT("sel not defined", procName, 1);
00871 
00872     selGetParameters(sel, &sy, &sx, &cy, &cx);
00873     for (i = 0; i < sy; i++) {
00874         for (j = 0; j < sx; j++) {
00875             if (i == cy && j == cx) {
00876                 selGetElement(sel, i, j, ptype);
00877                 return 0;
00878             }
00879         }
00880     }
00881 
00882     return ERROR_INT("sel origin not found", procName, 1);
00883 }
00884 
00885 
00886 /*!
00887  *  selaGetBrickName()
00888  *
00889  *      Input:  sela
00890  *              hsize, vsize (of brick sel)
00891  *      Return: sel name (new string), or null if no name or on error
00892  */
00893 char *
00894 selaGetBrickName(SELA    *sela,
00895                  l_int32  hsize,
00896                  l_int32  vsize)
00897 {
00898 l_int32  i, nsels, sx, sy;
00899 SEL     *sel;
00900 
00901     PROCNAME("selaGetBrickName");
00902 
00903     if (!sela)
00904         return (char *)ERROR_PTR("sela not defined", procName, NULL);
00905 
00906     nsels = selaGetCount(sela);
00907     for (i = 0; i < nsels; i++) {
00908         sel = selaGetSel(sela, i);
00909         selGetParameters(sel, &sy, &sx, NULL, NULL);
00910         if (hsize == sx && vsize == sy)
00911             return stringNew(selGetName(sel));
00912     }
00913 
00914     return (char *)ERROR_PTR("sel not found", procName, NULL);
00915 }
00916 
00917 
00918 /*!
00919  *  selaGetCombName()
00920  *
00921  *      Input:  sela
00922  *              size (the product of sizes of the brick and comb parts)
00923  *              direction (L_HORIZ, L_VERT)
00924  *      Return: sel name (new string), or null if name not found or on error
00925  *
00926  *  Notes:
00927  *      (1) Combs are by definition 1-dimensional, either horiz or vert.
00928  *      (2) Use this with comb Sels; e.g., from selaAddDwaCombs().
00929  */
00930 char *
00931 selaGetCombName(SELA    *sela,
00932                 l_int32  size,
00933                 l_int32  direction)
00934 {
00935 char    *selname;
00936 char     combname[L_BUF_SIZE];
00937 l_int32  i, nsels, sx, sy, found;
00938 SEL     *sel;
00939 
00940     PROCNAME("selaGetCombName");
00941 
00942     if (!sela)
00943         return (char *)ERROR_PTR("sela not defined", procName, NULL);
00944     if (direction != L_HORIZ && direction != L_VERT)
00945         return (char *)ERROR_PTR("invalid direction", procName, NULL);
00946 
00947         /* Derive the comb name we're looking for */
00948     if (direction == L_HORIZ)
00949         snprintf(combname, L_BUF_SIZE, "sel_comb_%dh", size);
00950     else  /* direction == L_VERT */
00951         snprintf(combname, L_BUF_SIZE, "sel_comb_%dv", size);
00952 
00953     found = FALSE;
00954     nsels = selaGetCount(sela);
00955     for (i = 0; i < nsels; i++) {
00956         sel = selaGetSel(sela, i);
00957         selGetParameters(sel, &sy, &sx, NULL, NULL);
00958         if (sy != 1 && sx != 1)  /* 2-D; not a comb */
00959             continue;
00960         selname = selGetName(sel);
00961         if (!strcmp(selname, combname)) {
00962             found = TRUE;
00963             break;
00964         }
00965     }
00966 
00967     if (found)
00968         return stringNew(selname);
00969     else
00970         return (char *)ERROR_PTR("sel not found", procName, NULL);
00971 }
00972 
00973 
00974 /* --------- Function used to generate code in this file  ---------- */
00975 #if 0
00976 static void selaComputeCompositeParameters(const char *fileout);
00977 
00978 /*!
00979  *  selaComputeCompParameters()
00980  *
00981  *      Input:  output filename
00982  *      Return: void 
00983  *
00984  *  Notes:
00985  *      (1) This static function was used to construct the comp_parameter_map[]
00986  *          array at the top of this file.  It is static because it does
00987  *          not need to be called again.  It remains here to show how
00988  *          the composite parameter map was computed.
00989  *      (2) The output file was pasted directly into comp_parameter_map[].
00990  *          The composite parameter map is used to quickly determine
00991  *          the linear decomposition parameters and sel names.
00992  */
00993 static void
00994 selaComputeCompositeParameters(const char  *fileout)
00995 {
00996 char    *str, *nameh1, *nameh2, *namev1, *namev2;
00997 char     buf[L_BUF_SIZE];
00998 l_int32  size, size1, size2, len;
00999 SARRAY  *sa;
01000 SELA    *selabasic, *selacomb;
01001 
01002     selabasic = selaAddBasic(NULL);
01003     selacomb = selaAddDwaCombs(NULL);
01004     sa = sarrayCreate(64);
01005     for (size = 2; size < 64; size++) {
01006         selectComposableSizes(size, &size1, &size2);
01007         nameh1 = selaGetBrickName(selabasic, size1, 1);
01008         namev1 = selaGetBrickName(selabasic, 1, size1);
01009         if (size2 > 1) {
01010             nameh2 = selaGetCombName(selacomb, size1 * size2, L_HORIZ);
01011             namev2 = selaGetCombName(selacomb, size1 * size2, L_VERT);
01012         }
01013         else {
01014             nameh2 = stringNew("");
01015             namev2 = stringNew("");
01016         }
01017         snprintf(buf, L_BUF_SIZE,
01018                  "      { %d, %d, %d, \"%s\", \"%s\", \"%s\", \"%s\" },",
01019                  size, size1, size2, nameh1, nameh2, namev1, namev2);
01020         sarrayAddString(sa, buf, L_COPY);
01021         FREE(nameh1);
01022         FREE(nameh2);
01023         FREE(namev1);
01024         FREE(namev2);
01025     }
01026     str = sarrayToString(sa, 1);
01027     len = strlen(str);
01028     l_binaryWrite(fileout, "w", str, len + 1);
01029     FREE(str);
01030     sarrayDestroy(&sa);
01031     selaDestroy(&selabasic);
01032     selaDestroy(&selacomb);
01033     return;
01034 }
01035 #endif
01036 /* -------------------------------------------------------------------- */
01037 
01038 
01039 /*!
01040  *  getCompositeParameters()
01041  *
01042  *      Input:  size
01043  *              &size1 (<optional return> brick factor size)
01044  *              &size2 (<optional return> comb factor size)
01045  *              &nameh1 (<optional return> name of horiz brick)
01046  *              &nameh2 (<optional return> name of horiz comb)
01047  *              &namev1 (<optional return> name of vert brick)
01048  *              &namev2 (<optional return> name of vert comb)
01049  *      Return: 0 if OK, 1 on error
01050  *
01051  *  Notes:
01052  *      (1) This uses the big lookup table at the top of this file.
01053  *      (2) All returned strings are copies that must be freed.
01054  */
01055 l_int32
01056 getCompositeParameters(l_int32   size,
01057                        l_int32  *psize1,
01058                        l_int32  *psize2,
01059                        char    **pnameh1,
01060                        char    **pnameh2,
01061                        char    **pnamev1,
01062                        char    **pnamev2)
01063 {
01064 l_int32  index;
01065 
01066     PROCNAME("selaGetSelnames");
01067 
01068     if (psize1) *psize1 = 0;
01069     if (psize2) *psize2 = 0;
01070     if (pnameh1) *pnameh1 = NULL;
01071     if (pnameh2) *pnameh2 = NULL;
01072     if (pnamev1) *pnamev1 = NULL;
01073     if (pnamev2) *pnamev2 = NULL;
01074     if (size < 2 || size > 63)
01075         return ERROR_INT("valid size range is {2 ... 63}", procName, 1);
01076     index = size - 2;
01077     if (psize1)
01078         *psize1 = comp_parameter_map[index].size1;
01079     if (psize2)
01080         *psize2 = comp_parameter_map[index].size2;
01081     if (pnameh1)
01082         *pnameh1 = stringNew(comp_parameter_map[index].selnameh1);
01083     if (pnameh2)
01084         *pnameh2 = stringNew(comp_parameter_map[index].selnameh2);
01085     if (pnamev1)
01086         *pnamev1 = stringNew(comp_parameter_map[index].selnamev1);
01087     if (pnamev2)
01088         *pnamev2 = stringNew(comp_parameter_map[index].selnamev2);
01089     return 0;
01090 }
01091 
01092 
01093 /*!
01094  *  selaGetSelnames()
01095  *
01096  *      Input:  sela
01097  *      Return: sa (of all sel names), or null on error
01098  */
01099 SARRAY *
01100 selaGetSelnames(SELA  *sela)
01101 {
01102 char    *selname;
01103 l_int32  i, n;
01104 SEL     *sel;
01105 SARRAY  *sa;
01106 
01107     PROCNAME("selaGetSelnames");
01108 
01109     if (!sela)
01110         return (SARRAY *)ERROR_PTR("sela not defined", procName, NULL);
01111     if ((n = selaGetCount(sela)) == 0)
01112         return (SARRAY *)ERROR_PTR("no sels in sela", procName, NULL);
01113 
01114     if ((sa = sarrayCreate(n)) == NULL)
01115         return (SARRAY *)ERROR_PTR("sa not made", procName, NULL);
01116     for (i = 0; i < n; i++) {
01117         sel = selaGetSel(sela, i);
01118         selname = selGetName(sel);
01119         sarrayAddString(sa, selname, 1);
01120     }
01121 
01122     return sa;
01123 }
01124 
01125 
01126 
01127 /*----------------------------------------------------------------------*
01128  *                Max translations for erosion and hmt                  *
01129  *----------------------------------------------------------------------*/
01130 /*!
01131  *  selFindMaxTranslations()
01132  *
01133  *      Input:  sel
01134  *              &xp, &yp, &xn, &yn  (<return> max shifts)
01135  *      Return: 0 if OK; 1 on error
01136  *
01137  *  Note: these are the maximum shifts for the erosion operation.
01138  *        For example, when j < cx, the shift of the image
01139  *        is +x to the cx.  This is a positive xp shift.
01140  */
01141 l_int32
01142 selFindMaxTranslations(SEL      *sel,
01143                        l_int32  *pxp,
01144                        l_int32  *pyp,
01145                        l_int32  *pxn,
01146                        l_int32  *pyn)
01147 {
01148 l_int32  sx, sy, cx, cy, i, j;
01149 l_int32  maxxp, maxyp, maxxn, maxyn;
01150 
01151     PROCNAME("selaFindMaxTranslations");
01152 
01153     if (!pxp || !pyp || !pxn || !pyn)
01154         return ERROR_INT("&xp (etc) defined", procName, 1);
01155     *pxp = *pyp = *pxn = *pyn = 0;
01156     if (!sel)
01157         return ERROR_INT("sel not defined", procName, 1);
01158     selGetParameters(sel, &sy, &sx, &cy, &cx);
01159 
01160     maxxp = maxyp = maxxn = maxyn = 0;
01161     for (i = 0; i < sy; i++) {
01162         for (j = 0; j < sx; j++) {
01163             if (sel->data[i][j] == 1) {
01164                 maxxp = L_MAX(maxxp, cx - j);
01165                 maxyp = L_MAX(maxyp, cy - i);
01166                 maxxn = L_MAX(maxxn, j - cx);
01167                 maxyn = L_MAX(maxyn, i - cy);
01168             }
01169         }
01170     }
01171 
01172     *pxp = maxxp;
01173     *pyp = maxyp;
01174     *pxn = maxxn;
01175     *pyn = maxyn;
01176 
01177     return 0;
01178 }
01179 
01180 
01181 /*----------------------------------------------------------------------*
01182  *                   Rotation by multiples of 90 degrees                *
01183  *----------------------------------------------------------------------*/
01184 /*!
01185  *  selRotateOrth()
01186  *
01187  *      Input:  sel
01188  *              quads (0 - 4; number of 90 degree cw rotations)
01189  *      Return: seld, or null on error
01190  */
01191 SEL  *
01192 selRotateOrth(SEL     *sel,
01193               l_int32  quads)
01194 {
01195 l_int32  i, j, ni, nj, sx, sy, cx, cy, nsx, nsy, ncx, ncy, type;
01196 SEL     *seld;
01197 
01198     PROCNAME("selRotateOrth");
01199 
01200     if (!sel)
01201         return (SEL *)ERROR_PTR("sel not defined", procName, NULL);
01202     if (quads < 0 || quads > 4)
01203         return (SEL *)ERROR_PTR("quads not in {0,1,2,3,4}", procName, NULL);
01204     if (quads == 0 || quads == 4)
01205         return selCopy(sel);
01206 
01207     selGetParameters(sel, &sy, &sx, &cy, &cx);
01208     if (quads == 1) {  /* 90 degrees cw */
01209         nsx = sy;
01210         nsy = sx;
01211         ncx = sy - cy - 1;
01212         ncy = cx;
01213     } else if (quads == 2) {  /* 180 degrees cw */
01214         nsx = sx;
01215         nsy = sy; 
01216         ncx = sx - cx - 1;
01217         ncy = sy - cy - 1;
01218     } else {  /* 270 degrees cw */
01219         nsx = sy;
01220         nsy = sx;
01221         ncx = cy;
01222         ncy = sx - cx - 1;
01223     }
01224     seld = selCreateBrick(nsy, nsx, ncy, ncx, SEL_DONT_CARE);
01225     if (sel->name)
01226         seld->name = stringNew(sel->name);
01227 
01228     for (i = 0; i < sy; i++) {
01229         for (j = 0; j < sx; j++) {
01230             selGetElement(sel, i, j, &type);
01231             if (quads == 1) {
01232                ni = j;
01233                nj = sy - i - 1;
01234             } else if (quads == 2) {
01235                ni = sy - i - 1;
01236                nj = sx - j - 1;
01237             } else {  /* quads == 3 */
01238                ni = sx - j - 1;
01239                nj = i;
01240             }
01241             selSetElement(seld, ni, nj, type);
01242         }
01243     }
01244 
01245     return seld;
01246 }
01247 
01248 
01249 /*----------------------------------------------------------------------*
01250  *                       Sela and Sel serialized I/O                    *
01251  *----------------------------------------------------------------------*/
01252 /*!
01253  *  selaRead()
01254  *
01255  *      Input:  filename
01256  *      Return: sela, or null on error
01257  */
01258 SELA  *
01259 selaRead(const char  *fname)
01260 {
01261 FILE  *fp;
01262 SELA  *sela;
01263 
01264     PROCNAME("selaRead");
01265 
01266     if (!fname)
01267         return (SELA *)ERROR_PTR("fname not defined", procName, NULL);
01268 
01269     if ((fp = fopenReadStream(fname)) == NULL)
01270         return (SELA *)ERROR_PTR("stream not opened", procName, NULL);
01271     if ((sela = selaReadStream(fp)) == NULL)
01272         return (SELA *)ERROR_PTR("sela not returned", procName, NULL);
01273     fclose(fp);
01274 
01275     return sela;
01276 }
01277 
01278 
01279 /*!
01280  *  selaReadStream()
01281  *
01282  *      Input:  stream
01283  *      Return: sela, or null on error
01284  */
01285 SELA  *
01286 selaReadStream(FILE  *fp)
01287 {
01288 l_int32  i, n, version;
01289 SEL     *sel;
01290 SELA    *sela;
01291 
01292     PROCNAME("selaReadStream");
01293 
01294     if (!fp)
01295         return (SELA *)ERROR_PTR("stream not defined", procName, NULL);
01296 
01297     if (fscanf(fp, "\nSela Version %d\n", &version) != 1)
01298         return (SELA *)ERROR_PTR("not a sela file", procName, NULL);
01299     if (version != SEL_VERSION_NUMBER)
01300         return (SELA *)ERROR_PTR("invalid sel version", procName, NULL);
01301     if (fscanf(fp, "Number of Sels = %d\n\n", &n) != 1)
01302         return (SELA *)ERROR_PTR("not a sela file", procName, NULL);
01303 
01304     if ((sela = selaCreate(n)) == NULL)
01305         return (SELA *)ERROR_PTR("sela not made", procName, NULL);
01306     sela->nalloc = n;
01307 
01308     for (i = 0; i < n; i++)
01309     {
01310         if ((sel = selReadStream(fp)) == NULL)
01311             return (SELA *)ERROR_PTR("sel not made", procName, NULL);
01312         selaAddSel(sela, sel, NULL, 0);
01313     }
01314 
01315     return sela;
01316 }
01317 
01318 
01319 /*!
01320  *  selRead()
01321  *
01322  *      Input:  filename
01323  *      Return: sel, or null on error
01324  */
01325 SEL  *
01326 selRead(const char  *fname)
01327 {
01328 FILE  *fp;
01329 SEL   *sel;
01330 
01331     PROCNAME("selRead");
01332 
01333     if (!fname)
01334         return (SEL *)ERROR_PTR("fname not defined", procName, NULL);
01335 
01336     if ((fp = fopenReadStream(fname)) == NULL)
01337         return (SEL *)ERROR_PTR("stream not opened", procName, NULL);
01338     if ((sel = selReadStream(fp)) == NULL)
01339         return (SEL *)ERROR_PTR("sela not returned", procName, NULL);
01340     fclose(fp);
01341 
01342     return sel;
01343 }
01344 
01345 
01346 /*!
01347  *  selReadStream()
01348  *
01349  *      Input:  stream
01350  *      Return: sel, or null on error
01351  */
01352 SEL  *
01353 selReadStream(FILE  *fp)
01354 {
01355 char    *selname;
01356 char     linebuf[L_BUF_SIZE];
01357 l_int32  sy, sx, cy, cx, i, j, version, ignore;
01358 SEL     *sel;
01359 
01360     PROCNAME("selReadStream");
01361 
01362     if (!fp)
01363         return (SEL *)ERROR_PTR("stream not defined", procName, NULL);
01364 
01365     if (fscanf(fp, "  Sel Version %d\n", &version) != 1)
01366         return (SEL *)ERROR_PTR("not a sel file", procName, NULL);
01367     if (version != SEL_VERSION_NUMBER)
01368         return (SEL *)ERROR_PTR("invalid sel version", procName, NULL);
01369 
01370     if (fgets(linebuf, L_BUF_SIZE, fp) == NULL)
01371         return (SEL *)ERROR_PTR("error reading into linebuf", procName, NULL);
01372     selname = stringNew(linebuf);
01373     sscanf(linebuf, "  ------  %s  ------", selname);
01374 
01375     if (fscanf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n",
01376             &sy, &sx, &cy, &cx) != 4)
01377         return (SEL *)ERROR_PTR("dimensions not read", procName, NULL);
01378 
01379     if ((sel = selCreate(sy, sx, selname)) == NULL)
01380         return (SEL *)ERROR_PTR("sel not made", procName, NULL);
01381     selSetOrigin(sel, cy, cx);
01382 
01383     for (i = 0; i < sy; i++) {
01384         ignore = fscanf(fp, "    ");
01385         for (j = 0; j < sx; j++)
01386             ignore = fscanf(fp, "%1d", &sel->data[i][j]);
01387         ignore = fscanf(fp, "\n");
01388     }
01389     ignore = fscanf(fp, "\n");
01390 
01391     FREE(selname);
01392     return sel;
01393 }
01394 
01395 
01396 /*!
01397  *  selaWrite()
01398  *
01399  *      Input:  filename
01400  *              sela
01401  *      Return: 0 if OK, 1 on error
01402  */
01403 l_int32
01404 selaWrite(const char  *fname,
01405           SELA        *sela)
01406 {
01407 FILE  *fp;
01408 
01409     PROCNAME("selaWrite");
01410 
01411     if (!fname)
01412         return ERROR_INT("fname not defined", procName, 1);
01413     if (!sela)
01414         return ERROR_INT("sela not defined", procName, 1);
01415 
01416     if ((fp = fopenWriteStream(fname, "wb")) == NULL)
01417         return ERROR_INT("stream not opened", procName, 1);
01418     selaWriteStream(fp, sela);
01419     fclose(fp);
01420 
01421     return 0;
01422 }
01423 
01424 
01425 /*!
01426  *  selaWriteStream()
01427  *
01428  *      Input:  stream
01429  *              sela
01430  *      Return: 0 if OK, 1 on error
01431  */
01432 l_int32
01433 selaWriteStream(FILE  *fp,
01434                 SELA  *sela)
01435 {
01436 l_int32  i, n;
01437 SEL     *sel;
01438 
01439     PROCNAME("selaWriteStream");
01440 
01441     if (!fp)
01442         return ERROR_INT("stream not defined", procName, 1);
01443     if (!sela)
01444         return ERROR_INT("sela not defined", procName, 1);
01445 
01446     n = selaGetCount(sela);
01447     fprintf(fp, "\nSela Version %d\n", SEL_VERSION_NUMBER);
01448     fprintf(fp, "Number of Sels = %d\n\n", n);
01449     for (i = 0; i < n; i++) {
01450         if ((sel = selaGetSel(sela, i)) == NULL)
01451             continue;
01452         selWriteStream(fp, sel);
01453     }
01454     return 0;
01455 }
01456 
01457 
01458 /*!
01459  *  selWrite()
01460  *
01461  *      Input:  filename
01462  *              sel
01463  *      Return: 0 if OK, 1 on error
01464  */
01465 l_int32
01466 selWrite(const char  *fname,
01467          SEL         *sel)
01468 {
01469 FILE  *fp;
01470 
01471     PROCNAME("selWrite");
01472 
01473     if (!fname)
01474         return ERROR_INT("fname not defined", procName, 1);
01475     if (!sel)
01476         return ERROR_INT("sel not defined", procName, 1);
01477 
01478     if ((fp = fopenWriteStream(fname, "wb")) == NULL)
01479         return ERROR_INT("stream not opened", procName, 1);
01480     selWriteStream(fp, sel);
01481     fclose(fp);
01482 
01483     return 0;
01484 }
01485 
01486 
01487 /*!
01488  *  selWriteStream()
01489  *
01490  *      Input:  stream
01491  *              sel
01492  *      Return: 0 if OK, 1 on error
01493  */
01494 l_int32
01495 selWriteStream(FILE  *fp,
01496                SEL   *sel)
01497 {
01498 l_int32  sx, sy, cx, cy, i, j;
01499 
01500     PROCNAME("selWriteStream");
01501 
01502     if (!fp)
01503         return ERROR_INT("stream not defined", procName, 1);
01504     if (!sel)
01505         return ERROR_INT("sel not defined", procName, 1);
01506     selGetParameters(sel, &sy, &sx, &cy, &cx);
01507 
01508     fprintf(fp, "  Sel Version %d\n", SEL_VERSION_NUMBER);
01509     fprintf(fp, "  ------  %s  ------\n", selGetName(sel));
01510     fprintf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx);
01511     for (i = 0; i < sy; i++) {
01512         fprintf(fp, "    ");
01513         for (j = 0; j < sx; j++)
01514             fprintf(fp, "%d", sel->data[i][j]);
01515         fprintf(fp, "\n");
01516     }
01517     fprintf(fp, "\n");
01518 
01519     return 0;
01520 }
01521 
01522 
01523 /*----------------------------------------------------------------------*
01524  *           Building custom hit-miss sels from compiled strings        *
01525  *----------------------------------------------------------------------*/
01526 /*!
01527  *  selCreateFromString()
01528  *
01529  *      Input:  text
01530  *              height, width
01531  *              name (<optional> sel name; can be null)
01532  *      Return: sel of the given size, or null on error
01533  *
01534  *  Notes:
01535  *      (1) The text is an array of chars (in row-major order) where
01536  *          each char can be one of the following:
01537  *             'x': hit
01538  *             'o': miss
01539  *             ' ': don't-care
01540  *      (2) Use an upper case char to indicate the origin of the Sel.
01541  *          When the origin falls on a don't-care, use 'C' as the uppecase
01542  *          for ' '.
01543  *      (3) The text can be input in a format that shows the 2D layout; e.g.,
01544  *              static const char *seltext = "x    "
01545  *                                           "x Oo "
01546  *                                           "x    "
01547  *                                           "xxxxx";
01548  */
01549 SEL *
01550 selCreateFromString(const char  *text,
01551                     l_int32      h,
01552                     l_int32      w,
01553                     const char  *name)
01554 {
01555 SEL     *sel;
01556 l_int32  y, x;
01557 char     ch;
01558 
01559     PROCNAME("selCreateFromString");
01560 
01561     if (h < 1)
01562         return (SEL *)ERROR_PTR("height must be > 0", procName, NULL);
01563     if (w < 1)
01564         return (SEL *)ERROR_PTR("width must be > 0", procName, NULL);
01565     
01566     sel = selCreate(h, w, name);
01567 
01568     for (y = 0; y < h; ++y) {
01569         for (x = 0; x < w; ++x) {
01570             ch = *(text++);
01571             switch (ch)
01572             {
01573                 case 'X':
01574                     selSetOrigin(sel, y, x);
01575                 case 'x':
01576                     selSetElement(sel, y, x, SEL_HIT);
01577                     break;
01578 
01579                 case 'O':
01580                     selSetOrigin(sel, y, x);
01581                 case 'o':
01582                     selSetElement(sel, y, x, SEL_MISS);
01583                     break;
01584 
01585                 case 'C':
01586                     selSetOrigin(sel, y, x);
01587                 case ' ':
01588                     selSetElement(sel, y, x, SEL_DONT_CARE);
01589                     break;
01590 
01591                 case '\n':
01592                     /* ignored */
01593                     continue;
01594 
01595                 default:
01596                     selDestroy(&sel);
01597                     return (SEL *)ERROR_PTR("unknown char", procName, NULL);
01598             }
01599         }
01600     }
01601 
01602     return sel;
01603 }
01604 
01605 
01606 /*!
01607  *  selPrintToString()
01608  *
01609  *      Input:  sel
01610  *      Return: str (string; caller must free)
01611  *
01612  *  Notes:
01613  *      (1) This is an inverse function of selCreateFromString.
01614  *          It prints a textual representation of the SEL to a malloc'd
01615  *          string.  The format is the same as selCreateFromString
01616  *          except that newlines are inserted into the output
01617  *          between rows.
01618  *      (2) This is useful for debugging.  However, if you want to
01619  *          save some Sels in a file, put them in a Sela and write
01620  *          them out with selaWrite().  They can then be read in
01621  *          with selaRead().
01622  */
01623 char *
01624 selPrintToString(SEL  *sel)
01625 {
01626 char     is_center;
01627 char    *str, *strptr;
01628 l_int32  type;
01629 l_int32  sx, sy, cx, cy, x, y;
01630 
01631     PROCNAME("selPrintToString");
01632 
01633     if (!sel)
01634         return (char *)ERROR_PTR("sel not defined", procName, NULL);
01635 
01636     selGetParameters(sel, &sy, &sx, &cy, &cx);
01637     if ((str = (char *)CALLOC(1, sy * (sx + 1) + 1)) == NULL)
01638         return (char *)ERROR_PTR("calloc fail for str", procName, NULL);
01639     strptr = str;
01640 
01641     for (y = 0; y < sy; ++y) {
01642         for (x = 0; x < sx; ++x) {
01643             selGetElement(sel, y, x, &type);
01644             is_center = (x == cx && y == cy);
01645             switch (type) {
01646                 case SEL_HIT:
01647                     *(strptr++) = is_center ? 'X' : 'x';
01648                     break;
01649                 case SEL_MISS:
01650                     *(strptr++) = is_center ? 'O' : 'o';
01651                     break;
01652                 case SEL_DONT_CARE:
01653                     *(strptr++) = is_center ? 'C' : ' ';
01654                     break;
01655             }
01656         }
01657         *(strptr++) = '\n';
01658     }
01659 
01660     return str;
01661 }
01662 
01663 
01664 /*----------------------------------------------------------------------*
01665  *         Building custom hit-miss sels from a simple file format      *
01666  *----------------------------------------------------------------------*/
01667 /*!
01668  *  selaCreateFromFile()
01669  *
01670  *      Input:  filename
01671  *      Return: sela, or null on error
01672  *
01673  *  Notes:
01674  *      (1) The file contains a sequence of Sel descriptions.
01675  *      (2) Each Sel is formatted as follows:
01676  *           - Any number of comment lines starting with '#' are ignored
01677  *           - The next line contains the selname
01678  *           - The next lines contain the Sel data.  They must be
01679  *             formatted similarly to the string format in
01680  *             selCreateFromString(), with each line beginning and
01681  *             ending with a double-quote, and showing the 2D layout.
01682  *           - Each Sel ends when a blank line, a comment line, or
01683  *             the end of file is reached.
01684  *      (3) See selCreateFromString() for a description of the string
01685  *          format for the Sel data.  As an example, here are the lines
01686  *          of is a valid file for a single Sel.  In the file, all lines 
01687  *          are left-justified:
01688  *                    # diagonal sel
01689  *                    sel_5diag
01690  *                    "x    "
01691  *                    " x   "
01692  *                    "  X  "
01693  *                    "   x "
01694  *                    "    x"
01695  */
01696 SELA *
01697 selaCreateFromFile(const char  *filename)
01698 {
01699 char    *filestr, *line;
01700 l_int32  i, n, first, last, nsel, insel;
01701 size_t   nbytes;
01702 NUMA    *nafirst, *nalast;
01703 SARRAY  *sa;
01704 SEL     *sel;
01705 SELA    *sela;
01706 
01707     PROCNAME("selaCreateFromFile");
01708 
01709     if (!filename)
01710         return (SELA *)ERROR_PTR("filename not defined", procName, NULL);
01711     
01712     filestr = (char *)l_binaryRead(filename, &nbytes);
01713     sa = sarrayCreateLinesFromString(filestr, 1);
01714     FREE(filestr);
01715     n = sarrayGetCount(sa);
01716     sela = selaCreate(0);
01717 
01718         /* Find the start and end lines for each Sel.
01719          * We allow the "blank" lines to be null strings or
01720          * to have standard whitespace (' ','\t',\'n') or be '#'. */
01721     nafirst = numaCreate(0);
01722     nalast = numaCreate(0);
01723     insel = FALSE;
01724     for (i = 0; i < n; i++) {
01725         line = sarrayGetString(sa, i, L_NOCOPY);
01726         if (!insel &&
01727             (line[0] != '\0' && line[0] != ' ' &&
01728              line[0] != '\t' && line[0] != '\n' && line[0] != '#')) {
01729             numaAddNumber(nafirst, i);
01730             insel = TRUE;
01731             continue;
01732         }           
01733         if (insel &&
01734             (line[0] == '\0' || line[0] == ' ' ||
01735              line[0] == '\t' || line[0] == '\n' || line[0] == '#')) {
01736             numaAddNumber(nalast, i - 1);
01737             insel = FALSE;
01738             continue;
01739         }           
01740     }
01741     if (insel)  /* fell off the end of the file */
01742         numaAddNumber(nalast, n - 1);
01743 
01744         /* Extract sels */
01745     nsel = numaGetCount(nafirst);
01746     for (i = 0; i < nsel; i++) {
01747         numaGetIValue(nafirst, i, &first);
01748         numaGetIValue(nalast, i, &last);
01749         if ((sel = selCreateFromSArray(sa, first, last)) == NULL) {
01750             fprintf(stderr, "Error reading sel from %d to %d\n", first, last);
01751             selaDestroy(&sela);
01752             sarrayDestroy(&sa);
01753             numaDestroy(&nafirst);
01754             numaDestroy(&nalast);
01755             return (SELA *)ERROR_PTR("bad sela file", procName, NULL);
01756         }
01757         selaAddSel(sela, sel, NULL, 0);
01758     }
01759 
01760     numaDestroy(&nafirst);
01761     numaDestroy(&nalast);
01762     sarrayDestroy(&sa);
01763     return sela;
01764 }
01765 
01766 
01767 /*!
01768  *  selCreateFromSArray()
01769  *
01770  *      Input:  sa
01771  *              first (line of sarray where Sel begins)
01772  *              last (line of sarray where Sel ends)
01773  *      Return: sela, or null on error
01774  *
01775  *  Notes:
01776  *      (1) The Sel contains the following lines:
01777  *          - The first line is the selname
01778  *          - The remaining lines contain the Sel data.  They must
01779  *            be formatted similarly to the string format in
01780  *            selCreateFromString(), with each line beginning and
01781  *            ending with a double-quote, and showing the 2D layout.
01782  *          - 'last' gives the last line in the Sel data.
01783  *      (2) See selCreateFromString() for a description of the string
01784  *          format for the Sel data.  As an example, here are the lines
01785  *          of is a valid file for a single Sel.  In the file, all lines 
01786  *          are left-justified:
01787  *                    # diagonal sel
01788  *                    sel_5diag
01789  *                    "x    "
01790  *                    " x   "
01791  *                    "  X  "
01792  *                    "   x "
01793  *                    "    x"
01794  */
01795 static SEL *
01796 selCreateFromSArray(SARRAY  *sa,
01797                     l_int32  first,
01798                     l_int32  last)
01799 {
01800 char     ch;
01801 char    *name, *line;
01802 l_int32  n, len, i, w, h, y, x;
01803 SEL     *sel;
01804 
01805     PROCNAME("selCreateFromSArray");
01806 
01807     if (!sa)
01808         return (SEL *)ERROR_PTR("sa not defined", procName, NULL);
01809     n = sarrayGetCount(sa);
01810     if (first < 0 || first >= n || last <= first || last >= n)
01811         return (SEL *)ERROR_PTR("invalid range", procName, NULL);
01812     
01813     name = sarrayGetString(sa, first, L_NOCOPY);
01814     h = last - first;
01815     line = sarrayGetString(sa, first + 1, L_NOCOPY);
01816     len = strlen(line);
01817     if (line[0] != '"' || line[len - 1] != '"')
01818         return (SEL *)ERROR_PTR("invalid format", procName, NULL);
01819     w = len - 2;
01820     if ((sel = selCreate(h, w, name)) == NULL)
01821         return (SEL *)ERROR_PTR("sel not made", procName, NULL);
01822     for (i = first + 1; i <= last; i++) {
01823         line = sarrayGetString(sa, i, L_NOCOPY);
01824         y = i - first - 1;
01825         for (x = 0; x < w; ++x) {
01826             ch = line[x + 1];  /* skip the leading double-quote */
01827             switch (ch)
01828             {
01829                 case 'X':
01830                     selSetOrigin(sel, y, x);
01831                 case 'x':
01832                     selSetElement(sel, y, x, SEL_HIT);
01833                     break;
01834 
01835                 case 'O':
01836                     selSetOrigin(sel, y, x);
01837                 case 'o':
01838                     selSetElement(sel, y, x, SEL_MISS);
01839                     break;
01840 
01841                 case 'C':
01842                     selSetOrigin(sel, y, x);
01843                 case ' ':
01844                     selSetElement(sel, y, x, SEL_DONT_CARE);
01845                     break;
01846                 default:
01847                     selDestroy(&sel);
01848                     return (SEL *)ERROR_PTR("unknown char", procName, NULL);
01849             }
01850         }
01851     }
01852 
01853     return sel;
01854 }
01855 
01856 
01857 /*----------------------------------------------------------------------*
01858  *               Making hit-only SELs from Pta and Pix                  *
01859  *----------------------------------------------------------------------*/
01860 /*!
01861  *  selCreateFromPta()
01862  *
01863  *      Input:  pta
01864  *              cy, cx (origin of sel)
01865  *              name (<optional> sel name; can be null)
01866  *      Return: sel (of minimum required size), or null on error
01867  *
01868  *  Notes:
01869  *      (1) The origin and all points in the pta must be positive.
01870  */
01871 SEL *
01872 selCreateFromPta(PTA         *pta,
01873                  l_int32      cy,
01874                  l_int32      cx,
01875                  const char  *name)
01876 {
01877 l_int32  i, n, x, y, w, h;
01878 BOX     *box;
01879 SEL     *sel;
01880 
01881     PROCNAME("selCreateFromPta");
01882 
01883     if (!pta)
01884         return (SEL *)ERROR_PTR("pta not defined", procName, NULL);
01885     if (cy < 0 || cx < 0)
01886         return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", procName, NULL);
01887     n = ptaGetCount(pta);
01888     if (n == 0)
01889         return (SEL *)ERROR_PTR("no pts in pta", procName, NULL);
01890 
01891     box = ptaGetBoundingRegion(pta);
01892     boxGetGeometry(box, &x, &y, &w, &h);
01893     boxDestroy(&box);
01894     if (x < 0 || y < 0)
01895         return (SEL *)ERROR_PTR("not all x and y >= 0", procName, NULL);
01896     
01897     sel = selCreate(y + h, x + w, name);
01898     selSetOrigin(sel, cy, cx);
01899     for (i = 0; i < n; i++) {
01900         ptaGetIPt(pta, i, &x, &y);
01901         selSetElement(sel, y, x, SEL_HIT);
01902     }
01903 
01904     return sel;
01905 }
01906 
01907 
01908 /*!
01909  *  selCreateFromPix()
01910  *
01911  *      Input:  pix
01912  *              cy, cx (origin of sel)
01913  *              name (<optional> sel name; can be null)
01914  *      Return: sel, or null on error
01915  *
01916  *  Notes:
01917  *      (1) The origin must be positive.
01918  */
01919 SEL *
01920 selCreateFromPix(PIX         *pix,
01921                  l_int32      cy,
01922                  l_int32      cx,
01923                  const char  *name)
01924 {
01925 SEL      *sel;
01926 l_int32   i, j, w, h, d;
01927 l_uint32  val;
01928 
01929     PROCNAME("selCreateFromPix");
01930 
01931     if (!pix)
01932         return (SEL *)ERROR_PTR("pix not defined", procName, NULL);
01933     if (cy < 0 || cx < 0)
01934         return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", procName, NULL);
01935     pixGetDimensions(pix, &w, &h, &d);
01936     if (d != 1)
01937         return (SEL *)ERROR_PTR("pix not 1 bpp", procName, NULL);
01938 
01939     sel = selCreate(h, w, name);
01940     selSetOrigin(sel, cy, cx);
01941     for (i = 0; i < h; i++) {
01942         for (j = 0; j < w; j++) {
01943             pixGetPixel(pix, j, i, &val);
01944             if (val)
01945                 selSetElement(sel, i, j, SEL_HIT);
01946         }
01947     }
01948 
01949     return sel;
01950 }
01951 
01952 
01953 /*----------------------------------------------------------------------*
01954  *            Making hit-miss sels from color Pix and image files             *
01955  *----------------------------------------------------------------------*/
01956 /*!
01957  *
01958  *  selReadFromColorImage()
01959  *
01960  *      Input:  pathname
01961  *      Return: sel if OK; null on error
01962  *
01963  *  Notes:
01964  *      (1) Loads an image from a file and creates a (hit-miss) sel.
01965  *      (2) The sel name is taken from the pathname without the directory
01966  *          and extension.
01967  */
01968 SEL *
01969 selReadFromColorImage(const char  *pathname)
01970 {
01971 PIX   *pix;
01972 SEL   *sel;
01973 char  *basename, *selname;
01974 
01975     PROCNAME("selReadFromColorImage");
01976 
01977     splitPathAtExtension (pathname, &basename, NULL);
01978     splitPathAtDirectory (basename, NULL, &selname);
01979     FREE(basename);
01980 
01981     if ((pix = pixRead(pathname)) == NULL)
01982         return (SEL *)ERROR_PTR("pix not returned", procName, NULL);
01983     if ((sel = selCreateFromColorPix(pix, selname)) == NULL)
01984         return (SEL *)ERROR_PTR("sel not made", procName, NULL);
01985     FREE(selname);
01986     pixDestroy(&pix);
01987 
01988     return sel;
01989 }
01990 
01991 
01992 /*!
01993  *
01994  *  selCreateFromColorPix()
01995  *
01996  *      Input:  pixs (cmapped or rgb)
01997  *              selname (<optional> sel name; can be null)
01998  *      Return: sel if OK, null on error
01999  *
02000  *  Notes:
02001  *      (1) The sel size is given by the size of pixs.
02002  *      (2) In pixs, hits are represented by green pixels, misses by red
02003  *          pixels, and don't-cares by white pixels.
02004  *      (3) In pixs, there may be no misses, but there must be at least 1 hit.
02005  *      (4) At most there can be only one origin pixel, which is optionally
02006  *          specified by using a lower-intensity pixel:
02007  *            if a hit:  dark green
02008  *            if a miss: dark red
02009  *            if a don't care: gray
02010  *          If there is no such pixel, the origin defaults to the approximate
02011  *          center of the sel.
02012  */
02013 SEL *
02014 selCreateFromColorPix(PIX   *pixs,
02015                       char  *selname)
02016 {
02017 PIXCMAP  *cmap;
02018 SEL      *sel;
02019 l_int32   hascolor, hasorigin, nohits;
02020 l_int32   w, h, d, i, j, red, green, blue;
02021 l_uint32  pixval;
02022 
02023     PROCNAME("selCreateFromColorPix");
02024 
02025     if (!pixs)
02026         return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
02027 
02028     hascolor = FALSE;
02029     cmap = pixGetColormap(pixs);
02030     if (cmap)
02031         pixcmapHasColor(cmap, &hascolor);
02032     pixGetDimensions(pixs, &w, &h, &d);
02033     if (hascolor == FALSE && d != 32)
02034         return (SEL *)ERROR_PTR("pixs has no color", procName, NULL);
02035 
02036     if ((sel = selCreate (h, w, NULL)) == NULL)
02037         return (SEL *)ERROR_PTR ("sel not made", procName, NULL);
02038     selSetOrigin (sel, h / 2, w / 2);
02039     selSetName(sel, selname);
02040 
02041     hasorigin = FALSE;
02042     nohits = TRUE;
02043     for (i = 0; i < h; i++) {
02044         for (j = 0; j < w; j++) {
02045             pixGetPixel (pixs, j, i, &pixval);
02046 
02047             if (cmap)
02048                 pixcmapGetColor (cmap, pixval, &red, &green, &blue);
02049             else {
02050                 red = GET_DATA_BYTE (&pixval, COLOR_RED);
02051                 green = GET_DATA_BYTE (&pixval, COLOR_GREEN);
02052                 blue = GET_DATA_BYTE (&pixval, COLOR_BLUE);
02053             }
02054 
02055             if (red < 255 && green < 255 && blue < 255) {
02056                 if (hasorigin)
02057                     L_WARNING("multiple origins in sel image", procName);
02058                 selSetOrigin (sel, i, j);
02059                 hasorigin = TRUE;
02060             }
02061             if (!red && green && !blue) {
02062                 nohits = FALSE;
02063                 selSetElement (sel, i, j, SEL_HIT);
02064             }
02065             else if (red && !green && !blue)
02066                 selSetElement (sel, i, j, SEL_MISS);
02067             else if (red && green && blue)
02068                 selSetElement (sel, i, j, SEL_DONT_CARE);
02069             else {
02070                 selDestroy(&sel);
02071                 return (SEL *)ERROR_PTR("invalid color", procName, NULL);
02072             }
02073         }
02074     }
02075 
02076     if (nohits) {
02077         selDestroy(&sel);
02078         return (SEL *)ERROR_PTR("no hits in sel", procName, NULL);
02079     }
02080     return sel;
02081 }
02082 
02083 
02084 /*----------------------------------------------------------------------*
02085  *                     Printable display of sel                         *
02086  *----------------------------------------------------------------------*/
02087 /*!
02088  *  selDisplayInPix()
02089  *
02090  *      Input:  sel
02091  *              size (of grid interiors; odd; minimum size of 13 is enforced)
02092  *              gthick (grid thickness; minimum size of 2 is enforced)
02093  *      Return: pix (display of sel), or null on error
02094  *
02095  *  Notes:
02096  *      (1) This gives a visual representation of a general (hit-miss) sel.
02097  *      (2) The empty sel is represented by a grid of intersecting lines.
02098  *      (3) Three different patterns are generated for the sel elements:
02099  *          - hit (solid black circle)
02100  *          - miss (black ring; inner radius is radius2)
02101  *          - origin (cross, XORed with whatever is there)
02102  */
02103 PIX *
02104 selDisplayInPix(SEL     *sel,
02105                 l_int32  size,
02106                 l_int32  gthick)
02107 {
02108 l_int32  i, j, w, h, sx, sy, cx, cy, type, width;
02109 l_int32  radius1, radius2, shift1, shift2, x0, y0;
02110 PIX     *pixd, *pix2, *pixh, *pixm, *pixorig;
02111 PTA     *pta1, *pta2, *pta1t, *pta2t;
02112 
02113     PROCNAME("selDisplayInPix");
02114 
02115     if (!sel)
02116         return (PIX *)ERROR_PTR("sel not defined", procName, NULL);
02117     if (size < 13) {
02118         L_WARNING("size < 13; setting to 13", procName);
02119         size = 13;
02120     }
02121     if (size % 2 == 0)
02122         size++;
02123     if (gthick < 2) {
02124         L_WARNING("grid thickness < 2; setting to 2", procName);
02125         gthick = 2;
02126     }
02127     selGetParameters(sel, &sy, &sx, &cy, &cx);
02128     w = size * sx + gthick * (sx + 1);
02129     h = size * sy + gthick * (sy + 1);
02130     pixd = pixCreate(w, h, 1);
02131 
02132         /* Generate grid lines */
02133     for (i = 0; i <= sy; i++)
02134         pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick),
02135                       w - 1, gthick / 2 + i * (size + gthick),
02136                       gthick, L_SET_PIXELS);
02137     for (j = 0; j <= sx; j++)
02138         pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0,
02139                       gthick / 2 + j * (size + gthick), h - 1,
02140                       gthick, L_SET_PIXELS);
02141 
02142         /* Generate hit and miss patterns */
02143     radius1 = (l_int32)(0.85 * ((size - 1) / 2) + 0.5);  /* of hit */
02144     radius2 = (l_int32)(0.65 * ((size - 1) / 2) + 0.5);  /* inner miss radius */
02145     pta1 = generatePtaFilledCircle(radius1);
02146     pta2 = generatePtaFilledCircle(radius2);
02147     shift1 = (size - 1) / 2 - radius1;  /* center circle in square */
02148     shift2 = (size - 1) / 2 - radius2;
02149     pta1t = ptaTransform(pta1, shift1, shift1, 1.0, 1.0);
02150     pta2t = ptaTransform(pta2, shift2, shift2, 1.0, 1.0);
02151     pixh = pixGenerateFromPta(pta1t, size, size);  /* hits */
02152     pix2 = pixGenerateFromPta(pta2t, size, size);
02153     pixm = pixSubtract(NULL, pixh, pix2);
02154 
02155         /* Generate crossed lines for origin pattern */
02156     pixorig = pixCreate(size, size, 1);
02157     width = size / 8;
02158     pixRenderLine(pixorig, size / 2, (l_int32)(0.12 * size),
02159                            size / 2, (l_int32)(0.88 * size),
02160                            width, L_SET_PIXELS);
02161     pixRenderLine(pixorig, (l_int32)(0.15 * size), size / 2,
02162                            (l_int32)(0.85 * size), size / 2,
02163                            width, L_FLIP_PIXELS);
02164     pixRasterop(pixorig, size / 2 - width, size / 2 - width,
02165                 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0);
02166 
02167         /* Specialize origin pattern for this sel */
02168     selGetTypeAtOrigin(sel, &type);
02169     if (type == SEL_HIT)
02170         pixXor(pixorig, pixorig, pixh);
02171     else if (type == SEL_MISS)
02172         pixXor(pixorig, pixorig, pixm);
02173 
02174         /* Paste the patterns in */
02175     y0 = gthick;
02176     for (i = 0; i < sy; i++) {
02177         x0 = gthick;
02178         for (j = 0; j < sx; j++) {
02179             selGetElement(sel, i, j, &type);
02180             if (i == cy && j == cx)  /* origin */
02181                 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixorig, 0, 0);
02182             else if (type == SEL_HIT)
02183                 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixh, 0, 0);
02184             else if (type == SEL_MISS)
02185                 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixm, 0, 0);
02186             x0 += size + gthick;
02187         }
02188         y0 += size + gthick;
02189     }
02190 
02191     pixDestroy(&pix2);
02192     pixDestroy(&pixh);
02193     pixDestroy(&pixm);
02194     pixDestroy(&pixorig);
02195     ptaDestroy(&pta1);
02196     ptaDestroy(&pta1t);
02197     ptaDestroy(&pta2);
02198     ptaDestroy(&pta2t);
02199     return pixd;
02200 }
02201 
02202 
02203 /*!
02204  *  selaDisplayInPix()
02205  *
02206  *      Input:  sela
02207  *              size (of grid interiors; odd; minimum size of 13 is enforced)
02208  *              gthick (grid thickness; minimum size of 2 is enforced)
02209  *              spacing (between sels, both horizontally and vertically)
02210  *              ncols (number of sels per "line")
02211  *      Return: pix (display of all sels in sela), or null on error
02212  *
02213  *  Notes:
02214  *      (1) This gives a visual representation of all the sels in a sela.
02215  *      (2) See notes in selDisplayInPix() for display params of each sel.
02216  *      (3) This gives the nicest results when all sels in the sela
02217  *          are the same size.
02218  */
02219 PIX *
02220 selaDisplayInPix(SELA    *sela,
02221                  l_int32  size,
02222                  l_int32  gthick,
02223                  l_int32  spacing,
02224                  l_int32  ncols)
02225 {
02226 l_int32  nsels, i, w, width;
02227 PIX     *pixt, *pixd;
02228 PIXA    *pixa;
02229 SEL     *sel;
02230 
02231     PROCNAME("selaDisplayInPix");
02232 
02233     if (!sela)
02234         return (PIX *)ERROR_PTR("sela not defined", procName, NULL);
02235     if (size < 13) {
02236         L_WARNING("size < 13; setting to 13", procName);
02237         size = 13;
02238     }
02239     if (size % 2 == 0)
02240         size++;
02241     if (gthick < 2) {
02242         L_WARNING("grid thickness < 2; setting to 2", procName);
02243         gthick = 2;
02244     }
02245     if (spacing < 5) {
02246         L_WARNING("spacing < 5; setting to 5", procName);
02247         spacing = 5;
02248     }
02249 
02250         /* Accumulate the pix of each sel */
02251     nsels = selaGetCount(sela);
02252     pixa = pixaCreate(nsels);
02253     for (i = 0; i < nsels; i++) {
02254         sel = selaGetSel(sela, i);
02255         pixt = selDisplayInPix(sel, size, gthick);
02256         pixaAddPix(pixa, pixt, L_INSERT);
02257     }
02258 
02259         /* Find the tiled output width, using just the first
02260          * ncols pix in the pixa.   If all pix have the same width,
02261          * they will align properly in columns. */
02262     width = 0;
02263     ncols = L_MIN(nsels, ncols);
02264     for (i = 0; i < ncols; i++) {
02265         pixt = pixaGetPix(pixa, i, L_CLONE);
02266         pixGetDimensions(pixt, &w, NULL, NULL);
02267         width += w;
02268         pixDestroy(&pixt);
02269     }
02270     width += (ncols + 1) * spacing;  /* add spacing all around as well */
02271     
02272     pixd = pixaDisplayTiledInRows(pixa, 1, width, 1.0, 0, spacing, 0);
02273     pixaDestroy(&pixa);
02274     return pixd;
02275 }
02276 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines