Beispiel #1
0
/*!
 *  gplotRead()
 * 
 *      Input:  filename
 *      Return: gplot, or NULL on error
 */
GPLOT *
gplotRead(const char  *filename)
{
char     buf[L_BUF_SIZE];
char    *rootname, *title, *xlabel, *ylabel;
l_int32  outformat, ret, version;
FILE    *fp;
GPLOT   *gplot;

    PROCNAME("gplotRead");

    if (!filename)
        return (GPLOT *)ERROR_PTR("filename not defined", procName, NULL);

    if ((fp = fopen(filename, "r")) == NULL)
        return (GPLOT *)ERROR_PTR("stream not opened", procName, NULL);

    ret = fscanf(fp, "Gplot Version %d\n", &version);
    if (ret != 1) {
        fclose(fp);
        return (GPLOT *)ERROR_PTR("not a gplot file", procName, NULL);
    }
    if (version != GPLOT_VERSION_NUMBER) {
        fclose(fp);
        return (GPLOT *)ERROR_PTR("invalid gplot version", procName, NULL);
    }

    fscanf(fp, "Rootname: %s\n", buf);
    rootname = stringNew(buf);
    fscanf(fp, "Output format: %d\n", &outformat);
    fgets(buf, L_BUF_SIZE, fp);   /* Title: ... */
    title = stringNew(buf + 7);
    title[strlen(title) - 1] = '\0';
    fgets(buf, L_BUF_SIZE, fp);   /* X axis label: ... */
    xlabel = stringNew(buf + 14);
    xlabel[strlen(xlabel) - 1] = '\0';
    fgets(buf, L_BUF_SIZE, fp);   /* Y axis label: ... */
    ylabel = stringNew(buf + 14);
    ylabel[strlen(ylabel) - 1] = '\0';

    if (!(gplot = gplotCreate(rootname, outformat, title, xlabel, ylabel))) {
        fclose(fp);
        return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL);
    }
    FREE(rootname);
    FREE(title);
    FREE(xlabel);
    FREE(ylabel);
    sarrayDestroy(&gplot->cmddata);
    sarrayDestroy(&gplot->datanames);
    sarrayDestroy(&gplot->plotdata);
    sarrayDestroy(&gplot->plottitles);
    numaDestroy(&gplot->plotstyles);

    fscanf(fp, "Commandfile name: %s\n", buf);
    stringReplace(&gplot->cmdname, buf);
    fscanf(fp, "\nCommandfile data:");
    gplot->cmddata = sarrayReadStream(fp);
    fscanf(fp, "\nDatafile names:");
    gplot->datanames = sarrayReadStream(fp);
    fscanf(fp, "\nPlot data:");
    gplot->plotdata = sarrayReadStream(fp);
    fscanf(fp, "\nPlot titles:");
    gplot->plottitles = sarrayReadStream(fp);
    fscanf(fp, "\nPlot styles:");
    gplot->plotstyles = numaReadStream(fp);

    fscanf(fp, "Number of plots: %d\n", &gplot->nplots);
    fscanf(fp, "Output file name: %s\n", buf);
    stringReplace(&gplot->outname, buf);
    fscanf(fp, "Axis scaling: %d\n", &gplot->scaling);

    fclose(fp);
    return gplot;
}
Beispiel #2
0
/*!
 *  sarrayMakeWplsCode()
 */
static SARRAY *
sarrayMakeWplsCode(SEL  *sel)
{
char     emptystring[] = "";
l_int32  i, j, ymax, dely, allvshifts;
l_int32  vshift[32];
SARRAY  *sa;

    PROCNAME("sarrayMakeWplsCode");

    if (!sel)
        return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL);

    for (i = 0; i < 32; i++)
        vshift[i] = 0;
    ymax = 0;
    for (i = 0; i < sel->sy; i++) {
        for (j = 0; j < sel->sx; j++) {
            if (sel->data[i][j] == 1) {
                dely = L_ABS(i - sel->cy);
                if (dely < 32)
                    vshift[dely] = 1;
                ymax = L_MAX(ymax, dely);
            }
        }
    }
    if (ymax > 31) {
        L_WARNING("ymax > 31; truncating to 31\n", procName);
        ymax = 31;
    }

        /* Test if this is a vertical brick */
    allvshifts = TRUE;
    for (i = 0; i < ymax; i++) {
        if (vshift[i] == 0) {
            allvshifts = FALSE;
            break;
        }
    }

    if ((sa = sarrayCreate(0)) == NULL)
        return (SARRAY *)ERROR_PTR("sa not made", procName, NULL);

        /* Add declarations */
    if (allvshifts == TRUE) {   /* packs them as well as possible */
        if (ymax > 4)
            sarrayAddString(sa, wpldecls[2], L_COPY);
        if (ymax > 8)
            sarrayAddString(sa, wpldecls[6], L_COPY);
        if (ymax > 12)
            sarrayAddString(sa, wpldecls[10], L_COPY);
        if (ymax > 16)
            sarrayAddString(sa, wpldecls[14], L_COPY);
        if (ymax > 20)
            sarrayAddString(sa, wpldecls[18], L_COPY);
        if (ymax > 24)
            sarrayAddString(sa, wpldecls[22], L_COPY);
        if (ymax > 28)
            sarrayAddString(sa, wpldecls[26], L_COPY);
        if (ymax > 1)
            sarrayAddString(sa, wpldecls[ymax - 2], L_COPY);
    } else {  /* puts them one/line */
        for (i = 2; i <= ymax; i++) {
            if (vshift[i])
                sarrayAddString(sa, wplgendecls[i - 2], L_COPY);
        }
    }

    sarrayAddString(sa, emptystring, L_COPY);

        /* Add definitions */
    for (i = 2; i <= ymax; i++) {
        if (vshift[i])
            sarrayAddString(sa, wpldefs[i - 2], L_COPY);
    }

    return sa;
}
Beispiel #3
0
PIX *
pixTestDFTGray(PIX     *pixs,
			   l_int32  shiftflag)
{
	return (PIX *)ERROR_PTR("function not present", "pixTestDFTGray", NULL);
}
Beispiel #4
0
/*!
 * \brief   boxaDisplayTiled()
 *
 * \param[in]    boxas
 * \param[in]    pixa          [optional] background for each box
 * \param[in]    first         index of first box
 * \param[in]    last          index of last box; use -1 to go to end
 * \param[in]    maxwidth      of output image
 * \param[in]    linewidth     width of box outlines, before scaling
 * \param[in]    scalefactor   applied to every box; use 1.0 for no scaling
 * \param[in]    background    0 for white, 1 for black; this is the color
 *                             of the spacing between the images
 * \param[in]    spacing       between images, and on outside
 * \param[in]    border        width of black border added to each image;
 *                             use 0 for no border
 * \return  pixd of tiled images of boxes, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) Displays each box separately in a tiled 32 bpp image.
 *      (2) If pixa is defined, it must have the same count as the boxa,
 *          and it will be a background over with each box is rendered.
 *          If pixa is not defined, the boxes will be rendered over
 *          blank images of identical size.
 *      (3) See pixaDisplayTiledInRows() for other parameters.
 * </pre>
 */
PIX *
boxaDisplayTiled(BOXA        *boxas,
                 PIXA        *pixa,
                 l_int32      first,
                 l_int32      last,
                 l_int32      maxwidth,
                 l_int32      linewidth,
                 l_float32    scalefactor,
                 l_int32      background,
                 l_int32      spacing,
                 l_int32      border)
{
char     buf[32];
l_int32  i, n, npix, w, h, fontsize;
L_BMF   *bmf;
BOX     *box;
BOXA    *boxa;
PIX     *pix1, *pix2, *pixd;
PIXA    *pixat;

    PROCNAME("boxaDisplayTiled");

    if (!boxas)
        return (PIX *)ERROR_PTR("boxas not defined", procName, NULL);

    boxa = boxaSaveValid(boxas, L_COPY);
    n = boxaGetCount(boxa);
    if (pixa) {
        npix = pixaGetCount(pixa);
        if (n != npix) {
            boxaDestroy(&boxa);
            return (PIX *)ERROR_PTR("boxa and pixa counts differ",
                                    procName, NULL);
        }
    }
    first = L_MAX(0, first);
    if (last < 0) last = n - 1;
    if (first >= n) {
        boxaDestroy(&boxa);
        return (PIX *)ERROR_PTR("invalid first", procName, NULL);
    }
    if (last >= n) {
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
                  procName, last, n - 1);
        last = n - 1;
    }
    if (first > last) {
        boxaDestroy(&boxa);
        return (PIX *)ERROR_PTR("first > last", procName, NULL);
    }

        /* Because the bitmap font will be reduced when tiled, choose the
         * font size inversely with the scale factor. */
    if (scalefactor > 0.8)
        fontsize = 6;
    else if (scalefactor > 0.6)
        fontsize = 10;
    else if (scalefactor > 0.4)
        fontsize = 14;
    else if (scalefactor > 0.3)
        fontsize = 18;
    else fontsize = 20;
    bmf = bmfCreate(NULL, fontsize);

    pixat = pixaCreate(n);
    boxaGetExtent(boxa, &w, &h, NULL);
    for (i = first; i <= last; i++) {
        box = boxaGetBox(boxa, i, L_CLONE);
        if (!pixa) {
            pix1 = pixCreate(w, h, 32);
            pixSetAll(pix1);
        } else {
            pix1 = pixaGetPix(pixa, i, L_COPY);
        }
        pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00);
        snprintf(buf, sizeof(buf), "%d", i);
        pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000,
                                     L_ADD_BELOW, NULL);
        pixDestroy(&pix1);
        pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0);
        pixaAddPix(pixat, pix2, L_INSERT);
        boxDestroy(&box);
    }
    bmfDestroy(&bmf);
    boxaDestroy(&boxa);

    pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background,
                                  spacing, border);
    pixaDestroy(&pixat);
    return pixd;
}
Beispiel #5
0
/*!
 *  pixReadStream()
 *
 *      Input:  fp (file stream)
 *              hint (bitwise OR of L_HINT_* values for jpeg; use 0 for no hint)
 *      Return: pix if OK; null on error
 *
 *  Notes:
 *      (1) The hint only applies to jpeg.
 */
PIX *
pixReadStream(FILE    *fp,
              l_int32  hint)
{
    l_int32   format, ret;
    l_uint8  *comment;
    PIX      *pix;

    PROCNAME("pixReadStream");

    if (!fp)
        return (PIX *)ERROR_PTR("stream not defined", procName, NULL);
    pix = NULL;

    findFileFormatStream(fp, &format);
    switch (format)
    {
    case IFF_BMP:
        if ((pix = pixReadStreamBmp(fp)) == NULL )
            return (PIX *)ERROR_PTR( "bmp: no pix returned", procName, NULL);
        break;

    case IFF_JFIF_JPEG:
        if ((pix = pixReadStreamJpeg(fp, 0, 1, NULL, hint)) == NULL)
            return (PIX *)ERROR_PTR( "jpeg: no pix returned", procName, NULL);
        ret = fgetJpegComment(fp, &comment);
        if (!ret && comment)
            pixSetText(pix, (char *)comment);
        FREE(comment);
        break;

    case IFF_PNG:
        if ((pix = pixReadStreamPng(fp)) == NULL)
            return (PIX *)ERROR_PTR("png: no pix returned", procName, NULL);
        break;

    case IFF_TIFF:
    case IFF_TIFF_PACKBITS:
    case IFF_TIFF_RLE:
    case IFF_TIFF_G3:
    case IFF_TIFF_G4:
    case IFF_TIFF_LZW:
    case IFF_TIFF_ZIP:
        if ((pix = pixReadStreamTiff(fp, 0)) == NULL)  /* page 0 by default */
            return (PIX *)ERROR_PTR("tiff: no pix returned", procName, NULL);
        break;

    case IFF_PNM:
        if ((pix = pixReadStreamPnm(fp)) == NULL)
            return (PIX *)ERROR_PTR("pnm: no pix returned", procName, NULL);
        break;

    case IFF_GIF:
        if ((pix = pixReadStreamGif(fp)) == NULL)
            return (PIX *)ERROR_PTR("gif: no pix returned", procName, NULL);
        break;

    case IFF_JP2:
        if ((pix = pixReadStreamJp2k(fp, 1, NULL, 0, 0)) == NULL)
            return (PIX *)ERROR_PTR("jp2: no pix returned", procName, NULL);
        break;

    case IFF_WEBP:
        if ((pix = pixReadStreamWebP(fp)) == NULL)
            return (PIX *)ERROR_PTR("webp: no pix returned", procName, NULL);
        break;

    case IFF_SPIX:
        if ((pix = pixReadStreamSpix(fp)) == NULL)
            return (PIX *)ERROR_PTR("spix: no pix returned", procName, NULL);
        break;

    case IFF_UNKNOWN:
        return (PIX *)ERROR_PTR( "Unknown format: no pix returned",
                                 procName, NULL);
        break;
    }

    if (pix)
        pixSetInputFormat(pix, format);
    return pix;
}
Beispiel #6
0
/*!
 *  selaAddBasic()
 *
 *      Input:  sela (<optional>)
 *      Return: sela with additional sels, or null on error
 *
 *  Notes:
 *      (1) Adds the following sels:
 *            - all linear (horiz, vert) brick sels that are
 *              necessary for decomposable sels up to size 63
 *            - square brick sels up to size 10
 *            - 4 diagonal sels
 */
