// clean up the image Pix *CubeLineSegmenter::CleanUp(Pix *orig_img) { // get rid of long horizontal lines Pix *pix_temp0 = pixMorphCompSequence(orig_img, "o300.2", 0); pixXor(pix_temp0, pix_temp0, orig_img); // get rid of long vertical lines Pix *pix_temp1 = pixMorphCompSequence(pix_temp0, "o2.300", 0); pixXor(pix_temp1, pix_temp1, pix_temp0); pixDestroy(&pix_temp0); // detect connected components Pixa *con_comps; Boxa *boxa = pixConnComp(pix_temp1, &con_comps, 8); if (boxa == NULL) { return NULL; } // detect and remove suspicious conn comps for (int con = 0; con < con_comps->n; con++) { Box *box = boxa->box[con]; // remove if suspc. conn comp if ((box->w > (box->h * kMaxHorzAspectRatio)) || (box->h > (box->w * kMaxVertAspectRatio)) || (box->w < kMinWid && box->h < kMinHgt)) { pixRasterop(pix_temp1, box->x, box->y, box->w, box->h, PIX_SRC ^ PIX_DST, con_comps->pix[con], 0, 0); } } pixaDestroy(&con_comps); boxaDestroy(&boxa); return pix_temp1; }
// perform a vertical Closing with the specified threshold // returning the resulting conn comps as a pixa Pixa *CubeLineSegmenter::VerticalClosing(Pix *pix, int threshold, Boxa **boxa) { char sequence_str[16]; // do the morphology sprintf(sequence_str, "c100.%d", threshold); Pix *morphed_pix = pixMorphCompSequence(pix, sequence_str, 0); if (morphed_pix == NULL) { return NULL; } // get the resulting lines by computing concomps Pixa *pixac; (*boxa) = pixConnComp(morphed_pix, &pixac, 8); pixDestroy(&morphed_pix); if ((*boxa) == NULL) { return NULL; } return pixac; }
main(int argc, char **argv) { char *str; char buffer1[256]; char buffer2[256]; l_int32 i, same, same2, factor1, factor2, diff, success; PIX *pixs, *pixsd, *pixt1, *pixt2, *pixt3; SEL *sel1, *sel2; static char mainName[] = "binmorph2_reg"; #if 1 pixs = pixRead("rabi.png"); pixsd = pixMorphCompSequence(pixs, "d5.5", 0); success = TRUE; for (i = 1; i < MAX_SEL_SIZE; i++) { /* Check if the size is exactly decomposable */ selectComposableSizes(i, &factor1, &factor2); diff = factor1 * factor2 - i; fprintf(stderr, "%d: (%d, %d): %d\n", i, factor1, factor2, diff); /* Carry out operations on identical sized Sels: dilation */ sprintf(buffer1, "d%d.%d", i + diff, i + diff); sprintf(buffer2, "d%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* ... erosion */ sprintf(buffer1, "e%d.%d", i + diff, i + diff); sprintf(buffer2, "e%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* ... opening */ sprintf(buffer1, "o%d.%d", i + diff, i + diff); sprintf(buffer2, "o%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* ... closing */ sprintf(buffer1, "c%d.%d", i + diff, i + diff); sprintf(buffer2, "c%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); } pixDestroy(&pixs); pixDestroy(&pixsd); if (success) fprintf(stderr, "\n---------- Success: no errors ----------\n"); else fprintf(stderr, "\n---------- Failure: error(s) found -----------\n"); #endif #if 0 for (i = 1; i < 400; i++) { selectComposableSizes(i, &factor1, &factor2); diff = factor1 * factor2 - i; fprintf(stderr, "%d: (%d, %d): %d\n", i, factor1, factor2, diff); selectComposableSels(i, L_HORIZ, &sel1, &sel2); selDestroy(&sel1); selDestroy(&sel2); } #endif #if 0 selectComposableSels(68, L_HORIZ, &sel1, &sel2); /* 17, 4 */ str = selPrintToString(sel2); fprintf(stderr, str); selDestroy(&sel1); selDestroy(&sel2); FREE(str); selectComposableSels(70, L_HORIZ, &sel1, &sel2); /* 10, 7 */ str = selPrintToString(sel2); selDestroy(&sel1); selDestroy(&sel2); fprintf(stderr, str); FREE(str); selectComposableSels(85, L_HORIZ, &sel1, &sel2); /* 17, 5 */ str = selPrintToString(sel2); selDestroy(&sel1); selDestroy(&sel2); fprintf(stderr, str); FREE(str); selectComposableSels(96, L_HORIZ, &sel1, &sel2); /* 12, 8 */ str = selPrintToString(sel2); selDestroy(&sel1); selDestroy(&sel2); fprintf(stderr, str); FREE(str); { SELA *sela; sela = selaAddBasic(NULL); selaWrite("/tmp/junksela.sela", sela); selaDestroy(&sela); } #endif return 0; }
int main(int argc, char **argv) { char buffer1[256]; char buffer2[256]; l_int32 i, same, same2, factor1, factor2, diff, success; PIX *pixs, *pixsd, *pixt1, *pixt2, *pixt3; static char mainName[] = "binmorph2_reg"; if (argc != 1) return ERROR_INT(" Syntax: binmorph2_reg", mainName, 1); pixs = pixRead("rabi.png"); pixsd = pixMorphCompSequence(pixs, "d5.5", 0); success = TRUE; for (i = 1; i < MAX_SEL_SIZE; i++) { /* Check if the size is exactly decomposable */ selectComposableSizes(i, &factor1, &factor2); diff = factor1 * factor2 - i; fprintf(stderr, "%d: (%d, %d): %d\n", i, factor1, factor2, diff); /* Carry out operations on identical sized Sels: dilation */ sprintf(buffer1, "d%d.%d", i + diff, i + diff); sprintf(buffer2, "d%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* ... erosion */ sprintf(buffer1, "e%d.%d", i + diff, i + diff); sprintf(buffer2, "e%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* ... opening */ sprintf(buffer1, "o%d.%d", i + diff, i + diff); sprintf(buffer2, "o%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* ... closing */ sprintf(buffer1, "c%d.%d", i + diff, i + diff); sprintf(buffer2, "c%d.%d", i, i); pixt1 = pixMorphSequence(pixsd, buffer1, 0); pixt2 = pixMorphCompSequence(pixsd, buffer2, 0); pixEqual(pixt1, pixt2, &same); if (i < 64) { pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0); pixEqual(pixt1, pixt3, &same2); } else { pixt3 = NULL; same2 = TRUE; } if (same && same2) writeResult(buffer1, 1); else { writeResult(buffer1, 0); success = FALSE; } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); } pixDestroy(&pixs); pixDestroy(&pixsd); if (success) fprintf(stderr, "\n---------- Success: no errors ----------\n"); else fprintf(stderr, "\n---------- Failure: error(s) found -----------\n"); return 0; }
int main(int argc, char **argv) { l_int32 w, h, d, w2, h2, i, ncols, ret; l_float32 angle, conf; BOX *box; BOXA *boxa, *boxa2; PIX *pix, *pixs, *pixb, *pixb2, *pixd; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6; PIXA *pixam; /* mask with a single component over each column */ PIXA *pixac, *pixad, *pixat; PIXAA *pixaa, *pixaa2; SEL *selsplit; static char mainName[] = "arabic_lines"; if (argc != 1) return ERROR_INT(" Syntax: arabic_lines", mainName, 1); pixDisplayWrite(NULL, -1); /* init debug output */ /* Binarize input */ pixs = pixRead("arabic.png"); pixGetDimensions(pixs, &w, &h, &d); pix = pixConvertTo1(pixs, 128); /* Deskew */ pixb = pixFindSkewAndDeskew(pix, 1, &angle, &conf); pixDestroy(&pix); fprintf(stderr, "Skew angle: %7.2f degrees; %6.2f conf\n", angle, conf); pixDisplayWrite(pixb, 1); /* Use full image morphology to find columns, at 2x reduction. This only works for very simple layouts where each column of text extends the full height of the input image. */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pix1 = pixMorphCompSequence(pixb2, "c5.500", 0); boxa = pixConnComp(pix1, &pixam, 8); ncols = boxaGetCount(boxa); fprintf(stderr, "Num columns: %d\n", ncols); pixDisplayWrite(pix1, 1); /* Use selective region-based morphology to get the textline mask. */ pixad = pixaMorphSequenceByRegion(pixb2, pixam, "c100.3", 0, 0); pixGetDimensions(pixb2, &w2, &h2, NULL); pix2 = pixaDisplay(pixad, w2, h2); pixDisplayWrite(pix2, 1); pixDestroy(&pix2); /* Some of the lines may be touching, so use a HMT to split the lines in each column, and use a pixaa to save the results. */ selsplit = selCreateFromString(seltext, 17, 7, "selsplit"); pixaa = pixaaCreate(ncols); for (i = 0; i < ncols; i++) { pix3 = pixaGetPix(pixad, i, L_CLONE); box = pixaGetBox(pixad, i, L_COPY); pix4 = pixHMT(NULL, pix3, selsplit); pixXor(pix4, pix4, pix3); boxa2 = pixConnComp(pix4, &pixac, 8); pixaaAddPixa(pixaa, pixac, L_INSERT); pixaaAddBox(pixaa, box, L_INSERT); pix5 = pixaDisplayRandomCmap(pixac, 0, 0); pixDisplayWrite(pix5, 1); fprintf(stderr, "Num textlines in col %d: %d\n", i, boxaGetCount(boxa2)); pixDestroy(&pix5); pixDestroy(&pix3); pixDestroy(&pix4); boxaDestroy(&boxa2); } /* Visual output */ ret = system("gthumb /tmp/display/file* &"); pixat = pixaReadFiles("/tmp/display", "file"); pix5 = selDisplayInPix(selsplit, 31, 2); pixaAddPix(pixat, pix5, L_INSERT); pix6 = pixaDisplayTiledAndScaled(pixat, 32, 400, 3, 0, 35, 3); pixWrite("/tmp/result.png", pix6, IFF_PNG); pixaDestroy(&pixat); pixDestroy(&pix6); /* Test pixaa I/O */ pixaaWrite("/tmp/pixaa", pixaa); pixaa2 = pixaaRead("/tmp/pixaa"); pixaaWrite("/tmp/pixaa2", pixaa2); /* Test pixaa display */ pixd = pixaaDisplay(pixaa, w2, h2); pixWrite("/tmp/textlines.png", pixd, IFF_PNG); pixDestroy(&pixd); /* Cleanup */ pixDestroy(&pixb2); pixDestroy(&pix1); pixaDestroy(&pixam); pixaDestroy(&pixad); pixaaDestroy(&pixaa); pixaaDestroy(&pixaa2); boxaDestroy(&boxa); selDestroy(&selsplit); pixDestroy(&pixs); pixDestroy(&pixb); return 0; }
main(int argc, char **argv) { char *filein, *fileout; l_int32 w, h, d, w2, h2, i, ncols; l_float32 angle, conf; BOX *box; BOXA *boxa, *boxas, *boxad, *boxa2; NUMA *numa; PIX *pixs, *pixt, *pixb, *pixb2, *pixd; PIX *pixtlm, *pixvws; PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; PIXA *pixam, *pixac, *pixad, *pixat; PIXAA *pixaa, *pixaa2; PTA *pta; SEL *selsplit; static char mainName[] = "textlinemask"; if (argc != 3) exit(ERROR_INT(" Syntax: textlinemask filein fileout", mainName, 1)); filein = argv[1]; fileout = argv[2]; pixDisplayWrite(NULL, -1); /* init debug output */ if ((pixs = pixRead(filein)) == NULL) return ERROR_INT("pixs not made", mainName, 1); pixGetDimensions(pixs, &w, &h, &d); /* Binarize input */ if (d == 8) pixt = pixThresholdToBinary(pixs, 128); else if (d == 1) pixt = pixClone(pixs); else { fprintf(stderr, "depth is %d\n", d); exit(1); } /* Deskew */ pixb = pixFindSkewAndDeskew(pixt, 1, &angle, &conf); pixDestroy(&pixt); fprintf(stderr, "Skew angle: %7.2f degrees; %6.2f conf\n", angle, conf); pixDisplayWrite(pixb, DEBUG_OUTPUT); #if 1 /* Use full image morphology to find columns, at 2x reduction. * This only works for very simple layouts where each column * of text extends the full height of the input image. * pixam has a pix component over each column. */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pixt1 = pixMorphCompSequence(pixb2, "c5.500", 0); boxa = pixConnComp(pixt1, &pixam, 8); ncols = boxaGetCount(boxa); fprintf(stderr, "Num columns: %d\n", ncols); pixDisplayWrite(pixt1, DEBUG_OUTPUT); /* Use selective region-based morphology to get the textline mask. */ pixad = pixaMorphSequenceByRegion(pixb2, pixam, "c100.3", 0, 0); pixGetDimensions(pixb2, &w2, &h2, NULL); if (DEBUG_OUTPUT) { pixt2 = pixaDisplay(pixad, w2, h2); pixDisplayWrite(pixt2, DEBUG_OUTPUT); pixDestroy(&pixt2); } /* Some of the lines may be touching, so use a HMT to split the * lines in each column, and use a pixaa to save the results. */ selsplit = selCreateFromString(seltext, 17, 7, "selsplit"); pixaa = pixaaCreate(ncols); for (i = 0; i < ncols; i++) { pixt3 = pixaGetPix(pixad, i, L_CLONE); box = pixaGetBox(pixad, i, L_COPY); pixt4 = pixHMT(NULL, pixt3, selsplit); pixXor(pixt4, pixt4, pixt3); boxa2 = pixConnComp(pixt4, &pixac, 8); pixaaAddPixa(pixaa, pixac, L_INSERT); pixaaAddBox(pixaa, box, L_INSERT); if (DEBUG_OUTPUT) { pixt5 = pixaDisplayRandomCmap(pixac, 0, 0); pixDisplayWrite(pixt5, DEBUG_OUTPUT); fprintf(stderr, "Num textlines in col %d: %d\n", i, boxaGetCount(boxa2)); pixDestroy(&pixt5); } pixDestroy(&pixt3); pixDestroy(&pixt4); boxaDestroy(&boxa2); } /* Visual output */ if (DEBUG_OUTPUT) { pixDisplayMultiple("/tmp/junk_write_display*"); pixat = pixaReadFiles("/tmp", "junk_write_display"); pixt5 = selDisplayInPix(selsplit, 31, 2); pixaAddPix(pixat, pixt5, L_INSERT); pixt6 = pixaDisplayTiledAndScaled(pixat, 32, 400, 3, 0, 35, 3); pixWrite(fileout, pixt6, IFF_PNG); pixaDestroy(&pixat); pixDestroy(&pixt6); } /* Test pixaa I/O */ pixaaWrite("/tmp/junkpixaa", pixaa); pixaa2 = pixaaRead("/tmp/junkpixaa"); pixaaWrite("/tmp/junkpixaa2", pixaa2); /* Test pixaa display */ pixd = pixaaDisplay(pixaa, w2, h2); pixWrite("/tmp/junkdisplay", pixd, IFF_PNG); pixDestroy(&pixd); /* Cleanup */ pixDestroy(&pixb2); pixDestroy(&pixt1); pixaDestroy(&pixam); pixaDestroy(&pixad); pixaaDestroy(&pixaa); pixaaDestroy(&pixaa2); boxaDestroy(&boxa); selDestroy(&selsplit); #endif #if 0 /* Use the baseline finder; not really what is needed */ numa = pixFindBaselines(pixb, &pta, 1); #endif #if 0 /* Use the textline mask function; parameters are not quite right */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pixtlm = pixGenTextlineMask(pixb2, &pixvws, NULL, 1); pixDisplay(pixtlm, 0, 100); pixDisplay(pixvws, 500, 100); pixDestroy(&pixb2); pixDestroy(&pixtlm); pixDestroy(&pixvws); #endif #if 0 /* Use the Breuel whitespace partition method; slow and we would * still need to work to extract the fg regions. */ pixb2 = pixReduceRankBinary2(pixb, 2, NULL); boxas = pixConnComp(pixb2, NULL, 8); boxad = boxaGetWhiteblocks(boxas, NULL, L_SORT_BY_HEIGHT, 3, 0.1, 200, 0.2, 0); pixd = pixDrawBoxa(pixb2, boxad, 7, 0xe0708000); pixDisplay(pixd, 100, 500); pixDestroy(&pixb2); pixDestroy(&pixd); boxaDestroy(&boxas); boxaDestroy(&boxad); #endif #if 0 /* Use morphology to find columns and then selective * region-based morphology to get the textline mask. * This is for display; we really want to get a pixa of the * specific textline masks. */ startTimer(); pixb2 = pixReduceRankBinary2(pixb, 2, NULL); pixt1 = pixMorphCompSequence(pixb2, "c5.500", 0); /* column mask */ pixt2 = pixMorphSequenceByRegion(pixb2, pixt1, "c100.3", 8, 0, 0, &boxa); fprintf(stderr, "time = %7.3f sec\n", stopTimer()); pixDisplay(pixt1, 100, 500); pixDisplay(pixt2, 800, 500); pixDestroy(&pixb2); pixDestroy(&pixt1); pixDestroy(&pixt2); boxaDestroy(&boxa); #endif pixDestroy(&pixs); pixDestroy(&pixb); exit(0); }
l_int32 DoPageSegmentation(PIX *pixs, /* should be at least 300 ppi */ l_int32 which) /* 1, 2, 3, 4 */ { char buf[256]; l_int32 zero; BOXA *boxatm, *boxahm; PIX *pixr; /* image reduced to 150 ppi */ PIX *pixhs; /* image of halftone seed, 150 ppi */ PIX *pixm; /* image of mask of components, 150 ppi */ PIX *pixhm1; /* image of halftone mask, 150 ppi */ PIX *pixhm2; /* image of halftone mask, 300 ppi */ PIX *pixht; /* image of halftone components, 150 ppi */ PIX *pixnht; /* image without halftone components, 150 ppi */ PIX *pixi; /* inverted image, 150 ppi */ PIX *pixvws; /* image of vertical whitespace, 150 ppi */ PIX *pixm1; /* image of closed textlines, 150 ppi */ PIX *pixm2; /* image of refined text line mask, 150 ppi */ PIX *pixm3; /* image of refined text line mask, 300 ppi */ PIX *pixb1; /* image of text block mask, 150 ppi */ PIX *pixb2; /* image of text block mask, 300 ppi */ PIX *pixnon; /* image of non-text or halftone, 150 ppi */ PIX *pix1, *pix2, *pix3, *pix4; PIXA *pixa; PIXCMAP *cmap; PTAA *ptaa; l_int32 ht_flag = 0; l_int32 ws_flag = 0; l_int32 text_flag = 0; l_int32 block_flag = 0; PROCNAME("DoPageSegmentation"); if (which == 1) ht_flag = 1; else if (which == 2) ws_flag = 1; else if (which == 3) text_flag = 1; else if (which == 4) block_flag = 1; else return ERROR_INT("invalid parameter: not in [1...4]", procName, 1); pixa = pixaCreate(0); lept_mkdir("lept/livre"); /* Reduce to 150 ppi */ pix1 = pixScaleToGray2(pixs); if (ws_flag || ht_flag || block_flag) pixaAddPix(pixa, pix1, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/orig.gray.150.png", pix1, IFF_PNG); pixDestroy(&pix1); pixr = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); /* Get seed for halftone parts */ pix1 = pixReduceRankBinaryCascade(pixr, 4, 4, 3, 0); pix2 = pixOpenBrick(NULL, pix1, 5, 5); pixhs = pixExpandBinaryPower2(pix2, 8); if (ht_flag) pixaAddPix(pixa, pixhs, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/htseed.150.png", pixhs, IFF_PNG); pixDestroy(&pix1); pixDestroy(&pix2); /* Get mask for connected regions */ pixm = pixCloseSafeBrick(NULL, pixr, 4, 4); if (ht_flag) pixaAddPix(pixa, pixm, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/ccmask.150.png", pixm, IFF_PNG); /* Fill seed into mask to get halftone mask */ pixhm1 = pixSeedfillBinary(NULL, pixhs, pixm, 4); if (ht_flag) pixaAddPix(pixa, pixhm1, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/htmask.150.png", pixhm1, IFF_PNG); pixhm2 = pixExpandBinaryPower2(pixhm1, 2); /* Extract halftone stuff */ pixht = pixAnd(NULL, pixhm1, pixr); if (which == 1) pixWrite("/tmp/lept/livre/ht.150.png", pixht, IFF_PNG); /* Extract non-halftone stuff */ pixnht = pixXor(NULL, pixht, pixr); if (text_flag) pixaAddPix(pixa, pixnht, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/text.150.png", pixnht, IFF_PNG); pixZero(pixht, &zero); if (zero) fprintf(stderr, "No halftone parts found\n"); else fprintf(stderr, "Halftone parts found\n"); /* Get bit-inverted image */ pixi = pixInvert(NULL, pixnht); if (ws_flag) pixaAddPix(pixa, pixi, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/invert.150.png", pixi, IFF_PNG); /* The whitespace mask will break textlines where there * is a large amount of white space below or above. * We can prevent this by identifying regions of the * inverted image that have large horizontal (bigger than * the separation between columns) and significant * vertical extent (bigger than the separation between * textlines), and subtracting this from the whitespace mask. */ pix1 = pixMorphCompSequence(pixi, "o80.60", 0); pix2 = pixSubtract(NULL, pixi, pix1); if (ws_flag) pixaAddPix(pixa, pix2, L_COPY); pixDestroy(&pix1); /* Identify vertical whitespace by opening inverted image */ pix3 = pixOpenBrick(NULL, pix2, 5, 1); /* removes thin vertical lines */ pixvws = pixOpenBrick(NULL, pix3, 1, 200); /* gets long vertical lines */ if (text_flag || ws_flag) pixaAddPix(pixa, pixvws, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/vertws.150.png", pixvws, IFF_PNG); pixDestroy(&pix2); pixDestroy(&pix3); /* Get proto (early processed) text line mask. */ /* First close the characters and words in the textlines */ pixm1 = pixCloseSafeBrick(NULL, pixnht, 30, 1); if (text_flag) pixaAddPix(pixa, pixm1, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/textmask1.150.png", pixm1, IFF_PNG); /* Next open back up the vertical whitespace corridors */ pixm2 = pixSubtract(NULL, pixm1, pixvws); if (which == 1) pixWrite("/tmp/lept/livre/textmask2.150.png", pixm2, IFF_PNG); /* Do a small opening to remove noise */ pixOpenBrick(pixm2, pixm2, 3, 3); if (text_flag) pixaAddPix(pixa, pixm2, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/textmask3.150.png", pixm2, IFF_PNG); pixm3 = pixExpandBinaryPower2(pixm2, 2); /* Join pixels vertically to make text block mask */ pixb1 = pixMorphSequence(pixm2, "c1.10 + o4.1", 0); if (block_flag) pixaAddPix(pixa, pixb1, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/textblock1.150.png", pixb1, IFF_PNG); /* Solidify the textblock mask and remove noise: * (1) For each c.c., close the blocks and dilate slightly * to form a solid mask. * (2) Small horizontal closing between components * (3) Open the white space between columns, again * (4) Remove small components */ pix1 = pixMorphSequenceByComponent(pixb1, "c30.30 + d3.3", 8, 0, 0, NULL); pixCloseSafeBrick(pix1, pix1, 10, 1); if (block_flag) pixaAddPix(pixa, pix1, L_COPY); pix2 = pixSubtract(NULL, pix1, pixvws); pix3 = pixSelectBySize(pix2, 25, 5, 8, L_SELECT_IF_BOTH, L_SELECT_IF_GTE, NULL); if (block_flag) pixaAddPix(pixa, pix3, L_COPY); if (which == 1) pixWrite("/tmp/lept/livre/textblock2.150.png", pix3, IFF_PNG); pixb2 = pixExpandBinaryPower2(pix3, 2); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); /* Identify the outlines of each textblock */ ptaa = pixGetOuterBordersPtaa(pixb2); pix1 = pixRenderRandomCmapPtaa(pixb2, ptaa, 1, 8, 1); cmap = pixGetColormap(pix1); pixcmapResetColor(cmap, 0, 130, 130, 130); /* set interior to gray */ if (which == 1) pixWrite("/tmp/lept/livre/textblock3.300.png", pix1, IFF_PNG); pixDisplayWithTitle(pix1, 480, 360, "textblock mask with outlines", DFLAG); ptaaDestroy(&ptaa); pixDestroy(&pix1); /* Fill line mask (as seed) into the original */ pix1 = pixSeedfillBinary(NULL, pixm3, pixs, 8); pixOr(pixm3, pixm3, pix1); pixDestroy(&pix1); if (which == 1) pixWrite("/tmp/lept/livre/textmask.300.png", pixm3, IFF_PNG); pixDisplayWithTitle(pixm3, 480, 360, "textline mask 4", DFLAG); /* Fill halftone mask (as seed) into the original */ pix1 = pixSeedfillBinary(NULL, pixhm2, pixs, 8); pixOr(pixhm2, pixhm2, pix1); pixDestroy(&pix1); if (which == 1) pixWrite("/tmp/lept/livre/htmask.300.png", pixhm2, IFF_PNG); pixDisplayWithTitle(pixhm2, 520, 390, "halftonemask 2", DFLAG); /* Find objects that are neither text nor halftones */ pix1 = pixSubtract(NULL, pixs, pixm3); /* remove text pixels */ pixnon = pixSubtract(NULL, pix1, pixhm2); /* remove halftone pixels */ pixDestroy(&pix1); if (which == 1) pixWrite("/tmp/lept/livre/other.300.png", pixnon, IFF_PNG); pixDisplayWithTitle(pixnon, 540, 420, "other stuff", DFLAG); /* Write out b.b. for text line mask and halftone mask components */ boxatm = pixConnComp(pixm3, NULL, 4); boxahm = pixConnComp(pixhm2, NULL, 8); if (which == 1) { boxaWrite("/tmp/lept/livre/textmask.boxa", boxatm); boxaWrite("/tmp/lept/livre/htmask.boxa", boxahm); } pix1 = pixaDisplayTiledAndScaled(pixa, 8, 250, 4, 0, 25, 2); pixDisplay(pix1, 0, 375 * (which - 1)); snprintf(buf, sizeof(buf), "/tmp/lept/livre/segout.%d.png", which); pixWrite(buf, pix1, IFF_PNG); pixDestroy(&pix1); pixaDestroy(&pixa); /* clean up to test with valgrind */ pixDestroy(&pixr); pixDestroy(&pixhs); pixDestroy(&pixm); pixDestroy(&pixhm1); pixDestroy(&pixhm2); pixDestroy(&pixht); pixDestroy(&pixi); pixDestroy(&pixnht); pixDestroy(&pixvws); pixDestroy(&pixm1); pixDestroy(&pixm2); pixDestroy(&pixm3); pixDestroy(&pixb1); pixDestroy(&pixb2); pixDestroy(&pixnon); boxaDestroy(&boxatm); boxaDestroy(&boxahm); return 0; }
/*! * pixGenTextlineMask() * * Input: pixs (1 bpp, assumed to be 150 to 200 ppi) * &pixvws (<return> vertical whitespace mask) * &tlfound (<optional return> 1 if the mask is not empty) * debug (flag: 1 for debug output) * Return: pixd (textline mask), or null on error * * Notes: * (1) The input pixs should be deskewed. * (2) pixs should have no halftone pixels. * (3) Both the input image and the returned textline mask * are at the same resolution. */ PIX * pixGenTextlineMask(PIX *pixs, PIX **ppixvws, l_int32 *ptlfound, l_int32 debug) { l_int32 empty; PIX *pixt1, *pixt2, *pixvws, *pixd; PROCNAME("pixGenTextlineMask"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!ppixvws) return (PIX *)ERROR_PTR("&pixvws not defined", procName, NULL); if (pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); /* First we need a vertical whitespace mask. Invert the image. */ pixt1 = pixInvert(NULL, pixs); /* The whitespace mask will break textlines where there * is a large amount of white space below or above. * This can be prevented by identifying regions of the * inverted image that have large horizontal extent (bigger than * the separation between columns) and significant * vertical extent (bigger than the separation between * textlines), and subtracting this from the bg. */ pixt2 = pixMorphCompSequence(pixt1, "o80.60", 0); pixSubtract(pixt1, pixt1, pixt2); pixDisplayWriteFormat(pixt1, debug, IFF_PNG); pixDestroy(&pixt2); /* Identify vertical whitespace by opening the remaining bg. * o5.1 removes thin vertical bg lines and o1.200 extracts * long vertical bg lines. */ pixvws = pixMorphCompSequence(pixt1, "o5.1 + o1.200", 0); *ppixvws = pixvws; pixDisplayWriteFormat(pixvws, debug, IFF_PNG); pixDestroy(&pixt1); /* Three steps to getting text line mask: * (1) close the characters and words in the textlines * (2) open the vertical whitespace corridors back up * (3) small opening to remove noise */ pixt1 = pixCloseSafeBrick(NULL, pixs, 30, 1); pixDisplayWrite(pixt1, debug); pixd = pixSubtract(NULL, pixt1, pixvws); pixOpenBrick(pixd, pixd, 3, 3); pixDisplayWriteFormat(pixd, debug, IFF_PNG); pixDestroy(&pixt1); /* Check if text line mask is empty */ if (ptlfound) { *ptlfound = 0; pixZero(pixd, &empty); if (!empty) *ptlfound = 1; } return pixd; }
main(int argc, char **argv) { char *infile; PIX *pixs, *pixg, *pixc, *pixd; static char mainName[] = "morphseq_reg"; if (argc != 1) return ERROR_INT(" Syntax: morphseq_reg", mainName, 1); pixs = pixRead("feyn.tif"); /* 1 bpp */ pixd = pixMorphSequence(pixs, SEQUENCE1, -1); pixDestroy(&pixd); pixd = pixMorphSequence(pixs, SEQUENCE1, DISPLAY_SEPARATION); pixWrite("/tmp/morphseq1.png", pixd, IFF_PNG); pixDestroy(&pixd); pixd = pixMorphCompSequence(pixs, SEQUENCE2, -2); pixDestroy(&pixd); pixd = pixMorphCompSequence(pixs, SEQUENCE2, DISPLAY_SEPARATION); pixWrite("/tmp/morphseq2.png", pixd, IFF_PNG); pixDestroy(&pixd); pixd = pixMorphSequenceDwa(pixs, SEQUENCE2, -3); pixDestroy(&pixd); pixd = pixMorphSequenceDwa(pixs, SEQUENCE2, DISPLAY_SEPARATION); pixWrite("/tmp/morphseq3.png", pixd, IFF_PNG); pixDestroy(&pixd); pixd = pixMorphCompSequenceDwa(pixs, SEQUENCE2, -4); pixDestroy(&pixd); pixd = pixMorphCompSequenceDwa(pixs, SEQUENCE2, DISPLAY_SEPARATION); pixWrite("/tmp/morphseq4.png", pixd, IFF_PNG); pixDestroy(&pixd); /* 8 bpp */ pixg = pixScaleToGray(pixs, 0.25); pixd = pixGrayMorphSequence(pixg, SEQUENCE3, -5, 150); pixDestroy(&pixd); pixd = pixGrayMorphSequence(pixg, SEQUENCE3, DISPLAY_SEPARATION, 150); pixWrite("/tmp/morphseq5.png", pixd, IFF_PNG); pixDestroy(&pixd); pixd = pixGrayMorphSequence(pixg, SEQUENCE4, -6, 300); pixWrite("/tmp/morphseq6.png", pixd, IFF_PNG); pixDestroy(&pixd); /* 32 bpp */ pixc = pixRead("wyom.jpg"); pixd = pixColorMorphSequence(pixc, SEQUENCE5, -7, 150); pixDestroy(&pixd); pixd = pixColorMorphSequence(pixc, SEQUENCE5, DISPLAY_SEPARATION, 450); pixWrite("/tmp/morphseq7.png", pixd, IFF_PNG); pixDestroy(&pixc); pixDestroy(&pixd); /* Syntax error handling */ fprintf(stderr, " ------------ Error messages follow ------------------\n"); pixd = pixMorphSequence(pixs, BAD_SEQUENCE, 50); /* fails; returns null */ pixd = pixGrayMorphSequence(pixg, BAD_SEQUENCE, 50, 0); /* this fails */ pixDestroy(&pixg); pixDestroy(&pixs); return 0; }
main(int argc, char **argv) { l_int32 i, ok, same; char sequence[512]; PIX *pixs, *pixref; PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; PIX *pixt7, *pixt8, *pixt9, *pixt10, *pixt11; PIX *pixt12, *pixt13, *pixt14; SEL *sel; static char mainName[] = "binmorph1_reg"; if (argc != 1) exit(ERROR_INT(" Syntax: binmorph1_reg", mainName, 1)); if ((pixs = pixRead("feyn.tif")) == NULL) exit(ERROR_INT("pix not made", mainName, 1)); #if TEST_SYMMETRIC /* This works properly if there is an added border */ resetMorphBoundaryCondition(SYMMETRIC_MORPH_BC); #if 1 pixt1 = pixAddBorder(pixs, 32, 0); pixTransferAllData(pixs, &pixt1, 0, 0); #endif #endif /* TEST_SYMMETRIC */ /* This is our test sel */ sel = selCreateBrick(HEIGHT, WIDTH, HEIGHT / 2, WIDTH / 2, SEL_HIT); /* Dilation */ fprintf(stderr, "Testing dilation\n"); ok = TRUE; pixref = pixDilate(NULL, pixs, sel); /* new one */ pixt1 = pixCreateTemplate(pixs); pixDilate(pixt1, pixs, sel); /* existing one */ pixEqual(pixref, pixt1, &same); if (!same) { fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE; } pixt2 = pixCopy(NULL, pixs); pixDilate(pixt2, pixt2, sel); /* in-place */ pixEqual(pixref, pixt2, &same); if (!same) { fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE; } sprintf(sequence, "d%d.%d", WIDTH, HEIGHT); pixt3 = pixMorphSequence(pixs, sequence, 0); /* sequence, atomic */ pixEqual(pixref, pixt3, &same); if (!same) { fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE; } sprintf(sequence, "d%d.1 + d1.%d", WIDTH, HEIGHT); pixt4 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable */ pixEqual(pixref, pixt4, &same); if (!same) { fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE; } pixt5 = pixDilateBrick(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt5, &same); if (!same) { fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE; } pixt6 = pixCreateTemplate(pixs); pixDilateBrick(pixt6, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt6, &same); if (!same) { fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE; } pixt7 = pixCopy(NULL, pixs); pixDilateBrick(pixt7, pixt7, WIDTH, HEIGHT); /* in-place */ pixEqual(pixref, pixt7, &same); if (!same) { fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE; } pixt8 = pixDilateBrickDwa(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt8, &same); if (!same) { fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE; } pixt9 = pixCreateTemplate(pixs); pixDilateBrickDwa(pixt9, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt9, &same); if (!same) { fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE; } pixt10 = pixCopy(NULL, pixs); pixDilateBrickDwa(pixt10, pixt10, WIDTH, HEIGHT); /* in-place */ pixEqual(pixref, pixt10, &same); if (!same) { fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE; } pixt11 = pixCreateTemplate(pixs); pixDilateCompBrickDwa(pixt11, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt11, &same); if (!same) { fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE; } sprintf(sequence, "d%d.%d", WIDTH, HEIGHT); pixt12 = pixMorphCompSequence(pixs, sequence, 0); /* comp sequence */ pixEqual(pixref, pixt12, &same); if (!same) { fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE; } pixt13 = pixMorphSequenceDwa(pixs, sequence, 0); /* dwa sequence */ pixEqual(pixref, pixt13, &same); if (!same) { fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE; } pixDestroy(&pixref); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); pixDestroy(&pixt7); pixDestroy(&pixt8); pixDestroy(&pixt9); pixDestroy(&pixt10); pixDestroy(&pixt11); pixDestroy(&pixt12); pixDestroy(&pixt13); /* Erosion */ fprintf(stderr, "Testing erosion\n"); pixref = pixErode(NULL, pixs, sel); /* new one */ pixt1 = pixCreateTemplate(pixs); pixErode(pixt1, pixs, sel); /* existing one */ pixEqual(pixref, pixt1, &same); if (!same) { fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE; } pixt2 = pixCopy(NULL, pixs); pixErode(pixt2, pixt2, sel); /* in-place */ pixEqual(pixref, pixt2, &same); if (!same) { fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE; } sprintf(sequence, "e%d.%d", WIDTH, HEIGHT); pixt3 = pixMorphSequence(pixs, sequence, 0); /* sequence, atomic */ pixEqual(pixref, pixt3, &same); if (!same) { fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE; } sprintf(sequence, "e%d.1 + e1.%d", WIDTH, HEIGHT); pixt4 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable */ pixEqual(pixref, pixt4, &same); if (!same) { fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE; } pixt5 = pixErodeBrick(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt5, &same); if (!same) { fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE; } pixt6 = pixCreateTemplate(pixs); pixErodeBrick(pixt6, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt6, &same); if (!same) { fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE; } pixt7 = pixCopy(NULL, pixs); pixErodeBrick(pixt7, pixt7, WIDTH, HEIGHT); /* in-place */ pixEqual(pixref, pixt7, &same); if (!same) { fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE; } pixt8 = pixErodeBrickDwa(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt8, &same); if (!same) { fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE; } pixt9 = pixCreateTemplate(pixs); pixErodeBrickDwa(pixt9, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt9, &same); if (!same) { fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE; } pixt10 = pixCopy(NULL, pixs); pixErodeBrickDwa(pixt10, pixt10, WIDTH, HEIGHT); /* in-place */ pixEqual(pixref, pixt10, &same); if (!same) { fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE; } pixt11 = pixCreateTemplate(pixs); pixErodeCompBrickDwa(pixt11, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt11, &same); if (!same) { fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE; } sprintf(sequence, "e%d.%d", WIDTH, HEIGHT); pixt12 = pixMorphCompSequence(pixs, sequence, 0); /* comp sequence */ pixEqual(pixref, pixt12, &same); if (!same) { fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE; } pixt13 = pixMorphSequenceDwa(pixs, sequence, 0); /* dwa sequence */ pixEqual(pixref, pixt13, &same); if (!same) { fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE; } pixDestroy(&pixref); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); pixDestroy(&pixt7); pixDestroy(&pixt8); pixDestroy(&pixt9); pixDestroy(&pixt10); pixDestroy(&pixt11); pixDestroy(&pixt12); pixDestroy(&pixt13); /* Opening */ fprintf(stderr, "Testing opening\n"); pixref = pixOpen(NULL, pixs, sel); /* new one */ pixt1 = pixCreateTemplate(pixs); pixOpen(pixt1, pixs, sel); /* existing one */ pixEqual(pixref, pixt1, &same); if (!same) { fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE; } pixt2 = pixCopy(NULL, pixs); pixOpen(pixt2, pixt2, sel); /* in-place */ pixEqual(pixref, pixt2, &same); if (!same) { fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE; } sprintf(sequence, "o%d.%d", WIDTH, HEIGHT); pixt3 = pixMorphSequence(pixs, sequence, 0); /* sequence, atomic */ pixEqual(pixref, pixt3, &same); if (!same) { fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE; } sprintf(sequence, "e%d.%d + d%d.%d", WIDTH, HEIGHT, WIDTH, HEIGHT); pixt4 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable */ pixEqual(pixref, pixt4, &same); if (!same) { fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE; } sprintf(sequence, "e%d.1 + e1.%d + d%d.1 + d1.%d", WIDTH, HEIGHT, WIDTH, HEIGHT); pixt5 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable^2 */ pixEqual(pixref, pixt5, &same); if (!same) { fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE; } pixt6 = pixOpenBrick(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt6, &same); if (!same) { fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE; } pixt7 = pixCreateTemplate(pixs); pixOpenBrick(pixt7, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt7, &same); if (!same) { fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE; } pixt8 = pixCopy(NULL, pixs); /* in-place */ pixOpenBrick(pixt8, pixt8, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt8, &same); if (!same) { fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE; } pixt9 = pixOpenBrickDwa(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt9, &same); if (!same) { fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE; } pixt10 = pixCreateTemplate(pixs); pixOpenBrickDwa(pixt10, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt10, &same); if (!same) { fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE; } pixt11 = pixCopy(NULL, pixs); pixOpenBrickDwa(pixt11, pixt11, WIDTH, HEIGHT); /* in-place */ pixEqual(pixref, pixt11, &same); if (!same) { fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE; } sprintf(sequence, "o%d.%d", WIDTH, HEIGHT); pixt12 = pixMorphCompSequence(pixs, sequence, 0); /* comp sequence */ pixEqual(pixref, pixt12, &same); if (!same) { fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE; } #if 0 pixWrite("/tmp/junkref.png", pixref, IFF_PNG); pixWrite("/tmp/junk12.png", pixt12, IFF_PNG); pixt13 = pixXor(NULL, pixref, pixt12); pixWrite("/tmp/junk12a.png", pixt13, IFF_PNG); pixDestroy(&pixt13); #endif pixt13 = pixMorphSequenceDwa(pixs, sequence, 0); /* dwa sequence */ pixEqual(pixref, pixt13, &same); if (!same) { fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE; } pixt14 = pixCreateTemplate(pixs); pixOpenCompBrickDwa(pixt14, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt14, &same); if (!same) { fprintf(stderr, "pixref != pixt14 !\n"); ok = FALSE; } pixDestroy(&pixref); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); pixDestroy(&pixt7); pixDestroy(&pixt8); pixDestroy(&pixt9); pixDestroy(&pixt10); pixDestroy(&pixt11); pixDestroy(&pixt12); pixDestroy(&pixt13); pixDestroy(&pixt14); /* Closing */ fprintf(stderr, "Testing closing\n"); pixref = pixClose(NULL, pixs, sel); /* new one */ pixt1 = pixCreateTemplate(pixs); pixClose(pixt1, pixs, sel); /* existing one */ pixEqual(pixref, pixt1, &same); if (!same) { fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE; } pixt2 = pixCopy(NULL, pixs); pixClose(pixt2, pixt2, sel); /* in-place */ pixEqual(pixref, pixt2, &same); if (!same) { fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE; } sprintf(sequence, "d%d.%d + e%d.%d", WIDTH, HEIGHT, WIDTH, HEIGHT); pixt3 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable */ pixEqual(pixref, pixt3, &same); if (!same) { fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE; } sprintf(sequence, "d%d.1 + d1.%d + e%d.1 + e1.%d", WIDTH, HEIGHT, WIDTH, HEIGHT); pixt4 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable^2 */ pixEqual(pixref, pixt4, &same); if (!same) { fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE; } pixt5 = pixCloseBrick(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt5, &same); if (!same) { fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE; } pixt6 = pixCreateTemplate(pixs); pixCloseBrick(pixt6, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt6, &same); if (!same) { fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE; } pixt7 = pixCopy(NULL, pixs); /* in-place */ pixCloseBrick(pixt7, pixt7, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt7, &same); if (!same) { fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE; } pixDestroy(&pixref); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); pixDestroy(&pixt7); /* Safe closing (using pix, not pixs) */ fprintf(stderr, "Testing safe closing\n"); pixref = pixCloseSafe(NULL, pixs, sel); /* new one */ pixt1 = pixCreateTemplate(pixs); pixCloseSafe(pixt1, pixs, sel); /* existing one */ pixEqual(pixref, pixt1, &same); if (!same) { fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE; } pixt2 = pixCopy(NULL, pixs); pixCloseSafe(pixt2, pixt2, sel); /* in-place */ pixEqual(pixref, pixt2, &same); if (!same) { fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE; } sprintf(sequence, "c%d.%d", WIDTH, HEIGHT); pixt3 = pixMorphSequence(pixs, sequence, 0); /* sequence, atomic */ pixEqual(pixref, pixt3, &same); if (!same) { fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE; } sprintf(sequence, "b32 + d%d.%d + e%d.%d", WIDTH, HEIGHT, WIDTH, HEIGHT); pixt4 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable */ pixEqual(pixref, pixt4, &same); if (!same) { fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE; } sprintf(sequence, "b32 + d%d.1 + d1.%d + e%d.1 + e1.%d", WIDTH, HEIGHT, WIDTH, HEIGHT); pixt5 = pixMorphSequence(pixs, sequence, 0); /* sequence, separable^2 */ pixEqual(pixref, pixt5, &same); if (!same) { fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE; } pixt6 = pixCloseSafeBrick(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt6, &same); if (!same) { fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE; } pixt7 = pixCreateTemplate(pixs); pixCloseSafeBrick(pixt7, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt7, &same); if (!same) { fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE; } pixt8 = pixCopy(NULL, pixs); /* in-place */ pixCloseSafeBrick(pixt8, pixt8, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt8, &same); if (!same) { fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE; } pixt9 = pixCloseBrickDwa(NULL, pixs, WIDTH, HEIGHT); /* new one */ pixEqual(pixref, pixt9, &same); if (!same) { fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE; } pixt10 = pixCreateTemplate(pixs); pixCloseBrickDwa(pixt10, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt10, &same); if (!same) { fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE; } pixt11 = pixCopy(NULL, pixs); pixCloseBrickDwa(pixt11, pixt11, WIDTH, HEIGHT); /* in-place */ pixEqual(pixref, pixt11, &same); if (!same) { fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE; } sprintf(sequence, "c%d.%d", WIDTH, HEIGHT); pixt12 = pixMorphCompSequence(pixs, sequence, 0); /* comp sequence */ pixEqual(pixref, pixt12, &same); if (!same) { fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE; } pixt13 = pixMorphSequenceDwa(pixs, sequence, 0); /* dwa sequence */ pixEqual(pixref, pixt13, &same); if (!same) { fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE; } pixt14 = pixCreateTemplate(pixs); pixCloseCompBrickDwa(pixt14, pixs, WIDTH, HEIGHT); /* existing one */ pixEqual(pixref, pixt14, &same); if (!same) { fprintf(stderr, "pixref != pixt14 !\n"); ok = FALSE; } #if 0 pixWrite("/tmp/junkref.png", pixref, IFF_PNG); pixWrite("/tmp/junk12.png", pixt12, IFF_PNG); pixt13 = pixXor(NULL, pixref, pixt12); pixWrite("/tmp/junk12a.png", pixt13, IFF_PNG); pixDestroy(&pixt13); #endif pixDestroy(&pixref); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); pixDestroy(&pixt7); pixDestroy(&pixt8); pixDestroy(&pixt9); pixDestroy(&pixt10); pixDestroy(&pixt11); pixDestroy(&pixt12); pixDestroy(&pixt13); pixDestroy(&pixt14); if (ok) fprintf(stderr, "All morph tests OK!\n"); pixDestroy(&pixs); selDestroy(&sel); exit(0); }
/*! * pixMirrorDetect() * * Input: pixs (1 bpp, deskewed, English text) * &conf (<return> confidence that text is not LR mirror reversed) * mincount (min number of left + right; use 0 for default) * debug (1 for debug output; 0 otherwise) * Return: 0 if OK, 1 on error * * Notes: * (1) For this test, it is necessary that the text is horizontally * oriented, with ascenders going up. * (2) conf is the normalized difference between the number of * right and left facing characters with ascenders. * Left-facing are {d}; right-facing are {b, h, k}. * At least that was the expectation. In practice, we can * really just say that it is the normalized difference in * hits using two specific hit-miss filters, textsel1 and textsel2, * after the image has been suitably pre-filtered so that * these filters are effective. See (4) for what's really happening. * (3) A large positive conf value indicates normal text, whereas * a large negative conf value means the page is mirror reversed. * (4) The implementation is a bit tricky. The general idea is * to fill the x-height part of characters, but not the space * between them, before doing the HMT. This is done by * finding pixels added using two different operations -- a * horizontal close and a vertical dilation -- and adding * the intersection of these sets to the original. It turns * out that the original intuition about the signal was largely * in error: much of the signal for right-facing characters * comes from the lower part of common x-height characters, like * the e and c, that remain open after these operations. * So it's important that the operations to close the x-height * parts of the characters are purposely weakened sufficiently * to allow these characters to remain open. The wonders * of morphology! */ l_int32 pixMirrorDetect(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug) { l_int32 count1, count2, nmax; l_float32 nleft, nright; PIX *pixt0, *pixt1, *pixt2, *pixt3; SEL *sel1, *sel2; PROCNAME("pixMirrorDetect"); if (!pconf) return ERROR_INT("&conf not defined", procName, 1); *pconf = 0.0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (mincount == 0) mincount = DEFAULT_MIN_MIRROR_FLIP_COUNT; sel1 = selCreateFromString(textsel1, 5, 6, NULL); sel2 = selCreateFromString(textsel2, 5, 6, NULL); /* Fill x-height characters but not space between them, sort of. */ pixt3 = pixMorphCompSequence(pixs, "d1.30", 0); pixXor(pixt3, pixt3, pixs); pixt0 = pixMorphCompSequence(pixs, "c15.1", 0); pixXor(pixt0, pixt0, pixs); pixAnd(pixt0, pixt0, pixt3); pixOr(pixt0, pixt0, pixs); pixDestroy(&pixt3); /* pixDisplayWrite(pixt0, 1); */ /* Filter the right-facing characters. */ pixt1 = pixHMT(NULL, pixt0, sel1); pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0); pixCountPixels(pixt3, &count1, NULL); pixDebugFlipDetect("junkpixright", pixs, pixt1, debug); pixDestroy(&pixt1); pixDestroy(&pixt3); /* Filter the left-facing characters. */ pixt2 = pixHMT(NULL, pixt0, sel2); pixt3 = pixReduceRankBinaryCascade(pixt2, 1, 1, 0, 0); pixCountPixels(pixt3, &count2, NULL); pixDebugFlipDetect("junkpixleft", pixs, pixt2, debug); pixDestroy(&pixt2); pixDestroy(&pixt3); nright = (l_float32)count1; nleft = (l_float32)count2; nmax = L_MAX(count1, count2); pixDestroy(&pixt0); selDestroy(&sel1); selDestroy(&sel2); if (nmax > mincount) *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft)); if (debug) { fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft); if (*pconf > DEFAULT_MIN_MIRROR_FLIP_CONF) fprintf(stderr, "Text is not mirror reversed\n"); if (*pconf < -DEFAULT_MIN_MIRROR_FLIP_CONF) fprintf(stderr, "Text is mirror reversed\n"); } return 0; }
/*! * pixUpDownDetectGeneral() * * Input: pixs (1 bpp, deskewed, English text, 150 - 300 ppi) * &conf (<return> confidence that text is rightside-up) * mincount (min number of up + down; use 0 for default) * npixels (number of pixels removed from each side of word box) * debug (1 for debug output; 0 otherwise) * Return: 0 if OK, 1 on error * * Notes: * (1) See pixOrientDetect() for other details. * (2) @conf is the normalized difference between the number of * detected up and down ascenders, assuming that the text * is either rightside-up or upside-down and not rotated * at a 90 degree angle. * (3) The typical mode of operation is @npixels == 0. * If @npixels > 0, this removes HMT matches at the * beginning and ending of "words." This is useful for * pages that may have mostly digits, because if npixels == 0, * leading "1" and "3" digits can register as having * ascenders or descenders, and "7" digits can match descenders. * Consequently, a page image of only digits may register * as being upside-down. * (4) We want to count the number of instances found using the HMT. * An expensive way to do this would be to count the * number of connected components. A cheap way is to do a rank * reduction cascade that reduces each component to a single * pixel, and results (after two or three 2x reductions) * in one pixel for each of the original components. * After the reduction, you have a much smaller pix over * which to count pixels. We do only 2 reductions, because * this function is designed to work for input pix between * 150 and 300 ppi, and an 8x reduction on a 150 ppi image * is going too far -- components will get merged. */ l_int32 pixUpDownDetectGeneral(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 npixels, l_int32 debug) { l_int32 countup, countdown, nmax; l_float32 nup, ndown; PIX *pixt0, *pixt1, *pixt2, *pixt3, *pixm; SEL *sel1, *sel2, *sel3, *sel4; PROCNAME("pixUpDownDetectGeneral"); if (!pconf) return ERROR_INT("&conf not defined", procName, 1); *pconf = 0.0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (mincount == 0) mincount = DEFAULT_MIN_UP_DOWN_COUNT; if (npixels < 0) npixels = 0; sel1 = selCreateFromString(textsel1, 5, 6, NULL); sel2 = selCreateFromString(textsel2, 5, 6, NULL); sel3 = selCreateFromString(textsel3, 5, 6, NULL); sel4 = selCreateFromString(textsel4, 5, 6, NULL); /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1). * This closes holes in x-height characters and joins them at * the x-height. There is more noise in the descender detection * from this, but it works fairly well. */ pixt0 = pixMorphCompSequence(pixs, "c1.8 + c30.1", 0); /* Optionally, make a mask of the word bounding boxes, shortening * each of them by a fixed amount at each end. */ pixm = NULL; if (npixels > 0) { l_int32 i, nbox, x, y, w, h; BOX *box; BOXA *boxa; pixt1 = pixMorphSequence(pixt0, "o10.1", 0); boxa = pixConnComp(pixt1, NULL, 8); pixm = pixCreateTemplate(pixt1); pixDestroy(&pixt1); nbox = boxaGetCount(boxa); for (i = 0; i < nbox; i++) { box = boxaGetBox(boxa, i, L_CLONE); boxGetGeometry(box, &x, &y, &w, &h); if (w > 2 * npixels) pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13, PIX_SET, NULL, 0, 0); boxDestroy(&box); } boxaDestroy(&boxa); } /* Find the ascenders and optionally filter with pixm. * For an explanation of the procedure used for counting the result * of the HMT, see comments at the beginning of this function. */ pixt1 = pixHMT(NULL, pixt0, sel1); pixt2 = pixHMT(NULL, pixt0, sel2); pixOr(pixt1, pixt1, pixt2); if (pixm) pixAnd(pixt1, pixt1, pixm); pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0); pixCountPixels(pixt3, &countup, NULL); pixDebugFlipDetect("junkpixup", pixs, pixt1, debug); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* Find the ascenders and optionally filter with pixm. */ pixt1 = pixHMT(NULL, pixt0, sel3); pixt2 = pixHMT(NULL, pixt0, sel4); pixOr(pixt1, pixt1, pixt2); if (pixm) pixAnd(pixt1, pixt1, pixm); pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0); pixCountPixels(pixt3, &countdown, NULL); pixDebugFlipDetect("junkpixdown", pixs, pixt1, debug); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); /* Evaluate statistically, generating a confidence that is * related to the probability with a gaussian distribution. */ nup = (l_float32)(countup); ndown = (l_float32)(countdown); nmax = L_MAX(countup, countdown); if (nmax > mincount) *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown)); if (debug) { if (pixm) pixWrite("junkpixm1", pixm, IFF_PNG); fprintf(stderr, "nup = %7.3f, ndown = %7.3f, conf = %7.3f\n", nup, ndown, *pconf); if (*pconf > DEFAULT_MIN_UP_DOWN_CONF) fprintf(stderr, "Text is rightside-up\n"); if (*pconf < -DEFAULT_MIN_UP_DOWN_CONF) fprintf(stderr, "Text is upside-down\n"); } pixDestroy(&pixt0); pixDestroy(&pixm); selDestroy(&sel1); selDestroy(&sel2); selDestroy(&sel3); selDestroy(&sel4); return 0; }
// Performs line segmentation bool CubeLineSegmenter::LineSegment() { // Use full image morphology to find columns // This only works for simple layouts where each column // of text extends the full height of the input image. Pix *pix_temp1 = pixMorphCompSequence(img_, "c5.500", 0); if (pix_temp1 == NULL) { return false; } // Mask with a single component over each column Pixa *pixam; Boxa *boxa = pixConnComp(pix_temp1, &pixam, 8); if (boxa == NULL) { return false; } int init_morph_min_hgt = kLineSepMorphMinHgt; char sequence_str[16]; sprintf(sequence_str, "c100.%d", init_morph_min_hgt); // Use selective region-based morphology to get the textline mask. Pixa *pixad = pixaMorphSequenceByRegion(img_, pixam, sequence_str, 0, 0); if (pixad == NULL) { return false; } // for all columns int col_cnt = boxaGetCount(boxa); // create columns columns_ = pixaaCreate(col_cnt); if (columns_ == NULL) { return false; } // index columns based on readind order (RTL) int *col_order = IndexRTL(pixad); if (col_order == NULL) { return false; } line_cnt_ = 0; for (int col_idx = 0; col_idx < col_cnt; col_idx++) { int col = col_order[col_idx]; // get the pix and box corresponding to the column Pix *pixt3 = pixaGetPix(pixad, col, L_CLONE); if (pixt3 == NULL) { return false; } Box *col_box = pixad->boxa->box[col]; Pixa *pixac; Boxa *boxa2 = pixConnComp(pixt3, &pixac, 8); if (boxa2 == NULL) { return false; } // offset the boxes by the column box for (int line = 0; line < pixac->n; line++) { pixac->boxa->box[line]->x += col_box->x; pixac->boxa->box[line]->y += col_box->y; } // add the lines if (AddLines(pixac) == true) { if (pixaaAddBox(columns_, col_box, L_CLONE) != 0) { return false; } } pixDestroy(&pixt3); boxaDestroy(&boxa2); line_cnt_ += columns_->pixa[col_idx]->n; } pixaDestroy(&pixam); pixaDestroy(&pixad); boxaDestroy(&boxa); delete []col_order; pixDestroy(&pix_temp1); return true; }