Exemplo n.º 1
0
/*!
 *  recogAppend()
 *
 *      Input:  recog1
 *              recog2 (gets added to recog1)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This is used to make a training recognizer from more than
 *          one trained recognizer source.  It should only be used
 *          when the bitmaps for corresponding character classes are
 *          very similar.  That constraint does not arise when
 *          the character classes are disjoint; e.g., if recog1 is
 *          digits and recog2 is alphabetical.
 *      (2) This is done by appending recog2 to recog1.  Averages are
 *          computed for each recognizer, if necessary, before appending.
 *      (3) Non-array fields are combined using the appropriate min and max.
 */
l_int32
recogAppend(L_RECOG  *recog1,
            L_RECOG  *recog2)
{
    PROCNAME("recogAppend");

    if (!recog1)
        return ERROR_INT("recog1 not defined", procName, 1);
    if (!recog2)
        return ERROR_INT("recog2 not defined", procName, 1);

        /* Make sure both are finalized with all arrays computed */
    recogAverageSamples(recog1, 0);
    recogAverageSamples(recog2, 0);

        /* Combine non-array field values */
    recog1->minwidth_u = L_MIN(recog1->minwidth_u, recog2->minwidth_u);
    recog1->maxwidth_u = L_MAX(recog1->maxwidth_u, recog2->maxwidth_u);
    recog1->minheight_u = L_MIN(recog1->minheight_u, recog2->minheight_u);
    recog1->maxheight_u = L_MAX(recog1->maxheight_u, recog2->maxheight_u);
    recog1->minwidth = L_MIN(recog1->minwidth, recog2->minwidth);
    recog1->maxwidth = L_MAX(recog1->maxwidth, recog2->maxwidth);
    recog1->min_splitw = L_MIN(recog1->min_splitw, recog2->min_splitw);
    recog1->min_splith = L_MIN(recog1->min_splith, recog2->min_splith);
    recog1->max_splith = L_MAX(recog1->max_splith, recog2->max_splith);

        /* Combine array field values */
    recog1->setsize += recog2->setsize;
    sarrayAppendRange(recog1->sa_text, recog2->sa_text, 0, -1);
    l_dnaJoin(recog1->dna_tochar, recog2->dna_tochar, 0, -1);
    pixaaJoin(recog1->pixaa_u, recog2->pixaa_u, 0, -1);
    pixaJoin(recog1->pixa_u, recog2->pixa_u, 0, -1);
    ptaaJoin(recog1->ptaa_u, recog2->ptaa_u, 0, -1);
    ptaJoin(recog1->pta_u, recog2->pta_u, 0, -1);
    numaaJoin(recog1->naasum_u, recog2->naasum_u, 0, -1);
    numaJoin(recog1->nasum_u, recog2->nasum_u, 0, -1);
    pixaaJoin(recog1->pixaa, recog2->pixaa, 0, -1);
    pixaJoin(recog1->pixa, recog2->pixa, 0, -1);
    ptaaJoin(recog1->ptaa, recog2->ptaa, 0, -1);
    ptaJoin(recog1->pta, recog2->pta, 0, -1);
    numaaJoin(recog1->naasum, recog2->naasum, 0, -1);
    numaJoin(recog1->nasum, recog2->nasum, 0, -1);
    return 0;
}
Exemplo n.º 2
0
/*
 *  fmorphautogen2()
 *
 *      Input:  sela
 *              fileindex
 *              filename (<optional>; can be null)
 *      Return: 0 if OK; 1 on error
 *
 *  Notes:
 *      (1) This function uses morphtemplate2.txt to create a
 *          low-level file that contains the low-level functions for
 *          implementing dilation and erosion for every sel
 *          in the input sela.
 *      (2) The fileindex parameter is inserted into the output
 *          filename, as described below.
 *      (3) If filename == NULL, the output file is fmorphgenlow.<n>.c,
 *          where <n> is equal to the 'fileindex' parameter.
 *      (4) If filename != NULL, the output file is <filename>low.<n>.c.
 */