SELA *
selaAddBasic(SELA  *sela)
{
	SEL     *sel;
	l_int32  i, size;
	char     name[L_BUF_SIZE];

	PROCNAME("selaAddBasic");

	if (!sela) {
		if ((sela = selaCreate(0)) == NULL)
			return (SELA *)ERROR_PTR("sela not made", procName, NULL);
	}

	/*--------------------------------------------------------------*
	 *             Linear horizontal and vertical sels              *
	 *--------------------------------------------------------------*/
	for (i = 0; i < num_linear; i++) {
		size = basic_linear[i];
		sel = selCreateBrick(1, size, 0, size / 2, 1);
		snprintf(name, L_BUF_SIZE, "sel_%dh", size);
		selaAddSel(sela, sel, name, 0);
	}
	for (i = 0; i < num_linear; i++) {
		size = basic_linear[i];
		sel = selCreateBrick(size, 1, size / 2, 0, 1);
		snprintf(name, L_BUF_SIZE, "sel_%dv", size);
		selaAddSel(sela, sel, name, 0);
	}

	/*-----------------------------------------------------------*
	 *                      2-d Bricks                           *
	 *-----------------------------------------------------------*/
	for (i = 2; i <= 5; i++) {
		sel = selCreateBrick(i, i, i / 2, i / 2, 1);
		snprintf(name, L_BUF_SIZE, "sel_%d", i);
		selaAddSel(sela, sel, name, 0);
	}

	/*-----------------------------------------------------------*
	 *                        Diagonals                          *
	 *-----------------------------------------------------------*/
	/*  0c  1
		1   0  */
	sel = selCreateBrick(2, 2, 0, 0, 1);
	selSetElement(sel, 0, 0, 0);
	selSetElement(sel, 1, 1, 0);
	selaAddSel(sela, sel, "sel_2dp", 0);

	/*  1c  0
		0   1   */
	sel = selCreateBrick(2, 2, 0, 0, 1);
	selSetElement(sel, 0, 1, 0);
	selSetElement(sel, 1, 0, 0);
	selaAddSel(sela, sel, "sel_2dm", 0);

	/*  Diagonal, slope +, size 5 */
	sel = selCreate(5, 5, "sel_5dp");
	sel->cy = 2;
	sel->cx = 2;
	selSetElement(sel, 0, 4, 1);
	selSetElement(sel, 1, 3, 1);
	selSetElement(sel, 2, 2, 1);
	selSetElement(sel, 3, 1, 1);
	selSetElement(sel, 4, 0, 1);
	selaAddSel(sela, sel, "sel_5dp", 0);

	/*  Diagonal, slope -, size 5 */
	sel = selCreate(5, 5, "sel_5dm");
	sel->cy = 2;
	sel->cx = 2;
	selSetElement(sel, 0, 0, 1);
	selSetElement(sel, 1, 1, 1);
	selSetElement(sel, 2, 2, 1);
	selSetElement(sel, 3, 3, 1);
	selSetElement(sel, 4, 4, 1);
	selaAddSel(sela, sel, "sel_5dm", 0);

	return sela;
}
Beispiel #7
0
/*!
 *  pixRunlengthTransform()
 *
 *      Input:   pixs (1 bpp)
 *               color (0 for white runs, 1 for black runs)
 *               direction (L_HORIZONTAL_RUNS, L_VERTICAL_RUNS)
 *               depth (8 or 16 bpp)
 *      Return:  pixd (8 or 16 bpp), or null on error
 *
 *  Notes:
 *      (1) The dest Pix is 8 or 16 bpp, with the pixel values
 *          equal to the runlength in which it is a member.
 *          The length is clipped to the max pixel value if necessary.
 *      (2) The color determines if we're labelling white or black runs.
 *      (3) A pixel that is not a member of the chosen color gets
 *          value 0; it belongs to a run of length 0 of the
 *          chosen color.
 *      (4) To convert for maximum dynamic range, either linear or
 *          log, use pixMaxDynamicRange().
 */
PIX *
pixRunlengthTransform(PIX     *pixs,
                      l_int32  color,
                      l_int32  direction,
                      l_int32  depth)
{
l_int32    i, j, w, h, wpld, bufsize, maxsize, n;
l_int32   *start, *end, *buffer;
l_uint32  *datad, *lined;
PIX       *pixt, *pixd;

    PROCNAME("pixRunlengthTransform");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (depth != 8 && depth != 16)
        return (PIX *)ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL);

    pixGetDimensions(pixs, &w, &h, NULL);
    if (direction == L_HORIZONTAL_RUNS)
        maxsize = 1 + w / 2;
    else if (direction == L_VERTICAL_RUNS)
        maxsize = 1 + h / 2;
    else
        return (PIX *)ERROR_PTR("invalid direction", procName, NULL);
    bufsize = L_MAX(w, h);

    if ((pixd = pixCreate(w, h, depth)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    datad = pixGetData(pixd);
    wpld = pixGetWpl(pixd);

    if ((start = (l_int32 *)CALLOC(maxsize, sizeof(l_int32))) == NULL)
        return (PIX *)ERROR_PTR("start not made", procName, NULL);
    if ((end = (l_int32 *)CALLOC(maxsize, sizeof(l_int32))) == NULL)
        return (PIX *)ERROR_PTR("end not made", procName, NULL);
    if ((buffer = (l_int32 *)CALLOC(bufsize, sizeof(l_int32))) == NULL)
        return (PIX *)ERROR_PTR("buffer not made", procName, NULL);

        /* Use fg runs for evaluation */
    if (color == 0)
        pixt = pixInvert(NULL, pixs);
    else
        pixt = pixClone(pixs);

    if (direction == L_HORIZONTAL_RUNS) {
        for (i = 0; i < h; i++) {
            pixFindHorizontalRuns(pixt, i, start, end, &n);
            runlengthMembershipOnLine(buffer, w, depth, start, end, n);
            lined = datad + i * wpld;
            if (depth == 8) {
                for (j = 0; j < w; j++)
                    SET_DATA_BYTE(lined, j, buffer[j]);
            } else {  /* depth == 16 */
                for (j = 0; j < w; j++)
                    SET_DATA_TWO_BYTES(lined, j, buffer[j]);
            }
        }
    } else {  /* L_VERTICAL_RUNS */
        for (j = 0; j < w; j++) {
            pixFindVerticalRuns(pixt, j, start, end, &n);
            runlengthMembershipOnLine(buffer, h, depth, start, end, n);
            if (depth == 8) {
                for (i = 0; i < h; i++) {
                    lined = datad + i * wpld;
                    SET_DATA_BYTE(lined, j, buffer[i]);
                }
            } else {  /* depth == 16 */
                for (i = 0; i < h; i++) {
                    lined = datad + i * wpld;
                    SET_DATA_TWO_BYTES(lined, j, buffer[i]);
                }
            }
        }
    }

    pixDestroy(&pixt);
    FREE(start);
    FREE(end);
    FREE(buffer);
    return pixd;
}
Beispiel #8
0
/*!
 *  pixaaDisplayByPixa()
 *
 *      Input:  pixaa
 *              xspace between pix in pixa
 *              yspace between pixa
 *              max width of output pix
 *      Return: pix, or null on error
 *
 *  Notes:
 *      (1) Displays each pixa on a line (or set of lines),
 *          in order from top to bottom.  Within each pixa,
 *          the pix are displayed in order from left to right.
 *      (2) The size of each pix in each pixa is assumed to be
 *          approximately equal to the size of the first pix in
 *          the pixa.  If this assumption is not correct, this
 *          function will not work properly.
 *      (3) This ignores the boxa of the pixaa.
 */
PIX *
pixaaDisplayByPixa(PIXAA   *pixaa,
                   l_int32  xspace,
                   l_int32  yspace,
                   l_int32  maxw)
{
l_int32  i, j, npixa, npix;
l_int32  width, height, depth, nlines, lwidth;
l_int32  x, y, w, h, w0, h0;
PIX     *pixt, *pixd;
PIXA    *pixa;

    PROCNAME("pixaaDisplayByPixa");

    if (!pixaa)
        return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL);
    
    if ((npixa = pixaaGetCount(pixaa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* Get size of output pix.  The width is the minimum of the
         * maxw and the largest pixa line width.  The height is whatever
         * it needs to be to accommodate all pixa. */
    height = 2 * yspace;
    width = 0;
    for (i = 0; i < npixa; i++) {
        pixa = pixaaGetPixa(pixaa, i, L_CLONE);
        npix = pixaGetCount(pixa);
        pixt = pixaGetPix(pixa, 0, L_CLONE);
        if (i == 0)
            depth = pixGetDepth(pixt);
        w = pixGetWidth(pixt);
        lwidth = npix * (w + xspace);
        nlines = (lwidth + maxw - 1) / maxw;
        if (nlines > 1)
            width = maxw;
        else
            width = L_MAX(lwidth, width);
        height += nlines * (pixGetHeight(pixt) + yspace);
        pixDestroy(&pixt);
        pixaDestroy(&pixa);
    }

    if ((pixd = pixCreate(width, height, depth)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);

        /* Now layout the pix by pixa */
    y = yspace;
    for (i = 0; i < npixa; i++) {
        x = 0;
        pixa = pixaaGetPixa(pixaa, i, L_CLONE);
        npix = pixaGetCount(pixa);
        for (j = 0; j < npix; j++) {
            pixt = pixaGetPix(pixa, j, L_CLONE);
            if (j == 0) {
                w0 = pixGetWidth(pixt);
                h0 = pixGetHeight(pixt);
            }
            w = pixGetWidth(pixt);
            if (width == maxw && x + w >= maxw) {
                x = 0;
                y += h0 + yspace;
            }
            h = pixGetHeight(pixt);
            pixRasterop(pixd, x, y, w, h, PIX_PAINT, pixt, 0, 0);
            pixDestroy(&pixt);
            x += w0 + xspace;
        }
        y += h0 + yspace;
        pixaDestroy(&pixa);
    }

    return pixd;
}
Beispiel #9
0
/*!
 *  pixThinGeneral()
 *
 *      Input:  pixs (1 bpp)
 *              type (L_THIN_FG, L_THIN_BG)
 *              sela (of Sels for parallel composite HMTs)
 *              maxiters (max number of iters allowed; use 0 to iterate
 *                        until completion)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) See notes in pixThin().  That function chooses among
 *          the best of the Sels for thinning.
 *      (2) This is a general function that takes a Sela of HMTs
 *          that are used in parallel for thinning from each
 *          of four directions.  One iteration consists of four
 *          such parallel thins.
 */
PIX *
pixThinGeneral(PIX     *pixs,
               l_int32  type,
               SELA    *sela,
               l_int32  maxiters)
{
l_int32  i, j, r, nsels, same;
PIXA    *pixahmt;
PIX    **pixhmt;  /* array owned by pixahmt; do not destroy! */
PIX     *pixd, *pixt;
SEL     *sel, *selr;

    PROCNAME("pixThinGeneral");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (type != L_THIN_FG && type != L_THIN_BG)
        return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
    if (!sela)
        return (PIX *)ERROR_PTR("sela not defined", procName, NULL);
    if (maxiters == 0) maxiters = 10000;

        /* Set up array of temp pix to hold hmts */
    nsels = selaGetCount(sela);
    pixahmt = pixaCreate(nsels);
    for (i = 0; i < nsels; i++) {
        pixt = pixCreateTemplate(pixs);
        pixaAddPix(pixahmt, pixt, L_INSERT);
    }
    pixhmt = pixaGetPixArray(pixahmt);
    if (!pixhmt)
        return (PIX *)ERROR_PTR("pixhmt array not made", procName, NULL);

#if  DEBUG_SELS
    pixt = selaDisplayInPix(sela, 35, 3, 15, 4);
    pixDisplayWithTitle(pixt, 100, 100, "allsels", 1);
    pixDestroy(&pixt);
#endif  /* DEBUG_SELS */

        /* Set up initial image for fg thinning */
    if (type == L_THIN_FG)
        pixd = pixCopy(NULL, pixs);
    else  /* bg thinning */
        pixd = pixInvert(NULL, pixs);

        /* Thin the fg, with up to maxiters iterations */
    for (i = 0; i < maxiters; i++) {
        pixt = pixCopy(NULL, pixd);  /* test for completion */
        for (r = 0; r < 4; r++) {  /* over 90 degree rotations of Sels */
            for (j = 0; j < nsels; j++) {  /* over individual sels in sela */
                sel = selaGetSel(sela, j);  /* not a copy */
                selr = selRotateOrth(sel, r);
                pixHMT(pixhmt[j], pixd, selr);
                selDestroy(&selr);
                if (j > 0)
                    pixOr(pixhmt[0], pixhmt[0], pixhmt[j]);  /* accum result */
            }
            pixSubtract(pixd, pixd, pixhmt[0]);  /* remove result */
        }
        pixEqual(pixd, pixt, &same);
        pixDestroy(&pixt);
        if (same) {
            L_INFO("%d iterations to completion\n", procName, i);
            break;
        }
    }

    if (type == L_THIN_BG)
        pixInvert(pixd, pixd);

    pixaDestroy(&pixahmt);
    return pixd;
}
Beispiel #10
0
/*!
 *  pixaDisplayTiledAndScaled()
 *
 *      Input:  pixa
 *              outdepth (output depth: 1, 8 or 32 bpp)
 *              tilewidth (each pix is scaled to this width)
 *              ncols (number of tiles in each row)
 *              background (0 for white, 1 for black; this is the color
 *                 of the spacing between the images)
 *              spacing  (between images, and on outside)
 *              border (width of additional black border on each image;
 *                      use 0 for no border)
 *      Return: pix of tiled images, or null on error
 *
 *  Notes:
 *      (1) This can be used to tile a number of renderings of
 *          an image that are at different scales and depths.
 *      (2) Each image, after scaling and optionally adding the
 *          black border, has width 'tilewidth'.  Thus, the border does
 *          not affect the spacing between the image tiles.  The
 *          maximum allowed border width is tilewidth / 5.
 */
PIX *
pixaDisplayTiledAndScaled(PIXA    *pixa,
                          l_int32  outdepth,
                          l_int32  tilewidth,
                          l_int32  ncols,
                          l_int32  background,
                          l_int32  spacing,
                          l_int32  border)
{
l_int32    x, y, w, h, wd, hd, d;
l_int32    i, n, nrows, maxht, ninrow, irow, bordval;
l_int32   *rowht;
l_float32  scalefact;
PIX       *pix, *pixn, *pixt, *pixb, *pixd;
PIXA      *pixan;

    PROCNAME("pixaDisplayTiledAndScaled");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    if (outdepth != 1 && outdepth != 8 && outdepth != 32)
        return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
    if (border < 0 || border > tilewidth / 5)
        border = 0;
    
    if ((n = pixaGetCount(pixa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* Normalize scale and depth for each pix; optionally add border */
    pixan = pixaCreate(n);
    bordval = (outdepth == 1) ? 1 : 0;
    for (i = 0; i < n; i++) {
        if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL)
            continue;

        pixGetDimensions(pix, &w, &h, &d);
        scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w;
        if (d == 1 && outdepth > 1 && scalefact < 1.0)
            pixt = pixScaleToGray(pix, scalefact);
        else
            pixt = pixScale(pix, scalefact, scalefact);

        if (outdepth == 1)
            pixn = pixConvertTo1(pixt, 128);
        else if (outdepth == 8)
            pixn = pixConvertTo8(pixt, FALSE);
        else  /* outdepth == 32 */
            pixn = pixConvertTo32(pixt);
        pixDestroy(&pixt);

        if (border)
            pixb = pixAddBorder(pixn, border, bordval);
        else
            pixb = pixClone(pixn);

        pixaAddPix(pixan, pixb, L_INSERT);
        pixDestroy(&pix);
        pixDestroy(&pixn);
    }
    if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */
        pixaDestroy(&pixan);
        return (PIX *)ERROR_PTR("no components", procName, NULL);
    }

        /* Determine the size of each row and of pixd */
    wd = tilewidth * ncols + spacing * (ncols + 1);
    nrows = (n + ncols - 1) / ncols;
    if ((rowht = (l_int32 *)CALLOC(nrows, sizeof(l_int32))) == NULL)
        return (PIX *)ERROR_PTR("rowht array not made", procName, NULL);
    maxht = 0;
    ninrow = 0;
    irow = 0;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixan, i, L_CLONE);
        ninrow++;
        pixGetDimensions(pix, &w, &h, NULL);
        maxht = L_MAX(h, maxht);
        if (ninrow == ncols) {
            rowht[irow] = maxht;
            maxht = ninrow = 0;  /* reset */
            irow++;
        }
        pixDestroy(&pix);
    }
    if (ninrow > 0) {   /* last fencepost */
        rowht[irow] = maxht;
        irow++;  /* total number of rows */
    }
    nrows = irow;
    hd = spacing * (nrows + 1);
    for (i = 0; i < nrows; i++)
        hd += rowht[i];

    pixd = pixCreate(wd, hd, outdepth);
    if ((background == 1 && outdepth == 1) ||
        (background == 0 && outdepth != 1))
        pixSetAll(pixd);

        /* Now blit images to pixd */
    x = y = spacing;
    irow = 0;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixan, i, L_CLONE);
        pixGetDimensions(pix, &w, &h, NULL);
        if (i && ((i % ncols) == 0)) {  /* start new row */
            x = spacing;
            y += spacing + rowht[irow];
            irow++;
        }
        pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0);
        x += tilewidth + spacing;
        pixDestroy(&pix);
    }

    pixaDestroy(&pixan);
    FREE(rowht);
    return pixd;
}
Beispiel #11
0
/*!
 *  pixaaDisplay()
 *
 *      Input:  pixaa
 *              w, h (if set to 0, determines the size from the
 *                    b.b. of the components in pixaa)
 *      Return: pix, or null on error
 *
 *  Notes:
 *      (1) Each pix of the pixaa is displayed at the location given by
 *          its box, translated by the box of the containing pixa
 *          if it exists.
 */
