Leptonica 1.83.1
Image processing and image analysis suite
Loading...
Searching...
No Matches
regutils.c
Go to the documentation of this file.
1/*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27
67
68#ifdef HAVE_CONFIG_H
69#include <config_auto.h>
70#endif /* HAVE_CONFIG_H */
71
72#include <string.h>
73#include "allheaders.h"
74
75extern l_int32 NumImageFileFormatExtensions;
76extern const char *ImageFileFormatExtensions[];
77
78static char *getRootNameFromArgv0(const char *argv0);
79
80
81/*--------------------------------------------------------------------*
82 * Regression test utilities *
83 *--------------------------------------------------------------------*/
122l_ok
123regTestSetup(l_int32 argc,
124 char **argv,
125 L_REGPARAMS **prp)
126{
127char *testname, *vers;
128char errormsg[64];
129L_REGPARAMS *rp;
130
131 if (argc != 1 && argc != 2) {
132 snprintf(errormsg, sizeof(errormsg),
133 "Syntax: %s [ [compare] | generate | display ]", argv[0]);
134 return ERROR_INT(errormsg, __func__, 1);
135 }
136
137 if ((testname = getRootNameFromArgv0(argv[0])) == NULL)
138 return ERROR_INT("invalid root", __func__, 1);
139
140 setLeptDebugOK(1); /* required for testing */
141
142 rp = (L_REGPARAMS *)LEPT_CALLOC(1, sizeof(L_REGPARAMS));
143 *prp = rp;
144 rp->testname = testname;
145 rp->index = -1; /* increment before each test */
146
147 /* Initialize to true. A failure in any test is registered
148 * as a failure of the regression test. */
149 rp->success = TRUE;
150
151 /* Make sure the lept/regout subdirectory exists */
152 lept_mkdir("lept/regout");
153
154 /* Only open a stream to a temp file for the 'compare' case */
155 if (argc == 1 || !strcmp(argv[1], "compare")) {
156 rp->mode = L_REG_COMPARE;
157 rp->tempfile = stringNew("/tmp/lept/regout/regtest_output.txt");
158 rp->fp = fopenWriteStream(rp->tempfile, "wb");
159 if (rp->fp == NULL) {
160 rp->success = FALSE;
161 return ERROR_INT("stream not opened for tempfile", __func__, 1);
162 }
163 } else if (!strcmp(argv[1], "generate")) {
164 rp->mode = L_REG_GENERATE;
165 lept_mkdir("lept/golden");
166 } else if (!strcmp(argv[1], "display")) {
167 rp->mode = L_REG_DISPLAY;
168 rp->display = TRUE;
169 } else {
170 LEPT_FREE(rp);
171 snprintf(errormsg, sizeof(errormsg),
172 "Syntax: %s [ [generate] | compare | display ]", argv[0]);
173 return ERROR_INT(errormsg, __func__, 1);
174 }
175
176 /* Print out test name and both the leptonica and
177 * image library versions */
178 lept_stderr("\n////////////////////////////////////////////////\n"
179 "//////////////// %s_reg ///////////////\n"
180 "////////////////////////////////////////////////\n",
181 rp->testname);
182 vers = getLeptonicaVersion();
183 lept_stderr("%s : ", vers);
184 LEPT_FREE(vers);
185 vers = getImagelibVersions();
186 lept_stderr("%s\n", vers);
187 LEPT_FREE(vers);
188
189 rp->tstart = startTimerNested();
190 return 0;
191}
192
193
206l_ok
207regTestCleanup(L_REGPARAMS *rp)
208{
209char result[512];
210char *results_file; /* success/failure output in 'compare' mode */
211char *text, *message;
212l_int32 retval;
213size_t nbytes;
214
215 if (!rp)
216 return ERROR_INT("rp not defined", __func__, 1);
217
218 lept_stderr("Time: %7.3f sec\n", stopTimerNested(rp->tstart));
219
220 /* If generating golden files or running in display mode, release rp */
221 if (!rp->fp) {
222 LEPT_FREE(rp->testname);
223 LEPT_FREE(rp->tempfile);
224 LEPT_FREE(rp);
225 return 0;
226 }
227
228 /* Compare mode: read back data from temp file */
229 fclose(rp->fp);
230 text = (char *)l_binaryRead(rp->tempfile, &nbytes);
231 LEPT_FREE(rp->tempfile);
232 if (!text) {
233 rp->success = FALSE;
234 LEPT_FREE(rp->testname);
235 LEPT_FREE(rp);
236 return ERROR_INT("text not returned", __func__, 1);
237 }
238
239 /* Prepare result message */
240 if (rp->success)
241 snprintf(result, sizeof(result), "SUCCESS: %s_reg\n", rp->testname);
242 else
243 snprintf(result, sizeof(result), "FAILURE: %s_reg\n", rp->testname);
244 message = stringJoin(text, result);
245 LEPT_FREE(text);
246 results_file = stringNew("/tmp/lept/reg_results.txt");
247 fileAppendString(results_file, message);
248 retval = (rp->success) ? 0 : 1;
249 LEPT_FREE(results_file);
250 LEPT_FREE(message);
251
252 LEPT_FREE(rp->testname);
253 LEPT_FREE(rp);
254 return retval;
255}
256
257
268l_ok
269regTestCompareValues(L_REGPARAMS *rp,
270 l_float32 val1,
271 l_float32 val2,
272 l_float32 delta)
273{
274l_float32 diff;
275
276 if (!rp)
277 return ERROR_INT("rp not defined", __func__, 1);
278
279 rp->index++;
280 diff = L_ABS(val2 - val1);
281
282 /* Record on failure */
283 if (diff > delta) {
284 if (rp->fp) {
285 fprintf(rp->fp,
286 "Failure in %s_reg: value comparison for index %d\n"
287 "difference = %f but allowed delta = %f\n",
288 rp->testname, rp->index, diff, delta);
289 }
290 lept_stderr("Failure in %s_reg: value comparison for index %d\n"
291 "difference = %f but allowed delta = %f\n",
292 rp->testname, rp->index, diff, delta);
293 rp->success = FALSE;
294 }
295 return 0;
296}
297
298
310l_ok
311regTestCompareStrings(L_REGPARAMS *rp,
312 l_uint8 *string1,
313 size_t bytes1,
314 l_uint8 *string2,
315 size_t bytes2)
316{
317l_int32 same;
318char buf[256];
319
320 if (!rp)
321 return ERROR_INT("rp not defined", __func__, 1);
322
323 rp->index++;
324 l_binaryCompare(string1, bytes1, string2, bytes2, &same);
325
326 /* Output on failure */
327 if (!same) {
328 /* Write the two strings to file */
329 snprintf(buf, sizeof(buf), "/tmp/lept/regout/string1_%d_%zu",
330 rp->index, bytes1);
331 l_binaryWrite(buf, "w", string1, bytes1);
332 snprintf(buf, sizeof(buf), "/tmp/lept/regout/string2_%d_%zu",
333 rp->index, bytes2);
334 l_binaryWrite(buf, "w", string2, bytes2);
335
336 /* Report comparison failure */
337 snprintf(buf, sizeof(buf), "/tmp/lept/regout/string*_%d_*", rp->index);
338 if (rp->fp) {
339 fprintf(rp->fp,
340 "Failure in %s_reg: string comp for index %d; "
341 "written to %s\n", rp->testname, rp->index, buf);
342 }
343 lept_stderr("Failure in %s_reg: string comp for index %d; "
344 "written to %s\n", rp->testname, rp->index, buf);
345 rp->success = FALSE;
346 }
347 return 0;
348}
349
350
365l_ok
366regTestComparePix(L_REGPARAMS *rp,
367 PIX *pix1,
368 PIX *pix2)
369{
370l_int32 same;
371
372 if (!rp)
373 return ERROR_INT("rp not defined", __func__, 1);
374 if (!pix1 || !pix2) {
375 rp->success = FALSE;
376 return ERROR_INT("pix1 and pix2 not both defined", __func__, 1);
377 }
378
379 rp->index++;
380 pixEqual(pix1, pix2, &same);
381
382 /* Record on failure */
383 if (!same) {
384 if (rp->fp) {
385 fprintf(rp->fp, "Failure in %s_reg: pix comparison for index %d\n",
386 rp->testname, rp->index);
387 }
388 lept_stderr("Failure in %s_reg: pix comparison for index %d\n",
389 rp->testname, rp->index);
390 rp->success = FALSE;
391 }
392 return 0;
393}
394
395
423l_ok
425 PIX *pix1,
426 PIX *pix2,
427 l_int32 mindiff,
428 l_float32 maxfract,
429 l_int32 printstats)
430{
431l_int32 w, h, factor, similar;
432
433 if (!rp)
434 return ERROR_INT("rp not defined", __func__, 1);
435 if (!pix1 || !pix2) {
436 rp->success = FALSE;
437 return ERROR_INT("pix1 and pix2 not both defined", __func__, 1);
438 }
439
440 rp->index++;
441 pixGetDimensions(pix1, &w, &h, NULL);
442 factor = L_MAX(w, h) / 400;
443 factor = L_MAX(1, L_MIN(factor, 4)); /* between 1 and 4 */
444 pixTestForSimilarity(pix1, pix2, factor, mindiff, maxfract, 0.0,
445 &similar, printstats);
446
447 /* Record on failure */
448 if (!similar) {
449 if (rp->fp) {
450 fprintf(rp->fp,
451 "Failure in %s_reg: pix similarity comp for index %d\n",
452 rp->testname, rp->index);
453 }
454 lept_stderr("Failure in %s_reg: pix similarity comp for index %d\n",
455 rp->testname, rp->index);
456 rp->success = FALSE;
457 }
458 return 0;
459}
460
461
488l_ok
489regTestCheckFile(L_REGPARAMS *rp,
490 const char *localname)
491{
492char *ext;
493char namebuf[256];
494l_int32 ret, same, format;
495PIX *pix1, *pix2;
496
497 if (!rp)
498 return ERROR_INT("rp not defined", __func__, 1);
499 if (!localname) {
500 rp->success = FALSE;
501 return ERROR_INT("local name not defined", __func__, 1);
502 }
503 if (rp->mode != L_REG_GENERATE && rp->mode != L_REG_COMPARE &&
504 rp->mode != L_REG_DISPLAY) {
505 rp->success = FALSE;
506 return ERROR_INT("invalid mode", __func__, 1);
507 }
508 rp->index++;
509
510 /* If display mode, no generation and no testing */
511 if (rp->mode == L_REG_DISPLAY) return 0;
512
513 /* Generate the golden file name; used in 'generate' and 'compare' */
514 splitPathAtExtension(localname, NULL, &ext);
515 snprintf(namebuf, sizeof(namebuf), "/tmp/lept/golden/%s_golden.%02d%s",
516 rp->testname, rp->index, ext);
517 LEPT_FREE(ext);
518
519 /* Generate mode. No testing. */
520 if (rp->mode == L_REG_GENERATE) {
521 /* Save the file as a golden file */
522 ret = fileCopy(localname, namebuf);
523#if 0 /* Enable for details on writing of golden files */
524 if (!ret) {
525 char *local = genPathname(localname, NULL);
526 char *golden = genPathname(namebuf, NULL);
527 L_INFO("Copy: %s to %s\n", __func__, local, golden);
528 LEPT_FREE(local);
529 LEPT_FREE(golden);
530 }
531#endif
532 return ret;
533 }
534
535 /* Compare mode: test and record on failure. This can be used
536 * for all image formats, as well as for all files of serialized
537 * data, such as boxa, pta, etc. In all cases except for
538 * GIF compressed images, we compare the files to see if they
539 * are identical. GIF doesn't support RGB images; to write
540 * a 32 bpp RGB image in GIF, we do a lossy quantization to
541 * 256 colors, so the cycle read-RGB/write-GIF is not idempotent.
542 * And although the read/write cycle for GIF images with bpp <= 8
543 * is idempotent in the image pixels, it is not idempotent in the
544 * actual file bytes; tests comparing file bytes before and after
545 * a GIF read/write cycle will fail. So for GIF we uncompress
546 * the two images and compare the actual pixels. PNG is both
547 * lossless and idempotent in file bytes on read/write, so it is
548 * not necessary to compare pixels. (Comparing pixels requires
549 * decompression, and thus would increase the regression test
550 * time. JPEG is lossy and not idempotent in the image pixels,
551 * so no tests are constructed that would require it. */
552 findFileFormat(localname, &format);
553 if (format == IFF_GIF) {
554 same = 0;
555 pix1 = pixRead(localname);
556 pix2 = pixRead(namebuf);
557 pixEqual(pix1, pix2, &same);
558 pixDestroy(&pix1);
559 pixDestroy(&pix2);
560 } else {
561 filesAreIdentical(localname, namebuf, &same);
562 }
563 if (!same) {
564 fprintf(rp->fp, "Failure in %s_reg, index %d: comparing %s with %s\n",
565 rp->testname, rp->index, localname, namebuf);
566 lept_stderr("Failure in %s_reg, index %d: comparing %s with %s\n",
567 rp->testname, rp->index, localname, namebuf);
568 rp->success = FALSE;
569 }
570
571 return 0;
572}
573
574
594l_ok
595regTestCompareFiles(L_REGPARAMS *rp,
596 l_int32 index1,
597 l_int32 index2)
598{
599char *name1, *name2;
600char namebuf[256];
601l_int32 same;
602SARRAY *sa;
603
604 if (!rp)
605 return ERROR_INT("rp not defined", __func__, 1);
606 if (index1 < 0 || index2 < 0) {
607 rp->success = FALSE;
608 return ERROR_INT("index1 and/or index2 is negative", __func__, 1);
609 }
610 if (index1 == index2) {
611 rp->success = FALSE;
612 return ERROR_INT("index1 must differ from index2", __func__, 1);
613 }
614
615 rp->index++;
616 if (rp->mode != L_REG_COMPARE) return 0;
617
618 /* Generate the golden file names */
619 snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d", rp->testname, index1);
620 sa = getSortedPathnamesInDirectory("/tmp/lept/golden", namebuf, 0, 0);
621 if (sarrayGetCount(sa) != 1) {
622 sarrayDestroy(&sa);
623 rp->success = FALSE;
624 L_ERROR("golden file %s not found\n", __func__, namebuf);
625 return 1;
626 }
627 name1 = sarrayGetString(sa, 0, L_COPY);
628 sarrayDestroy(&sa);
629
630 snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d", rp->testname, index2);
631 sa = getSortedPathnamesInDirectory("/tmp/lept/golden", namebuf, 0, 0);
632 if (sarrayGetCount(sa) != 1) {
633 sarrayDestroy(&sa);
634 rp->success = FALSE;
635 LEPT_FREE(name1);
636 L_ERROR("golden file %s not found\n", __func__, namebuf);
637 return 1;
638 }
639 name2 = sarrayGetString(sa, 0, L_COPY);
640 sarrayDestroy(&sa);
641
642 /* Test and record on failure */
643 filesAreIdentical(name1, name2, &same);
644 if (!same) {
645 fprintf(rp->fp,
646 "Failure in %s_reg, index %d: comparing %s with %s\n",
647 rp->testname, rp->index, name1, name2);
648 lept_stderr("Failure in %s_reg, index %d: comparing %s with %s\n",
649 rp->testname, rp->index, name1, name2);
650 rp->success = FALSE;
651 }
652
653 LEPT_FREE(name1);
654 LEPT_FREE(name2);
655 return 0;
656}
657
658
685l_ok
687 PIX *pix,
688 l_int32 format)
689{
690char namebuf[256];
691
692 if (!rp)
693 return ERROR_INT("rp not defined", __func__, 1);
694 if (!pix) {
695 rp->success = FALSE;
696 return ERROR_INT("pix not defined", __func__, 1);
697 }
698 if (format < 0 || format >= NumImageFileFormatExtensions) {
699 rp->success = FALSE;
700 return ERROR_INT("invalid format", __func__, 1);
701 }
702
703 /* Use bmp format for testing if library for requested
704 * format for jpeg, png or tiff is not available */
705 changeFormatForMissingLib(&format);
706
707 /* Generate the local file name */
708 snprintf(namebuf, sizeof(namebuf), "/tmp/lept/regout/%s.%02d.%s",
709 rp->testname, rp->index + 1, ImageFileFormatExtensions[format]);
710
711 /* Write the local file */
712 if (pixGetDepth(pix) < 8)
713 pixSetPadBits(pix, 0);
714 pixWrite(namebuf, pix, format);
715
716 /* Either write the golden file ("generate") or check the
717 local file against an existing golden file ("compare") */
718 regTestCheckFile(rp, namebuf);
719
720 return 0;
721}
722
723
754l_ok
756 void *data,
757 size_t nbytes,
758 const char *ext)
759{
760char namebuf[256];
761
762 if (!rp)
763 return ERROR_INT("rp not defined", __func__, 1);
764 if (!data || nbytes == 0) {
765 rp->success = FALSE;
766 return ERROR_INT("data not defined or size == 0", __func__, 1);
767 }
768
769 /* Generate the local file name */
770 snprintf(namebuf, sizeof(namebuf), "/tmp/lept/regout/%s.%02d.%s",
771 rp->testname, rp->index + 1, ext);
772
773 /* Write the local file */
774 l_binaryWrite(namebuf, "w", data, nbytes);
775
776 /* Either write the golden file ("generate") or check the
777 local file against an existing golden file ("compare") */
778 regTestCheckFile(rp, namebuf);
779 return 0;
780}
781
782
803char *
805 l_int32 index,
806 l_int32 format)
807{
808char buf[64];
809l_int32 ind;
810
811 if (!rp)
812 return (char *)ERROR_PTR("rp not defined", __func__, NULL);
813
814 ind = (index >= 0) ? index : rp->index;
815 snprintf(buf, sizeof(buf), "/tmp/lept/regout/%s.%02d.%s",
816 rp->testname, ind, ImageFileFormatExtensions[format]);
817 return stringNew(buf);
818}
819
820
837static char *
838getRootNameFromArgv0(const char *argv0)
839{
840l_int32 len;
841char *root;
842
843 splitPathAtDirectory(argv0, NULL, &root);
844 if ((len = strlen(root)) <= 4) {
845 LEPT_FREE(root);
846 return (char *)ERROR_PTR("invalid argv0; too small", __func__, NULL);
847 }
848
849#ifndef _WIN32
850 {
851 char *newroot;
852 l_int32 loc;
853 if (stringFindSubstr(root, "-", &loc)) {
854 newroot = stringNew(root + loc + 1); /* strip out "lt-" */
855 LEPT_FREE(root);
856 root = newroot;
857 len = strlen(root);
858 }
859 len -= 4; /* remove the "_reg" suffix */
860 }
861#else
862 if (strstr(root, ".exe") != NULL)
863 len -= 4;
864 if (strstr(root, "_reg") == root + len - 4)
865 len -= 4;
866#endif /* ! _WIN32 */
867
868 root[len] = '\0'; /* terminate */
869 return root;
870}
struct Sarray SARRAY
Definition array.h:81
@ L_COPY
Definition pix.h:505
struct Pix PIX
Definition pix.h:228
l_ok regTestSetup(l_int32 argc, char **argv, L_REGPARAMS **prp)
regTestSetup()
Definition regutils.c:123
l_ok regTestCompareSimilarPix(L_REGPARAMS *rp, PIX *pix1, PIX *pix2, l_int32 mindiff, l_float32 maxfract, l_int32 printstats)
regTestCompareSimilarPix()
Definition regutils.c:424
l_ok regTestCompareFiles(L_REGPARAMS *rp, l_int32 index1, l_int32 index2)
regTestCompareFiles()
Definition regutils.c:595
l_ok regTestCompareStrings(L_REGPARAMS *rp, l_uint8 *string1, size_t bytes1, l_uint8 *string2, size_t bytes2)
regTestCompareStrings()
Definition regutils.c:311
l_ok regTestWritePixAndCheck(L_REGPARAMS *rp, PIX *pix, l_int32 format)
regTestWritePixAndCheck()
Definition regutils.c:686
static char * getRootNameFromArgv0(const char *argv0)
getRootNameFromArgv0()
Definition regutils.c:838
l_ok regTestCheckFile(L_REGPARAMS *rp, const char *localname)
regTestCheckFile()
Definition regutils.c:489
l_ok regTestComparePix(L_REGPARAMS *rp, PIX *pix1, PIX *pix2)
regTestComparePix()
Definition regutils.c:366
l_ok regTestCompareValues(L_REGPARAMS *rp, l_float32 val1, l_float32 val2, l_float32 delta)
regTestCompareValues()
Definition regutils.c:269
l_ok regTestCleanup(L_REGPARAMS *rp)
regTestCleanup()
Definition regutils.c:207
l_ok regTestWriteDataAndCheck(L_REGPARAMS *rp, void *data, size_t nbytes, const char *ext)
regTestWriteDataAndCheck()
Definition regutils.c:755
char * regTestGenLocalFilename(L_REGPARAMS *rp, l_int32 index, l_int32 format)
regTestGenLocalFilename()
Definition regutils.c:804
char * testname
Definition regutils.h:120
l_int32 success
Definition regutils.h:124
char * tempfile
Definition regutils.h:121
l_int32 display
Definition regutils.h:125
l_int32 index
Definition regutils.h:123
l_int32 mode
Definition regutils.h:122
L_TIMER tstart
Definition regutils.h:126
FILE * fp
Definition regutils.h:119