/*! * pixWriteMemGif() * * Input: &data (<return> data of tiff compressed image) * &size (<return> size of returned data) * pix * Return: 0 if OK, 1 on error * * Notes: * (1) Of course, we are cheating here -- writing the pix out * as a gif file and then reading it back into memory. */ l_int32 pixWriteMemGif(l_uint8 **pdata, size_t *psize, PIX *pix) { char *tname; l_uint8 *data; l_int32 nbytes; PROCNAME("pixWriteMemGif"); if (!pdata) return ERROR_INT("&data not defined", procName, 1 ); if (!psize) return ERROR_INT("&size not defined", procName, 1 ); if (!pix) return ERROR_INT("&pix not defined", procName, 1 ); tname = genTempFilename("/tmp/", "junk_mem_gif.blah", 1); pixWrite(tname, pix, IFF_GIF); data = arrayRead(tname, &nbytes); FREE(tname); if (!data) return ERROR_INT("data not returned", procName, 1 ); *pdata = data; *psize = nbytes; return 0; }
/* * 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; }
/*! * extractJpegDataFromFile() * * Input: filein * &data (<return> binary data consisting of the entire jpeg file) * &nbytes (<return> size of binary data) * &w (<optional return> image width) * &h (<optional return> image height) * &bps (<optional return> bits/sample; should be 8) * &spp (<optional return> samples/pixel; should be 1 or 3) * Return: 0 if OK, 1 on error */ l_int32 extractJpegDataFromFile(const char *filein, l_uint8 **pdata, l_int32 *pnbytes, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp) { l_uint8 *data; l_int32 format, nbytes; FILE *fpin; PROCNAME("extractJpegDataFromFile"); if (!filein) return ERROR_INT("filein not defined", procName, 1); if (!pdata) return ERROR_INT("&data not defined", procName, 1); if (!pnbytes) return ERROR_INT("&nbytes not defined", procName, 1); if (!pw && !ph && !pbps && !pspp) return ERROR_INT("no output data requested", procName, 1); *pdata = NULL; *pnbytes = 0; if ((fpin = fopen(filein, "rb")) == NULL) return ERROR_INT("filein not defined", procName, 1); findFileFormat(fpin, &format); fclose(fpin); if (format != IFF_JFIF_JPEG) return ERROR_INT("filein not jfif jpeg", procName, 1); if ((data = arrayRead(filein, &nbytes)) == NULL) return ERROR_INT("inarray not made", procName, 1); *pnbytes = nbytes; *pdata = data; /* On error, free the data */ if (extractJpegDataFromArray(data, nbytes, pw, ph, pbps, pspp)) { FREE(data); *pdata = NULL; *pnbytes = 0; } return 0; }
/*! * regTestCleanup() * * Input: argc (to regtest: either 1 or 2) * argv (to regtest: if @argc == 2, @argv[1] is either * "generate" or a log file name) * fp (stream that was used writing to a temporary file; * null for the "generate" case) * success (overall for this reg test) * rp (regression test params; can be null) * Return: 0 if OK, 1 on error * * Notes: * (1) This outputs anything written to the temporary file and * closes the stream to that file. * (2) If a rp struct is made in regTestSetup(), it must be * passed in here for destruction. */ l_int32 regTestCleanup(l_int32 argc, char **argv, FILE *fp, l_int32 success, L_REGPARAMS *rp) { char result[128]; char *tempname, *text, *message; l_int32 nbytes; PROCNAME("regTestCleanup"); if (!fp) { /* for generating golden files; release rp if it exists */ if (rp) FREE(rp); return 0; } fclose(fp); /* Read back data from temp file */ tempname = genTempFilename("/tmp", "regtest_output.txt", 1); text = (char *)arrayRead(tempname, &nbytes); FREE(tempname); if (!text) { if (rp) FREE(rp); return ERROR_INT("text not returned", procName, 1); } /* Prepare result message */ if (rp) /* if either is 0, success == FALSE */ success = rp->success && success; if (success) snprintf(result, sizeof(result), "SUCCESS: %s\n", argv[0]); else snprintf(result, sizeof(result), "FAILURE: %s\n", argv[0]); message = stringJoin(text, result); FREE(text); if (argc == 1) fprintf(stderr, "%s", message); else fileAppendString(argv[1], message); FREE(message); if (rp) FREE(rp); return 0; }
/*! * kernelCreateFromFile() * * Input: filename * Return: kernel, or null on error * * Notes: * (1) The file contains, in the following order: * - Any number of comment lines starting with '#' are ignored * - The height and width of the kernel * - The y and x values of the kernel origin * - The kernel data, formatted as lines of numbers (integers * or floats) for the kernel values in row-major order, * and with no other punctuation. * (Note: this differs from kernelCreateFromString(), * where each line must begin and end with a double-quote * to tell the compiler it's part of a string.) * - The kernel specification ends when a blank line, * a comment line, or the end of file is reached. * (2) All lines must be left-justified. * (3) See kernelCreateFromString() for a description of the string * format for the kernel data. As an example, here are the lines * of a valid kernel description file In the file, all lines * are left-justified: * # small 3x3 kernel * 3 3 * 1 1 * 25.5 51 24.3 * 70.2 146.3 73.4 * 20 50.9 18.4 */ L_KERNEL * kernelCreateFromFile(const char *filename) { char *filestr, *line; l_int32 nbytes, nlines, i, j, first, index, w, h, cx, cy, n; l_float32 val; NUMA *na, *nat; SARRAY *sa; L_KERNEL *kel; PROCNAME("kernelCreateFromFile"); if (!filename) return (L_KERNEL *)ERROR_PTR("filename not defined", procName, NULL); filestr = (char *)arrayRead(filename, &nbytes); sa = sarrayCreateLinesFromString(filestr, 1); FREE(filestr); nlines = sarrayGetCount(sa); /* Find the first data line. */ for (i = 0; i < nlines; i++) { line = sarrayGetString(sa, i, L_NOCOPY); if (line[0] != '#') { first = i; break; } } /* Find the kernel dimensions and origin location. */ line = sarrayGetString(sa, first, L_NOCOPY); if (sscanf(line, "%d %d", &h, &w) != 2) return (L_KERNEL *)ERROR_PTR("error reading h,w", procName, NULL); line = sarrayGetString(sa, first + 1, L_NOCOPY); if (sscanf(line, "%d %d", &cy, &cx) != 2) return (L_KERNEL *)ERROR_PTR("error reading cy,cx", procName, NULL); /* Extract the data. This ends when we reach eof, or when we * encounter a line of data that is either a null string or * contains just a newline. */ na = numaCreate(0); for (i = first + 2; i < nlines; i++) { line = sarrayGetString(sa, i, L_NOCOPY); if (line[0] == '\0' || line[0] == '\n' || line[0] == '#') break; nat = parseStringForNumbers(line, " \t\n"); numaJoin(na, nat, 0, 0); numaDestroy(&nat); } sarrayDestroy(&sa); n = numaGetCount(na); if (n != w * h) { numaDestroy(&na); fprintf(stderr, "w = %d, h = %d, num ints = %d\n", w, h, n); return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL); } kel = kernelCreate(h, w); kernelSetOrigin(kel, cy, cx); index = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { numaGetFValue(na, index, &val); kernelSetElement(kel, i, j, val); index++; } } numaDestroy(&na); return kel; }
main(int argc, char **argv) { char buf[256]; size_t nbytes; l_int32 i, w, h, success, display; l_int32 format, bps, spp, iscmap, format2, w2, h2, bps2, spp2, iscmap2; l_uint8 *data; l_uint32 *data32, *data32r; BOX *box; FILE *fp; PIX *pixs, *pixt, *pixt2, *pixd; if (regTestSetup(argc, argv, &fp, &display, &success, NULL)) return 1; /* Test basic serialization/deserialization */ for (i = 0; i < nfiles; i++) { pixs = pixRead(filename[i]); /* Serialize to memory */ pixSerializeToMemory(pixs, &data32, &nbytes); /* Just for fun, write and read back from file */ arrayWrite("/tmp/array", "w", data32, nbytes); data32r = (l_uint32 *)arrayRead("/tmp/array", (l_int32 *)(&nbytes)); /* Deserialize */ pixd = pixDeserializeFromMemory(data32r, nbytes); regTestComparePix(fp, argv, pixs, pixd, i, &success); pixDestroy(&pixd); pixDestroy(&pixs); FREE(data32); FREE(data32r); } /* Test read/write fileio interface */ for (i = 0; i < nfiles; i++) { pixs = pixRead(filename[i]); pixGetDimensions(pixs, &w, &h, NULL); box = boxCreate(0, 0, L_MIN(150, w), L_MIN(150, h)); pixt = pixClipRectangle(pixs, box, NULL); boxDestroy(&box); snprintf(buf, sizeof(buf), "/tmp/pixs.%d", i); pixWrite(buf, pixt, IFF_SPIX); regTestCheckFile(fp, argv, buf, i, &success); pixt2 = pixRead(buf); regTestComparePix(fp, argv, pixt, pixt2, nfiles + i, &success); pixDestroy(&pixs); pixDestroy(&pixt); pixDestroy(&pixt2); } /* Test read header. Note that for rgb input, spp = 3, * but for 32 bpp spix, we set spp = 4. */ for (i = 0; i < nfiles; i++) { pixs = pixRead(filename[i]); pixWriteMem(&data, &nbytes, pixs, IFF_SPIX); pixReadHeader(filename[i], &format, &w, &h, &bps, &spp, &iscmap); pixReadHeaderMem(data, nbytes, &format2, &w2, &h2, &bps2, &spp2, &iscmap2); if (format2 != 16 || w != w2 || h != h2 || bps != bps2 || iscmap != iscmap2) { if (fp) fprintf(fp, "Failure comparing data"); else fprintf(stderr, "Failure comparing data"); success = FALSE; } pixDestroy(&pixs); FREE(data); } #if 0 /* Do timing */ for (i = 0; i < nfiles; i++) { pixs = pixRead(filename[i]); startTimer(); pixSerializeToMemory(pixs, &data32, &nbytes); pixd = pixDeserializeFromMemory(data32, nbytes); fprintf(stderr, "Time for %s: %7.3f sec\n", filename[i], stopTimer()); FREE(data32); pixDestroy(&pixs); pixDestroy(&pixd); } #endif regTestCleanup(argc, argv, fp, success, NULL); return 0; }
main(int argc, char **argv) { l_uint8 *array1, *array2; l_int32 i, n, np, same, diff, nbytes1, nbytes2; FILE *fp; BOX *box; BOXA *boxa, *boxa2; PIX *pixs, *pixd; PIXA *pixa; PIXCMAP *cmap; static char mainName[] = "conncomp_reg"; if (argc != 1) exit(ERROR_INT(" Syntax: conncomp_reg", mainName, 1)); if ((pixs = pixRead("feyn.tif")) == NULL) exit(ERROR_INT("pixs not made", mainName, 1)); /* Test pixConnComp() with output to both boxa and pixa */ /* First, test with 4-cc */ boxa = pixConnComp(pixs, &pixa, 4); n = boxaGetCount(boxa); fprintf(stderr, "Number of 4 c.c. b.b: %d\n", n); np = pixaGetCount(pixa); fprintf(stderr, "Number of 4 c.c. pix: %d\n", np); pixd = pixaDisplay(pixa, pixGetWidth(pixs), pixGetHeight(pixs)); pixWrite("/tmp/junkout1.png", pixd, IFF_PNG); pixEqual(pixs, pixd, &same); if (same == 1) fprintf(stderr, "Source and reconstructed pix are the same.\n"); else fprintf(stderr, "Error: source and reconstructed pix differ!\n"); pixaDestroy(&pixa); boxaDestroy(&boxa); pixDestroy(&pixd); /* Test with 8-cc */ boxa = pixConnComp(pixs, &pixa, 8); n = boxaGetCount(boxa); fprintf(stderr, "Number of 8 c.c. b.b: %d\n", n); np = pixaGetCount(pixa); fprintf(stderr, "Number of 8 c.c. pix: %d\n", np); pixd = pixaDisplay(pixa, pixGetWidth(pixs), pixGetHeight(pixs)); pixWrite("/tmp/junkout2.png", pixd, IFF_PNG); pixEqual(pixs, pixd, &same); if (same == 1) fprintf(stderr, "Source and reconstructed pix are the same.\n"); else fprintf(stderr, "Error: source and reconstructed pix differ!\n"); pixaDestroy(&pixa); boxaDestroy(&boxa); pixDestroy(&pixd); /* Test i/o */ boxa = pixConnComp(pixs, NULL, 4); fp = fopen("/tmp/junk1.ba", "wb+"); boxaWriteStream(fp, boxa); fclose(fp); fp = fopen("/tmp/junk1.ba", "r"); boxa2 = boxaReadStream(fp); fclose(fp); fp = fopen("/tmp/junk2.ba", "wb+"); boxaWriteStream(fp, boxa2); fclose(fp); array1 = arrayRead("/tmp/junk1.ba", &nbytes1); array2 = arrayRead("/tmp/junk2.ba", &nbytes2); diff = strcmp((char *)array1, (char *)array2); if (nbytes1 != nbytes2 || diff) fprintf(stderr, "I/O error for boxes.\n"); else fprintf(stderr, "I/O valid for boxes.\n"); FREE(array1); FREE(array2); boxaDestroy(&boxa); boxaDestroy(&boxa2); /* Just for fun, display each component as a random color * in cmapped 8 bpp. Background is color 0; it is set to white. */ boxa = pixConnComp(pixs, &pixa, 4); pixd = pixaDisplayRandomCmap(pixa, pixGetWidth(pixs), pixGetHeight(pixs)); cmap = pixGetColormap(pixd); pixcmapResetColor(cmap, 0, 255, 255, 255); /* reset background to white */ pixDisplay(pixd, 100, 100); boxaDestroy(&boxa); pixDestroy(&pixd); pixaDestroy(&pixa); pixDestroy(&pixs); exit(0); }
/* * 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 that the signature found by cpp is neither * static nor extern. We get 'extern' declarations from * header files, and with some versions of cpp running on * #include <sys/stat.h> we get something of the form: * extern ... (( ... )) ... ( ... ) { ... * For this, the 1st '(' is the lp, the 2nd ')' is the rp, * and there is a lot of garbage between the rp and the lb. * It is easiest to simply reject any signature that starts * with 'extern'. Note also that an 'extern' token has been * prepended to each prototype, so the 'static' or * 'extern' keywords we are looking for, if they exist, * would be the second word. */ satest = sarrayCreateWordsFromString(str); secondword = sarrayGetString(satest, 1, 0); if (strcmp(secondword, "static") && /* not static */ strcmp(secondword, "extern")) { /* not extern */ 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; }