PIX *
pixaaDisplay(PIXAA   *pixaa,
             l_int32  w,
             l_int32  h)
{
l_int32  i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb;
BOXA    *boxa1;  /* top-level boxa */
BOXA    *boxa;
PIX     *pixt, *pixd;
PIXA    *pixa;

    PROCNAME("pixaaDisplay");

    if (!pixaa)
        return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL);
    
    n = pixaaGetCount(pixaa);
    if (n == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* If w and h not input, determine the minimum size required
         * to contain the origin and all c.c. */
    boxa1 = pixaaGetBoxa(pixaa, L_CLONE);
    nbox = boxaGetCount(boxa1);
    if (w == 0 || h == 0) {
        if (nbox == n)
            boxaGetExtent(boxa1, &w, &h, NULL);
        else {  /* have to use the lower-level boxa for each pixa */
            wmax = hmax = 0;
            for (i = 0; i < n; i++) {
                pixa = pixaaGetPixa(pixaa, i, L_CLONE);
                boxa = pixaGetBoxa(pixa, L_CLONE);
                boxaGetExtent(boxa, &w, &h, NULL);
                wmax = L_MAX(wmax, w);
                hmax = L_MAX(hmax, h);
                pixaDestroy(&pixa);
                boxaDestroy(&boxa);
            }
            w = wmax;
            h = hmax;
        }
    }

        /* Get depth from first pix */
    pixa = pixaaGetPixa(pixaa, 0, L_CLONE);
    pixt = pixaGetPix(pixa, 0, L_CLONE);
    d = pixGetDepth(pixt);
    pixaDestroy(&pixa);
    pixDestroy(&pixt);

    if ((pixd = pixCreate(w, h, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    
    x = y = 0;
    for (i = 0; i < n; i++) {
        pixa = pixaaGetPixa(pixaa, i, L_CLONE);
        if (nbox == n)
            boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL);
        na = pixaGetCount(pixa);
        for (j = 0; j < na; j++) {
            pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb);
            pixt = pixaGetPix(pixa, j, L_CLONE);
            pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pixt, 0, 0);
            pixDestroy(&pixt);
        }
        pixaDestroy(&pixa);
    }
    boxaDestroy(&boxa1);

    return pixd;
}
Beispiel #12
0
/*!
 *  pixaDisplayTiledInRows()
 *
 *      Input:  pixa
 *              outdepth (output depth: 1, 8 or 32 bpp)
 *              maxwidth (of output image)
 *              scalefactor (applied to every pix; use 1.0 for no scaling)
 *              background (0 for white, 1 for black; this is the color
 *                 of the spacing between the images)
 *              spacing  (between images, and on outside)
 *              border (width of black border added to each image;
 *                      use 0 for no border)
 *      Return: pixd (of tiled images), or null on error
 *
 *  Notes:
 *      (1) This saves a pixa to a single image file of width not to
 *          exceed maxwidth, with background color either white or black,
 *          and with each row tiled such that the top of each pix is
 *          aligned and separated by 'spacing' from the next one.
 *          A black border can be added to each pix.
 *      (2) All pix are converted to outdepth; existing colormaps are removed.
 *      (3) This does a reasonably spacewise-efficient job of laying
 *          out the individual pix images into a tiled composite.
 */