l_int32
fmorphautogen2(SELA        *sela,
               l_int32      fileindex,
               const char  *filename)
{
char    *filestr, *linestr, *fname;
char    *str_doc1, *str_doc2, *str_doc3, *str_doc4, *str_def1;
char     bigbuf[L_BUF_SIZE];
char     breakstring[] = "        break;";
char     staticstring[] = "static void";
l_int32  i, nsels, nbytes, actstart, end, newstart;
l_int32  argstart, argend, loopstart, loopend, finalstart, finalend;
size_t   size;
SARRAY  *sa1, *sa2, *sa3, *sa4, *sa5, *sa6;
SEL     *sel;

    PROCNAME("fmorphautogen2");

    if (!sela)
        return ERROR_INT("sela not defined", procName, 1);
    if (fileindex < 0)
        fileindex = 0;
    if ((nsels = selaGetCount(sela)) == 0)
        return ERROR_INT("no sels in sela", procName, 1);

        /* Make the array of textlines from morphtemplate2.txt */
    if ((filestr = (char *)l_binaryRead(TEMPLATE2, &size)) == NULL)
        return ERROR_INT("filestr not made", procName, 1);
    sa1 = sarrayCreateLinesFromString(filestr, 1);
    LEPT_FREE(filestr);
    if (!sa1)
        return ERROR_INT("sa1 not made", procName, 1);

        /* Make the array of static function names */
    if ((sa2 = sarrayCreate(2 * nsels)) == NULL) {
        sarrayDestroy(&sa1);
        return ERROR_INT("sa2 not made", procName, 1);
    }
    for (i = 0; i < nsels; i++) {
        sprintf(bigbuf, "fdilate_%d_%d", fileindex, i);
        sarrayAddString(sa2, bigbuf, L_COPY);
        sprintf(bigbuf, "ferode_%d_%d", fileindex, i);
        sarrayAddString(sa2, bigbuf, L_COPY);
    }

        /* Make the static prototype strings */
    sa3 = sarrayCreate(2 * nsels);  /* should be ok */
    for (i = 0; i < 2 * nsels; i++) {
        fname = sarrayGetString(sa2, i, L_NOCOPY);
        sprintf(bigbuf, "static void  %s%s", fname, PROTOARGS);
        sarrayAddString(sa3, bigbuf, L_COPY);
    }

        /* Make strings containing function names */
    sprintf(bigbuf, " *             l_int32    fmorphopgen_low_%d()",
            fileindex);
    str_doc1 = stringNew(bigbuf);
    sprintf(bigbuf, " *             void       fdilate_%d_*()", fileindex);
    str_doc2 = stringNew(bigbuf);
    sprintf(bigbuf, " *             void       ferode_%d_*()", fileindex);
    str_doc3 = stringNew(bigbuf);
    sprintf(bigbuf, " *  fmorphopgen_low_%d()", fileindex);
    str_doc4 = stringNew(bigbuf);
    sprintf(bigbuf, "fmorphopgen_low_%d(l_uint32  *datad,", fileindex);
    str_def1 = stringNew(bigbuf);

        /* Output to this sa */
    sa4 = sarrayCreate(0);

        /* Copyright notice and info header */
    sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa4, sa1, actstart, end);

        /* Insert function names as documentation */
    sarrayAddString(sa4, str_doc1, L_INSERT);
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa4, sa1, actstart, end);
    sarrayAddString(sa4, str_doc2, L_INSERT);
    sarrayAddString(sa4, str_doc3, L_INSERT);
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa4, sa1, actstart, end);

        /* Insert static protos */
    for (i = 0; i < 2 * nsels; i++) {
        if ((linestr = sarrayGetString(sa3, i, L_COPY)) == NULL) {
            sarrayDestroy(&sa1);
            sarrayDestroy(&sa2);
            sarrayDestroy(&sa3);
            sarrayDestroy(&sa4);
            return ERROR_INT("linestr not retrieved", procName, 1);
        }
        sarrayAddString(sa4, linestr, L_INSERT);
    }

        /* Insert function header */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa4, sa1, actstart, end);
    sarrayAddString(sa4, str_doc4, L_INSERT);
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa4, sa1, actstart, end);
    sarrayAddString(sa4, str_def1, L_INSERT);
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa4, sa1, actstart, end);

        /* Generate and insert the dispatcher code */
    for (i = 0; i < 2 * nsels; i++) {
        sprintf(bigbuf, "    case %d:", i);
        sarrayAddString(sa4, bigbuf, L_COPY);
        sprintf(bigbuf, "        %s(datad, w, h, wpld, datas, wpls);",
               sarrayGetString(sa2, i, L_NOCOPY));
        sarrayAddString(sa4, bigbuf, L_COPY);
        sarrayAddString(sa4, breakstring, L_COPY);
    }

        /* Finish the dispatcher and introduce the low-level code */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa4, sa1, actstart, end);

        /* Get the range for the args common to all functions */
    sarrayParseRange(sa1, newstart, &argstart, &argend, &newstart, "--", 0);

        /* Get the range for the loop code common to all functions */
    sarrayParseRange(sa1, newstart, &loopstart, &loopend, &newstart, "--", 0);

        /* Get the range for the ending code common to all functions */
    sarrayParseRange(sa1, newstart, &finalstart, &finalend, &newstart, "--", 0);

        /* Do all the static functions */
    for (i = 0; i < 2 * nsels; i++) {
            /* Generate the function header and add the common args */
        sarrayAddString(sa4, staticstring, L_COPY);
        fname = sarrayGetString(sa2, i, L_NOCOPY);
        sprintf(bigbuf, "%s(l_uint32  *datad,", fname);
        sarrayAddString(sa4, bigbuf, L_COPY);
        sarrayAppendRange(sa4, sa1, argstart, argend);

            /* Declare and define wplsN args, as necessary */
        if ((sel = selaGetSel(sela, i/2)) == NULL) {
            sarrayDestroy(&sa1);
            sarrayDestroy(&sa2);
            sarrayDestroy(&sa3);
            sarrayDestroy(&sa4);
            return ERROR_INT("sel not returned", procName, 1);
        }
        sa5 = sarrayMakeWplsCode(sel);
        sarrayJoin(sa4, sa5);
        sarrayDestroy(&sa5);

            /* Add the function loop code */
        sarrayAppendRange(sa4, sa1, loopstart, loopend);

            /* Insert barrel-op code for *dptr */
        sa6 = sarrayMakeInnerLoopDWACode(sel, i);
        sarrayJoin(sa4, sa6);
        sarrayDestroy(&sa6);

            /* Finish the function code */
        sarrayAppendRange(sa4, sa1, finalstart, finalend);
    }

        /* Output to file */
    filestr = sarrayToString(sa4, 1);
    nbytes = strlen(filestr);
    if (filename)
        snprintf(bigbuf, L_BUF_SIZE, "%slow.%d.c", filename, fileindex);
    else
        sprintf(bigbuf, "%slow.%d.c", OUTROOT, fileindex);
    l_binaryWrite(bigbuf, "w", filestr, nbytes);
    sarrayDestroy(&sa1);
    sarrayDestroy(&sa2);
    sarrayDestroy(&sa3);
    sarrayDestroy(&sa4);
    LEPT_FREE(filestr);
    return 0;
}
/*!
 * \brief   strcodeFinalize()
 *
 * \param[in,out]  pstrcode destroys after .c and .h files have been generated
 * \param[in]      outdir [optional] if NULL, files are made in /tmp/lept/auto
 * \return  void
 */
