/*! * gplotCreate() * * Input: rootname (root for all output files) * outformat (GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_X11, * GPLOT_LATEX) * title (<optional> overall title) * xlabel (<optional> x axis label) * ylabel (<optional> y axis label) * Return: gplot, or null on error * * Notes: * (1) This initializes the plot. * (2) The 'title', 'xlabel' and 'ylabel' strings can have spaces, * double quotes and backquotes, but not single quotes. */ GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel) { char *newroot; char buf[L_BUF_SIZE]; GPLOT *gplot; PROCNAME("gplotCreate"); if (!rootname) return (GPLOT *)ERROR_PTR("rootname not defined", procName, NULL); if (outformat != GPLOT_PNG && outformat != GPLOT_PS && outformat != GPLOT_EPS && outformat != GPLOT_X11 && outformat != GPLOT_LATEX) return (GPLOT *)ERROR_PTR("outformat invalid", procName, NULL); if ((gplot = (GPLOT *)CALLOC(1, sizeof(GPLOT))) == NULL) return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL); gplot->cmddata = sarrayCreate(0); gplot->datanames = sarrayCreate(0); gplot->plotdata = sarrayCreate(0); gplot->plottitles = sarrayCreate(0); gplot->plotstyles = numaCreate(0); /* Save title, labels, rootname, outformat, cmdname, outname */ newroot = genPathname(rootname, NULL); gplot->rootname = newroot; gplot->outformat = outformat; snprintf(buf, L_BUF_SIZE, "%s.cmd", newroot); gplot->cmdname = stringNew(buf); if (outformat == GPLOT_PNG) snprintf(buf, L_BUF_SIZE, "%s.png", newroot); else if (outformat == GPLOT_PS) snprintf(buf, L_BUF_SIZE, "%s.ps", newroot); else if (outformat == GPLOT_EPS) snprintf(buf, L_BUF_SIZE, "%s.eps", newroot); else if (outformat == GPLOT_LATEX) snprintf(buf, L_BUF_SIZE, "%s.tex", newroot); else /* outformat == GPLOT_X11 */ buf[0] = '\0'; gplot->outname = stringNew(buf); if (title) gplot->title = stringNew(title); if (xlabel) gplot->xlabel = stringNew(xlabel); if (ylabel) gplot->ylabel = stringNew(ylabel); return gplot; }
/*! * sarraySelectBySubstring() * * Input: sain (input sarray) * substr (<optional> substring for matching; can be NULL) * Return: saout (output sarray, filtered with substring) or null on error * * Notes: * (1) This selects all strings in sain that have substr as a substring. * Note that we can't use strncmp() because we're looking for * a match to the substring anywhere within each filename. * (2) If substr == NULL, returns a copy of the sarray. */ SARRAY * sarraySelectBySubstring(SARRAY *sain, const char *substr) { char *str; l_int32 n, i, offset, found; SARRAY *saout; PROCNAME("sarraySelectBySubstring"); if (!sain) return (SARRAY *)ERROR_PTR("sain not defined", procName, NULL); n = sarrayGetCount(sain); if (!substr || n == 0) return sarrayCopy(sain); saout = sarrayCreate(n); for (i = 0; i < n; i++) { str = sarrayGetString(sain, i, L_NOCOPY); arrayFindSequence((l_uint8 *)str, strlen(str), (l_uint8 *)substr, strlen(substr), &offset, &found); if (found) sarrayAddString(saout, str, L_COPY); } return saout; }
/*! * \brief sarraySortByIndex() * * \param[in] sain * \param[in] naindex na that maps from the new sarray to the input sarray * \return saout sorted, or NULL on error */ SARRAY * sarraySortByIndex(SARRAY *sain, NUMA *naindex) { char *str; l_int32 i, n, index; SARRAY *saout; PROCNAME("sarraySortByIndex"); if (!sain) return (SARRAY *)ERROR_PTR("sain not defined", procName, NULL); if (!naindex) return (SARRAY *)ERROR_PTR("naindex not defined", procName, NULL); n = sarrayGetCount(sain); saout = sarrayCreate(n); for (i = 0; i < n; i++) { numaGetIValue(naindex, i, &index); str = sarrayGetString(sain, index, L_COPY); sarrayAddString(saout, str, L_INSERT); } return saout; }
/*! * strcodeCreate() * * Input: fileno (integer that labels the two output files) * Return: initialized L_StrCode, or null on error * * Notes: * (1) This struct exists to build two files containing code for * any number of data objects. The two files are named * autogen.<fileno>.c * autogen.<fileno>.h */ L_STRCODE * strcodeCreate(l_int32 fileno) { L_STRCODE *strcode; PROCNAME("strcodeCreate"); if ((strcode = (L_STRCODE *)CALLOC(1, sizeof(L_STRCODE))) == NULL) return (L_STRCODE *)ERROR_PTR("strcode not made", procName, NULL); strcode->fileno = fileno; strcode->function = sarrayCreate(0); strcode->data = sarrayCreate(0); strcode->descr = sarrayCreate(0); return strcode; }
/* * captureProtoSignature() * * Input: sa (output from cpp, by line) * start (starting index to search; never a comment line) * stop (index of line on which pattern is completed) * charindex (char index of completing ')' character) * Return: cleanstr (prototype string), or NULL on error * * Notes: * (1) Return all characters, ending with a ';' after the ')' */ static char * captureProtoSignature(SARRAY *sa, l_int32 start, l_int32 stop, l_int32 charindex) { char *str, *newstr, *protostr, *cleanstr; SARRAY *sap; l_int32 i; PROCNAME("captureProtoSignature"); if (!sa) return (char *)ERROR_PTR("sa not defined", procName, NULL); sap = sarrayCreate(0); for (i = start; i < stop; i++) { str = sarrayGetString(sa, i, L_COPY); sarrayAddString(sap, str, L_INSERT); } str = sarrayGetString(sa, stop, L_COPY); str[charindex + 1] = '\0'; newstr = stringJoin(str, ";"); sarrayAddString(sap, newstr, L_INSERT); LEPT_FREE(str); protostr = sarrayToString(sap, 2); sarrayDestroy(&sap); cleanstr = cleanProtoSignature(protostr); LEPT_FREE(protostr); return cleanstr; }
/*! * \brief sarrayRemoveDupsByAset() * * \param[in] sas * \return sad with duplicates removed, or NULL on error * * <pre> * Notes: * (1) This is O(nlogn), considerably slower than * sarrayRemoveDupsByHash() for large string arrays. * (2) The key for each string is a 64-bit hash. * (3) Build a set, using hashed strings as keys. As the set is * built, first do a find; if not found, add the key to the * set and add the string to the output sarray. * </pre> */ SARRAY * sarrayRemoveDupsByAset(SARRAY *sas) { char *str; l_int32 i, n; l_uint64 hash; L_ASET *set; RB_TYPE key; SARRAY *sad; PROCNAME("sarrayRemoveDupsByAset"); if (!sas) return (SARRAY *)ERROR_PTR("sas not defined", procName, NULL); set = l_asetCreate(L_UINT_TYPE); sad = sarrayCreate(0); n = sarrayGetCount(sas); for (i = 0; i < n; i++) { str = sarrayGetString(sas, i, L_NOCOPY); l_hashStringToUint64(str, &hash); key.utype = hash; if (!l_asetFind(set, key)) { sarrayAddString(sad, str, L_COPY); l_asetInsert(set, key); } } l_asetDestroy(&set); return sad; }
/*! * sarrayCreateWordsFromString() * * Input: string * Return: sarray, or null on error * * Notes: * (1) This finds the number of word substrings, creates an sarray * of this size, and puts copies of each substring into the sarray. */ SARRAY * sarrayCreateWordsFromString(const char *string) { char separators[] = " \n\t"; l_int32 i, nsub, size, inword; SARRAY *sa; PROCNAME("sarrayCreateWordsFromString"); if (!string) return (SARRAY *)ERROR_PTR("textstr not defined", procName, NULL); /* Find the number of words */ size = strlen(string); nsub = 0; inword = FALSE; for (i = 0; i < size; i++) { if (inword == FALSE && (string[i] != ' ' && string[i] != '\t' && string[i] != '\n')) { inword = TRUE; nsub++; } else if (inword == TRUE && (string[i] == ' ' || string[i] == '\t' || string[i] == '\n')) { inword = FALSE; } } if ((sa = sarrayCreate(nsub)) == NULL) return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); sarraySplitString(sa, string, separators); return sa; }
/*! * recogCreate() * * Input: scalew (scale all widths to this; use 0 for no scaling) * scaleh (scale all heights to this; use 0 for no scaling) * templ_type (L_USE_AVERAGE or L_USE_ALL) * threshold (for binarization; typically ~128) * maxyshift (from nominal centroid alignment; typically 0 or 1) * Return: recog, or null on error * * Notes: * (1) For a set trained on one font, such as numbers in a book, * it is sensible to set scalew = scaleh = 0. * (2) For a mixed training set, scaling to a fixed height, * such as 32 pixels, but leaving the width unscaled, is effective. * (3) The storage for most of the arrays is allocated when training * is finished. */ L_RECOG * recogCreate(l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift) { L_RECOG *recog; PIXA *pixa; PIXAA *paa; PROCNAME("recogCreate"); if (scalew < 0 || scaleh < 0) return (L_RECOG *)ERROR_PTR("invalid scalew or scaleh", procName, NULL); if (templ_type != L_USE_AVERAGE && templ_type != L_USE_ALL) return (L_RECOG *)ERROR_PTR("invalid templ_type flag", procName, NULL); if (threshold < 1 || threshold > 255) return (L_RECOG *)ERROR_PTR("invalid threshold", procName, NULL); if ((recog = (L_RECOG *)CALLOC(1, sizeof(L_RECOG))) == NULL) return (L_RECOG *)ERROR_PTR("rec not made", procName, NULL); recog->templ_type = templ_type; recog->threshold = threshold; recog->scalew = scalew; recog->scaleh = scaleh; recog->maxyshift = maxyshift; recog->asperity_fr = DEFAULT_ASPERITY_FRACT; recogSetPadParams(recog, NULL, NULL, NULL, -1, -1, -1); recog->bmf = bmfCreate(NULL, 6); recog->bmf_size = 6; recog->maxarraysize = MAX_EXAMPLES_IN_CLASS; recog->index = -1; /* Generate the LUTs */ recog->centtab = makePixelCentroidTab8(); recog->sumtab = makePixelSumTab8(); recog->sa_text = sarrayCreate(0); recog->dna_tochar = l_dnaCreate(0); /* Input default values for min component size for splitting. * These are overwritten when pixTrainingFinished() is called. */ recog->min_splitw = 6; recog->min_splith = 6; recog->max_splith = 60; /* Generate the storage for the unscaled training bitmaps */ paa = pixaaCreate(recog->maxarraysize); pixa = pixaCreate(1); pixaaInitFull(paa, pixa); pixaDestroy(&pixa); recog->pixaa_u = paa; /* Generate the storage for debugging */ recog->pixadb_boot = pixaCreate(2); recog->pixadb_split = pixaCreate(2); return recog; }
/* * parseForProtos() * * Input: filein (output of cpp) * prestring (<optional> string that prefaces each decl; * use NULL to omit) * Return: parsestr (string of function prototypes), or NULL on error * * Notes: * (1) We parse the output of cpp: * cpp -ansi <filein> * Three plans were attempted, with success on the third. * (2) Plan 1. A cursory examination of the cpp output indicated that * every function was preceeded by a cpp comment statement. * So we just need to look at statements beginning after comments. * Unfortunately, this is NOT the case. Some functions start * without cpp comment lines, typically when there are no * comments in the source that immediately precede the function. * (3) Plan 2. Consider the keywords in the language that start * parts of the cpp file. Some, like 'typedef', 'enum', * 'union' and 'struct', are followed after a while by '{', * and eventually end with '}, plus an optional token and a * final ';' Others, like 'extern' and 'static', are never * the beginnings of global function definitions. Function * prototypes have one or more sets of '(' followed eventually * by a ')', and end with ';'. But function definitions have * tokens, followed by '(', more tokens, ')' and then * immediately a '{'. We would generate a prototype from this * by adding a ';' to all tokens up to the ')'. So we use * these special tokens to decide what we are parsing. And * whenever a function definition is found and the prototype * extracted, we skip through the rest of the function * past the corresponding '}'. This token ends a line, and * is often on a line of its own. But as it turns out, * the only keyword we need to consider is 'static'. * (4) Plan 3. Consider the parentheses and braces for various * declarations. A struct, enum, or union has a pair of * braces followed by a semicolon. They cannot have parentheses * before the left brace, but a struct can have lots of parentheses * within the brace set. A function prototype has no braces. * A function declaration can have sets of left and right * parentheses, but these are followed by a left brace. * So plan 3 looks at the way parentheses and braces are * organized. Once the beginning of a function definition * is found, the prototype is extracted and we search for * the ending right brace. * (5) To find the ending right brace, it is necessary to do some * careful parsing. For example, in this file, we have * left and right braces as characters, and these must not * be counted. Somewhat more tricky, the file fhmtauto.c * generates code, and includes a right brace in a string. * So we must not include braces that are in strings. But how * do we know if something is inside a string? Keep state, * starting with not-inside, and every time you hit a double quote * that is not escaped, toggle the condition. Any brace * found in the state of being within a string is ignored. * (6) When a prototype is extracted, it is put in a canonical * form (i.e., cleaned up). Finally, we check that it is * not static and save it. (If static, it is ignored). * (7) The @prestring for unix is NULL; it is included here so that * you can use Microsoft's declaration for importing or * exporting to a dll. See environ.h for examples of use. * Here, we set: @prestring = "LEPT_DLL ". Note in particular * the space character that will separate 'LEPT_DLL' from * the standard unix prototype that follows. */ char * parseForProtos(const char *filein, const char *prestring) { char *strdata, *str, *newstr, *parsestr, *secondword; l_int32 nbytes, start, next, stop, charindex, found; SARRAY *sa, *saout, *satest; PROCNAME("parseForProtos"); if (!filein) return (char *)ERROR_PTR("filein not defined", procName, NULL); /* Read in the cpp output into memory, one string for each * line in the file, omitting blank lines. */ strdata = (char *)arrayRead(filein, &nbytes); sa = sarrayCreateLinesFromString(strdata, 0); saout = sarrayCreate(0); next = 0; while (1) { /* repeat after each non-static prototype is extracted */ searchForProtoSignature(sa, next, &start, &stop, &charindex, &found); if (!found) break; /* fprintf(stderr, " start = %d, stop = %d, charindex = %d\n", start, stop, charindex); */ str = captureProtoSignature(sa, start, stop, charindex); /* Make sure it is not static. Note that 'extern' has * been prepended to the prototype, so the 'static' * keyword, if it exists, would be the second word. */ satest = sarrayCreateWordsFromString(str); secondword = sarrayGetString(satest, 1, 0); if (strcmp(secondword, "static")) { /* not static */ if (prestring) { /* prepend it to the prototype */ newstr = stringJoin(prestring, str); sarrayAddString(saout, newstr, L_INSERT); FREE(str); } else sarrayAddString(saout, str, L_INSERT); } else FREE(str); sarrayDestroy(&satest); skipToEndOfFunction(sa, stop, charindex, &next); if (next == -1) break; } /* Flatten into a string with newlines between prototypes */ parsestr = sarrayToString(saout, 1); FREE(strdata); sarrayDestroy(&sa); sarrayDestroy(&saout); return parsestr; }
/*! * sarrayCreateLinesFromString() * * Input: string * blankflag (0 to exclude blank lines; 1 to include) * Return: sarray, or null on error * * Notes: * (1) This finds the number of line substrings, creates an sarray of * this size, and puts copies of each substring into the sarray. */ SARRAY * sarrayCreateLinesFromString(char *string, l_int32 blankflag) { l_int32 i, nsub, size, startptr; char *cstring, *substring; SARRAY *sa; PROCNAME("sarrayCreateLinesFromString"); if (!string) return (SARRAY *)ERROR_PTR("textstr not defined", procName, NULL); /* find the number of lines */ size = strlen(string); nsub = 0; for (i = 0; i < size; i++) { if (string[i] == '\n') nsub++; } if ((sa = sarrayCreate(nsub)) == NULL) return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); if (blankflag) { /* keep blank lines as null strings */ /* Make a copy for munging */ if ((cstring = stringNew(string)) == NULL) return (SARRAY *)ERROR_PTR("cstring not made", procName, NULL); /* We'll insert nulls like strtok */ startptr = 0; for (i = 0; i < size; i++) { if (cstring[i] == '\n') { cstring[i] = '\0'; if ((substring = stringNew(cstring + startptr)) == NULL) return (SARRAY *)ERROR_PTR("substring not made", procName, NULL); sarrayAddString(sa, substring, L_INSERT); /* fprintf(stderr, "substring = %s\n", substring); */ startptr = i + 1; } } if (startptr < size) { /* no newline at end of last line */ if ((substring = stringNew(cstring + startptr)) == NULL) return (SARRAY *)ERROR_PTR("substring not made", procName, NULL); sarrayAddString(sa, substring, L_INSERT); /* fprintf(stderr, "substring = %s\n", substring); */ } FREE(cstring); } else { /* remove blank lines; use strtok */ sarraySplitString(sa, string, "\n"); } return sa; }
/* * cleanProtoSignature() * * Input: instr (input prototype string) * Return: cleanstr (clean prototype string), or NULL on error * * Notes: * (1) Adds 'extern' at beginning and regularizes spaces * between tokens. */ static char * cleanProtoSignature(char *instr) { char *str, *cleanstr; char buf[L_BUF_SIZE]; char externstring[] = "extern"; l_int32 i, j, nwords, nchars, index, len; SARRAY *sa, *saout; PROCNAME("cleanProtoSignature"); if (!instr) return (char *)ERROR_PTR("instr not defined", procName, NULL); sa = sarrayCreateWordsFromString(instr); nwords = sarrayGetCount(sa); saout = sarrayCreate(0); sarrayAddString(saout, externstring, 1); for (i = 0; i < nwords; i++) { str = sarrayGetString(sa, i, 0); nchars = strlen(str); index = 0; for (j = 0; j < nchars; j++) { if (index > L_BUF_SIZE - 6) return (char *)ERROR_PTR("token too large", procName, NULL); if (str[j] == '(') { buf[index++] = ' '; buf[index++] = '('; buf[index++] = ' '; } else if (str[j] == ')') { buf[index++] = ' '; buf[index++] = ')'; } else buf[index++] = str[j]; } buf[index] = '\0'; sarrayAddString(saout, buf, 1); } /* Flatten to a prototype string with spaces added after * each word, and remove the last space */ cleanstr = sarrayToString(saout, 2); len = strlen(cleanstr); cleanstr[len - 1] = '\0'; sarrayDestroy(&sa); sarrayDestroy(&saout); return cleanstr; }
/* Build all possible strings, up to a max of 5 roman alphabet characters */ static SARRAY * BuildShortStrings(l_int32 nchars, /* 3, 4 or 5 */ l_int32 add_dups) { char buf[64]; l_int32 i, j, k, l, m; l_uint64 hash; SARRAY *sa; sa = sarrayCreate(1000); for (i = 0; i < 26; i++) { sprintf(buf, "%c", i + 0x61); sarrayAddString(sa, buf, L_COPY); for (j = 0; j < 26; j++) { sprintf(buf, "%c%c", i + 0x61, j + 0x61); sarrayAddString(sa, buf, L_COPY); for (k = 0; k < 26; k++) { sprintf(buf, "%c%c%c", i + 0x61, j + 0x61, k + 0x61); sarrayAddString(sa, buf, L_COPY); if (add_dups && k < 4) /* add redundant strings */ sarrayAddString(sa, buf, L_COPY); if (nchars > 3) { for (l = 0; l < 26; l++) { sprintf(buf, "%c%c%c%c", i + 0x61, j + 0x61, k + 0x61, l + 0x61); sarrayAddString(sa, buf, L_COPY); if (add_dups && l < 4) /* add redundant strings */ sarrayAddString(sa, buf, L_COPY); if (nchars > 4) { for (m = 0; m < 26; m++) { sprintf(buf, "%c%c%c%c%c", i + 0x61, j + 0x61, k + 0x61, l + 0x61, m + 0x61); sarrayAddString(sa, buf, L_COPY); if (!add_dups && i == 17 && j == 12 && k == 4 && l == 21) { l_hashStringToUint64(buf, &hash); fprintf(stderr, " %llx\n", hash); } if (add_dups && m < 4) /* add redundant */ sarrayAddString(sa, buf, L_COPY); } } } } } } } return sa; }
/*! * \brief sarrayIntersectionByHash() * * \param[in] sa1, sa2 * \return sad intersection of the strings, or NULL on error * * <pre> * Notes: * (1) This is faster than sarrayIntersectionByAset(), because the * bucket lookup is O(n). * </pre> */ SARRAY * sarrayIntersectionByHash(SARRAY *sa1, SARRAY *sa2) { char *str; l_int32 n1, n2, nsmall, i, index1, index2; l_uint32 nsize2; l_uint64 key; L_DNAHASH *dahash1, *dahash2; SARRAY *sa_small, *sa_big, *sad; PROCNAME("sarrayIntersectionByHash"); if (!sa1) return (SARRAY *)ERROR_PTR("sa1 not defined", procName, NULL); if (!sa2) return (SARRAY *)ERROR_PTR("sa2 not defined", procName, NULL); /* Put the elements of the biggest sarray into a dnahash */ n1 = sarrayGetCount(sa1); n2 = sarrayGetCount(sa2); sa_small = (n1 < n2) ? sa1 : sa2; /* do not destroy sa_small */ sa_big = (n1 < n2) ? sa2 : sa1; /* do not destroy sa_big */ dahash1 = l_dnaHashCreateFromSarray(sa_big); /* Build up the intersection of strings. Add to %sad * if the string is in sa_big (using dahash1) but hasn't * yet been seen in the traversal of sa_small (using dahash2). */ sad = sarrayCreate(0); nsmall = sarrayGetCount(sa_small); findNextLargerPrime(nsmall / 20, &nsize2); /* buckets in hash table */ dahash2 = l_dnaHashCreate(nsize2, 0); for (i = 0; i < nsmall; i++) { str = sarrayGetString(sa_small, i, L_NOCOPY); sarrayFindStringByHash(sa_big, dahash1, str, &index1); if (index1 >= 0) { sarrayFindStringByHash(sa_small, dahash2, str, &index2); if (index2 == -1) { sarrayAddString(sad, str, L_COPY); l_hashStringToUint64(str, &key); l_dnaHashAdd(dahash2, key, (l_float64)i); } } } l_dnaHashDestroy(&dahash1); l_dnaHashDestroy(&dahash2); return sad; }
/*----------------------------------------------------------------------* * Miscellaneous operations * *----------------------------------------------------------------------*/ SARRAY * sarrayGenerateIntegers(l_int32 n) { char buf[32]; l_int32 i; SARRAY *sa; PROCNAME("sarrayGenerateIntegers"); if ((sa = sarrayCreate(n)) == NULL) return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); for (i = 0; i < n; i++) { snprintf(buf, sizeof(buf), "%d", i); sarrayAddString(sa, buf, L_COPY); } return sa; }
/*! * sarrayReadStream() * * Input: stream * Return: sarray, or null on error * * Notes: * (1) We store the size of each string along with the string. * (2) This allows a string to have embedded newlines. By reading * the entire string, as determined by its size, we are * not affected by any number of embedded newlines. */ SARRAY * sarrayReadStream(FILE *fp) { char *stringbuf; l_int32 i, n, size, index, bufsize, ret, version; SARRAY *sa; PROCNAME("sarrayReadStream"); if (!fp) return (SARRAY *)ERROR_PTR("stream not defined", procName, NULL); ret = fscanf(fp, "\nSarray Version %d\n", &version); if (ret != 1) return (SARRAY *)ERROR_PTR("not an sarray file", procName, NULL); if (version != SARRAY_VERSION_NUMBER) return (SARRAY *)ERROR_PTR("invalid sarray version", procName, NULL); fscanf(fp, "Number of strings = %d\n", &n); if ((sa = sarrayCreate(n)) == NULL) return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); bufsize = L_BUF_SIZE + 1; if ((stringbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) return (SARRAY *)ERROR_PTR("stringbuf not made", procName, NULL); for (i = 0; i < n; i++) { /* Get the size of the stored string */ fscanf(fp, "%d[%d]:", &index, &size); /* Expand the string buffer if necessary */ if (size > bufsize - 5) { FREE(stringbuf); bufsize = (l_int32)(1.5 * size); stringbuf = (char *)CALLOC(bufsize, sizeof(char)); } /* Read the stored string, plus leading spaces and trailing \n */ fread(stringbuf, 1, size + 3, fp); /* Remove the \n that was added by sarrayWriteStream() */ stringbuf[size + 2] = '\0'; /* Copy it in, skipping the 2 leading spaces */ sarrayAddString(sa, stringbuf + 2, L_COPY); } fscanf(fp, "\n"); FREE(stringbuf); return sa; }
/*! * numaConvertToSarray() * * Input: na * size1 (size of conversion field) * size2 (for float conversion: size of field to the right * of the decimal point) * addzeros (for integer conversion: to add lead zeros) * type (L_INTEGER_VALUE, L_FLOAT_VALUE) * Return: a sarray of the float values converted to strings * representing either integer or float values; or null on error. * * Notes: * (1) For integer conversion, size2 is ignored. * For float conversion, addzeroes is ignored. */ SARRAY * numaConvertToSarray(NUMA *na, l_int32 size1, l_int32 size2, l_int32 addzeros, l_int32 type) { char fmt[32], strbuf[64]; l_int32 i, n, ival; l_float32 fval; SARRAY *sa; PROCNAME("numaConvertToSarray"); if (!na) return (SARRAY *)ERROR_PTR("na not defined", procName, NULL); if (type != L_INTEGER_VALUE && type != L_FLOAT_VALUE) return (SARRAY *)ERROR_PTR("invalid type", procName, NULL); if (type == L_INTEGER_VALUE) { if (addzeros) snprintf(fmt, sizeof(fmt), "%%0%dd", size1); else snprintf(fmt, sizeof(fmt), "%%%dd", size1); } else { /* L_FLOAT_VALUE */ snprintf(fmt, sizeof(fmt), "%%%d.%df", size1, size2); } n = numaGetCount(na); if ((sa = sarrayCreate(n)) == NULL) return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); for (i = 0; i < n; i++) { if (type == L_INTEGER_VALUE) { numaGetIValue(na, i, &ival); snprintf(strbuf, sizeof(strbuf), fmt, ival); } else { /* L_FLOAT_VALUE */ numaGetFValue(na, i, &fval); snprintf(strbuf, sizeof(strbuf), fmt, fval); } sarrayAddString(sa, strbuf, L_COPY); } return sa; }
/*! * \brief sarrayRemoveDupsByHash() * * \param[in] sas * \param[out] psad unique set of strings; duplicates removed * \param[out] pdahash [optional] dnahash used for lookup * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) Generates a sarray with unique values. * (2) The dnahash is built up with sad to assure uniqueness. * It can be used to find if a string is in the set: * sarrayFindValByHash(sad, dahash, str, \&index) * (3) The hash of the string location is simple and fast. It scales * up with the number of buckets to insure a fairly random * bucket selection input strings. * (4) This is faster than sarrayRemoveDupsByAset(), because the * bucket lookup is O(n), although there is a double-loop * lookup within the dna in each bucket. * </pre> */ l_int32 sarrayRemoveDupsByHash(SARRAY *sas, SARRAY **psad, L_DNAHASH **pdahash) { char *str; l_int32 i, n, index, items; l_uint32 nsize; l_uint64 key; SARRAY *sad; L_DNAHASH *dahash; PROCNAME("sarrayRemoveDupsByHash"); if (pdahash) *pdahash = NULL; if (!psad) return ERROR_INT("&sad not defined", procName, 1); *psad = NULL; if (!sas) return ERROR_INT("sas not defined", procName, 1); n = sarrayGetCount(sas); findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ dahash = l_dnaHashCreate(nsize, 8); sad = sarrayCreate(n); *psad = sad; for (i = 0, items = 0; i < n; i++) { str = sarrayGetString(sas, i, L_NOCOPY); sarrayFindStringByHash(sad, dahash, str, &index); if (index < 0) { /* not found */ l_hashStringToUint64(str, &key); l_dnaHashAdd(dahash, key, (l_float64)items); sarrayAddString(sad, str, L_COPY); items++; } } if (pdahash) *pdahash = dahash; else l_dnaHashDestroy(&dahash); return 0; }
/*! * sarrayCopy() * * Input: sarray * Return: copy of sarray, or null on error */ SARRAY * sarrayCopy(SARRAY *sa) { l_int32 i; SARRAY *csa; PROCNAME("sarrayCopy"); if (!sa) return (SARRAY *)ERROR_PTR("sa not defined", procName, NULL); if ((csa = sarrayCreate(sa->nalloc)) == NULL) return (SARRAY *)ERROR_PTR("csa not made", procName, NULL); for (i = 0; i < sa->n; i++) sarrayAddString(csa, sa->array[i], L_COPY); return csa; }
/*! * \brief sarrayIntersectionByAset() * * \param[in] sa1, sa2 * \return sad with the intersection of the string set, or NULL on error * * <pre> * Notes: * (1) Algorithm: put the smaller sarray into a set, using the string * hashes as the key values. Then run through the larger sarray, * building an output sarray and a second set from the strings * in the larger array: if a string is in the first set but * not in the second, add the string to the output sarray and hash * it into the second set. The second set is required to make * sure only one instance of each string is put into the output sarray. * This is O(mlogn), {m,n} = sizes of {smaller,larger} input arrays. * </pre> */ SARRAY * sarrayIntersectionByAset(SARRAY *sa1, SARRAY *sa2) { char *str; l_int32 n1, n2, i, n; l_uint64 hash; L_ASET *set1, *set2; RB_TYPE key; SARRAY *sa_small, *sa_big, *sad; PROCNAME("sarrayIntersectionByAset"); if (!sa1) return (SARRAY *)ERROR_PTR("sa1 not defined", procName, NULL); if (!sa2) return (SARRAY *)ERROR_PTR("sa2 not defined", procName, NULL); /* Put the elements of the biggest array into a set */ n1 = sarrayGetCount(sa1); n2 = sarrayGetCount(sa2); sa_small = (n1 < n2) ? sa1 : sa2; /* do not destroy sa_small */ sa_big = (n1 < n2) ? sa2 : sa1; /* do not destroy sa_big */ set1 = l_asetCreateFromSarray(sa_big); /* Build up the intersection of strings */ sad = sarrayCreate(0); n = sarrayGetCount(sa_small); set2 = l_asetCreate(L_UINT_TYPE); for (i = 0; i < n; i++) { str = sarrayGetString(sa_small, i, L_NOCOPY); l_hashStringToUint64(str, &hash); key.utype = hash; if (l_asetFind(set1, key) && !l_asetFind(set2, key)) { sarrayAddString(sad, str, L_COPY); l_asetInsert(set2, key); } } l_asetDestroy(&set1); l_asetDestroy(&set2); return sad; }
/*! * getSortedPathnamesInDirectory() * * Input: directory name * substr (<optional> substring filter on filenames; can be NULL) * firstpage (0-based) * npages (use 0 for all to the end) * Return: sarray of sorted pathnames, or NULL on error * * Notes: * (1) If 'substr' is not NULL, only filenames that contain * the substring can be returned. If 'substr' is NULL, * none of the filenames are filtered out. * (2) The files in the directory, after optional filtering by * the substring, are lexically sorted in increasing order. * The full pathnames are returned for the requested sequence. * If no files are found after filtering, returns an empty sarray. */ SARRAY * getSortedPathnamesInDirectory(const char *dirname, const char *substr, l_int32 firstpage, l_int32 npages) { char *fname, *fullname; l_int32 i, nfiles, lastpage; SARRAY *sa, *safiles, *saout; PROCNAME("getSortedPathnamesInDirectory"); if (!dirname) return (SARRAY *)ERROR_PTR("dirname not defined", procName, NULL); if ((sa = getFilenamesInDirectory(dirname)) == NULL) return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); safiles = sarraySelectBySubstring(sa, substr); sarrayDestroy(&sa); nfiles = sarrayGetCount(safiles); if (nfiles == 0) { L_WARNING("no files found", procName); return safiles; } sarraySort(safiles, safiles, L_SORT_INCREASING); firstpage = L_MIN(L_MAX(firstpage, 0), nfiles - 1); if (npages == 0) npages = nfiles - firstpage; lastpage = L_MIN(firstpage + npages - 1, nfiles - 1); saout = sarrayCreate(lastpage - firstpage + 1); for (i = firstpage; i <= lastpage; i++) { fname = sarrayGetString(safiles, i, L_NOCOPY); fullname = genPathname(dirname, fname); sarrayAddString(saout, fullname, L_INSERT); } sarrayDestroy(&safiles); return saout; }
SARRAY * getFilenamesInDirectory(const char *dirname) { char *name; l_int32 len; SARRAY *safiles; DIR *pdir; struct dirent *pdirentry; PROCNAME("getFilenamesInDirectory"); if (!dirname) return (SARRAY *)ERROR_PTR("dirname not defined", procName, NULL); if ((safiles = sarrayCreate(0)) == NULL) return (SARRAY *)ERROR_PTR("safiles not made", procName, NULL); if ((pdir = opendir(dirname)) == NULL) return (SARRAY *)ERROR_PTR("pdir not opened", procName, NULL); while ((pdirentry = readdir(pdir))) { /* It's nice to ignore directories. For this it is necessary to * define _BSD_SOURCE in the CC command, because the DT_DIR * flag is non-standard. */ #if !defined(__MINGW32__) && !defined(_CYGWIN_ENVIRON) && !defined(__SOLARIS__) if (pdirentry->d_type == DT_DIR) continue; #endif /* Filter out "." and ".." if they're passed through */ name = pdirentry->d_name; len = strlen(name); if (len == 1 && name[len - 1] == '.') continue; if (len == 2 && name[len - 1] == '.' && name[len - 2] == '.') continue; sarrayAddString(safiles, name, L_COPY); } closedir(pdir); return safiles; }
SARRAY * getFilenamesInDirectory(const char *dirname) { SARRAY *safiles; WIN32_FIND_DATAA ffd; size_t length_of_path; CHAR szDir[MAX_PATH]; /* MAX_PATH is defined in stdlib.h */ HANDLE hFind = INVALID_HANDLE_VALUE; PROCNAME("getFilenamesInDirectory"); if (!dirname) return (SARRAY *)ERROR_PTR("dirname not defined", procName, NULL); length_of_path = strlen(dirname); if (length_of_path > (MAX_PATH - 2)) return (SARRAY *)ERROR_PTR("dirname is to long", procName, NULL); strncpy(szDir, dirname, MAX_PATH); szDir[MAX_PATH - 1] = '\0'; strncat(szDir, TEXT("\\*"), MAX_PATH - strlen(szDir)); if ((safiles = sarrayCreate(0)) == NULL) return (SARRAY *)ERROR_PTR("safiles not made", procName, NULL); hFind = FindFirstFileA(szDir, &ffd); if (INVALID_HANDLE_VALUE == hFind) { sarrayDestroy(&safiles); return (SARRAY *)ERROR_PTR("hFind not opened", procName, NULL); } while (FindNextFileA(hFind, &ffd) != 0) { if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) /* skip dirs */ continue; sarrayAddString(safiles, ffd.cFileName, L_COPY); } FindClose(hFind); return safiles; }
/*! * numaCreateFromString() * * Input: string (of comma-separated numbers) * Return: na, or null on error * * Notes: * (1) The numbers can be ints or floats; they will be interpreted * and stored as floats. To use them as integers (e.g., for * indexing into arrays), use numaGetIValue(...). */ NUMA * numaCreateFromString(const char *str) { char *substr; l_int32 i, n, nerrors; l_float32 val; NUMA *na; SARRAY *sa; PROCNAME("numaCreateFromString"); if (!str || (strlen(str) == 0)) return (NUMA *)ERROR_PTR("str not defined or empty", procName, NULL); sa = sarrayCreate(0); sarraySplitString(sa, str, ","); n = sarrayGetCount(sa); na = numaCreate(n); nerrors = 0; for (i = 0; i < n; i++) { substr = sarrayGetString(sa, i, L_NOCOPY); if (sscanf(substr, "%f", &val) != 1) { L_ERROR("substr %d not float\n", procName, i); nerrors++; } else { numaAddNumber(na, val); } } sarrayDestroy(&sa); if (nerrors > 0) { numaDestroy(&na); return (NUMA *)ERROR_PTR("non-floats in string", procName, NULL); } return na; }
/*! * \brief l_genDataString() * * \param[in] filein input file of serialized data * \param[in] ifunc index into set of functions in output file * \return encoded ascii data string, or NULL on error reading from file */ static char * l_genDataString(const char *filein, l_int32 ifunc) { char buf[80]; char *cdata1, *cdata2, *cdata3; l_uint8 *data1, *data2; l_int32 csize1, csize2; size_t size1, size2; SARRAY *sa; PROCNAME("l_genDataString"); if (!filein) return (char *)ERROR_PTR("filein not defined", procName, NULL); /* Read it in, gzip it, encode, and reformat. We gzip because some * serialized data has a significant amount of ascii content. */ if ((data1 = l_binaryRead(filein, &size1)) == NULL) return (char *)ERROR_PTR("bindata not returned", procName, NULL); data2 = zlibCompress(data1, size1, &size2); cdata1 = encodeBase64(data2, size2, &csize1); cdata2 = reformatPacked64(cdata1, csize1, 4, 72, 1, &csize2); LEPT_FREE(data1); LEPT_FREE(data2); LEPT_FREE(cdata1); /* Prepend the string declaration signature and put it together */ sa = sarrayCreate(3); snprintf(buf, sizeof(buf), "static const char *l_strdata_%d =\n", ifunc); sarrayAddString(sa, buf, L_COPY); sarrayAddString(sa, cdata2, L_INSERT); sarrayAddString(sa, (char *)";\n", L_COPY); cdata3 = sarrayToString(sa, 0); sarrayDestroy(&sa); return cdata3; }
int main(int argc, char **argv) { char *filein, *str, *prestring, *outprotos, *protostr; const char *spacestr = " "; char buf[L_BUF_SIZE]; l_uint8 *allheaders; l_int32 i, maxindex, in_line, nflags, protos_added, firstfile, len, ret; size_t nbytes; L_BYTEA *ba, *ba2; SARRAY *sa, *safirst; static char mainName[] = "xtractprotos"; if (argc == 1) { fprintf(stderr, "xtractprotos [-prestring=<string>] [-protos=<where>] " "[list of C files]\n" "where the prestring is prepended to each prototype, and \n" "protos can be either 'inline' or the name of an output " "prototype file\n"); return 1; } /* ---------------------------------------------------------------- */ /* Parse input flags and find prestring and outprotos, if requested */ /* ---------------------------------------------------------------- */ prestring = outprotos = NULL; in_line = FALSE; nflags = 0; maxindex = L_MIN(3, argc); for (i = 1; i < maxindex; i++) { if (argv[i][0] == '-') { if (!strncmp(argv[i], "-prestring", 10)) { nflags++; ret = sscanf(argv[i] + 1, "prestring=%s", buf); if (ret != 1) { fprintf(stderr, "parse failure for prestring\n"); return 1; } if ((len = strlen(buf)) > L_BUF_SIZE - 3) { L_WARNING("prestring too large; omitting!\n", mainName); } else { buf[len] = ' '; buf[len + 1] = '\0'; prestring = stringNew(buf); } } else if (!strncmp(argv[i], "-protos", 7)) { nflags++; ret = sscanf(argv[i] + 1, "protos=%s", buf); if (ret != 1) { fprintf(stderr, "parse failure for protos\n"); return 1; } outprotos = stringNew(buf); if (!strncmp(outprotos, "inline", 7)) in_line = TRUE; } } } if (argc - nflags < 2) { fprintf(stderr, "no files specified!\n"); return 1; } /* ---------------------------------------------------------------- */ /* Generate the prototype string */ /* ---------------------------------------------------------------- */ ba = l_byteaCreate(500); /* First the extern C head */ sa = sarrayCreate(0); sarrayAddString(sa, (char *)"/*", 1); snprintf(buf, L_BUF_SIZE, " * These prototypes were autogen'd by xtractprotos, v. %s", version); sarrayAddString(sa, buf, 1); sarrayAddString(sa, (char *)" */", 1); sarrayAddString(sa, (char *)"#ifdef __cplusplus", 1); sarrayAddString(sa, (char *)"extern \"C\" {", 1); sarrayAddString(sa, (char *)"#endif /* __cplusplus */\n", 1); str = sarrayToString(sa, 1); l_byteaAppendString(ba, str); lept_free(str); sarrayDestroy(&sa); /* Then the prototypes */ firstfile = 1 + nflags; protos_added = FALSE; for (i = firstfile; i < argc; i++) { filein = argv[i]; len = strlen(filein); if (filein[len - 1] == 'h') /* skip .h files */ continue; snprintf(buf, L_BUF_SIZE, "cpp -ansi -DNO_PROTOS %s %s", filein, tempfile); ret = system(buf); if (ret) { fprintf(stderr, "cpp failure for %s; continuing\n", filein); continue; } if ((str = parseForProtos(tempfile, prestring)) == NULL) { fprintf(stderr, "parse failure for %s; continuing\n", filein); continue; } if (strlen(str) > 1) { /* strlen(str) == 1 is a file without protos */ l_byteaAppendString(ba, str); protos_added = TRUE; } lept_free(str); } /* Lastly the extern C tail */ sa = sarrayCreate(0); sarrayAddString(sa, (char *)"\n#ifdef __cplusplus", 1); sarrayAddString(sa, (char *)"}", 1); sarrayAddString(sa, (char *)"#endif /* __cplusplus */", 1); str = sarrayToString(sa, 1); l_byteaAppendString(ba, str); lept_free(str); sarrayDestroy(&sa); protostr = (char *)l_byteaCopyData(ba, &nbytes); l_byteaDestroy(&ba); /* ---------------------------------------------------------------- */ /* Generate the output */ /* ---------------------------------------------------------------- */ if (!outprotos) { /* just write to stdout */ fprintf(stderr, "%s\n", protostr); lept_free(protostr); return 0; } /* If no protos were found, do nothing further */ if (!protos_added) { fprintf(stderr, "No protos found\n"); lept_free(protostr); return 1; } /* Make the output files */ ba = l_byteaInitFromFile("allheaders_top.txt"); if (!in_line) { snprintf(buf, sizeof(buf), "#include \"%s\"\n", outprotos); l_byteaAppendString(ba, buf); l_binaryWrite(outprotos, "w", protostr, nbytes); } else { l_byteaAppendString(ba, protostr); } ba2 = l_byteaInitFromFile("allheaders_bot.txt"); l_byteaJoin(ba, &ba2); l_byteaWrite("allheaders.h", ba, 0, 0); l_byteaDestroy(&ba); lept_free(protostr); return 0; }
/*! * \brief recogCreate() * * \param[in] scalew scale all widths to this; use 0 otherwise * \param[in] scaleh scale all heights to this; use 0 otherwise * \param[in] linew width of normalized strokes; use 0 to skip * \param[in] threshold for binarization; typically ~128; 0 for default * \param[in] maxyshift from nominal centroid alignment; default is 1 * \return recog, or NULL on error * * <pre> * Notes: * (1) If %scalew == 0 and %scaleh == 0, no scaling is done. * If one of these is 0 and the other is > 0, scaling is isotropic * to the requested size. We typically do not set both > 0. * (2) Use linew > 0 to convert the templates to images with fixed * width strokes. linew == 0 skips the conversion. * (3) The only valid values for %maxyshift are 0, 1 and 2. * It is recommended to use %maxyshift == 1 (default value). * Using %maxyshift == 0 is much faster than %maxyshift == 1, but * it is much less likely to find the template with the best * correlation. Use of anything but 1 results in a warning. * (4) Scaling is used for finding outliers and for training a * book-adapted recognizer (BAR) from a bootstrap recognizer (BSR). * Scaling the height to a fixed value and scaling the width * accordingly (e.g., %scaleh = 40, %scalew = 0) is recommended. * (5) The storage for most of the arrays is allocated when training * is finished. * </pre> */ L_RECOG * recogCreate(l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift) { L_RECOG *recog; PROCNAME("recogCreate"); if (scalew < 0 || scaleh < 0) return (L_RECOG *)ERROR_PTR("invalid scalew or scaleh", procName, NULL); if (linew > 10) return (L_RECOG *)ERROR_PTR("invalid linew > 10", procName, NULL); if (threshold == 0) threshold = DEFAULT_THRESHOLD; if (threshold < 0 || threshold > 255) { L_WARNING("invalid threshold; using default\n", procName); threshold = DEFAULT_THRESHOLD; } if (maxyshift < 0 || maxyshift > 2) { L_WARNING("invalid maxyshift; using default value\n", procName); maxyshift = DEFAULT_MAXYSHIFT; } else if (maxyshift == 0) { L_WARNING("Using maxyshift = 0; faster, worse correlation results\n", procName); } else if (maxyshift == 2) { L_WARNING("Using maxyshift = 2; slower\n", procName); } if ((recog = (L_RECOG *)LEPT_CALLOC(1, sizeof(L_RECOG))) == NULL) return (L_RECOG *)ERROR_PTR("rec not made", procName, NULL); recog->templ_use = L_USE_ALL_TEMPLATES; /* default */ recog->threshold = threshold; recog->scalew = scalew; recog->scaleh = scaleh; recog->linew = linew; recog->maxyshift = maxyshift; recogSetParams(recog, 1, -1, -1.0, -1.0); recog->bmf = bmfCreate(NULL, 6); recog->bmf_size = 6; recog->maxarraysize = MAX_EXAMPLES_IN_CLASS; /* Generate the LUTs */ recog->centtab = makePixelCentroidTab8(); recog->sumtab = makePixelSumTab8(); recog->sa_text = sarrayCreate(0); recog->dna_tochar = l_dnaCreate(0); /* Input default values for min component size for splitting. * These are overwritten when pixTrainingFinished() is called. */ recog->min_splitw = 6; recog->max_splith = 60; /* Allocate the paa for the unscaled training bitmaps */ recog->pixaa_u = pixaaCreate(recog->maxarraysize); /* Generate the storage for debugging */ recog->pixadb_boot = pixaCreate(2); recog->pixadb_split = pixaCreate(2); return recog; }
/*! * pixMorphCompSequenceDwa() * * Input: pixs * sequence (string specifying sequence) * dispsep (horizontal separation in pixels between * successive displays; use zero to suppress display) * Return: pixd, or null on error * * Notes: * (1) This does dwa morphology on binary images, using brick Sels. * (2) This runs a pipeline of operations; no branching is allowed. * (3) It implements all brick Sels that have dimensions up to 63 * on each side, using a composite (linear + comb) when useful. * (4) A new image is always produced; the input image is not changed. * (5) This contains an interpreter, allowing sequences to be * generated and run. * (6) See pixMorphSequence() for further information about usage. */ PIX * pixMorphCompSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep) { char *rawop, *op; l_int32 nops, i, j, nred, fact, w, h, x, y, border; l_int32 level[4]; PIX *pixt1, *pixt2; SARRAY *sa; PROCNAME("pixMorphCompSequenceDwa"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!sequence) return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); /* Split sequence into individual operations */ sa = sarrayCreate(0); sarraySplitString(sa, sequence, "+"); nops = sarrayGetCount(sa); if (!morphSequenceVerify(sa)) { sarrayDestroy(&sa); return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); } /* Parse and operate */ border = 0; pixt1 = pixCopy(NULL, pixs); pixt2 = NULL; x = y = 0; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, 0); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixDilateCompBrickDwa(NULL, pixt1, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, y); x += dispsep; } break; case 'e': case 'E': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixErodeCompBrickDwa(NULL, pixt1, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, y); x += dispsep; } break; case 'o': case 'O': sscanf(&op[1], "%d.%d", &w, &h); pixOpenCompBrickDwa(pixt1, pixt1, w, h); if (dispsep > 0) { pixDisplay(pixt1, x, y); x += dispsep; } break; case 'c': case 'C': sscanf(&op[1], "%d.%d", &w, &h); pixCloseCompBrickDwa(pixt1, pixt1, w, h); if (dispsep > 0) { pixDisplay(pixt1, x, y); x += dispsep; } break; case 'r': case 'R': nred = strlen(op) - 1; for (j = 0; j < nred; j++) level[j] = op[j + 1] - '0'; for (j = nred; j < 4; j++) level[j] = 0; pixt2 = pixReduceRankBinaryCascade(pixt1, level[0], level[1], level[2], level[3]); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, y); x += dispsep; } break; case 'x': case 'X': sscanf(&op[1], "%d", &fact); pixt2 = pixExpandReplicate(pixt1, fact); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, y); x += dispsep; } break; case 'b': case 'B': sscanf(&op[1], "%d", &border); pixt2 = pixAddBorder(pixt1, border, 0); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, y); x += dispsep; } break; default: /* All invalid ops are caught in the first pass */ break; } FREE(op); } if (border > 0) { pixt2 = pixRemoveBorder(pixt1, border); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); } sarrayDestroy(&sa); return pixt1; }
/*! * gplotAddPlot() * * Input: gplot * nax (<optional> numa: set to null for Y_VS_I; * required for Y_VS_X) * nay (numa: required for both Y_VS_I and Y_VS_X) * plotstyle (GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, * GPLOT_LINESPOINTS, GPLOT_DOTS) * plottitle (<optional> title for individual plot) * Return: 0 if OK, 1 on error * * Notes: * (1) There are 2 options for (x,y) values: * o To plot an array vs a linear function of the * index, set nax = NULL. * o To plot one array vs another, use both nax and nay. * (2) If nax is NULL, the x value corresponding to the i-th * value of nay is found from the startx and delx fields * in nay: * x = startx + i * delx * These are set with numaSetParameters(). Their default * values are startx = 0.0, delx = 1.0. * (3) If nax is defined, it must be the same size as nay. * (4) The 'plottitle' string can have spaces, double * quotes and backquotes, but not single quotes. */ l_int32 gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plottitle) { char buf[L_BUF_SIZE]; char emptystring[] = ""; char *datastr, *title; l_int32 n, i; l_float32 valx, valy, startx, delx; SARRAY *sa; PROCNAME("gplotAddPlot"); if (!gplot) return ERROR_INT("gplot not defined", procName, 1); if (!nay) return ERROR_INT("nay not defined", procName, 1); if (plotstyle != GPLOT_LINES && plotstyle != GPLOT_POINTS && plotstyle != GPLOT_IMPULSES && plotstyle != GPLOT_LINESPOINTS && plotstyle != GPLOT_DOTS) return ERROR_INT("invalid plotstyle", procName, 1); n = numaGetCount(nay); numaGetParameters(nay, &startx, &delx); if (nax) { if (n != numaGetCount(nax)) return ERROR_INT("nax and nay sizes differ", procName, 1); } /* Save plotstyle and plottitle */ numaAddNumber(gplot->plotstyles, plotstyle); if (plottitle) { title = stringNew(plottitle); sarrayAddString(gplot->plottitles, title, L_INSERT); } else { sarrayAddString(gplot->plottitles, emptystring, L_COPY); } /* Generate and save data filename */ gplot->nplots++; snprintf(buf, L_BUF_SIZE, "%s.data.%d", gplot->rootname, gplot->nplots); sarrayAddString(gplot->datanames, buf, L_COPY); /* Generate data and save as a string */ sa = sarrayCreate(n); for (i = 0; i < n; i++) { if (nax) numaGetFValue(nax, i, &valx); else valx = startx + i * delx; numaGetFValue(nay, i, &valy); snprintf(buf, L_BUF_SIZE, "%f %f\n", valx, valy); sarrayAddString(sa, buf, L_COPY); } datastr = sarrayToString(sa, 0); sarrayAddString(gplot->plotdata, datastr, L_INSERT); sarrayDestroy(&sa); return 0; }
/*! * pixGrayMorphSequence() * * Input: pixs * sequence (string specifying sequence) * dispsep (horizontal separation in pixels between * successive displays; use zero to suppress display) * dispy (if dispsep != 0, this gives the y-value of the * UL corner for display; otherwise it is ignored) * Return: pixd, or null on error * * Notes: * (1) This works on 8 bpp grayscale images. * (2) This runs a pipeline of operations; no branching is allowed. * (3) This only uses brick SELs. * (4) A new image is always produced; the input image is not changed. * (5) This contains an interpreter, allowing sequences to be * generated and run. * (6) The format of the sequence string is defined below. * (7) In addition to morphological operations, the composite * morph/subtract tophat can be performed. * (8) Sel sizes (width, height) must each be odd numbers. * (9) Intermediate results can optionally be displayed * (10) The sequence string is formatted as follows: * - An arbitrary number of operations, each separated * by a '+' character. White space is ignored. * - Each operation begins with a case-independent character * specifying the operation: * d or D (dilation) * e or E (erosion) * o or O (opening) * c or C (closing) * t or T (tophat) * - The args to the morphological operations are bricks of hits, * and are formatted as a.b, where a and b are horizontal and * vertical dimensions, rsp. (each must be an odd number) * - The args to the tophat are w or W (for white tophat) * or b or B (for black tophat), followed by a.b as for * the dilation, erosion, opening and closing. * Example valid sequences are: * "c5.3 + o7.5" * "c9.9 + tw9.9" */ PIX * pixGrayMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy) { char *rawop, *op; l_int32 nops, i, valid, w, h, x; PIX *pixt1, *pixt2; SARRAY *sa; PROCNAME("pixGrayMorphSequence"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!sequence) return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); /* Split sequence into individual operations */ sa = sarrayCreate(0); sarraySplitString(sa, sequence, "+"); nops = sarrayGetCount(sa); /* Verify that the operation sequence is valid */ valid = TRUE; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, 0); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': case 'e': case 'E': case 'o': case 'O': case 'c': case 'C': if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { fprintf(stderr, "*** op: %s invalid\n", op); valid = FALSE; break; } if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { fprintf(stderr, "*** op: %s; w = %d, h = %d; must both be odd\n", op, w, h); valid = FALSE; break; } /* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ break; case 't': case 'T': if (op[1] != 'w' && op[1] != 'W' && op[1] != 'b' && op[1] != 'B') { fprintf(stderr, "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]); valid = FALSE; break; } sscanf(&op[2], "%d.%d", &w, &h); if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { fprintf(stderr, "*** op: %s; w = %d, h = %d; must both be odd\n", op, w, h); valid = FALSE; break; } /* fprintf(stderr, "op = %s", op); */ break; default: fprintf(stderr, "*** nonexistent op = %s\n", op); valid = FALSE; } FREE(op); } if (!valid) { sarrayDestroy(&sa); return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); } /* Parse and operate */ pixt1 = pixCopy(NULL, pixs); pixt2 = NULL; x = 0; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, 0); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixDilateGray(pixt1, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; case 'e': case 'E': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixErodeGray(pixt1, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; case 'o': case 'O': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixOpenGray(pixt1, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; case 'c': case 'C': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixCloseGray(pixt1, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; case 't': case 'T': sscanf(&op[2], "%d.%d", &w, &h); if (op[1] == 'w' || op[1] == 'W') pixt2 = pixTophat(pixt1, w, h, L_TOPHAT_WHITE); else /* 'b' or 'B' */ pixt2 = pixTophat(pixt1, w, h, L_TOPHAT_BLACK); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; default: /* All invalid ops are caught in the first pass */ break; } FREE(op); } sarrayDestroy(&sa); return pixt1; }
main(int argc, char **argv) { char *errorstr; l_int32 same, error; PIX *pixs1, *pixs2, *pixs4, *pixs8, *pixs16, *pixs32, *pixd; PIX *pixc2, *pixc4, *pixc8; PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; PIXCMAP *cmap; SARRAY *sa; static char mainName[] = "convert_reg"; if (argc != 1) exit(ERROR_INT(" Syntax: convert_rt", mainName, 1)); if ((pixs1 = pixRead("test1.png")) == NULL) exit(ERROR_INT("pixs1 not made", mainName, 1)); if ((pixs2 = pixRead("dreyfus2.png")) == NULL) exit(ERROR_INT("pixs2 not made", mainName, 1)); if ((pixc2 = pixRead("weasel2.4c.png")) == NULL) exit(ERROR_INT("pixc2 not made", mainName, 1)); if ((pixs4 = pixRead("weasel4.16g.png")) == NULL) exit(ERROR_INT("pixs4 not made", mainName, 1)); if ((pixc4 = pixRead("weasel4.11c.png")) == NULL) exit(ERROR_INT("pixc4 not made", mainName, 1)); if ((pixs8 = pixRead("karen8.jpg")) == NULL) exit(ERROR_INT("pixs8 not made", mainName, 1)); if ((pixc8 = pixRead("weasel8.240c.png")) == NULL) exit(ERROR_INT("pixc8 not made", mainName, 1)); if ((pixs16 = pixRead("test16.tif")) == NULL) exit(ERROR_INT("pixs16 not made", mainName, 1)); if ((pixs32 = pixRead("marge.jpg")) == NULL) exit(ERROR_INT("pixs32 not made", mainName, 1)); error = FALSE; sa = sarrayCreate(0); /* Conversion: 1 bpp --> 8 bpp --> 1 bpp */ pixt1 = pixConvertTo8(pixs1, FALSE); pixt2 = pixThreshold8(pixt1, 1, 0, 0); pixEqual(pixs1, pixt2, &same); if (!same) { pixDisplayWithTitle(pixs1, 100, 100, "1 bpp, no cmap", DFLAG); pixDisplayWithTitle(pixt2, 500, 100, "1 bpp, no cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 1 bpp <==> 8 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 1 bpp <==> 8 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Conversion: 2 bpp --> 8 bpp --> 2 bpp */ /* Conversion: 2 bpp cmap --> 8 bpp cmap --> 2 bpp cmap */ pixt1 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_GRAYSCALE); pixt2 = pixThreshold8(pixt1, 2, 4, 0); pixt3 = pixConvertTo8(pixt2, FALSE); pixt4 = pixThreshold8(pixt3, 2, 4, 0); pixEqual(pixt2, pixt4, &same); if (!same) { pixDisplayWithTitle(pixt2, 100, 100, "2 bpp, no cmap", DFLAG); pixDisplayWithTitle(pixt4, 500, 100, "2 bpp, no cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 2 bpp <==> 8 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 2 bpp <==> 8 bpp\n"); pixt5 = pixConvertTo8(pixs2, TRUE); pixt6 = pixThreshold8(pixt5, 2, 4, 1); pixEqual(pixs2, pixt6, &same); if (!same) { pixDisplayWithTitle(pixs2, 100, 100, "2 bpp, cmap", DFLAG); pixDisplayWithTitle(pixt6, 500, 100, "2 bpp, cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 2 bpp <==> 8 bpp; cmap", L_COPY); } else fprintf(stderr, "OK: conversion 2 bpp <==> 8 bpp; cmap\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); /* Conversion: 4 bpp --> 8 bpp --> 4 bpp */ /* Conversion: 4 bpp cmap --> 8 bpp cmap --> 4 bpp cmap */ pixt1 = pixRemoveColormap(pixs4, REMOVE_CMAP_TO_GRAYSCALE); pixt2 = pixThreshold8(pixt1, 4, 16, 0); pixt3 = pixConvertTo8(pixt2, FALSE); pixt4 = pixThreshold8(pixt3, 4, 16, 0); pixEqual(pixt2, pixt4, &same); if (!same) { pixDisplayWithTitle(pixt2, 100, 100, "4 bpp, no cmap", DFLAG); pixDisplayWithTitle(pixt4, 500, 100, "4 bpp, no cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 4 bpp <==> 8 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 4 bpp <==> 8 bpp\n"); pixt5 = pixConvertTo8(pixs4, TRUE); pixt6 = pixThreshold8(pixt5, 4, 16, 1); pixEqual(pixs4, pixt6, &same); if (!same) { pixDisplayWithTitle(pixs4, 100, 100, "4 bpp, cmap", DFLAG); pixDisplayWithTitle(pixt6, 500, 100, "4 bpp, cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 4 bpp <==> 8 bpp, cmap", L_COPY); } else fprintf(stderr, "OK: conversion 4 bpp <==> 8 bpp; cmap\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); /* Conversion: 2 bpp cmap --> 2 bpp --> 2 bpp cmap --> 2 bpp */ pixt1 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_GRAYSCALE); pixt2 = pixConvertGrayToColormap(pixt1); pixt3 = pixRemoveColormap(pixt2, REMOVE_CMAP_TO_GRAYSCALE); pixt4 = pixThresholdTo2bpp(pixt3, 4, 1); pixEqual(pixt1, pixt4, &same); if (!same) { pixDisplayWithTitle(pixs2, 100, 100, "2 bpp, cmap", DFLAG); pixDisplayWithTitle(pixt4, 500, 100, "2 bpp, cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 2 bpp <==> 2 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 2 bpp <==> 2 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); /* Conversion: 4 bpp cmap --> 4 bpp --> 4 bpp cmap --> 4 bpp */ pixt1 = pixRemoveColormap(pixs4, REMOVE_CMAP_TO_GRAYSCALE); pixt2 = pixConvertGrayToColormap(pixt1); pixt3 = pixRemoveColormap(pixt2, REMOVE_CMAP_TO_GRAYSCALE); pixt4 = pixThresholdTo4bpp(pixt3, 16, 1); pixEqual(pixt1, pixt4, &same); if (!same) { pixDisplayWithTitle(pixs4, 100, 100, "4 bpp, cmap", DFLAG); pixDisplayWithTitle(pixt4, 500, 100, "4 bpp, cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 4 bpp <==> 4 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 4 bpp <==> 4 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); /* Conversion: 8 bpp --> 8 bpp cmap --> 8 bpp */ pixt1 = pixConvertTo8(pixs8, TRUE); pixt2 = pixConvertTo8(pixt1, FALSE); pixEqual(pixs8, pixt2, &same); if (!same) { pixDisplayWithTitle(pixt1, 100, 100, "8 bpp, cmap", DFLAG); pixDisplayWithTitle(pixt2, 500, 100, "8 bpp, no cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 8 bpp <==> 8 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 8 bpp <==> 8 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Conversion: 2 bpp cmap --> 32 bpp --> 2 bpp cmap */ pixt1 = pixConvertTo8(pixc2, TRUE); pixt2 = pixConvertTo32(pixt1); pixt3 = pixConvertTo32(pixc2); pixEqual(pixt2, pixt3, &same); if (!same) { pixDisplayWithTitle(pixt2, 100, 100, "32 bpp", DFLAG); pixDisplayWithTitle(pixt3, 500, 100, "32 bpp", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 2 bpp ==> 32 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 2 bpp <==> 32 bpp\n"); cmap = pixGetColormap(pixc2); pixt4 = pixOctcubeQuantFromCmap(pixt3, cmap, 2, 4, L_EUCLIDEAN_DISTANCE); pixEqual(pixc2, pixt4, &same); if (!same) { pixDisplayWithTitle(pixc2, 100, 100, "4 bpp, cmap", DFLAG); pixDisplayWithTitle(pixt4, 500, 100, "4 bpp, cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 2 bpp <==> 32 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 2 bpp <==> 32 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); /* Conversion: 4 bpp cmap --> 32 bpp --> 4 bpp cmap */ pixt1 = pixConvertTo8(pixc4, TRUE); pixt2 = pixConvertTo32(pixt1); pixt3 = pixConvertTo32(pixc4); pixEqual(pixt2, pixt3, &same); if (!same) { pixDisplayWithTitle(pixt2, 100, 100, "32 bpp", DFLAG); pixDisplayWithTitle(pixt3, 500, 100, "32 bpp", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 4 bpp ==> 32 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 4 bpp <==> 32 bpp\n"); cmap = pixGetColormap(pixc4); pixt4 = pixOctcubeQuantFromCmap(pixt3, cmap, 2, 4, L_EUCLIDEAN_DISTANCE); pixEqual(pixc4, pixt4, &same); if (!same) { pixDisplayWithTitle(pixc4, 100, 100, "4 bpp, cmap", DFLAG); pixDisplayWithTitle(pixt4, 500, 100, "4 bpp, cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 4 bpp <==> 32 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 4 bpp <==> 32 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); /* Conversion: 8 bpp --> 32 bpp --> 8 bpp */ pixt1 = pixConvertTo32(pixs8); pixt2 = pixConvertTo8(pixt1, FALSE); pixEqual(pixs8, pixt2, &same); if (!same) { pixDisplayWithTitle(pixs8, 100, 100, "8 bpp", DFLAG); pixDisplayWithTitle(pixt2, 500, 100, "8 bpp", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 8 bpp <==> 32 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 8 bpp <==> 32 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Conversion: 8 bpp --> 16 bpp --> 8 bpp */ pixt1 = pixConvert8To16(pixs8, 8); pixt2 = pixConvertTo8(pixt1, FALSE); pixEqual(pixs8, pixt2, &same); if (!same) { pixDisplayWithTitle(pixs8, 100, 100, "8 bpp", DFLAG); pixDisplayWithTitle(pixt2, 500, 100, "8 bpp", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 8 bpp <==> 16 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 8 bpp <==> 16 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Conversion: 16 bpp --> 8 bpp --> 16 bpp */ pixt1 = pixConvert16To8(pixs16, 1); pixt2 = pixConvertTo16(pixt1); pixWrite("/tmp/junkpix.png", pixt2, IFF_PNG); pixEqual(pixs16, pixt2, &same); if (!same) { pixDisplayWithTitle(pixs16, 100, 100, "16 bpp", DFLAG); pixDisplayWithTitle(pixt2, 500, 100, "16 bpp", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 16 bpp <==> 8 bpp", L_COPY); } else fprintf(stderr, "OK: conversion 16 bpp <==> 8 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Conversion: 8 bpp cmap --> 32 bpp --> 8 bpp cmap */ /* Required to go to level 6 of octcube to get identical result */ pixt1 = pixConvertTo32(pixc8); cmap = pixGetColormap(pixc8); pixt2 = pixOctcubeQuantFromCmap(pixt1, cmap, 2, 6, L_EUCLIDEAN_DISTANCE); pixEqual(pixc8, pixt2, &same); if (!same) { pixDisplayWithTitle(pixc8, 100, 100, "8 bpp cmap", DFLAG); pixDisplayWithTitle(pixt2, 500, 100, "8 bpp cmap", DFLAG); error = TRUE; sarrayAddString(sa, (char *)"conversion 8 bpp cmap <==> 32 bpp cmap", L_COPY); } else fprintf(stderr, "OK: conversion 8 bpp <==> 32 bpp\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Summarize results */ if (error == FALSE) fprintf(stderr, "No errors found\n"); else { errorstr = sarrayToString(sa, 1); fprintf(stderr, "Errors in the following:\n %s", errorstr); lept_free(errorstr); } sarrayDestroy(&sa); pixDestroy(&pixs1); pixDestroy(&pixs2); pixDestroy(&pixs4); pixDestroy(&pixc2); pixDestroy(&pixc4); pixDestroy(&pixs8); pixDestroy(&pixc8); pixDestroy(&pixs16); pixDestroy(&pixs32); return 0; }