PIX *
pixaDisplayTiledInRows(PIXA      *pixa,
                       l_int32    outdepth,
                       l_int32    maxwidth,
                       l_float32  scalefactor,
                       l_int32    background,
                       l_int32    spacing,
                       l_int32    border)
{
l_int32  h;  /* cumulative height over all the rows */
l_int32  w;  /* cumulative height in the current row */
l_int32  bordval, wtry, wt, ht;
l_int32  irow;  /* index of current pix in current row */
l_int32  wmaxrow;  /* width of the largest row */
l_int32  maxh;  /* max height in row */
l_int32  i, j, index, n, x, y, nrows, ninrow;
NUMA    *nainrow;  /* number of pix in the row */
NUMA    *namaxh;  /* height of max pix in the row */
PIX     *pix, *pixn, *pixt, *pixd;
PIXA    *pixan;

    PROCNAME("pixaDisplayTiledInRows");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    if (outdepth != 1 && outdepth != 8 && outdepth != 32)
        return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
    if (border < 0)
        border = 0;
    if (scalefactor <= 0.0) scalefactor = 1.0;
    
    if ((n = pixaGetCount(pixa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* Normalize depths, scale, remove colormaps; optionally add border */
    pixan = pixaCreate(n);
    bordval = (outdepth == 1) ? 1 : 0;
    for (i = 0; i < n; i++) {
        if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL)
            continue;

        if (outdepth == 1)
            pixn = pixConvertTo1(pix, 128);
        else if (outdepth == 8)
            pixn = pixConvertTo8(pix, FALSE);
        else  /* outdepth == 32 */
            pixn = pixConvertTo32(pix);
        pixDestroy(&pix);

        if (scalefactor != 1.0)
            pixt = pixScale(pixn, scalefactor, scalefactor);
        else
            pixt = pixClone(pixn);
        if (border)
            pixd = pixAddBorder(pixt, border, bordval);
        else
            pixd = pixClone(pixt);
        pixDestroy(&pixn);
        pixDestroy(&pixt);

        pixaAddPix(pixan, pixd, L_INSERT);
    }
    if (pixaGetCount(pixan) != n) {
        n = pixaGetCount(pixan);
        L_WARNING_INT("only got %d components", procName, n);
        if (n == 0) {
            pixaDestroy(&pixan);
            return (PIX *)ERROR_PTR("no components", procName, NULL);
        }
    }

        /* Compute parameters for layout */
    nainrow = numaCreate(0);
    namaxh = numaCreate(0);
    wmaxrow = 0;
    w = h = spacing;
    maxh = 0;  /* max height in row */
    for (i = 0, irow = 0; i < n; i++, irow++) {
        pixaGetPixDimensions(pixan, i, &wt, &ht, NULL);
        wtry = w + wt + spacing;
        if (wtry > maxwidth) {  /* end the current row and start next one */
            numaAddNumber(nainrow, irow); 
            numaAddNumber(namaxh, maxh); 
            wmaxrow = L_MAX(wmaxrow, w);
            h += maxh + spacing;
            irow = 0;
            w = wt + 2 * spacing;
            maxh = ht;
        } else {
            w = wtry;
            maxh = L_MAX(maxh, ht);
        }
    }

        /* Enter the parameters for the last row */
    numaAddNumber(nainrow, irow); 
    numaAddNumber(namaxh, maxh); 
    wmaxrow = L_MAX(wmaxrow, w);
    h += maxh + spacing;
            
    if ((pixd = pixCreate(wmaxrow, h, outdepth)) == NULL) {
        numaDestroy(&nainrow);
        numaDestroy(&namaxh);
        pixaDestroy(&pixan);
	return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }

        /* Reset the background color if necessary */
    if ((background == 1 && outdepth == 1) ||
        (background == 0 && outdepth != 1))
        pixSetAll(pixd);

        /* Blit the images to the dest */
    nrows = numaGetCount(nainrow);
    y = spacing;
    for (i = 0, index = 0; i < nrows; i++) {  /* over rows */
        numaGetIValue(nainrow, i, &ninrow);
        numaGetIValue(namaxh, i, &maxh);
        x = spacing;
        for (j = 0; j < ninrow; j++, index++) {   /* over pix in row */
            pix = pixaGetPix(pixan, index, L_CLONE);
            pixGetDimensions(pix, &wt, &ht, NULL);
            pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0);
            pixDestroy(&pix);
            x += wt + spacing;
        }
        y += maxh + spacing;
    }

    numaDestroy(&nainrow);
    numaDestroy(&namaxh);
    pixaDestroy(&pixan);
    return pixd;
}
Beispiel #13
0
/*!
 *  pixaDisplayTiled()
 *
 *      Input:  pixa
 *              maxwidth (of output image)
 *              background (0 for white, 1 for black)
 *              spacing
 *      Return: pix of tiled images, or null on error
 *
 *  Notes:
 *      (1) This saves a pixa to a single image file of width not to
 *          exceed maxwidth, with background color either white or black,
 *          and with each subimage spaced on a regular lattice.
 *      (2) The lattice size is determined from the largest width and height,
 *          separately, of all pix in the pixa.
 *      (3) All pix in the pixa must be of equal depth.
 *      (4) If any pix has a colormap, all pix are rendered in rgb.
 *      (5) Careful: because no components are omitted, this is
 *          dangerous if there are thousands of small components and
 *          one or more very large one, because the size of the
 *          resulting pix can be huge!
 */
PIX *
pixaDisplayTiled(PIXA    *pixa,
                 l_int32  maxwidth,
                 l_int32  background,
                 l_int32  spacing)
{
l_int32  w, h, wmax, hmax, wd, hd, d, hascmap;
l_int32  i, j, n, ni, ncols, nrows;
l_int32  ystart, xstart, wt, ht;
PIX     *pix, *pixt, *pixd;
PIXA    *pixat;

    PROCNAME("pixaDisplayTiled");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);

        /* If any pix have colormaps, generate rgb */
    if ((n = pixaGetCount(pixa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);
    pixaAnyColormaps(pixa, &hascmap);
    if (hascmap) {
        pixat = pixaCreate(n);
        for (i = 0; i < n; i++) {
            pixt = pixaGetPix(pixa, i, L_CLONE);
            pix = pixConvertTo32(pixt);
            pixaAddPix(pixat, pix, L_INSERT);
            pixDestroy(&pixt);
        }
    }
    else
        pixat = pixaCopy(pixa, L_CLONE);

        /* Find the largest width and height of the subimages */
    wmax = hmax = 0;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixat, i, L_CLONE);
        pixGetDimensions(pix, &w, &h, NULL);
        if (i == 0)
            d = pixGetDepth(pix);
        else if (d != pixGetDepth(pix)) {
            pixDestroy(&pix);
            pixaDestroy(&pixat);
            return (PIX *)ERROR_PTR("depths not equal", procName, NULL);
        }
        if (w > wmax)
            wmax = w;
        if (h > hmax)
            hmax = h;
        pixDestroy(&pix);
    }

        /* Get the number of rows and columns and the output image size */
    spacing = L_MAX(spacing, 0);
    ncols = (l_int32)((l_float32)(maxwidth - spacing) /
                      (l_float32)(wmax + spacing));
    nrows = (n + ncols - 1) / ncols;
    wd = wmax * ncols + spacing * (ncols + 1);
    hd = hmax * nrows + spacing * (nrows + 1);
    if ((pixd = pixCreate(wd, hd, d)) == NULL) {
        pixaDestroy(&pixat);
	return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }

#if 0
    fprintf(stderr, " nrows = %d, ncols = %d, wmax = %d, hmax = %d\n",
            nrows, ncols, wmax, hmax);
    fprintf(stderr, " space = %d, wd = %d, hd = %d, n = %d\n",
            space, wd, hd, n);
#endif

        /* Reset the background color if necessary */
    if ((background == 1 && d == 1) || (background == 0 && d != 1))
        pixSetAll(pixd);

        /* Blit the images to the dest */
    for (i = 0, ni = 0; i < nrows; i++) {
        ystart = spacing + i * (hmax + spacing);
        for (j = 0; j < ncols && ni < n; j++, ni++) {
            xstart = spacing + j * (wmax + spacing);
            pix = pixaGetPix(pixat, ni, L_CLONE);
            wt = pixGetWidth(pix);
            ht = pixGetHeight(pix);
            pixRasterop(pixd, xstart, ystart, wt, ht, PIX_SRC, pix, 0, 0);
            pixDestroy(&pix);
        }
    }

    pixaDestroy(&pixat);
    return pixd;
}
Beispiel #14
0
/*!
 *  pixaDisplayOnLattice()
 *
 *      Input:  pixa
 *              xspace
 *              yspace
 *      Return: pix of composite images, or null on error
 *
 *  Notes:
 *      (1) This places each pix on sequentially on a regular lattice
 *          in the rendered composite.  If a pix is too large to fit in the
 *          allocated lattice space, it is not rendered.
 *      (2) If any pix has a colormap, all pix are rendered in rgb.
 *      (3) This is useful when putting bitmaps of components,
 *          such as characters, into a single image.
 */