l_int32
strcodeFinalize(L_STRCODE  **pstrcode,
                const char  *outdir)
{
char        buf[256];
char       *filestr, *casestr, *descr, *datastr, *realoutdir;
l_int32     actstart, end, newstart, fileno, nbytes;
size_t      size;
L_STRCODE  *strcode;
SARRAY     *sa1, *sa2, *sa3;

    PROCNAME("strcodeFinalize");

    lept_mkdir("lept/auto");

    if (!pstrcode || *pstrcode == NULL)
        return ERROR_INT("No input data", procName, 1);
    strcode = *pstrcode;
    if (!outdir) {
        L_INFO("no outdir specified; writing to /tmp/lept/auto\n", procName);
        realoutdir = stringNew("/tmp/lept/auto");
    } else {
        realoutdir = stringNew(outdir);
    }

    /* ------------------------------------------------------- */
    /*              Make the output autogen.*.c file           */
    /* ------------------------------------------------------- */

       /* Make array of textlines from TEMPLATE1 */
    if ((filestr = (char *)l_binaryRead(TEMPLATE1, &size)) == NULL)
        return ERROR_INT("filestr not made", procName, 1);
    if ((sa1 = sarrayCreateLinesFromString(filestr, 1)) == NULL)
        return ERROR_INT("sa1 not made", procName, 1);
    LEPT_FREE(filestr);

    if ((sa3 = sarrayCreate(0)) == NULL)
        return ERROR_INT("sa3 not made", procName, 1);

        /* Copyright notice */
    sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);

        /* File name comment */
    fileno = strcode->fileno;
    snprintf(buf, sizeof(buf), " *   autogen.%d.c", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* More text */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);

        /* Description of function types by index */
    descr = sarrayToString(strcode->descr, 1);
    descr[strlen(descr) - 1] = '\0';
    sarrayAddString(sa3, descr, L_INSERT);

        /* Includes */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);
    snprintf(buf, sizeof(buf), "#include \"autogen.%d.h\"", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* Header for auto-generated deserializers */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);

        /* Function name (as comment) */
    snprintf(buf, sizeof(buf), " *  l_autodecode_%d()", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* Input and return values */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);

        /* Function name */
    snprintf(buf, sizeof(buf), "l_autodecode_%d(l_int32 index)", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* Stack vars */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);

        /* Declaration of nfunc on stack */
    snprintf(buf, sizeof(buf), "l_int32   nfunc = %d;\n", strcode->n);
    sarrayAddString(sa3, buf, L_COPY);

        /* Declaration of PROCNAME */
    snprintf(buf, sizeof(buf), "    PROCNAME(\"l_autodecode_%d\");", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* Test input variables */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);

        /* Insert case string */
    casestr = sarrayToString(strcode->function, 0);
    casestr[strlen(casestr) - 1] = '\0';
    sarrayAddString(sa3, casestr, L_INSERT);

        /* End of function */
    sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa1, actstart, end);

        /* Flatten to string and output to autogen*.c file */
    if ((filestr = sarrayToString(sa3, 1)) == NULL)
        return ERROR_INT("filestr from sa3 not made", procName, 1);
    nbytes = strlen(filestr);
    snprintf(buf, sizeof(buf), "%s/autogen.%d.c", realoutdir, fileno);
    l_binaryWrite(buf, "w", filestr, nbytes);
    LEPT_FREE(filestr);
    sarrayDestroy(&sa1);
    sarrayDestroy(&sa3);

    /* ------------------------------------------------------- */
    /*              Make the output autogen.*.h file           */
    /* ------------------------------------------------------- */

       /* Make array of textlines from TEMPLATE2 */
    if ((filestr = (char *)l_binaryRead(TEMPLATE2, &size)) == NULL)
        return ERROR_INT("filestr not made", procName, 1);
    if ((sa2 = sarrayCreateLinesFromString(filestr, 1)) == NULL)
        return ERROR_INT("sa2 not made", procName, 1);
    LEPT_FREE(filestr);

    if ((sa3 = sarrayCreate(0)) == NULL)
        return ERROR_INT("sa3 not made", procName, 1);

        /* Copyright notice */
    sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* File name comment */
    snprintf(buf, sizeof(buf), " *   autogen.%d.h", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* More text */
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Beginning header protection */
    snprintf(buf, sizeof(buf), "#ifndef  LEPTONICA_AUTOGEN_%d_H\n"
                               "#define  LEPTONICA_AUTOGEN_%d_H",
             fileno, fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* Prototype header text */
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Prototype declaration */
    snprintf(buf, sizeof(buf), "void *l_autodecode_%d(l_int32 index);", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* Prototype trailer text */
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Insert serialized data strings */
    datastr = sarrayToString(strcode->data, 1);
    datastr[strlen(datastr) - 1] = '\0';
    sarrayAddString(sa3, datastr, L_INSERT);

        /* End header protection */
    snprintf(buf, sizeof(buf), "#endif  /* LEPTONICA_AUTOGEN_%d_H */", fileno);
    sarrayAddString(sa3, buf, L_COPY);

        /* Flatten to string and output to autogen*.h file */
    if ((filestr = sarrayToString(sa3, 1)) == NULL)
        return ERROR_INT("filestr from sa3 not made", procName, 1);
    nbytes = strlen(filestr);
    snprintf(buf, sizeof(buf), "%s/autogen.%d.h", realoutdir, fileno);
    l_binaryWrite(buf, "w", filestr, nbytes);
    LEPT_FREE(filestr);
    LEPT_FREE(realoutdir);
    sarrayDestroy(&sa2);
    sarrayDestroy(&sa3);

        /* Cleanup */
    strcodeDestroy(pstrcode);
    return 0;
}
Exemplo n.º 4
0
/*!
 * \brief   fmorphautogen1()
 *
 * \param[in]    sela
 * \param[in]    fileindex
 * \param[in]    filename [optional]; can be null
 * \return  0 if OK; 1 on error
 *
 * <pre>
 * Notes:
 *      (1) This function uses morphtemplate1.txt to create a
 *          top-level file that contains two functions.  These
 *          functions will carry out dilation, erosion,
 *          opening or closing for any of the sels in the input sela.
 *      (2) The fileindex parameter is inserted into the output
 *          filename, as described below.
 *      (3) If filename == NULL, the output file is fmorphgen.<n>.c,
 *          where <n> is equal to the 'fileindex' parameter.
 *      (4) If filename != NULL, the output file is <filename>.<n>.c.
 * </pre>
 */
l_int32
fmorphautogen1(SELA        *sela,
               l_int32      fileindex,
               const char  *filename)
{
char    *filestr;
char    *str_proto1, *str_proto2, *str_proto3;
char    *str_doc1, *str_doc2, *str_doc3, *str_doc4;
char    *str_def1, *str_def2, *str_proc1, *str_proc2;
char    *str_dwa1, *str_low_dt, *str_low_ds, *str_low_ts;
char    *str_low_tsp1, *str_low_dtp1;
char     bigbuf[L_BUF_SIZE];
l_int32  i, nsels, nbytes, actstart, end, newstart;
size_t   size;
SARRAY  *sa1, *sa2, *sa3;

    PROCNAME("fmorphautogen1");

    if (!sela)
        return ERROR_INT("sela not defined", procName, 1);
    if (fileindex < 0)
        fileindex = 0;
    if ((nsels = selaGetCount(sela)) == 0)
        return ERROR_INT("no sels in sela", procName, 1);

        /* Make array of textlines from morphtemplate1.txt */
    if ((filestr = (char *)l_binaryRead(TEMPLATE1, &size)) == NULL)
        return ERROR_INT("filestr not made", procName, 1);
    sa2 = sarrayCreateLinesFromString(filestr, 1);
    LEPT_FREE(filestr);
    if (!sa2)
        return ERROR_INT("sa2 not made", procName, 1);

        /* Make array of sel names */
    sa1 = selaGetSelnames(sela);

        /* Make strings containing function call names */
    sprintf(bigbuf, "PIX *pixMorphDwa_%d(PIX *pixd, PIX *pixs, "
                    "l_int32 operation, char *selname);", fileindex);
    str_proto1 = stringNew(bigbuf);
    sprintf(bigbuf, "PIX *pixFMorphopGen_%d(PIX *pixd, PIX *pixs, "
                    "l_int32 operation, char *selname);", fileindex);
    str_proto2 = stringNew(bigbuf);
    sprintf(bigbuf, "l_int32 fmorphopgen_low_%d(l_uint32 *datad, l_int32 w,\n"
        "                          l_int32 h, l_int32 wpld,\n"
        "                          l_uint32 *datas, l_int32 wpls,\n"
        "                          l_int32 index);", fileindex);
    str_proto3 = stringNew(bigbuf);
    sprintf(bigbuf, " *             PIX     *pixMorphDwa_%d()", fileindex);
    str_doc1 = stringNew(bigbuf);
    sprintf(bigbuf, " *             PIX     *pixFMorphopGen_%d()", fileindex);
    str_doc2 = stringNew(bigbuf);
    sprintf(bigbuf, " *  pixMorphDwa_%d()", fileindex);
    str_doc3 = stringNew(bigbuf);
    sprintf(bigbuf, " *  pixFMorphopGen_%d()", fileindex);
    str_doc4 = stringNew(bigbuf);
    sprintf(bigbuf, "pixMorphDwa_%d(PIX     *pixd,", fileindex);
    str_def1 = stringNew(bigbuf);
    sprintf(bigbuf, "pixFMorphopGen_%d(PIX     *pixd,", fileindex);
    str_def2 = stringNew(bigbuf);
    sprintf(bigbuf, "    PROCNAME(\"pixMorphDwa_%d\");", fileindex);
    str_proc1 = stringNew(bigbuf);
    sprintf(bigbuf, "    PROCNAME(\"pixFMorphopGen_%d\");", fileindex);
    str_proc2 = stringNew(bigbuf);
    sprintf(bigbuf,
            "    pixt2 = pixFMorphopGen_%d(NULL, pixt1, operation, selname);",
            fileindex);
    str_dwa1 = stringNew(bigbuf);
    sprintf(bigbuf,
      "            fmorphopgen_low_%d(datad, w, h, wpld, datat, wpls, index);",
      fileindex);
    str_low_dt = stringNew(bigbuf);
    sprintf(bigbuf,
      "            fmorphopgen_low_%d(datad, w, h, wpld, datas, wpls, index);",
      fileindex);
    str_low_ds = stringNew(bigbuf);
    sprintf(bigbuf,
     "            fmorphopgen_low_%d(datat, w, h, wpls, datas, wpls, index+1);",
      fileindex);
    str_low_tsp1 = stringNew(bigbuf);
    sprintf(bigbuf,
      "            fmorphopgen_low_%d(datat, w, h, wpls, datas, wpls, index);",
      fileindex);
    str_low_ts = stringNew(bigbuf);
    sprintf(bigbuf,
     "            fmorphopgen_low_%d(datad, w, h, wpld, datat, wpls, index+1);",
      fileindex);
    str_low_dtp1 = stringNew(bigbuf);

        /* Make the output sa */
    sa3 = sarrayCreate(0);

        /* Copyright notice and info header */
    sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Insert function names as documentation */
    sarrayAddString(sa3, str_doc1, L_INSERT);
    sarrayAddString(sa3, str_doc2, L_INSERT);

        /* Add '#include's */
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Insert function prototypes */
    sarrayAddString(sa3, str_proto1, L_INSERT);
    sarrayAddString(sa3, str_proto2, L_INSERT);
    sarrayAddString(sa3, str_proto3, L_INSERT);

        /* Add static globals */
    sprintf(bigbuf, "\nstatic l_int32   NUM_SELS_GENERATED = %d;", nsels);
    sarrayAddString(sa3, bigbuf, L_COPY);
    sprintf(bigbuf, "static char  SEL_NAMES[][80] = {");
    sarrayAddString(sa3, bigbuf, L_COPY);
    for (i = 0; i < nsels - 1; i++) {
        sprintf(bigbuf, "                             \"%s\",",
                sarrayGetString(sa1, i, L_NOCOPY));
        sarrayAddString(sa3, bigbuf, L_COPY);
    }
    sprintf(bigbuf, "                             \"%s\"};",
            sarrayGetString(sa1, i, L_NOCOPY));
    sarrayAddString(sa3, bigbuf, L_COPY);

        /* Start pixMorphDwa_*() function description */
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_doc3, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Finish pixMorphDwa_*() function definition */
    sarrayAddString(sa3, str_def1, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_proc1, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_dwa1, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Start pixFMorphopGen_*() function description */
    sarrayAddString(sa3, str_doc4, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Finish pixFMorphopGen_*() function definition */
    sarrayAddString(sa3, str_def2, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_proc2, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_low_dt, L_COPY);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_low_ds, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_low_tsp1, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_low_dt, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_low_ts, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);
    sarrayAddString(sa3, str_low_dtp1, L_INSERT);
    sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);
    sarrayAppendRange(sa3, sa2, actstart, end);

        /* Output to file */
    filestr = sarrayToString(sa3, 1);
    nbytes = strlen(filestr);
    if (filename)
        snprintf(bigbuf, L_BUF_SIZE, "%s.%d.c", filename, fileindex);
    else
        sprintf(bigbuf, "%s.%d.c", OUTROOT, fileindex);
    l_binaryWrite(bigbuf, "w", filestr, nbytes);
    sarrayDestroy(&sa1);
    sarrayDestroy(&sa2);
    sarrayDestroy(&sa3);
    LEPT_FREE(filestr);
    return 0;
}