/*! * \brief pixFindStrokeWidth() * * \param[in] pixs 1 bpp * \param[in] thresh fractional count threshold relative to distance 1 * \param[in] tab8 [optional] table for counting fg pixels; can be NULL * \param[out] *pwidth estimated width of the strokes * \param[out] *pnahisto [optional] histo of pixel distances from bg * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This uses two methods to estimate the stroke width: * (a) half the fg boundary length * (b) a value derived from the histogram of the fg distance transform * (2) Distance is measured in 8-connected * (3) %thresh is the minimum fraction N(dist=d)/N(dist=1) of pixels * required to determine if the pixels at distance d are above * the noise. It is typically about 0.15. * </pre> */ l_int32 pixFindStrokeWidth(PIX *pixs, l_float32 thresh, l_int32 *tab8, l_float32 *pwidth, NUMA **pnahisto) { l_int32 i, n, count, length, first, last; l_int32 *tab; l_float32 width1, width2, ratio, extra; l_float32 *fa; NUMA *na1, *na2; PIX *pix1; PROCNAME("pixFindStrokeWidth"); if (!pwidth) return ERROR_INT("&width not defined", procName, 1); *pwidth = 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); tab = (tab8) ? tab8 : makePixelSumTab8(); /* ------- Method 1: via boundary length ------- */ /* The computed stroke length is a bit larger than that actual * length, because of the addition of the 'caps' at the * stroke ends. Therefore the computed width is a bit * smaller than the average width. */ pixFindStrokeLength(pixs, tab8, &length); pixCountPixels(pixs, &count, tab8); width1 = (l_float32)count / (l_float32)length; /* ------- Method 2: via distance transform ------- */ /* First get the histogram of distances */ pix1 = pixDistanceFunction(pixs, 8, 8, L_BOUNDARY_BG); na1 = pixGetGrayHistogram(pix1, 1); pixDestroy(&pix1); numaGetNonzeroRange(na1, 0.1, &first, &last); na2 = numaClipToInterval(na1, 0, last); numaWriteStream(stderr, na2); /* Find the bucket with the largest distance whose contents * exceed the threshold. */ fa = numaGetFArray(na2, L_NOCOPY); n = numaGetCount(na2); for (i = n - 1; i > 0; i--) { ratio = fa[i] / fa[1]; if (ratio > thresh) break; } /* Let the last skipped bucket contribute to the stop bucket. * This is the 'extra' term below. The result may be a slight * over-correction, so the computed width may be a bit larger * than the average width. */ extra = (i < n - 1) ? fa[i + 1] / fa[1] : 0; width2 = 2.0 * (i - 1.0 + ratio + extra); fprintf(stderr, "width1 = %5.2f, width2 = %5.2f\n", width1, width2); /* Average the two results */ *pwidth = (width1 + width2) / 2.0; if (!tab8) LEPT_FREE(tab); numaDestroy(&na1); if (pnahisto) *pnahisto = na2; else numaDestroy(&na2); return 0; }
int main(int argc, char **argv) { l_int32 type, comptype, d1, d2, same, first, last; l_float32 fract, diff, rmsdiff; char *filein1, *filein2, *fileout; GPLOT *gplot; NUMA *na1, *na2; PIX *pixs1, *pixs2, *pixd; static char mainName[] = "comparetest"; if (argc != 5) return ERROR_INT(" Syntax: comparetest filein1 filein2 type fileout", mainName, 1); filein1 = argv[1]; filein2 = argv[2]; type = atoi(argv[3]); pixd = NULL; fileout = argv[4]; l_pngSetReadStrip16To8(0); if ((pixs1 = pixRead(filein1)) == NULL) return ERROR_INT("pixs1 not made", mainName, 1); if ((pixs2 = pixRead(filein2)) == NULL) return ERROR_INT("pixs2 not made", mainName, 1); d1 = pixGetDepth(pixs1); d2 = pixGetDepth(pixs2); if (d1 == 1 && d2 == 1) { pixEqual(pixs1, pixs2, &same); if (same) { fprintf(stderr, "Images are identical\n"); pixd = pixCreateTemplate(pixs1); /* write empty pix for diff */ } else { if (type == 0) comptype = L_COMPARE_XOR; else comptype = L_COMPARE_SUBTRACT; pixCompareBinary(pixs1, pixs2, comptype, &fract, &pixd); fprintf(stderr, "Fraction of different pixels: %10.6f\n", fract); } pixWrite(fileout, pixd, IFF_PNG); } else { if (type == 0) comptype = L_COMPARE_ABS_DIFF; else comptype = L_COMPARE_SUBTRACT; pixCompareGrayOrRGB(pixs1, pixs2, comptype, GPLOT_X11, &same, &diff, &rmsdiff, &pixd); if (type == 0) { if (same) fprintf(stderr, "Images are identical\n"); else { fprintf(stderr, "Images differ: <diff> = %10.6f\n", diff); fprintf(stderr, " <rmsdiff> = %10.6f\n", rmsdiff); } } else { /* subtraction */ if (same) fprintf(stderr, "pixs2 strictly greater than pixs1\n"); else { fprintf(stderr, "Images differ: <diff> = %10.6f\n", diff); fprintf(stderr, " <rmsdiff> = %10.6f\n", rmsdiff); } } if (d1 != 16) pixWrite(fileout, pixd, IFF_JFIF_JPEG); else pixWrite(fileout, pixd, IFF_PNG); if (d1 != 16 && !same) { na1 = pixCompareRankDifference(pixs1, pixs2, 1); if (na1) { fprintf(stderr, "na1[150] = %20.10f\n", na1->array[150]); fprintf(stderr, "na1[200] = %20.10f\n", na1->array[200]); fprintf(stderr, "na1[250] = %20.10f\n", na1->array[250]); numaGetNonzeroRange(na1, 0.00005, &first, &last); fprintf(stderr, "Nonzero diff range: first = %d, last = %d\n", first, last); na2 = numaClipToInterval(na1, first, last); gplot = gplotCreate("/tmp/junkrank", GPLOT_X11, "Pixel Rank Difference", "pixel val", "rank"); gplotAddPlot(gplot, NULL, na2, GPLOT_LINES, "rank"); gplotMakeOutput(gplot); gplotDestroy(&gplot); numaDestroy(&na1); numaDestroy(&na2); } } } pixDestroy(&pixs1); pixDestroy(&pixs2); pixDestroy(&pixd); return 0; }