PIX *
pixaDisplayOnLattice(PIXA    *pixa,
                     l_int32  xspace,
                     l_int32  yspace)
{
l_int32  n, nw, nh, w, h, d, wt, ht;
l_int32  index, i, j, hascmap;
PIX     *pix, *pixt, *pixd;
PIXA    *pixat;

    PROCNAME("pixaDisplayOnLattice");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    
        /* If any pix have colormaps, generate rgb */
    if ((n = pixaGetCount(pixa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);
    pixaAnyColormaps(pixa, &hascmap);
    if (hascmap) {
        pixat = pixaCreate(n);
        for (i = 0; i < n; i++) {
            pixt = pixaGetPix(pixa, i, L_CLONE);
            pix = pixConvertTo32(pixt);
            pixaAddPix(pixat, pix, L_INSERT);
            pixDestroy(&pixt);
        }
    }
    else
        pixat = pixaCopy(pixa, L_CLONE);

    nw = (l_int32)sqrt((l_float64)n);
    nh = (n + nw - 1) / nw;
    w = xspace * nw;
    h = yspace * nh;

        /* Use the first pix in pixa to determine the depth.  */
    pixaGetPixDimensions(pixat, 0, NULL, NULL, &d);

    if ((pixd = pixCreate(w, h, d)) == NULL) {
        pixaDestroy(&pixat);
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }
    
    index = 0;
    for (i = 0; i < nh; i++) {
        for (j = 0; j < nw && index < n; j++, index++) {
            pixt = pixaGetPix(pixat, index, L_CLONE);
            pixGetDimensions(pixt, &wt, &ht, NULL);
            if (wt > xspace || ht > yspace) {
                fprintf(stderr, "pix(%d) omitted; size %dx%d\n", index, wt, ht);
                pixDestroy(&pixt);
                continue;
            }
            pixRasterop(pixd, j * xspace, i * yspace, wt, ht, 
                        PIX_PAINT, pixt, 0, 0);
            pixDestroy(&pixt);
        }
    }

    pixaDestroy(&pixat);
    return pixd;
}
Beispiel #15
0
/*!
 *  selaAddHitMiss()
 *
 *      Input:  sela  (<optional>)
 *      Return: sela with additional sels, or null on error
 */
SELA *
selaAddHitMiss(SELA  *sela)
{
SEL  *sel;

    PROCNAME("selaAddHitMiss");

    if (!sela) {
        if ((sela = selaCreate(0)) == NULL)
            return (SELA *)ERROR_PTR("sela not made", procName, NULL);
    }

#if 0   /*  use just for testing */
    sel = selCreateBrick(3, 3, 1, 1, 2);
    selaAddSel(sela, sel, "sel_bad", 0);
#endif


    /*--------------------------------------------------------------*
     *                   Isolated foreground pixel                  *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(3, 3, 1, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_HIT);
    selaAddSel(sela, sel, "sel_3hm", 0);

    /*--------------------------------------------------------------*
     *                Horizontal and vertical edges                 *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(2, 3, 0, 1, SEL_HIT);
    selSetElement(sel, 1, 0, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_MISS);
    selSetElement(sel, 1, 2, SEL_MISS);
    selaAddSel(sela, sel, "sel_3de", 0);

    sel = selCreateBrick(2, 3, 1, 1, SEL_HIT);
    selSetElement(sel, 0, 0, SEL_MISS);
    selSetElement(sel, 0, 1, SEL_MISS);
    selSetElement(sel, 0, 2, SEL_MISS);
    selaAddSel(sela, sel, "sel_3ue", 0);

    sel = selCreateBrick(3, 2, 1, 0, SEL_HIT);
    selSetElement(sel, 0, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_MISS);
    selSetElement(sel, 2, 1, SEL_MISS);
    selaAddSel(sela, sel, "sel_3re", 0);

    sel = selCreateBrick(3, 2, 1, 1, SEL_HIT);
    selSetElement(sel, 0, 0, SEL_MISS);
    selSetElement(sel, 1, 0, SEL_MISS);
    selSetElement(sel, 2, 0, SEL_MISS);
    selaAddSel(sela, sel, "sel_3le", 0);

    /*--------------------------------------------------------------*
     *                        Slanted edge                          *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(13, 6, 6, 2, SEL_DONT_CARE);
    selSetElement(sel, 0, 3, SEL_MISS);
    selSetElement(sel, 0, 5, SEL_HIT);
    selSetElement(sel, 4, 2, SEL_MISS);
    selSetElement(sel, 4, 4, SEL_HIT);
    selSetElement(sel, 8, 1, SEL_MISS);
    selSetElement(sel, 8, 3, SEL_HIT);
    selSetElement(sel, 12, 0, SEL_MISS);
    selSetElement(sel, 12, 2, SEL_HIT);
    selaAddSel(sela, sel, "sel_sl1", 0);

    /*--------------------------------------------------------------*
     *                           Corners                            *
     *  This allows for up to 3 missing edge pixels at the corner   *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(4, 4, 1, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
    selSetElement(sel, 1, 3, SEL_HIT);
    selSetElement(sel, 2, 2, SEL_HIT);
    selSetElement(sel, 2, 3, SEL_HIT);
    selSetElement(sel, 3, 1, SEL_HIT);
    selSetElement(sel, 3, 2, SEL_HIT);
    selSetElement(sel, 3, 3, SEL_HIT);
    selaAddSel(sela, sel, "sel_ulc", 0);

    sel = selCreateBrick(4, 4, 1, 2, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
    selSetElement(sel, 1, 0, SEL_HIT);
    selSetElement(sel, 2, 0, SEL_HIT);
    selSetElement(sel, 2, 1, SEL_HIT);
    selSetElement(sel, 3, 0, SEL_HIT);
    selSetElement(sel, 3, 1, SEL_HIT);
    selSetElement(sel, 3, 2, SEL_HIT);
    selaAddSel(sela, sel, "sel_urc", 0);

    sel = selCreateBrick(4, 4, 2, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
    selSetElement(sel, 0, 1, SEL_HIT);
    selSetElement(sel, 0, 2, SEL_HIT);
    selSetElement(sel, 0, 3, SEL_HIT);
    selSetElement(sel, 1, 2, SEL_HIT);
    selSetElement(sel, 1, 3, SEL_HIT);
    selSetElement(sel, 2, 3, SEL_HIT);
    selaAddSel(sela, sel, "sel_llc", 0);

    sel = selCreateBrick(4, 4, 2, 2, SEL_MISS);
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
    selSetElement(sel, 0, 0, SEL_HIT);
    selSetElement(sel, 0, 1, SEL_HIT);
    selSetElement(sel, 0, 2, SEL_HIT);
    selSetElement(sel, 1, 0, SEL_HIT);
    selSetElement(sel, 1, 1, SEL_HIT);
    selSetElement(sel, 2, 0, SEL_HIT);
    selaAddSel(sela, sel, "sel_lrc", 0);

    return sela;
}
Beispiel #16
0
/*!
 *  pixThinExamples()
 *
 *      Input:  pixs (1 bpp)
 *              type (L_THIN_FG, L_THIN_BG)
 *              index (into specific examples; valid 1-9; see notes)
 *              maxiters (max number of iters allowed; use 0 to iterate
 *                        until completion)
 *              selfile (<optional> filename for output sel display)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) See notes in pixThin().  The examples are taken from
 *          the paper referenced there.
 *      (2) Here we allow specific sets of HMTs to be used in
 *          parallel for thinning from each of four directions.
 *          One iteration consists of four such parallel thins.
 *      (3) The examples are indexed as follows:
 *          Thinning  (e.g., run to completion):
 *              index = 1     sel_4_1, sel_4_5, sel_4_6
 *              index = 2     sel_4_1, sel_4_7, sel_4_7_rot
 *              index = 3     sel_48_1, sel_48_1_rot, sel_48_2
 *              index = 4     sel_8_2, sel_8_3, sel_48_2
 *              index = 5     sel_8_1, sel_8_5, sel_8_6
 *              index = 6     sel_8_2, sel_8_3, sel_8_8, sel_8_9
 *              index = 7     sel_8_5, sel_8_6, sel_8_7, sel_8_7_rot
 *          Thickening:
 *              index = 8     sel_4_2, sel_4_3 (e.g,, do just a few iterations)
 *              index = 9     sel_8_4 (e.g., do just a few iterations)
 */
PIX *
pixThinExamples(PIX         *pixs,
                l_int32      type,
                l_int32      index,
                l_int32      maxiters,
                const char  *selfile)
{
PIX   *pixd, *pixt;
SEL   *sel;
SELA  *sela;

    PROCNAME("pixThinExamples");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (type != L_THIN_FG && type != L_THIN_BG)
        return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
    if (index < 1 || index > 9)
        return (PIX *)ERROR_PTR("invalid index", procName, NULL);
    if (maxiters == 0) maxiters = 10000;

    switch(index)
    {
    case 1:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 2:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7");
        selaAddSel(sela, sel, NULL, 0);
	sel = selRotateOrth(sel, 1);
        selaAddSel(sela, sel, "sel_4_7_rot", 0);
        break;
    case 3:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1");
        selaAddSel(sela, sel, NULL, 0);
	sel = selRotateOrth(sel, 1);
        selaAddSel(sela, sel, "sel_48_1_rot", 0);
        sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 4:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 5:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 6:
        sela = selaCreate(4);
        sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 7:
        sela = selaCreate(4);
        sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7");
        selaAddSel(sela, sel, NULL, 0);
        sel = selRotateOrth(sel, 1);
        selaAddSel(sela, sel, "sel_8_7_rot", 0);
        break;
    case 8:  /* thicken for this one; just a few iterations */
        sela = selaCreate(2);
        sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
        selaAddSel(sela, sel, NULL, 0);
        pixt = pixThinGeneral(pixs, type, sela, maxiters);
        pixd = pixRemoveBorderConnComps(pixt, 4);
        pixDestroy(&pixt);
        break;
    case 9:  /* thicken for this one; just a few iterations */
        sela = selaCreate(1);
        sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4");
        selaAddSel(sela, sel, NULL, 0);
        pixt = pixThinGeneral(pixs, type, sela, maxiters);
        pixd = pixRemoveBorderConnComps(pixt, 4);
        pixDestroy(&pixt);
        break;
    default:
        return (PIX *)ERROR_PTR("invalid index", procName, NULL);
    }

    if (index <= 7)
        pixd = pixThinGeneral(pixs, type, sela, maxiters);

        /* Optionally display the sels */
    if (selfile) {
        pixt = selaDisplayInPix(sela, 35, 3, 15, 4);
        pixWrite(selfile, pixt, IFF_PNG);
        pixDestroy(&pixt);
    }

    selaDestroy(&sela);
    return pixd;
}
Beispiel #17
0
/*!
 *  selaAddTJunctions()
 *
 *      Input:  sela (<optional>)
 *              hlsize (length of each line of hits from origin)
 *              mdist (distance of misses from the origin)
 *              norient (number of orientations; max of 8)
 *              debugflag (1 for debug output)
 *      Return: sela with additional sels, or null on error
 *
 *  Notes:
 *      (1) Adds hitmiss Sels for the T-junction of two lines.
 *          If the lines are very thin, they must be nearly orthogonal
 *          to register.
 *      (2) The number of Sels generated is 4 * @norient.
 *      (3) It is suggested that @hlsize be chosen at least 1 greater
 *          than @mdist.  Try values of (@hlsize, @mdist) such as
 *          (6,5), (7,6), (8,7), (9,7), etc.
 */
SELA *
selaAddTJunctions(SELA      *sela,
                  l_float32  hlsize,
                  l_float32  mdist,
                  l_int32    norient,
                  l_int32    debugflag)
{
char       name[L_BUF_SIZE];
l_int32    i, j, k, w, xc, yc;
l_float64  pi, halfpi, radincr, jang, radang;
l_float64  angle[3], dist[3];
PIX       *pixc, *pixm, *pixt;
PIXA      *pixa;
PTA       *pta1, *pta2, *pta3;
SEL       *sel;

    PROCNAME("selaAddTJunctions");

    if (hlsize <= 2)
        return (SELA *)ERROR_PTR("hlsizel not > 1", procName, NULL);
    if (norient < 1 || norient > 8)
        return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL);

    if (!sela) {
        if ((sela = selaCreate(0)) == NULL)
            return (SELA *)ERROR_PTR("sela not made", procName, NULL);
    }

    pi = 3.1415926535;
    halfpi = 3.1415926535 / 2.0;
    radincr = halfpi / (l_float32)norient;
    w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5));
    if (w % 2 == 0)
        w++;
    xc = w / 2;
    yc = w / 2;

    pixa = pixaCreate(4 * norient);
    for (i = 0; i < norient; i++) {
        for (j = 0; j < 4; j++) {  /* 4 orthogonal orientations */
            jang = (l_float32)j * halfpi;

                /* Set the don't cares */
            pixc = pixCreate(w, w, 32);
            pixSetAll(pixc);

                /* Add the green lines of hits */
            pixm = pixCreate(w, w, 1);
            radang = (l_float32)i * radincr;
            pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang);
            pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1,
                                         jang + radang + halfpi);
            pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1,
                                         jang + radang + pi);
            ptaJoin(pta1, pta2, 0, -1);
            ptaJoin(pta1, pta3, 0, -1);
            pixRenderPta(pixm, pta1, L_SET_PIXELS);
            pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
            ptaDestroy(&pta1);
            ptaDestroy(&pta2);
            ptaDestroy(&pta3);

                /* Add red misses between the lines */
            angle[0] = radang + jang - halfpi;
            angle[1] = radang + jang + 0.5 * halfpi;
            angle[2] = radang + jang + 1.5 * halfpi;
            dist[0] = 0.8 * mdist;
            dist[1] = dist[2] = mdist;
            for (k = 0; k < 3; k++) {
                pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])),
                            yc + (l_int32)(dist[k] * sin(angle[k])),
                            0xff000000);
            }

                /* Add dark green for origin */
            pixSetPixel(pixc, xc, yc, 0x00550000);

                /* Generate the sel */
            sel = selCreateFromColorPix(pixc, NULL);
            sprintf(name, "sel_cross_%d", 4 * i + j);
            selaAddSel(sela, sel, name, 0);

            if (debugflag) {
                pixt = pixScaleBySampling(pixc, 10.0, 10.0);
                pixaAddPix(pixa, pixt, L_INSERT);
            }
            pixDestroy(&pixm);
            pixDestroy(&pixc);
        }
    }

    if (debugflag) {
        l_int32  w;
        pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
        pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2);
        pixWriteTempfile("/tmp", "tsel1.png", pixt, IFF_PNG, 0);
        pixDisplay(pixt, 0, 100);
        pixDestroy(&pixt);
        pixt = selaDisplayInPix(sela, 15, 2, 20, 4);
        pixWriteTempfile("/tmp", "tsel2.png", pixt, IFF_PNG, 0);
        pixDisplay(pixt, 500, 100);
        pixDestroy(&pixt);
        selaWriteStream(stderr, sela);
    }
    pixaDestroy(&pixa);

    return sela;
}
Beispiel #18
0
/*!
 *  jbWordsInTextlines()
 *
 *      Input:  dirin (directory of input pages)
 *              reduction (1 for full res; 2 for half-res)
 *              maxwidth (of word mask components, to be kept)
 *              maxheight (of word mask components, to be kept)
 *              thresh (on correlation; 0.80 is reasonable)
 *              weight (for handling thick text; 0.6 is reasonable)
 *              natl (<return> numa with textline index for each component)
 *              firstpage (0-based)
 *              npages (use 0 for all pages in dirin)
 *      Return: classer (for the set of pages)
 *
 *  Notes:
 *      (1) This is a high-level function.  See prog/jbwords for example
 *          of usage.
 *      (2) Typically, words can be found reasonably well at a resolution
 *          of about 150 ppi.  For highest accuracy, you should use 300 ppi.
 *          Assuming that the input images are 300 ppi, use reduction = 1
 *          for finding words at full res, and reduction = 2 for finding
 *          them at 150 ppi.
 */
JBCLASSER *
jbWordsInTextlines(const char  *dirin,
                   l_int32      reduction,
                   l_int32      maxwidth,
                   l_int32      maxheight,
                   l_float32    thresh,
                   l_float32    weight,
                   NUMA       **pnatl,
                   l_int32      firstpage,
                   l_int32      npages)
{
char       *fname;
l_int32     nfiles, i, w, h;
BOXA       *boxa;
JBCLASSER  *classer;
NUMA       *nai, *natl;
PIX        *pix;
PIXA       *pixa;
SARRAY     *safiles;

    PROCNAME("jbWordsInTextlines");

    if (!pnatl)
        return (JBCLASSER *)ERROR_PTR("&natl not defined", procName, NULL);
    *pnatl = NULL;
    if (!dirin)
        return (JBCLASSER *)ERROR_PTR("dirin not defined", procName, NULL);
    if (reduction != 1 && reduction != 2)
        return (JBCLASSER *)ERROR_PTR("reduction not in {1,2}", procName, NULL);

    safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
    nfiles = sarrayGetCount(safiles);

        /* Classify components */
    classer = jbCorrelationInit(JB_WORDS, maxwidth, maxheight, thresh, weight);
    classer->safiles = sarrayCopy(safiles);
    natl = numaCreate(0);
    *pnatl = natl;
    for (i = 0; i < nfiles; i++) {
        fname = sarrayGetString(safiles, i, 0);
        if ((pix = pixRead(fname)) == NULL) {
            L_WARNING("image file %d not read\n", procName, i);
            continue;
        }
        pixGetDimensions(pix, &w, &h, NULL);
        if (reduction == 1) {
            classer->w = w;
            classer->h = h;
        } else {  /* reduction == 2 */
            classer->w = w / 2;
            classer->h = h / 2;
        }
        pixGetWordsInTextlines(pix, reduction, JB_WORDS_MIN_WIDTH,
                               JB_WORDS_MIN_HEIGHT, maxwidth, maxheight,
                               &boxa, &pixa, &nai);
        jbAddPageComponents(classer, pix, boxa, pixa);
        numaJoin(natl, nai, 0, -1);
        pixDestroy(&pix);
        numaDestroy(&nai);
        boxaDestroy(&boxa);
        pixaDestroy(&pixa);
    }

    sarrayDestroy(&safiles);
    return classer;
}
Beispiel #19
0
/*!
 *  pixReadStreamGif()
 *
 *      Input:  stream
 *      Return: pix, or null on error
 */
PIX *
pixReadStreamGif(FILE  *fp)
{
    l_int32          fd, wpl, i, j, w, h, d, cindex, ncolors;
    l_int32          rval, gval, bval;
    l_uint32        *data, *line;
    GifFileType     *gif;
    PIX             *pixd;
    PIXCMAP         *cmap;
    ColorMapObject  *gif_cmap;
    SavedImage       si;

    PROCNAME("pixReadStreamGif");

    if ((fd = fileno(fp)) < 0)
        return (PIX *)ERROR_PTR("invalid file descriptor", procName, NULL);
#ifndef COMPILER_MSVC
    lseek(fd, 0, SEEK_SET);
#else
    _lseek(fd, 0, SEEK_SET);
#endif  /* COMPILER_MSVC */

    if ((gif = DGifOpenFileHandle(fd)) == NULL)
        return (PIX *)ERROR_PTR("invalid file or file not found",
                                procName, NULL);

    /* Read all the data, but use only the first image found */
    if (DGifSlurp(gif) != GIF_OK) {
        DGifCloseFile(gif);
        return (PIX *)ERROR_PTR("failed to read GIF data", procName, NULL);
    }

    if (gif->SavedImages == NULL) {
        DGifCloseFile(gif);
        return (PIX *)ERROR_PTR("no images found in GIF", procName, NULL);
    }

    si = gif->SavedImages[0];
    w = si.ImageDesc.Width;
    h = si.ImageDesc.Height;
    if (w <= 0 || h <= 0) {
        DGifCloseFile(gif);
        return (PIX *)ERROR_PTR("invalid image dimensions", procName, NULL);
    }

    if (si.RasterBits == NULL) {
        DGifCloseFile(gif);
        return (PIX *)ERROR_PTR("no raster data in GIF", procName, NULL);
    }

    if (si.ImageDesc.ColorMap) {
        /* private cmap for this image */
        gif_cmap = si.ImageDesc.ColorMap;
    }
    else if (gif->SColorMap) {
        /* global cmap for whole picture */
        gif_cmap = gif->SColorMap;
    }
    else {
        /* don't know where to take cmap from */
        DGifCloseFile(gif);
        return (PIX *)ERROR_PTR("color map is missing", procName, NULL);
    }

    ncolors = gif_cmap->ColorCount;
    if (ncolors <= 2)
        d = 1;
    else if (ncolors <= 4)
        d = 2;
    else if (ncolors <= 16)
        d = 4;
    else
        d = 8;
    if ((cmap = pixcmapCreate(d)) == NULL)
        return (PIX *)ERROR_PTR("cmap creation failed", procName, NULL);

    for (cindex = 0; cindex < ncolors; cindex++) {
        rval = gif_cmap->Colors[cindex].Red;
        gval = gif_cmap->Colors[cindex].Green;
        bval = gif_cmap->Colors[cindex].Blue;
        pixcmapAddColor(cmap, rval, gval, bval);
    }

    if ((pixd = pixCreate(w, h, d)) == NULL) {
        DGifCloseFile(gif);
        pixcmapDestroy(&cmap);
        return (PIX *)ERROR_PTR("failed to allocate pixd", procName, NULL);
    }
    pixSetColormap(pixd, cmap);

    wpl = pixGetWpl(pixd);
    data = pixGetData(pixd);
    for (i = 0; i < h; i++) {
        line = data + i * wpl;
        if (d == 1) {
            for (j = 0; j < w; j++) {
                if (si.RasterBits[i * w + j])
                    SET_DATA_BIT(line, j);
            }
        }
        else if (d == 2) {
            for (j = 0; j < w; j++)
                SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]);
        }
        else if (d == 4) {
            for (j = 0; j < w; j++)
                SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]);
        }
        else {  /* d == 8 */
            for (j = 0; j < w; j++)
                SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]);
        }
    }

    DGifCloseFile(gif);
    return pixd;
}
PIX * pixReadMemJpeg(const l_uint8 *cdata, size_t size, l_int32 cmflag,
                     l_int32 reduction, l_int32 *pnwarn, l_int32 hint)
{
    return (PIX * )ERROR_PTR("function not present", "pixReadMemJpeg", NULL);
}
Beispiel #21
0
/*!
 * \brief   gifToPix()
 *
 * \param[in]  gif   opened gif stream
 * \return  pix, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) This decodes the pix from the compressed gif stream and
 *          closes the stream.
 *      (2) It is static so that the stream is not exposed to clients.
 * </pre>
 */
static PIX *
gifToPix(GifFileType  *gif)
{
l_int32          wpl, i, j, w, h, d, cindex, ncolors;
l_int32          rval, gval, bval;
l_uint32        *data, *line;
PIX             *pixd;
PIXCMAP         *cmap;
ColorMapObject  *gif_cmap;
SavedImage       si;
int              giferr;

    PROCNAME("gifToPix");

        /* Read all the data, but use only the first image found */
    if (DGifSlurp(gif) != GIF_OK) {
        DGifCloseFile(gif, &giferr);
        return (PIX *)ERROR_PTR("failed to read GIF data", procName, NULL);
    }

    if (gif->SavedImages == NULL) {
        DGifCloseFile(gif, &giferr);
        return (PIX *)ERROR_PTR("no images found in GIF", procName, NULL);
    }

    si = gif->SavedImages[0];
    w = si.ImageDesc.Width;
    h = si.ImageDesc.Height;
    if (w <= 0 || h <= 0) {
        DGifCloseFile(gif, &giferr);
        return (PIX *)ERROR_PTR("invalid image dimensions", procName, NULL);
    }

    if (si.RasterBits == NULL) {
        DGifCloseFile(gif, &giferr);
        return (PIX *)ERROR_PTR("no raster data in GIF", procName, NULL);
    }

    if (si.ImageDesc.ColorMap) {
            /* private cmap for this image */
        gif_cmap = si.ImageDesc.ColorMap;
    } else if (gif->SColorMap) {
            /* global cmap for whole picture */
        gif_cmap = gif->SColorMap;
    } else {
            /* don't know where to take cmap from */
        DGifCloseFile(gif, &giferr);
        return (PIX *)ERROR_PTR("color map is missing", procName, NULL);
    }

    ncolors = gif_cmap->ColorCount;
    if (ncolors <= 2)
        d = 1;
    else if (ncolors <= 4)
        d = 2;
    else if (ncolors <= 16)
        d = 4;
    else
        d = 8;
    if ((cmap = pixcmapCreate(d)) == NULL) {
        DGifCloseFile(gif, &giferr);
        return (PIX *)ERROR_PTR("cmap creation failed", procName, NULL);
    }

    for (cindex = 0; cindex < ncolors; cindex++) {
        rval = gif_cmap->Colors[cindex].Red;
        gval = gif_cmap->Colors[cindex].Green;
        bval = gif_cmap->Colors[cindex].Blue;
        pixcmapAddColor(cmap, rval, gval, bval);
    }

    if ((pixd = pixCreate(w, h, d)) == NULL) {
        DGifCloseFile(gif, &giferr);
        pixcmapDestroy(&cmap);
        return (PIX *)ERROR_PTR("failed to allocate pixd", procName, NULL);
    }
    pixSetInputFormat(pixd, IFF_GIF);
    pixSetColormap(pixd, cmap);

    wpl = pixGetWpl(pixd);
    data = pixGetData(pixd);
    for (i = 0; i < h; i++) {
        line = data + i * wpl;
        if (d == 1) {
            for (j = 0; j < w; j++) {
                if (si.RasterBits[i * w + j])
                    SET_DATA_BIT(line, j);
            }
        } else if (d == 2) {
            for (j = 0; j < w; j++)
                SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]);
        } else if (d == 4) {
            for (j = 0; j < w; j++)
                SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]);
        } else {  /* d == 8 */
            for (j = 0; j < w; j++)
                SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]);
        }
    }

    /* Versions before 5.0 required un-interlacing to restore
     * the raster lines to normal order if the image
     * had been interlaced (for viewing in a browser):
         if (gif->Image.Interlace) {
             PIX *pixdi = pixUninterlaceGIF(pixd);
             pixTransferAllData(pixd, &pixdi, 0, 0);
         }
     * This is no longer required. */

    DGifCloseFile(gif, &giferr);
    return pixd;
}
PIX * pixReadJpeg(const char *filename, l_int32 cmflag, l_int32 reduction,
                  l_int32 *pnwarn, l_int32 hint)
{
    return (PIX * )ERROR_PTR("function not present", "pixReadJpeg", NULL);
}
Beispiel #23
0
/*!
 * \brief   boxaMakeSizeIndicator()
 *
 * \param[in]    boxa
 * \param[in]    width, height   threshold dimensions
 * \param[in]    type            L_SELECT_WIDTH, L_SELECT_HEIGHT,
 *                               L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
 * \param[in]    relation        L_SELECT_IF_LT, L_SELECT_IF_GT,
 *                               L_SELECT_IF_LTE, L_SELECT_IF_GTE
 * \return  na indicator array, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) The args specify constraints on the size of the
 *          components that are kept.
 *      (2) If the selection type is L_SELECT_WIDTH, the input
 *          height is ignored, and v.v.
 *      (3) To keep small components, use relation = L_SELECT_IF_LT or
 *          L_SELECT_IF_LTE.
 *          To keep large components, use relation = L_SELECT_IF_GT or
 *          L_SELECT_IF_GTE.
 * </pre>
 */
NUMA *
boxaMakeSizeIndicator(BOXA     *boxa,
                      l_int32   width,
                      l_int32   height,
                      l_int32   type,
                      l_int32   relation)
{
l_int32  i, n, w, h, ival;
NUMA    *na;

    PROCNAME("boxaMakeSizeIndicator");

    if (!boxa)
        return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL);
    if ((n = boxaGetCount(boxa)) == 0)
        return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL);
    if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
        type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
        return (NUMA *)ERROR_PTR("invalid type", procName, NULL);
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
        return (NUMA *)ERROR_PTR("invalid relation", procName, NULL);

    na = numaCreate(n);
    for (i = 0; i < n; i++) {
        ival = 0;
        boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
        switch (type)
        {
        case L_SELECT_WIDTH:
            if ((relation == L_SELECT_IF_LT && w < width) ||
                (relation == L_SELECT_IF_GT && w > width) ||
                (relation == L_SELECT_IF_LTE && w <= width) ||
                (relation == L_SELECT_IF_GTE && w >= width))
                ival = 1;
            break;
        case L_SELECT_HEIGHT:
            if ((relation == L_SELECT_IF_LT && h < height) ||
                (relation == L_SELECT_IF_GT && h > height) ||
                (relation == L_SELECT_IF_LTE && h <= height) ||
                (relation == L_SELECT_IF_GTE && h >= height))
                ival = 1;
            break;
        case L_SELECT_IF_EITHER:
            if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) ||
                ((relation == L_SELECT_IF_GT) && (w > width || h > height)) ||
               ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) ||
                ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height)))
                    ival = 1;
            break;
        case L_SELECT_IF_BOTH:
            if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) ||
                ((relation == L_SELECT_IF_GT) && (w > width && h > height)) ||
               ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) ||
                ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height)))
                    ival = 1;
            break;
        default:
            L_WARNING("can't get here!\n", procName);
            break;
        }
        numaAddNumber(na, ival);
    }

    return na;
}
PIX * pixReadStreamJpeg(FILE *fp, l_int32 cmflag, l_int32 reduction,
                        l_int32 *pnwarn, l_int32 hint)
{
    return (PIX * )ERROR_PTR("function not present", "pixReadStreamJpeg", NULL);
}
Beispiel #25
0
/*!
 *  pixReadMem()
 *
 *      Input:  data (const; encoded)
 *              datasize (size of data)
 *      Return: pix, or null on error
 *
 *  Notes:
 *      (1) This is a variation of pixReadStream(), where the data is read
 *          from a memory buffer rather than a file.
 *      (2) On windows, this only reads tiff formatted files directly from
 *          memory.  For other formats, it write to a temp file and
 *          decompress from file.
 *      (3) findFileFormatBuffer() requires up to 12 bytes to decide on
 *          the format.  That determines the constraint here.  But in
 *          fact the data must contain the entire compressed string for
 *          the image.
 */
PIX *
pixReadMem(const l_uint8  *data,
           size_t          size)
{
    l_int32  format;
    PIX     *pix;

    PROCNAME("pixReadMem");

    if (!data)
        return (PIX *)ERROR_PTR("data not defined", procName, NULL);
    if (size < 12)
        return (PIX *)ERROR_PTR("size < 12", procName, NULL);
    pix = NULL;

    findFileFormatBuffer(data, &format);
    switch (format)
    {
    case IFF_BMP:
        if ((pix = pixReadMemBmp(data, size)) == NULL )
            return (PIX *)ERROR_PTR( "bmp: no pix returned", procName, NULL);
        break;

    case IFF_JFIF_JPEG:
        if ((pix = pixReadMemJpeg(data, size, 0, 1, NULL, 0)) == NULL)
            return (PIX *)ERROR_PTR( "jpeg: no pix returned", procName, NULL);
        break;

    case IFF_PNG:
        if ((pix = pixReadMemPng(data, size)) == NULL)
            return (PIX *)ERROR_PTR("png: no pix returned", procName, NULL);
        break;

    case IFF_TIFF:
    case IFF_TIFF_PACKBITS:
    case IFF_TIFF_RLE:
    case IFF_TIFF_G3:
    case IFF_TIFF_G4:
    case IFF_TIFF_LZW:
    case IFF_TIFF_ZIP:
        /* Reading page 0 by default */
        if ((pix = pixReadMemTiff(data, size, 0)) == NULL)
            return (PIX *)ERROR_PTR("tiff: no pix returned", procName, NULL);
        break;

    case IFF_PNM:
        if ((pix = pixReadMemPnm(data, size)) == NULL)
            return (PIX *)ERROR_PTR("pnm: no pix returned", procName, NULL);
        break;

    case IFF_GIF:
        if ((pix = pixReadMemGif(data, size)) == NULL)
            return (PIX *)ERROR_PTR("gif: no pix returned", procName, NULL);
        break;

    case IFF_JP2:
        if ((pix = pixReadMemJp2k(data, size, 1, NULL, 0, 0)) == NULL)
            return (PIX *)ERROR_PTR("jp2k: no pix returned", procName, NULL);
        break;

    case IFF_WEBP:
        if ((pix = pixReadMemWebP(data, size)) == NULL)
            return (PIX *)ERROR_PTR("webp: no pix returned", procName, NULL);
        break;

    case IFF_SPIX:
        if ((pix = pixReadMemSpix(data, size)) == NULL)
            return (PIX *)ERROR_PTR("spix: no pix returned", procName, NULL);
        break;

    case IFF_UNKNOWN:
        return (PIX *)ERROR_PTR("Unknown format: no pix returned",
                                procName, NULL);
        break;
    }

    /* Set the input format.  For tiff reading from memory we lose
     * the actual input format; for 1 bpp, default to G4.  */
    if (pix) {
        if (format == IFF_TIFF && pixGetDepth(pix) == 1)
            format = IFF_TIFF_G4;
        pixSetInputFormat(pix, format);
    }

    return pix;
}
Beispiel #26
0
/*!
 *  sudokuReadFile()
 *
 *      Input:  filename (of formatted sudoku file)
 *      Return: array (of 81 numbers), or null on error
 *
 *  Notes:
 *      (1) The file format has:
 *          * any number of comment lines beginning with '#'
 *          * a set of 9 lines, each having 9 digits (0-9) separated
 *            by a space
 */
l_int32 *
sudokuReadFile(const char  *filename)
{
char     *str, *strj;
l_uint8  *data;
l_int32   i, j, nlines, val, index, error;
l_int32  *array;
size_t    size;
SARRAY   *saline, *sa1, *sa2;

    PROCNAME("sudokuReadFile");

    if (!filename)
        return (l_int32 *)ERROR_PTR("filename not defined", procName, NULL);
    data = l_binaryRead(filename, &size);
    sa1 = sarrayCreateLinesFromString((char *)data, 0);
    sa2 = sarrayCreate(9);

        /* Filter out the comment lines; verify that there are 9 data lines */
    nlines = sarrayGetCount(sa1);
    for (i = 0; i < nlines; i++) {
        str = sarrayGetString(sa1, i, L_NOCOPY);
        if (str[0] != '#')
            sarrayAddString(sa2, str, L_COPY);
    }
    LEPT_FREE(data);
    sarrayDestroy(&sa1);
    nlines = sarrayGetCount(sa2);
    if (nlines != 9) {
        sarrayDestroy(&sa2);
        L_ERROR("file has %d lines\n", procName, nlines);
        return (l_int32 *)ERROR_PTR("invalid file", procName, NULL);
    }

        /* Read the data into the array, verifying that each data
         * line has 9 numbers. */
    error = FALSE;
    array = (l_int32 *)LEPT_CALLOC(81, sizeof(l_int32));
    for (i = 0, index = 0; i < 9; i++) {
        str = sarrayGetString(sa2, i, L_NOCOPY);
        saline = sarrayCreateWordsFromString(str);
        if (sarrayGetCount(saline) != 9) {
            error = TRUE;
            sarrayDestroy(&saline);
            break;
        }
        for (j = 0; j < 9; j++) {
            strj = sarrayGetString(saline, j, L_NOCOPY);
            if (sscanf(strj, "%d", &val) != 1)
                error = TRUE;
            else
                array[index++] = val;
        }
        sarrayDestroy(&saline);
        if (error) break;
    }
    sarrayDestroy(&sa2);

    if (error) {
        LEPT_FREE(array);
        return (l_int32 *)ERROR_PTR("invalid data", procName, NULL);
    }

    return array;
}
Beispiel #27
0
/*!
 *  sarrayMakeInnerLoopDWACode()
 */
static SARRAY *
sarrayMakeInnerLoopDWACode(SEL     *sel,
                           l_int32  index)
{
char    *tstr, *string;
char     logicalor[] = "|";
char     logicaland[] = "&";
char     bigbuf[L_BUF_SIZE];
l_int32  i, j, optype, count, nfound, delx, dely;
SARRAY  *sa;

    PROCNAME("sarrayMakeInnerLoopDWACode");

    if (!sel)
        return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL);

    if (index % 2 == 0) {
        optype = L_MORPH_DILATE;
        tstr = logicalor;
    } else {
        optype = L_MORPH_ERODE;
        tstr = logicaland;
    }

    count = 0;
    for (i = 0; i < sel->sy; i++) {
        for (j = 0; j < sel->sx; j++) {
            if (sel->data[i][j] == 1)
                count++;
        }
    }

    if ((sa = sarrayCreate(0)) == NULL)
        return (SARRAY *)ERROR_PTR("sa not made", procName, NULL);
    if (count == 0) {
        L_WARNING("no hits in Sel %d\n", procName, index);
        return sa;  /* no code inside! */
    }

    nfound = 0;
    for (i = 0; i < sel->sy; i++) {
        for (j = 0; j < sel->sx; j++) {
            if (sel->data[i][j] == 1) {
                nfound++;
                if (optype == L_MORPH_DILATE) {
                    dely = sel->cy - i;
                    delx = sel->cx - j;
                } else if (optype == L_MORPH_ERODE) {
                    dely = i - sel->cy;
                    delx = j - sel->cx;
                }
                if ((string = makeBarrelshiftString(delx, dely)) == NULL) {
                    L_WARNING("barrel shift string not made\n", procName);
                    continue;
                }
                if (count == 1)  /* just one item */
                    sprintf(bigbuf, "            *dptr = %s;", string);
                else if (nfound == 1)
                    sprintf(bigbuf, "            *dptr = %s %s", string, tstr);
                else if (nfound < count)
                    sprintf(bigbuf, "                    %s %s", string, tstr);
                else  /* nfound == count */
                    sprintf(bigbuf, "                    %s;", string);
                sarrayAddString(sa, bigbuf, L_COPY);
                LEPT_FREE(string);
            }
        }
    }

    return sa;
}
Beispiel #28
0
/*!
 *  sudokuGenerate()
 *
 *      Input:  array (of 81 numbers, 9 rows of 9 numbers each)
 *              seed (random number)
 *              minelems (min non-zero elements allowed; <= 80)
 *              maxtries (max tries to remove a number and get a valid sudoku)
 *      Return: l_sudoku, or null on error
 *
 *  Notes:
 *      (1) This is a brute force generator.  It starts with a completed
 *          sudoku solution and, by removing elements (setting them to 0),
 *          generates a valid (unique) sudoku initial condition.
 *      (2) The process stops when either @minelems, the minimum
 *          number of non-zero elements, is reached, or when the
 *          number of attempts to remove the next element exceeds @maxtries.
 *      (3) No sudoku is known with less than 17 nonzero elements.
 */
L_SUDOKU *
sudokuGenerate(l_int32  *array,
               l_int32   seed,
               l_int32   minelems,
               l_int32   maxtries)
{
l_int32    index, sector, nzeros, removefirst, tries, val, oldval, unique;
L_SUDOKU  *sud, *testsud;

    PROCNAME("sudokuGenerate");

    if (!array)
        return (L_SUDOKU *)ERROR_PTR("array not defined", procName, NULL);
    if (minelems > 80)
        return (L_SUDOKU *)ERROR_PTR("minelems must be < 81", procName, NULL);

        /* Remove up to 30 numbers at random from the solution.
         * Test if the solution is valid -- the initial 'solution' may
         * have been invalid.  Then test if the sudoku with 30 zeroes
         * is unique -- it almost always will be. */
    srand(seed);
    nzeros = 0;
    sector = 0;
    removefirst = L_MIN(30, 81 - minelems);
    while (nzeros < removefirst) {
        genRandomIntegerInRange(9, 0, &val);
        index = 27 * (sector / 3) + 3 * (sector % 3) +
                9 * (val / 3) + (val % 3);
        if (array[index] == 0) continue;
        array[index] = 0;
        nzeros++;
        sector++;
        sector %= 9;
    }
    testsud = sudokuCreate(array);
    sudokuSolve(testsud);
    if (testsud->failure) {
        sudokuDestroy(&testsud);
        L_ERROR("invalid initial solution\n", procName);
        return NULL;
    }
    sudokuTestUniqueness(testsud->init, &unique);
    sudokuDestroy(&testsud);
    if (!unique) {
        L_ERROR("non-unique result with 30 zeroes\n", procName);
        return NULL;
    }

        /* Remove more numbers, testing at each removal for uniqueness. */
    tries = 0;
    sector = 0;
    while (1) {
        if (tries > maxtries) break;
        if (81 - nzeros <= minelems) break;

        if (tries == 0) {
            fprintf(stderr, "Trying %d zeros\n", nzeros);
            tries = 1;
        }

            /* Choose an element to be zeroed.  We choose one
             * at random in succession from each of the nine sectors. */
        genRandomIntegerInRange(9, 0, &val);
        index = 27 * (sector / 3) + 3 * (sector % 3) +
                9 * (val / 3) + (val % 3);
        sector++;
        sector %= 9;
        if (array[index] == 0) continue;

            /* Save the old value in case we need to revert */
        oldval = array[index];

            /* Is there a solution?  If not, try again. */
        array[index] = 0;
        testsud = sudokuCreate(array);
        sudokuSolve(testsud);
        if (testsud->failure == TRUE) {
            sudokuDestroy(&testsud);
            array[index] = oldval;  /* revert */
            tries++;
            continue;
        }

            /* Is the solution unique?  If not, try again. */
        sudokuTestUniqueness(testsud->init, &unique);
        sudokuDestroy(&testsud);
        if (!unique) {  /* revert and try again */
            array[index] = oldval;
            tries++;
        } else {  /* accept this */
            tries = 0;
            fprintf(stderr, "Have %d zeros\n", nzeros);
            nzeros++;
        }
    }
    fprintf(stderr, "Final: nelems = %d\n", 81 - nzeros);

        /* Show that we can recover the solution */
    sud = sudokuCreate(array);
    sudokuOutput(sud, L_SUDOKU_INIT);
    sudokuSolve(sud);
    sudokuOutput(sud, L_SUDOKU_STATE);

    return sud;
}
Beispiel #29
0
/*!
 *  pixFMorphopGen_3()
 *
 *      Input:  pixd (usual 3 choices: null, == pixs, != pixs)
 *              pixs (1 bpp)
 *              operation  (L_MORPH_DILATE, L_MORPH_ERODE,
 *                          L_MORPH_OPEN, L_MORPH_CLOSE)
 *              sel name
 *      Return: pixd
 *
 *  Notes:
 *      (1) This is a dwa operation, and the Sels must be limited in
 *          size to not more than 31 pixels about the origin.
 *      (2) A border of appropriate size (32 pixels, or 64 pixels
 *          for safe closing with asymmetric b.c.) must be added before
 *          this function is called.
 *      (3) This handles all required setting of the border pixels
 *          before erosion and dilation.
 *      (4) The closing operation is safe; no pixels can be removed
 *          near the boundary.
 */
PIX *
pixFMorphopGen_3(PIX     *pixd,
                 PIX     *pixs,
                 l_int32  operation,
                 char    *selname)
{
l_int32    i, index, found, w, h, wpls, wpld, bordercolor, erodeop, borderop;
l_uint32  *datad, *datas, *datat;
PIX       *pixt;

    PROCNAME("pixFMorphopGen_3");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd);

        /* Get boundary colors to use */
    bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
    if (bordercolor == 1)
        erodeop = PIX_SET;
    else
        erodeop = PIX_CLR;

    found = FALSE;
    for (i = 0; i < NUM_SELS_GENERATED; i++) {
        if (strcmp(selname, SEL_NAMES[i]) == 0) {
            found = TRUE;
            index = 2 * i;
            break;
        }
    }
    if (found == FALSE)
        return (PIX *)ERROR_PTR("sel index not found", procName, pixd);

    if (!pixd) {
        if ((pixd = pixCreateTemplate(pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }
    else  /* for in-place or pre-allocated */
        pixResizeImageData(pixd, pixs);
    wpls = pixGetWpl(pixs);
    wpld = pixGetWpl(pixd);

        /* The images must be surrounded, in advance, with a border of
         * size 32 pixels (or 64, for closing), that we'll read from.
         * Fabricate a "proper" image as the subimage within the 32
         * pixel border, having the following parameters:  */
    w = pixGetWidth(pixs) - 64;
    h = pixGetHeight(pixs) - 64;
    datas = pixGetData(pixs) + 32 * wpls + 1;
    datad = pixGetData(pixd) + 32 * wpld + 1;

    if (operation == L_MORPH_DILATE || operation == L_MORPH_ERODE) {
        borderop = PIX_CLR;
        if (operation == L_MORPH_ERODE) {
            borderop = erodeop;
            index++;
        }
        if (pixd == pixs) {  /* in-place; generate a temp image */
            if ((pixt = pixCopy(NULL, pixs)) == NULL)
                return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
            datat = pixGetData(pixt) + 32 * wpls + 1;
            pixSetOrClearBorder(pixt, 32, 32, 32, 32, borderop);
            fmorphopgen_low_3(datad, w, h, wpld, datat, wpls, index);
            pixDestroy(&pixt);
        }
        else { /* not in-place */
            pixSetOrClearBorder(pixs, 32, 32, 32, 32, borderop);
            fmorphopgen_low_3(datad, w, h, wpld, datas, wpls, index);
        }
    }
    else {  /* opening or closing; generate a temp image */
        if ((pixt = pixCreateTemplate(pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
        datat = pixGetData(pixt) + 32 * wpls + 1;
        if (operation == L_MORPH_OPEN) {
            pixSetOrClearBorder(pixs, 32, 32, 32, 32, erodeop);
            fmorphopgen_low_3(datat, w, h, wpls, datas, wpls, index+1);
            pixSetOrClearBorder(pixt, 32, 32, 32, 32, PIX_CLR);
            fmorphopgen_low_3(datad, w, h, wpld, datat, wpls, index);
        }
        else {  /* closing */
            pixSetOrClearBorder(pixs, 32, 32, 32, 32, PIX_CLR);
            fmorphopgen_low_3(datat, w, h, wpls, datas, wpls, index);
            pixSetOrClearBorder(pixt, 32, 32, 32, 32, erodeop);
            fmorphopgen_low_3(datad, w, h, wpld, datat, wpls, index+1);
        }
        pixDestroy(&pixt);
    }

    return pixd;
}
/*!
 *  pixReadStreamJpeg()
 *
 *      Input:  stream
 *              colormap flag (0 means return RGB image if color;
 *                             1 means create colormap and return 8 bpp
 *                               palette image if color)
 *              reduction (scaling factor: 1, 2, 4 or 8)
 *              &pnwarn (<optional return> number of warnings)
 *              hint: (a bitwise OR of L_HINT_* values); use 0 for no hints
 *      Return: pix, or null on error
 *
 *  Usage: see pixReadJpeg()
 */
PIX *
pixReadStreamJpeg(FILE     *fp,
                  l_int32   cmflag,
                  l_int32   reduction,
                  l_int32  *pnwarn,
                  l_int32   hint)
{
l_uint8                        cyan, yellow, magenta, black, white;
l_int32                        rval, gval, bval;
l_int32                        i, j, k;
l_int32                        w, h, wpl, spp, ncolors, cindex, ycck, cmyk;
l_uint32                      *data;
l_uint32                      *line, *ppixel;
JSAMPROW                       rowbuffer;
PIX                           *pix;
PIXCMAP                       *cmap;
struct jpeg_decompress_struct  cinfo;
struct jpeg_error_mgr          jerr;
l_uint8                       *comment = NULL;

    PROCNAME("pixReadStreamJpeg");

    if (!fp)
        return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
    if (pnwarn)
        *pnwarn = 0;  /* init */
    if (cmflag != 0 && cmflag != 1)
        cmflag = 0;  /* default */
    if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8)
        return (PIX *)ERROR_PTR("reduction not in {1,2,4,8}", procName, NULL);

    if (BITS_IN_JSAMPLE != 8)  /* set in jmorecfg.h */
        return (PIX *)ERROR_PTR("BITS_IN_JSAMPLE != 8", procName, NULL);

    rewind(fp);

    pix = NULL;  /* init */
    if (setjmp(jpeg_jmpbuf)) {
        pixDestroy(&pix);
        FREE(rowbuffer);
        return (PIX *)ERROR_PTR("internal jpeg error", procName, NULL);
    }

    rowbuffer = NULL;
    cinfo.err = jpeg_std_error(&jerr);
    jerr.error_exit = jpeg_error_do_not_exit; /* catch error; do not exit! */

    jpeg_create_decompress(&cinfo);

    cinfo.client_data = &comment;
    jpeg_set_marker_processor(&cinfo, JPEG_COM, jpeg_comment_callback);
    jpeg_stdio_src(&cinfo, fp);
    jpeg_read_header(&cinfo, TRUE);
    cinfo.scale_denom = reduction;
    cinfo.scale_num = 1;
    if (hint & L_HINT_GRAY)
        cinfo.out_color_space = JCS_GRAYSCALE;
    jpeg_calc_output_dimensions(&cinfo);

        /* Allocate the image and a row buffer */
    spp = cinfo.out_color_components;
    w = cinfo.output_width;
    h = cinfo.output_height;
    ycck = (cinfo.jpeg_color_space == JCS_YCCK && spp == 4 && cmflag == 0);
    cmyk = (cinfo.jpeg_color_space == JCS_CMYK && spp == 4 && cmflag == 0);
    if (spp != 1 && spp != 3 && !ycck && !cmyk) {
        if (comment) FREE(comment);
        return (PIX *)ERROR_PTR("spp must be 1 or 3, or YCCK or CMYK",
                                procName, NULL);
    }
    if ((spp == 3 && cmflag == 0) || ycck || cmyk) {  /* rgb or 4 bpp color */
        rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), spp * w);
        pix = pixCreate(w, h, 32);
    }
    else {  /* 8 bpp gray or colormapped */
        rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), w);
        pix = pixCreate(w, h, 8);
    }
    if (!rowbuffer || !pix) {
        if (comment) FREE(comment);
	if (rowbuffer) FREE(rowbuffer);
	pixDestroy(&pix);
        return (PIX *)ERROR_PTR("rowbuffer or pix not made", procName, NULL);
    }

    if (comment) {
        pixSetText(pix, (char *)comment);
	FREE(comment);
    }

    if (spp == 1)  /* Grayscale or colormapped */
        jpeg_start_decompress(&cinfo);
    else  {        /* Color; spp == 3 or YCCK or CMYK */
        if (cmflag == 0) {   /* -- 24 bit color in 32 bit pix or YCCK/CMYK -- */
            cinfo.quantize_colors = FALSE;
            jpeg_start_decompress(&cinfo);
        }
        else {      /* Color quantize to 8 bits */
            cinfo.quantize_colors = TRUE;
            cinfo.desired_number_of_colors = 256;
            jpeg_start_decompress(&cinfo);

                /* Construct a pix cmap */
            cmap = pixcmapCreate(8);
            ncolors = cinfo.actual_number_of_colors;
            for (cindex = 0; cindex < ncolors; cindex++)
            {
                rval = cinfo.colormap[0][cindex];
                gval = cinfo.colormap[1][cindex];
                bval = cinfo.colormap[2][cindex];
                pixcmapAddColor(cmap, rval, gval, bval);
            }
            pixSetColormap(pix, cmap);
        }
    }
    wpl  = pixGetWpl(pix);
    data = pixGetData(pix);

        /* Decompress */
    if ((spp == 3 && cmflag == 0) || ycck || cmyk) {   /* -- 24 bit color -- */
        for (i = 0; i < h; i++) {
            if (jpeg_read_scanlines(&cinfo, &rowbuffer, (JDIMENSION)1) != 1)
                return (PIX *)ERROR_PTR("bad read scanline", procName, NULL);
            ppixel = data + i * wpl;
            if (spp == 3) {
                for (j = k = 0; j < w; j++) {
                    SET_DATA_BYTE(ppixel, COLOR_RED, rowbuffer[k++]);
                    SET_DATA_BYTE(ppixel, COLOR_GREEN, rowbuffer[k++]);
                    SET_DATA_BYTE(ppixel, COLOR_BLUE, rowbuffer[k++]);
                    ppixel++;
                }
            } else {
                    /* This is a conversion from CMYK -> RGB that ignores
                       color profiles, and is invoked when the image header
                       claims to be in CMYK or YCCK colorspace.  If in YCCK,
                       libjpeg may be doing YCCK -> CMYK under the hood.
                       To understand why the colors are inverted on read-in,
                       see the "Special color spaces" section of
                       "Using the IJG JPEG Library" by Thomas G. Lane.  */
                for (j = k = 0; j < w; j++) {
                    cyan = 255 - rowbuffer[k++];
                    magenta = 255 - rowbuffer[k++];
                    yellow = 255 - rowbuffer[k++];
                    white = rowbuffer[k++];
                    black = 255 - white;
                    rval = 255 - (cyan    * white) / 255 - black;
                    gval = 255 - (magenta * white) / 255 - black;
                    bval = 255 - (yellow  * white) / 255 - black;
                    rval = L_MIN(L_MAX(rval, 0), 255);
                    gval = L_MIN(L_MAX(gval, 0), 255);
                    bval = L_MIN(L_MAX(bval, 0), 255);
                    composeRGBPixel(rval, gval, bval, ppixel);
                    ppixel++;
                }
            }
        }
    }
    else {    /* 8 bpp grayscale or colormapped pix */
        for (i = 0; i < h; i++) {
            if (jpeg_read_scanlines(&cinfo, &rowbuffer, (JDIMENSION)1) != 1)
                return (PIX *)ERROR_PTR("bad read scanline", procName, NULL);
            line = data + i * wpl;
            for (j = 0; j < w; j++)
                SET_DATA_BYTE(line, j, rowbuffer[j]);
        }
    }

    if (pnwarn)
        *pnwarn = cinfo.err->num_warnings;

    switch (cinfo.density_unit)
    {
    case 1:  /* pixels per inch */
        pixSetXRes(pix, cinfo.X_density);
        pixSetYRes(pix, cinfo.Y_density);
        break;
    case 2:  /* pixels per centimeter */
        pixSetXRes(pix, (l_int32)((l_float32)cinfo.X_density * 2.54 + 0.5));
        pixSetYRes(pix, (l_int32)((l_float32)cinfo.Y_density * 2.54 + 0.5));
        break;
    default:   /* the pixel density may not be defined; ignore */
        break;
    }

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    FREE(rowbuffer);

    return pix;
}