void AddTestSet(PIXA *pixa, PIX *pixs, l_int32 filtertype, l_int32 edgethresh, l_int32 smoothx, l_int32 smoothy, l_float32 gamma, l_int32 minval, l_int32 maxval, l_int32 targetthresh) { PIX *pixb, *pixd, *pixth; pixThresholdSpreadNorm(pixs, filtertype, edgethresh, smoothx, smoothy, gamma, minval, maxval, targetthresh, &pixth, NULL, &pixd); pixSaveTiled(pixth, pixa, 1.0, 1, 20, 0); pixSaveTiled(pixd, pixa, 1.0, 0, 20, 0); pixb = pixThresholdToBinary(pixd, targetthresh - 20); pixSaveTiled(pixb, pixa, 1.0, 0, 20, 0); pixDestroy(&pixb); pixb = pixThresholdToBinary(pixd, targetthresh); pixSaveTiled(pixb, pixa, 1.0, 0, 20, 0); pixDestroy(&pixb); pixb = pixThresholdToBinary(pixd, targetthresh + 20); pixSaveTiled(pixb, pixa, 1.0, 0, 20, 0); pixDestroy(&pixb); pixb = pixThresholdToBinary(pixd, targetthresh + 40); pixSaveTiled(pixb, pixa, 1.0, 0, 20, 0); pixDestroy(&pixb); pixDestroy(&pixth); pixDestroy(&pixd); return; }
main(int argc, char **argv) { l_int32 i, w, h, d; l_float32 time; PIX *pixs, *pixf, *pixd; PIXA *pixa; char *filein, *fileout; static char mainName[] = "edgetest"; if (argc != 3) exit(ERROR_INT(" Syntax: edgetest filein fileout", mainName, 1)); filein = argv[1]; fileout = argv[2]; if ((pixs = pixRead(filein)) == NULL) exit(ERROR_INT("pix not made", mainName, 1)); pixGetDimensions(pixs, &w, &h, &d); if (d != 8) exit(ERROR_INT("pix not 8 bpp", mainName, 1)); /* Speed: about 12 Mpix/GHz/sec */ startTimer(); pixf = pixSobelEdgeFilter(pixs, L_HORIZONTAL_EDGES); pixd = pixThresholdToBinary(pixf, 60); pixInvert(pixd, pixd); time = stopTimer(); fprintf(stderr, "Time = %7.3f sec\n", time); fprintf(stderr, "MPix/sec: %7.3f\n", 0.000001 * w * h / time); pixDisplay(pixs, 0, 0); pixInvert(pixf, pixf); pixDisplay(pixf, 480, 0); pixDisplay(pixd, 960, 0); pixWrite(fileout, pixf, IFF_PNG); pixDestroy(&pixd); /* Threshold at different values */ pixInvert(pixf, pixf); for (i = 10; i <= 120; i += 10) { pixd = pixThresholdToBinary(pixf, i); pixInvert(pixd, pixd); pixDisplayWrite(pixd, 1); pixDestroy(&pixd); } pixDestroy(&pixf); /* Display tiled */ pixa = pixaReadFiles("/tmp", "junk_write_display"); pixd = pixaDisplayTiledAndScaled(pixa, 8, 400, 3, 0, 25, 2); pixWrite("/tmp/junktiles.jpg", pixd, IFF_JFIF_JPEG); pixDestroy(&pixd); pixaDestroy(&pixa); pixDestroy(&pixs); exit(0); }
l_int32 main(int argc, char **argv) { l_int32 pageno; L_DEWARP *dew1; L_DEWARPA *dewa; PIX *pixs, *pixn, *pixg, *pixb; static char mainName[] = "dewarptest2"; if (argc != 1 && argc != 3) return ERROR_INT("Syntax: dewarptest2 [image pageno]", mainName, 1); if (argc == 1) { pixs = pixRead("cat-35.jpg"); pageno = 35; } else { pixs = pixRead(argv[1]); pageno = atoi(argv[2]); } if (!pixs) return ERROR_INT("image not read", mainName, 1); dewa = dewarpaCreate(40, 30, 1, 6, 50); #if NORMALIZE /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); pixDestroy(&pixn); #else /* Don't normalize; just threshold and clean edges */ pixg = pixConvertTo8(pixs, 0); pixb = pixThresholdToBinary(pixg, 100); pixSetOrClearBorder(pixb, 30, 30, 40, 40, PIX_CLR); #endif /* Run the basic functions */ dew1 = dewarpCreate(pixb, pageno); dewarpaInsertDewarp(dewa, dew1); dewarpBuildModel(dew1, "/tmp/dewarp_model1.pdf"); dewarpaApplyDisparity(dewa, pageno, pixg, "/tmp/dewarp_apply1.pdf"); dewarpaDestroy(&dewa); pixDestroy(&pixs); pixDestroy(&pixg); pixDestroy(&pixb); return 0; }
jlong Java_com_googlecode_leptonica_android_GrayQuant_nativePixThresholdToBinary(JNIEnv *env, jclass clazz, jlong nativePix, jint thresh) { PIX *pixs = (PIX *) nativePix; PIX *pixd = pixThresholdToBinary(pixs, (l_int32) thresh); return (jlong) pixd; }
/* load PNG, TIFF, JPG, GIF or BMP to PIX datastructure. The actual supported * formats depends on how the leptonica was compiled */ PIX *loadimage(char *filename){ PIX *pix, *pixt; int format, bpp; format=fileformat(filename); // In later versions of leptonica you will have to do this // pixReadHeader(filename, format,NULL,NULL,NULL,bpp,NULL); if(format!=IFF_PNG && format!=IFF_JFIF_JPEG && format!=IFF_TIFF && format!= IFF_GIF && format!=7 && format!=8){ dfprintf(stderr,"Not recognised file format %i", format); return NULL; } if ((pix = pixRead(filename)) == NULL) return NULL; /* TODO: convert image to 1-bpp 300dpi regardless of scan */ bpp=pixGetDepth(pix); if(bpp>1){ /* printf("Bits per pixel=%i",bpp); exit(1); */ //pixThresholdForFgBg(pix,5,100,NULL,NULL); //pixContrastTRC(pix, pix, 1000); pixt = pixContrastNorm(NULL, pix, 10, 10, 40, 2, 2); pixDestroy(&pix); pix = pixGammaTRC(NULL, pixt, 1.5, 50, 235); pixt=pixThresholdToBinary(pix, 200); //pixt=pixThreshold8(pix,1,1,0); pixDestroy(&pix); pix=pixt; } return pix; }
/*! * pixRotateBinaryNice() * * Input: pixs (1 bpp) * angle (radians; clockwise is positive; about the center) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) * Return: pixd, or null on error * * Notes: * (1) For very small rotations, just return a clone. * (2) This does a computationally expensive rotation of 1 bpp images. * The fastest rotators (using shears or subsampling) leave * visible horizontal and vertical shear lines across which * the image shear changes by one pixel. To ameliorate the * visual effect one can introduce random dithering. One * way to do this in a not-too-random fashion is given here. * We convert to 8 bpp, do a very small blur, rotate using * linear interpolation (same as area mapping), do a * small amount of sharpening to compensate for the initial * blur, and threshold back to binary. The shear lines * are magically removed. * (3) This operation is about 5x slower than rotation by sampling. */ PIX * pixRotateBinaryNice(PIX *pixs, l_float32 angle, l_int32 incolor) { PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixd; PROCNAME("pixRotateBinaryNice"); if (!pixs || pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); pixt1 = pixConvertTo8(pixs, 0); pixt2 = pixBlockconv(pixt1, 1, 1); /* smallest blur allowed */ pixt3 = pixRotateAM(pixt2, angle, incolor); pixt4 = pixUnsharpMasking(pixt3, 1, 1.0); /* sharpen a bit */ pixd = pixThresholdToBinary(pixt4, 128); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); return pixd; }
main(int argc, char **argv) { char *infile, *outfile; l_int32 d; PIX *pixs, *pixgr, *pixb; static char mainName[] = "showedges"; if (argc != 3) exit(ERROR_INT(" Syntax: showedges infile outfile", mainName, 1)); infile = argv[1]; outfile = argv[2]; pixs = pixRead(infile); d = pixGetDepth(pixs); if (d != 8 && d != 32) exit(ERROR_INT("d not 8 or 32 bpp", mainName, 1)); pixgr = pixHalfEdgeByBandpass(pixs, SMOOTH_WIDTH_1, SMOOTH_WIDTH_1, SMOOTH_WIDTH_2, SMOOTH_WIDTH_2); pixb = pixThresholdToBinary(pixgr, THRESHOLD); pixInvert(pixb, pixb); /* pixWrite("junkpixgr", pixgr, IFF_JFIF_JPEG); */ pixWrite(outfile, pixb, IFF_PNG); return 0; }
/*! * dewarpSinglePageInit() * * Input: pixs (with text, any depth) * thresh (for global thresholding to 1 bpp; ignored otherwise) * adaptive (1 for adaptive thresholding; 0 for global threshold) * use_both (1 for horizontal and vertical; 0 for vertical only) * &pixb (<return> 1 bpp image) * &dewa (<return> initialized dewa) * Return: 0 if OK, 1 on error (list of page numbers), or null on error * * Notes: * (1) This binarizes the input pixs if necessary, returning the * binarized image. It also initializes the dewa to default values * for the model parameters. * (2) If pixs is 1 bpp, the parameters @adaptive and @thresh are ignored. * (3) To change the model parameters, call dewarpaSetCurvatures() * before running dewarpSinglePageRun(). For DC: * dewarpSinglePageInit(pixs, 0, 1, 1, &pixb, &dewa); * dewarpaSetCurvatures(dewa, 250, -1, -1, 80, 70, 150); * dewarpSinglePageRun(pixs, pixb, dewa, &pixd, 0); * dewarpaDestroy(&dewa); * pixDestroy(&pixb); */ l_int32 dewarpSinglePageInit(PIX *pixs, l_int32 thresh, l_int32 adaptive, l_int32 use_both, PIX **ppixb, L_DEWARPA **pdewa) { PIX *pix1; PROCNAME("dewarpSinglePageInit"); if (ppixb) *ppixb = NULL; if (pdewa) *pdewa = NULL; if (!ppixb || !pdewa) return ERROR_INT("&pixb and &dewa not both defined", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); *pdewa = dewarpaCreate(1, 0, 1, 0, -1); dewarpaUseBothArrays(*pdewa, use_both); /* Generate a binary image, if necessary */ if (pixGetDepth(pixs) > 1) { pix1 = pixConvertTo8(pixs, 0); if (adaptive) *ppixb = pixAdaptThresholdToBinary(pix1, NULL, 1.0); else *ppixb = pixThresholdToBinary(pix1, thresh); pixDestroy(&pix1); } else { *ppixb = pixClone(pixs); } return 0; }
// threshold the image -- warning, operates ONLY on grayscale pixmaps // original image is unchanged - a new modified image at raster's end ///////////////////////////////////////////////////////////////////////////////////////////////////////////// Pix Pix::ThresholdToBinary(int threshold) { if(IsEmpty()) return Pix(); PIX *dPix = pixThresholdToBinary(pix, threshold); if(!dPix) return Pix(); return Pix(&dPix); } // END Pix::ThresholdToBinary()
/* * Binarize and fix uneven illumination */ void MainWindow::on_actionBinarizeUnIl_triggered() { QApplication::setOverrideCursor(Qt::WaitCursor); PIX *pixc, *pixg, *pixsg, *pixd; /* Convert the RGB image to grayscale. */ this->statusBar()->showMessage(tr("Convert the RGB image to grayscale.")); pixsg = pixConvertRGBToLuminance(pixs); setPixToScene(pixsg); /* Remove the text in the fg. */ this->statusBar()->showMessage(tr("Remove the text in the fg.")); pixc = pixCloseGray(pixsg, 25, 25); setPixToScene(pixc); /* Smooth the bg with a convolution. */ // pixsm = pixBlockconv(pixc, 15, 15); // pixDestroy(&pixsm); /* Normalize for uneven illumination on gray image. */ this->statusBar()->showMessage(tr("Normalize for uneven illumination on gray image.")); pixBackgroundNormGrayArrayMorph(pixsg, NULL, 4, 5, 200, &pixg); pixc = pixApplyInvBackgroundGrayMap(pixsg, pixg, 4, 4); pixDestroy(&pixsg); pixDestroy(&pixg); setPixToScene(pixc); /* Increase the dynamic range. */ // make dark gray *black* and light gray *white* this->statusBar()->showMessage(tr("Increase the dynamic range.")); pixd = pixGammaTRC(NULL, pixc, 1.0, 50, 220); setPixToScene(pixd); /* Threshold to 1 bpp. */ this->statusBar()->showMessage(tr("Threshold to 1 bpp.")); pixs = pixThresholdToBinary(pixd, 120); pixDestroy(&pixd); setPixToScene(); this->statusBar()->showMessage(tr("Finished..."), 2000); modified = true; updateTitle(); QApplication::restoreOverrideCursor(); }
jint Java_com_googlecode_leptonica_android_Dewarp_nativeDewarp(JNIEnv *env, jclass clazz, jint nativePix) { LOGV("%s",__FUNCTION__); PIX* pixd=NULL; PIX *pixs = (PIX *) nativePix; PIX* pixb = pixThresholdToBinary(pixs, 130); // Basic functioning: L_Dewarp *dew = dewarpCreate(pixb,0,60,5,1); int buildresult=dewarpBuildModel(dew, 0); if(buildresult==0){ int applyresult=dewarpApplyDisparity(dew, pixs, 0); if(applyresult==0){ pixd = pixClone(dew->pixd); } } pixDestroy(&pixb); dewarpDestroy(&dew); if(pixd!=NULL){ return (jint) pixd; } else { return (jint) 0; } }
char* _process_frame_tickertext(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int index) { PIX *im; PIX *edge_im; PIX *lum_im; PIX *feat_im; char *subtitle_text=NULL; im = pixCreate(width,height,32); lum_im = pixCreate(width,height,32); feat_im = pixCreate(width,height,32); int i,j; for(i=(92*height)/100;i<height;i++) { for(j=0;j<width;j++) { int p=j*3+i*frame->linesize[0]; int r=frame->data[0][p]; int g=frame->data[0][p+1]; int b=frame->data[0][p+2]; pixSetRGBPixel(im,j,i,r,g,b); float L,A,B; rgb_to_lab((float)r,(float)g,(float)b,&L,&A,&B); if(L > ctx->lum_thresh) pixSetRGBPixel(lum_im,j,i,255,255,255); else pixSetRGBPixel(lum_im,j,i,0,0,0); } } //Handle the edge image edge_im = pixCreate(width,height,8); edge_im = pixConvertRGBToGray(im,0.0,0.0,0.0); edge_im = pixSobelEdgeFilter(edge_im, L_VERTICAL_EDGES); edge_im = pixDilateGray(edge_im, 21, 11); edge_im = pixThresholdToBinary(edge_im,50); for(i=92*(height/100);i<height;i++) { for(j=0;j<width;j++) { unsigned int p1,p2,p3; pixGetPixel(edge_im,j,i,&p1); // pixGetPixel(pixd,j,i,&p2); pixGetPixel(lum_im,j,i,&p3); if(p1==0&&p3>0) pixSetRGBPixel(feat_im,j,i,255,255,255); else pixSetRGBPixel(feat_im,j,i,0,0,0); } } // Tesseract OCR for the ticker text here subtitle_text = get_ocr_text_simple(ctx, lum_im); char write_path[100]; sprintf(write_path,"./lum_im%04d.jpg",index); pixWrite(write_path,lum_im,IFF_JFIF_JPEG); sprintf(write_path,"./im%04d.jpg",index); pixWrite(write_path,im,IFF_JFIF_JPEG); pixDestroy(&lum_im); pixDestroy(&im); pixDestroy(&edge_im); pixDestroy(&feat_im); return subtitle_text; }
void _display_frame(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int timestamp) { // Debug: Display the frame after processing PIX *im; im = pixCreate(width,height,32); PIX *hue_im = pixCreate(width,height,32); int i,j; for(i=0;i<height;i++) { for(j=0;j<width;j++) { int p=j*3+i*frame->linesize[0]; int r=frame->data[0][p]; int g=frame->data[0][p+1]; int b=frame->data[0][p+2]; pixSetRGBPixel(im,j,i,r,g,b); float H,S,V; rgb_to_hsv((float)r,(float)g,(float)b,&H,&S,&V); if(abs(H-ctx->hue)<20) { pixSetRGBPixel(hue_im,j,i,r,g,b); } } } PIX *edge_im = pixCreate(width,height,8),*edge_im_2 = pixCreate(width,height,8); edge_im = pixConvertRGBToGray(im,0.0,0.0,0.0); edge_im = pixSobelEdgeFilter(edge_im, L_VERTICAL_EDGES); edge_im = pixDilateGray(edge_im, 21, 1); edge_im = pixThresholdToBinary(edge_im,50); PIX *pixd = pixCreate(width,height,1); pixSauvolaBinarize(pixConvertRGBToGray(hue_im,0.0,0.0,0.0), 15, 0.3, 1, NULL, NULL, NULL, &pixd); edge_im_2 = pixConvertRGBToGray(hue_im,0.0,0.0,0.0); edge_im_2 = pixDilateGray(edge_im_2, 5, 5); PIX *feat_im = pixCreate(width,height,32); for(i=3*(height/4);i<height;i++) { for(j=0;j<width;j++) { unsigned int p1,p2,p3,p4; pixGetPixel(edge_im,j,i,&p1); pixGetPixel(pixd,j,i,&p2); // pixGetPixel(hue_im,j,i,&p3); pixGetPixel(edge_im_2,j,i,&p4); if(p1==0&&p2==0&&p4>0)//if(p4>0&&p1==0)//if(p2==0&&p1==0&&p3>0) { pixSetRGBPixel(feat_im,j,i,255,255,255); } } } char *txt=NULL; // txt = get_ocr_text_simple(ctx, feat_im); // txt=get_ocr_text_wordwise_threshold(ctx, feat_im, ctx->conf_thresh); // if(txt != NULL)printf("%s\n", txt); pixDestroy(&im); pixDestroy(&edge_im); pixDestroy(&feat_im); pixDestroy(&edge_im_2); pixDestroy(&pixd); }
char* _process_frame_white_basic(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int index) { //printf("frame : %04d\n", index); PIX *im; PIX *edge_im; PIX *lum_im; PIX *feat_im; char *subtitle_text=NULL; im = pixCreate(width,height,32); lum_im = pixCreate(width,height,32); feat_im = pixCreate(width,height,32); int i,j; for(i=(3*height)/4;i<height;i++) { for(j=0;j<width;j++) { int p=j*3+i*frame->linesize[0]; int r=frame->data[0][p]; int g=frame->data[0][p+1]; int b=frame->data[0][p+2]; pixSetRGBPixel(im,j,i,r,g,b); float L,A,B; rgb_to_lab((float)r,(float)g,(float)b,&L,&A,&B); if(L > ctx->lum_thresh) pixSetRGBPixel(lum_im,j,i,255,255,255); else pixSetRGBPixel(lum_im,j,i,0,0,0); } } //Handle the edge image edge_im = pixCreate(width,height,8); edge_im = pixConvertRGBToGray(im,0.0,0.0,0.0); edge_im = pixSobelEdgeFilter(edge_im, L_VERTICAL_EDGES); edge_im = pixDilateGray(edge_im, 21, 11); edge_im = pixThresholdToBinary(edge_im,50); for(i=3*(height/4);i<height;i++) { for(j=0;j<width;j++) { unsigned int p1,p2,p3; pixGetPixel(edge_im,j,i,&p1); // pixGetPixel(pixd,j,i,&p2); pixGetPixel(lum_im,j,i,&p3); if(p1==0&&p3>0) pixSetRGBPixel(feat_im,j,i,255,255,255); else pixSetRGBPixel(feat_im,j,i,0,0,0); } } if(ctx->detect_italics) { ctx->ocr_mode = HARDSUBX_OCRMODE_WORD; } // TESSERACT OCR FOR THE FRAME HERE switch(ctx->ocr_mode) { case HARDSUBX_OCRMODE_WORD: if(ctx->conf_thresh > 0) subtitle_text = get_ocr_text_wordwise_threshold(ctx, lum_im, ctx->conf_thresh); else subtitle_text = get_ocr_text_wordwise(ctx, lum_im); break; case HARDSUBX_OCRMODE_LETTER: if(ctx->conf_thresh > 0) subtitle_text = get_ocr_text_letterwise_threshold(ctx, lum_im, ctx->conf_thresh); else subtitle_text = get_ocr_text_letterwise(ctx, lum_im); break; case HARDSUBX_OCRMODE_FRAME: if(ctx->conf_thresh > 0) subtitle_text = get_ocr_text_simple_threshold(ctx, lum_im, ctx->conf_thresh); else subtitle_text = get_ocr_text_simple(ctx, lum_im); break; default: fatal(EXIT_MALFORMED_PARAMETER,"Invalid OCR Mode"); } pixDestroy(&lum_im); pixDestroy(&im); pixDestroy(&edge_im); pixDestroy(&feat_im); return subtitle_text; }
l_int32 main(int argc, char **argv) { l_int32 i, n; l_float32 a, b, c, d, e; NUMA *nax, *nafit; PIX *pixs, *pixn, *pixg, *pixb, *pixt1, *pixt2; PIXA *pixa; PTA *pta, *ptad; PTAA *ptaa1, *ptaa2; pixs = pixRead("cat-35.jpg"); /* pixs = pixRead("zanotti-78.jpg"); */ /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); pixDestroy(&pixn); pixDestroy(&pixg); /* Get the textline centers */ pixa = pixaCreate(6); ptaa1 = dewarpGetTextlineCenters(pixb, 0); pixt1 = pixCreateTemplate(pixs); pixSetAll(pixt1); pixt2 = pixDisplayPtaa(pixt1, ptaa1); pixWrite("/tmp/textline1.png", pixt2, IFF_PNG); pixDisplayWithTitle(pixt2, 0, 100, "textline centers 1", 1); pixaAddPix(pixa, pixt2, L_INSERT); pixDestroy(&pixt1); /* Remove short lines */ fprintf(stderr, "Num all lines = %d\n", ptaaGetCount(ptaa1)); ptaa2 = dewarpRemoveShortLines(pixb, ptaa1, 0.8, 0); pixt1 = pixCreateTemplate(pixs); pixSetAll(pixt1); pixt2 = pixDisplayPtaa(pixt1, ptaa2); pixWrite("/tmp/textline2.png", pixt2, IFF_PNG); pixDisplayWithTitle(pixt2, 300, 100, "textline centers 2", 1); pixaAddPix(pixa, pixt2, L_INSERT); pixDestroy(&pixt1); n = ptaaGetCount(ptaa2); fprintf(stderr, "Num long lines = %d\n", n); ptaaDestroy(&ptaa1); pixDestroy(&pixb); /* Long lines over input image */ pixt1 = pixCopy(NULL, pixs); pixt2 = pixDisplayPtaa(pixt1, ptaa2); pixWrite("/tmp/textline3.png", pixt2, IFF_PNG); pixDisplayWithTitle(pixt2, 600, 100, "textline centers 3", 1); pixaAddPix(pixa, pixt2, L_INSERT); pixDestroy(&pixt1); /* Quadratic fit to curve */ pixt1 = pixCopy(NULL, pixs); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetQuadraticLSF(pta, &a, &b, &c, &nafit); fprintf(stderr, "Quadratic: a = %10.6f, b = %7.3f, c = %7.3f\n", a, b, c); ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt1, pixt1, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } pixWrite("/tmp/textline4.png", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 900, 100, "textline centers 4", 1); pixaAddPix(pixa, pixt1, L_INSERT); /* Cubic fit to curve */ pixt1 = pixCopy(NULL, pixs); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetCubicLSF(pta, &a, &b, &c, &d, &nafit); fprintf(stderr, "Cubic: a = %10.6f, b = %10.6f, c = %7.3f, d = %7.3f\n", a, b, c, d); ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt1, pixt1, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } pixWrite("/tmp/textline5.png", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 1200, 100, "textline centers 5", 1); pixaAddPix(pixa, pixt1, L_INSERT); /* Quartic fit to curve */ pixt1 = pixCopy(NULL, pixs); for (i = 0; i < n; i++) { pta = ptaaGetPta(ptaa2, i, L_CLONE); ptaGetArrays(pta, &nax, NULL); ptaGetQuarticLSF(pta, &a, &b, &c, &d, &e, &nafit); fprintf(stderr, "Quartic: a = %7.3f, b = %7.3f, c = %9.5f, d = %7.3f, e = %7.3f\n", a, b, c, d, e); ptad = ptaCreateFromNuma(nax, nafit); pixDisplayPta(pixt1, pixt1, ptad); ptaDestroy(&pta); ptaDestroy(&ptad); numaDestroy(&nax); numaDestroy(&nafit); } pixWrite("/tmp/textline6.png", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 1500, 100, "textline centers 6", 1); pixaAddPix(pixa, pixt1, L_INSERT); pixaConvertToPdf(pixa, 300, 0.5, L_JPEG_ENCODE, 75, "LS fittings to textlines", "/tmp/dewarp_fittings.pdf"); pixaDestroy(&pixa); pixDestroy(&pixs); ptaaDestroy(&ptaa2); return 0; }
int main(int argc, char **argv) { char *infile; l_int32 w, d, threshval, ival, newval; l_uint32 val; PIX *pixs, *pixg, *pixg2; PIX *pix1, *pix2; PIXA *pixa; static char mainName[] = "binarize_set"; if (argc != 2) return ERROR_INT(" Syntax: binarize_set infile", mainName, 1); infile = argv[1]; pixa = pixaCreate(5); pixs = pixRead(infile); pixGetDimensions(pixs, &w, NULL, &d); pixSaveTiled(pixs, pixa, 1.0, 1, 50, 32); pixDisplay(pixs, 100, 0); #if ALL /* 1. Standard background normalization with a global threshold. */ pixg = pixConvertTo8(pixs, 0); pix1 = pixBackgroundNorm(pixg, NULL, NULL, 10, 15, 100, 50, 255, 2, 2); pix2 = pixThresholdToBinary(pix1, 160); pixWrite("/tmp/binar1.png", pix2, IFF_PNG); pixDisplay(pix2, 100, 0); pixSaveTiled(pix2, pixa, 1.0, 1, 50, 32); pixDestroy(&pixg); pixDestroy(&pix1); pixDestroy(&pix2); #endif #if ALL /* 2. Background normalization followed by Otsu thresholding. Otsu * binarization attempts to split the image into two roughly equal * sets of pixels, and it does a very poor job when there are large * amounts of dark background. By doing a background normalization * first (to get the background near 255), we remove this problem. * Then we use a modified Otsu to estimate the best global * threshold on the normalized image. */ pixg = pixConvertTo8(pixs, 0); pix1 = pixOtsuThreshOnBackgroundNorm(pixg, NULL, 10, 15, 100, 50, 255, 2, 2, 0.10, &threshval); fprintf(stderr, "thresh val = %d\n", threshval); pixSaveTiled(pix1, pixa, 1.0, 1, 50, 32); pixWrite("/tmp/binar2.png", pix1, IFF_PNG); pixDisplay(pix1, 100, 200); pixDestroy(&pixg); pixDestroy(&pix1); #endif #if ALL /* 3. Background normalization with Otsu threshold estimation and * masking for threshold selection. */ pixg = pixConvertTo8(pixs, 0); pix1 = pixMaskedThreshOnBackgroundNorm(pixg, NULL, 10, 15, 100, 50, 2, 2, 0.10, &threshval); fprintf(stderr, "thresh val = %d\n", threshval); pixSaveTiled(pix1, pixa, 1.0, 1, 50, 32); pixWrite("/tmp/binar3.png", pix1, IFF_PNG); pixDisplay(pix1, 100, 400); pixDestroy(&pixg); pixDestroy(&pix1); #endif #if ALL /* 4. Background normalization followed by Sauvola binarization */ if (d == 32) pixg = pixConvertRGBToGray(pixs, 0.2, 0.7, 0.1); else pixg = pixConvertTo8(pixs, 0); pixg2 = pixContrastNorm(NULL, pixg, 20, 20, 130, 2, 2); pixSauvolaBinarizeTiled(pixg2, 25, 0.40, 1, 1, NULL, &pix1); pixSaveTiled(pix1, pixa, 1.0, 1, 50, 32); pixWrite("/tmp/binar4.png", pix1, IFF_PNG); pixDisplay(pix1, 100, 600); pixDestroy(&pixg); pixDestroy(&pixg2); pixDestroy(&pix1); #endif #if ALL /* 5. Contrast normalization followed by background normalization, and * thresholding. */ if (d == 32) pixg = pixConvertRGBToGray(pixs, 0.2, 0.7, 0.1); else pixg = pixConvertTo8(pixs, 0); pixOtsuAdaptiveThreshold(pixg, 5000, 5000, 0, 0, 0.1, &pix1, NULL); pixGetPixel(pix1, 0, 0, &val); ival = (l_int32) val; newval = ival + (l_int32)(0.6 * (110 - ival)); fprintf(stderr, "th1 = %d, th2 = %d\n", ival, newval); pixDestroy(&pix1); pixContrastNorm(pixg, pixg, 50, 50, 130, 2, 2); pixg2 = pixBackgroundNorm(pixg, NULL, NULL, 20, 20, 70, 40, 200, 2, 2); ival = L_MIN(ival, 110); pix1 = pixThresholdToBinary(pixg2, ival); pixSaveTiled(pix1, pixa, 1.0, 1, 50, 32); pixWrite("/tmp/binar5.png", pix1, IFF_PNG); pixDisplay(pix1, 100, 800); pixDestroy(&pixg); pixDestroy(&pixg2); pixDestroy(&pix1); #endif pix1 = pixaDisplayTiledInRows(pixa, 32, w + 100, 1.0, 0, 30, 2); pixWrite("/tmp/binar6.png", pix1, IFF_PNG); pixDisplay(pix1, 1000, 0); pixDestroy(&pix1); pixaDestroy(&pixa); pixDestroy(&pixs); return 0; }
l_int32 main(int argc, char **argv) { l_int32 method, pageno; L_DEWARP *dew1; L_DEWARPA *dewa; PIX *pixs, *pixn, *pixg, *pixb, *pixd; static char mainName[] = "dewarptest2"; if (argc != 2 && argc != 4) return ERROR_INT("Syntax: dewarptest2 method [image pageno]", mainName, 1); if (argc == 2) { pixs = pixRead("cat-35.jpg"); pageno = 35; } else { pixs = pixRead(argv[2]); pageno = atoi(argv[3]); } if (!pixs) return ERROR_INT("image not read", mainName, 1); method = atoi(argv[1]); lept_mkdir("lept"); if (method == 1) { /* Use single page dewarp function */ dewarpSinglePage(pixs, 1, 100, 1, &pixd, NULL, 1); pixDisplay(pixd, 100, 100); } else { /* Break down into multiple steps; require min of only 6 lines */ dewa = dewarpaCreate(40, 30, 1, 6, 50); dewarpaUseBothArrays(dewa, 1); #if NORMALIZE /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); pixDestroy(&pixn); #else /* Don't normalize; just threshold and clean edges */ pixg = pixConvertTo8(pixs, 0); pixb = pixThresholdToBinary(pixg, 100); pixSetOrClearBorder(pixb, 30, 30, 40, 40, PIX_CLR); #endif /* Run the basic functions */ dew1 = dewarpCreate(pixb, pageno); dewarpaInsertDewarp(dewa, dew1); dewarpBuildPageModel(dew1, "/tmp/lept/test2_model.pdf"); dewarpaApplyDisparity(dewa, pageno, pixg, -1, 0, 0, &pixd, "/tmp/lept/test2_apply.pdf"); dewarpaInfo(stderr, dewa); dewarpaDestroy(&dewa); pixDestroy(&pixg); pixDestroy(&pixb); } pixDestroy(&pixs); pixDestroy(&pixd); return 0; }
int main(int argc, char **argv) { l_int32 index; l_uint32 val32; BOX *box, *box1, *box2, *box3, *box4, *box5; BOXA *boxa; L_KERNEL *kel; PIX *pixs, *pixg, *pixb, *pixd, *pixt, *pix1, *pix2, *pix3, *pix4; PIXA *pixa; PIXCMAP *cmap; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixa = pixaCreate(0); /* Color non-white pixels on RGB */ pixs = pixRead("lucasta-frag.jpg"); pixt = pixConvert8To32(pixs); box = boxCreate(120, 30, 200, 200); pixColorGray(pixt, box, L_PAINT_DARK, 220, 0, 0, 255); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 0 */ pixaAddPix(pixa, pixt, L_COPY); pixColorGray(pixt, NULL, L_PAINT_DARK, 220, 255, 100, 100); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 1 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Color non-white pixels on colormap */ pixt = pixThresholdTo4bpp(pixs, 6, 1); box = boxCreate(120, 30, 200, 200); pixColorGray(pixt, box, L_PAINT_DARK, 220, 0, 0, 255); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 2 */ pixaAddPix(pixa, pixt, L_COPY); pixColorGray(pixt, NULL, L_PAINT_DARK, 220, 255, 100, 100); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 3 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Color non-black pixels on RGB */ pixt = pixConvert8To32(pixs); box = boxCreate(120, 30, 200, 200); pixColorGray(pixt, box, L_PAINT_LIGHT, 20, 0, 0, 255); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 4 */ pixaAddPix(pixa, pixt, L_COPY); pixColorGray(pixt, NULL, L_PAINT_LIGHT, 80, 255, 100, 100); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 5 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Color non-black pixels on colormap */ pixt = pixThresholdTo4bpp(pixs, 6, 1); box = boxCreate(120, 30, 200, 200); pixColorGray(pixt, box, L_PAINT_LIGHT, 20, 0, 0, 255); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 6 */ pixaAddPix(pixa, pixt, L_COPY); pixColorGray(pixt, NULL, L_PAINT_LIGHT, 20, 255, 100, 100); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 7 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Add highlight color to RGB */ pixt = pixConvert8To32(pixs); box = boxCreate(507, 5, 385, 45); pixg = pixClipRectangle(pixs, box, NULL); pixb = pixThresholdToBinary(pixg, 180); pixInvert(pixb, pixb); pixDisplayWrite(pixb, 1); composeRGBPixel(50, 0, 250, &val32); pixPaintThroughMask(pixt, pixb, box->x, box->y, val32); boxDestroy(&box); pixDestroy(&pixg); pixDestroy(&pixb); box = boxCreate(236, 107, 262, 40); pixg = pixClipRectangle(pixs, box, NULL); pixb = pixThresholdToBinary(pixg, 180); pixInvert(pixb, pixb); composeRGBPixel(250, 0, 50, &val32); pixPaintThroughMask(pixt, pixb, box->x, box->y, val32); boxDestroy(&box); pixDestroy(&pixg); pixDestroy(&pixb); box = boxCreate(222, 208, 247, 43); pixg = pixClipRectangle(pixs, box, NULL); pixb = pixThresholdToBinary(pixg, 180); pixInvert(pixb, pixb); composeRGBPixel(60, 250, 60, &val32); pixPaintThroughMask(pixt, pixb, box->x, box->y, val32); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 8 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); pixDestroy(&pixg); pixDestroy(&pixb); /* Add highlight color to colormap */ pixt = pixThresholdTo4bpp(pixs, 5, 1); cmap = pixGetColormap(pixt); pixcmapGetIndex(cmap, 255, 255, 255, &index); box = boxCreate(507, 5, 385, 45); pixSetSelectCmap(pixt, box, index, 50, 0, 250); boxDestroy(&box); box = boxCreate(236, 107, 262, 40); pixSetSelectCmap(pixt, box, index, 250, 0, 50); boxDestroy(&box); box = boxCreate(222, 208, 247, 43); pixSetSelectCmap(pixt, box, index, 60, 250, 60); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 9 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Paint lines on RGB */ pixt = pixConvert8To32(pixs); pixRenderLineArb(pixt, 450, 20, 850, 320, 5, 200, 50, 125); pixRenderLineArb(pixt, 30, 40, 440, 40, 5, 100, 200, 25); box = boxCreate(70, 80, 300, 245); pixRenderBoxArb(pixt, box, 3, 200, 200, 25); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 10 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Paint lines on colormap */ pixt = pixThresholdTo4bpp(pixs, 5, 1); pixRenderLineArb(pixt, 450, 20, 850, 320, 5, 200, 50, 125); pixRenderLineArb(pixt, 30, 40, 440, 40, 5, 100, 200, 25); box = boxCreate(70, 80, 300, 245); pixRenderBoxArb(pixt, box, 3, 200, 200, 25); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 11 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Blend lines on RGB */ pixt = pixConvert8To32(pixs); pixRenderLineBlend(pixt, 450, 20, 850, 320, 5, 200, 50, 125, 0.35); pixRenderLineBlend(pixt, 30, 40, 440, 40, 5, 100, 200, 25, 0.35); box = boxCreate(70, 80, 300, 245); pixRenderBoxBlend(pixt, box, 3, 200, 200, 25, 0.6); regTestWritePixAndCheck(rp, pixt, IFF_JFIF_JPEG); /* 12 */ pixaAddPix(pixa, pixt, L_INSERT); boxDestroy(&box); /* Colorize gray on cmapped image. */ pix1 = pixRead("lucasta.150.jpg"); pix2 = pixThresholdTo4bpp(pix1, 7, 1); box1 = boxCreate(73, 206, 140, 27); pixColorGrayCmap(pix2, box1, L_PAINT_LIGHT, 130, 207, 43); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 13 */ pixaAddPix(pixa, pix2, L_COPY); if (rp->display) pixPrintStreamInfo(stderr, pix2, "One box added"); box2 = boxCreate(255, 404, 197, 25); pixColorGrayCmap(pix2, box2, L_PAINT_LIGHT, 230, 67, 119); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 14 */ pixaAddPix(pixa, pix2, L_COPY); if (rp->display) pixPrintStreamInfo(stderr, pix2, "Two boxes added"); box3 = boxCreate(122, 756, 224, 22); pixColorGrayCmap(pix2, box3, L_PAINT_DARK, 230, 67, 119); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 15 */ pixaAddPix(pixa, pix2, L_COPY); if (rp->display) pixPrintStreamInfo(stderr, pix2, "Three boxes added"); box4 = boxCreate(11, 780, 147, 22); pixColorGrayCmap(pix2, box4, L_PAINT_LIGHT, 70, 137, 229); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 16 */ pixaAddPix(pixa, pix2, L_COPY); if (rp->display) pixPrintStreamInfo(stderr, pix2, "Four boxes added"); box5 = boxCreate(163, 605, 78, 22); pixColorGrayCmap(pix2, box5, L_PAINT_LIGHT, 70, 137, 229); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 17 */ pixaAddPix(pixa, pix2, L_INSERT); if (rp->display) pixPrintStreamInfo(stderr, pix2, "Five boxes added"); pixDestroy(&pix1); boxDestroy(&box1); boxDestroy(&box2); boxDestroy(&box3); boxDestroy(&box4); boxDestroy(&box5); pixDestroy(&pixs); /* Make a gray image and identify the fg pixels (val > 230) */ pixs = pixRead("feyn-fract.tif"); pix1 = pixConvertTo8(pixs, 0); kel = makeGaussianKernel(2, 2, 1.5, 1.0); pix2 = pixConvolve(pix1, kel, 8, 1); pix3 = pixThresholdToBinary(pix2, 230); boxa = pixConnComp(pix3, NULL, 8); pixDestroy(&pixs); pixDestroy(&pix1); pixDestroy(&pix3); kernelDestroy(&kel); /* Color the individual components in the gray image */ pix4 = pixColorGrayRegions(pix2, boxa, L_PAINT_DARK, 230, 255, 0, 0); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 18 */ pixaAddPix(pixa, pix4, L_INSERT); pixDisplayWithTitle(pix4, 0, 0, NULL, rp->display); /* Threshold to 10 levels of gray */ pix3 = pixThresholdOn8bpp(pix2, 10, 1); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 19 */ pixaAddPix(pixa, pix3, L_COPY); /* Color the individual components in the cmapped image */ pix4 = pixColorGrayRegions(pix3, boxa, L_PAINT_DARK, 230, 255, 0, 0); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 20 */ pixaAddPix(pixa, pix4, L_INSERT); pixDisplayWithTitle(pix4, 0, 100, NULL, rp->display); boxaDestroy(&boxa); /* Color the entire gray image (not component-wise) */ pixColorGray(pix2, NULL, L_PAINT_DARK, 230, 255, 0, 0); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 21 */ pixaAddPix(pixa, pix2, L_INSERT); /* Color the entire cmapped image (not component-wise) */ pixColorGray(pix3, NULL, L_PAINT_DARK, 230, 255, 0, 0); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 22 */ pixaAddPix(pixa, pix3, L_INSERT); /* Reconstruct cmapped images */ pixd = ReconstructByValue(rp, "weasel2.4c.png"); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 23 */ pixaAddPix(pixa, pixd, L_INSERT); pixd = ReconstructByValue(rp, "weasel4.11c.png"); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 24 */ pixaAddPix(pixa, pixd, L_INSERT); pixd = ReconstructByValue(rp, "weasel8.240c.png"); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 25 */ pixaAddPix(pixa, pixd, L_INSERT); /* Fake reconstruct cmapped images, with one color into a band */ pixd = FakeReconstructByBand(rp, "weasel2.4c.png"); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 26 */ pixaAddPix(pixa, pixd, L_INSERT); pixd = FakeReconstructByBand(rp, "weasel4.11c.png"); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 27 */ pixaAddPix(pixa, pixd, L_INSERT); pixd = FakeReconstructByBand(rp, "weasel8.240c.png"); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 28 */ pixaAddPix(pixa, pixd, L_INSERT); /* If in testing mode, make a pdf */ if (rp->display) { pixaConvertToPdf(pixa, 100, 1.0, L_FLATE_ENCODE, 0, "Colorize and paint", "/tmp/lept/regout/paint.pdf"); L_INFO("Output pdf: /tmp/lept/regout/paint.pdf\n", rp->testname); } pixaDestroy(&pixa); return regTestCleanup(rp); }
main(int argc, char **argv) { l_float32 sum, sumx, sumy, diff; L_DEWARP *dew; L_DEWARPA *dewa; FPIX *fpixs, *fpixs2, *fpixs3, *fpixs4, *fpixg, *fpixd; FPIX *fpix1, *fpix2, *fpixt1, *fpixt2; DPIX *dpix, *dpix2; L_KERNEL *kel, *kelx, *kely; PIX *pixs, *pixs2, *pixs3, *pixt, *pixd, *pixg, *pixb, *pixn; PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; PIXA *pixa; PTA *ptas, *ptad; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixa = pixaCreate(0); /* Gaussian kernel */ kel = makeGaussianKernel(5, 5, 3.0, 4.0); kernelGetSum(kel, &sum); if (rp->display) fprintf(stderr, "Sum for 2d gaussian kernel = %f\n", sum); pixt = kernelDisplayInPix(kel, 41, 2); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 0 */ pixSaveTiled(pixt, pixa, 1, 1, 20, 8); pixDestroy(&pixt); /* Separable gaussian kernel */ makeGaussianKernelSep(5, 5, 3.0, 4.0, &kelx, &kely); kernelGetSum(kelx, &sumx); if (rp->display) fprintf(stderr, "Sum for x gaussian kernel = %f\n", sumx); kernelGetSum(kely, &sumy); if (rp->display) fprintf(stderr, "Sum for y gaussian kernel = %f\n", sumy); if (rp->display) fprintf(stderr, "Sum for x * y gaussian kernel = %f\n", sumx * sumy); pixt = kernelDisplayInPix(kelx, 41, 2); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 1 */ pixSaveTiled(pixt, pixa, 1, 0, 20, 8); pixDestroy(&pixt); pixt = kernelDisplayInPix(kely, 41, 2); regTestWritePixAndCheck(rp, pixt, IFF_PNG); /* 2 */ pixSaveTiled(pixt, pixa, 1, 0, 20, 8); pixDestroy(&pixt); /* Use pixRasterop() to generate source image */ pixs = pixRead("test8.jpg"); pixs2 = pixRead("karen8.jpg"); pixRasterop(pixs, 150, 125, 150, 100, PIX_SRC, pixs2, 75, 100); regTestWritePixAndCheck(rp, pixs, IFF_JFIF_JPEG); /* 3 */ /* Convolution directly with pix */ pixt1 = pixConvolve(pixs, kel, 8, 1); regTestWritePixAndCheck(rp, pixt1, IFF_JFIF_JPEG); /* 4 */ pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixConvolveSep(pixs, kelx, kely, 8, 1); regTestWritePixAndCheck(rp, pixt2, IFF_JFIF_JPEG); /* 5 */ pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); /* Convolution indirectly with fpix, using fpixRasterop() * to generate the source image. */ fpixs = pixConvertToFPix(pixs, 3); fpixs2 = pixConvertToFPix(pixs2, 3); fpixRasterop(fpixs, 150, 125, 150, 100, fpixs2, 75, 100); fpixt1 = fpixConvolve(fpixs, kel, 1); pixt3 = fpixConvertToPix(fpixt1, 8, L_CLIP_TO_ZERO, 1); regTestWritePixAndCheck(rp, pixt3, IFF_JFIF_JPEG); /* 6 */ pixSaveTiled(pixt3, pixa, 1, 1, 20, 8); fpixt2 = fpixConvolveSep(fpixs, kelx, kely, 1); pixt4 = fpixConvertToPix(fpixt2, 8, L_CLIP_TO_ZERO, 1); regTestWritePixAndCheck(rp, pixt4, IFF_JFIF_JPEG); /* 7 */ pixSaveTiled(pixt4, pixa, 1, 0, 20, 8); pixDestroy(&pixs2); fpixDestroy(&fpixs2); fpixDestroy(&fpixt1); fpixDestroy(&fpixt2); /* Comparison of results */ pixCompareGray(pixt1, pixt2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of pixConvolve and pixConvolveSep: %f\n", diff); pixCompareGray(pixt3, pixt4, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of fpixConvolve and fpixConvolveSep: %f\n", diff); pixCompareGray(pixt1, pixt3, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of pixConvolve and fpixConvolve: %f\n", diff); pixCompareGray(pixt2, pixt4, L_COMPARE_ABS_DIFF, GPLOT_PNG, NULL, &diff, NULL, NULL); if (rp->display) fprintf(stderr, "Ave diff of pixConvolveSep and fpixConvolveSep: %f\n", diff); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); /* Test arithmetic operations; add in a fraction rotated by 180 */ pixs3 = pixRotate180(NULL, pixs); regTestWritePixAndCheck(rp, pixs3, IFF_JFIF_JPEG); /* 8 */ pixSaveTiled(pixs3, pixa, 1, 1, 20, 8); fpixs3 = pixConvertToFPix(pixs3, 3); fpixd = fpixLinearCombination(NULL, fpixs, fpixs3, 20.0, 5.0); fpixAddMultConstant(fpixd, 0.0, 23.174); /* multiply up in magnitude */ pixd = fpixDisplayMaxDynamicRange(fpixd); /* bring back to 8 bpp */ regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 9 */ pixSaveTiled(pixd, pixa, 1, 0, 20, 8); pixDestroy(&pixs3); fpixDestroy(&fpixs3); fpixDestroy(&fpixd); pixDestroy(&pixd); pixDestroy(&pixs); fpixDestroy(&fpixs); /* Save the comparison graph; gnuplot should have made it by now! */ #ifndef _WIN32 sleep(2); #else Sleep(2000); #endif /* _WIN32 */ pixt5 = pixRead("/tmp/grayroot.png"); regTestWritePixAndCheck(rp, pixt5, IFF_PNG); /* 10 */ pixSaveTiled(pixt5, pixa, 1, 1, 20, 8); pixDestroy(&pixt5); /* Display results */ pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 11 */ pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); /* Test some more convolutions, with sampled output. First on pix */ pixa = pixaCreate(0); pixs = pixRead("1555-7.jpg"); pixg = pixConvertTo8(pixs, 0); l_setConvolveSampling(5, 5); pixt1 = pixConvolve(pixg, kel, 8, 1); regTestWritePixAndCheck(rp, pixt1, IFF_JFIF_JPEG); /* 12 */ pixSaveTiled(pixt1, pixa, 1, 1, 20, 32); pixt2 = pixConvolveSep(pixg, kelx, kely, 8, 1); regTestWritePixAndCheck(rp, pixt2, IFF_JFIF_JPEG); /* 13 */ pixSaveTiled(pixt2, pixa, 1, 0, 20, 32); pixt3 = pixConvolveRGB(pixs, kel); regTestWritePixAndCheck(rp, pixt3, IFF_JFIF_JPEG); /* 14 */ pixSaveTiled(pixt3, pixa, 1, 0, 20, 32); pixt4 = pixConvolveRGBSep(pixs, kelx, kely); regTestWritePixAndCheck(rp, pixt4, IFF_JFIF_JPEG); /* 15 */ pixSaveTiled(pixt4, pixa, 1, 0, 20, 32); /* Then on fpix */ fpixg = pixConvertToFPix(pixg, 1); fpixt1 = fpixConvolve(fpixg, kel, 1); pixt5 = fpixConvertToPix(fpixt1, 8, L_CLIP_TO_ZERO, 0); regTestWritePixAndCheck(rp, pixt5, IFF_JFIF_JPEG); /* 16 */ pixSaveTiled(pixt5, pixa, 1, 1, 20, 32); fpixt2 = fpixConvolveSep(fpixg, kelx, kely, 1); pixt6 = fpixConvertToPix(fpixt2, 8, L_CLIP_TO_ZERO, 0); regTestWritePixAndCheck(rp, pixt6, IFF_JFIF_JPEG); /* 17 */ pixSaveTiled(pixt2, pixa, 1, 0, 20, 32); regTestCompareSimilarPix(rp, pixt1, pixt5, 2, 0.00, 0); /* 18 */ regTestCompareSimilarPix(rp, pixt2, pixt6, 2, 0.00, 0); /* 19 */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); fpixDestroy(&fpixg); fpixDestroy(&fpixt1); fpixDestroy(&fpixt2); pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 20 */ pixDisplayWithTitle(pixd, 600, 100, NULL, rp->display); pixDestroy(&pixs); pixDestroy(&pixg); pixDestroy(&pixd); pixaDestroy(&pixa); /* Test extension (continued and slope). * First, build a smooth vertical disparity array; * then extend and show the contours. */ pixs = pixRead("cat-35.jpg"); pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); dewa = dewarpaCreate(1, 30, 1, 15, 0); dew = dewarpCreate(pixb, 35); dewarpaInsertDewarp(dewa, dew); dewarpBuildModel(dew, NULL); dewarpPopulateFullRes(dew, NULL); fpixs = dew->fullvdispar; fpixs2 = fpixAddContinuedBorder(fpixs, 200, 200, 100, 300); fpixs3 = fpixAddSlopeBorder(fpixs, 200, 200, 100, 300); dpix = fpixConvertToDPix(fpixs3); fpixs4 = dpixConvertToFPix(dpix); pixt1 = fpixRenderContours(fpixs, 2.0, 0.2); pixt2 = fpixRenderContours(fpixs2, 2.0, 0.2); pixt3 = fpixRenderContours(fpixs3, 2.0, 0.2); pixt4 = fpixRenderContours(fpixs4, 2.0, 0.2); pixt5 = pixRead("karen8.jpg"); dpix2 = pixConvertToDPix(pixt5, 1); pixt6 = dpixConvertToPix(dpix2, 8, L_CLIP_TO_ZERO, 0); regTestWritePixAndCheck(rp, pixt1, IFF_PNG); /* 21 */ pixDisplayWithTitle(pixt1, 0, 100, NULL, rp->display); regTestWritePixAndCheck(rp, pixt2, IFF_PNG); /* 22 */ pixDisplayWithTitle(pixt2, 470, 100, NULL, rp->display); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 23 */ pixDisplayWithTitle(pixt3, 1035, 100, NULL, rp->display); regTestComparePix(rp, pixt3, pixt4); /* 24 */ regTestComparePix(rp, pixt5, pixt6); /* 25 */ pixDestroy(&pixs); pixDestroy(&pixn); pixDestroy(&pixg); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixt5); pixDestroy(&pixt6); fpixDestroy(&fpixs2); fpixDestroy(&fpixs3); fpixDestroy(&fpixs4); dpixDestroy(&dpix); dpixDestroy(&dpix2); /* Test affine and projective transforms on fpix */ fpixWrite("/tmp/fpix1.fp", dew->fullvdispar); fpix1 = fpixRead("/tmp/fpix1.fp"); pixt1 = fpixAutoRenderContours(fpix1, 40); regTestWritePixAndCheck(rp, pixt1, IFF_PNG); /* 26 */ pixDisplayWithTitle(pixt1, 0, 500, NULL, rp->display); pixDestroy(&pixt1); MakePtasAffine(1, &ptas, &ptad); fpix2 = fpixAffinePta(fpix1, ptad, ptas, 200, 0.0); pixt2 = fpixAutoRenderContours(fpix2, 40); regTestWritePixAndCheck(rp, pixt2, IFF_PNG); /* 27 */ pixDisplayWithTitle(pixt2, 400, 500, NULL, rp->display); fpixDestroy(&fpix2); pixDestroy(&pixt2); ptaDestroy(&ptas); ptaDestroy(&ptad); MakePtas(1, &ptas, &ptad); fpix2 = fpixProjectivePta(fpix1, ptad, ptas, 200, 0.0); pixt3 = fpixAutoRenderContours(fpix2, 40); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 28 */ pixDisplayWithTitle(pixt3, 400, 500, NULL, rp->display); fpixDestroy(&fpix2); pixDestroy(&pixt3); ptaDestroy(&ptas); ptaDestroy(&ptad); fpixDestroy(&fpix1); dewarpaDestroy(&dewa); kernelDestroy(&kel); kernelDestroy(&kelx); kernelDestroy(&kely); return regTestCleanup(rp); }
/*! * deskew() * * Input: pixs * redsearch (for binary search: reduction factor = 1, 2 or 4) * Return: deskewed pix, or NULL on error */ PIX * deskew(PIX *pixs, l_int32 redsearch) { l_float32 angle, conf, deg2rad; PIX *pixg; /* gray version */ PIX *pixb; /* binary version */ PIX *pixd; /* destination image */ PROCNAME("deskew"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); /* Calculate a skew angle. We may need to make a binary version of the * image for this calculation. */ if (pixGetDepth(pixs) != 1) { /* FIX ME: We should probably pick a threshold value with more care. */ /* Create a grayscale image if we need one. */ if (pixGetDepth(pixs) >= 24) { pixg = pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0); } else { pixg = pixs; } pixb = pixThresholdToBinary(pixg, 127); if (pixg != pixs) { pixDestroy(&pixg); } /* Assert: We are done with any gray image. */ } else { pixb = pixs; } /* Assert: We have a valid binary image. */ if (redsearch != 1 && redsearch != 2 && redsearch != 4) return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); deg2rad = 3.1415926535 / 180.; if (pixFindSkewSweepAndSearch(pixb, &angle, &conf, DEFAULT_SWEEP_REDUCTION, redsearch, DEFAULT_SWEEP_RANGE, DEFAULT_SWEEP_DELTA, DEFAULT_MINBS_DELTA)) { pixd = pixClone(pixs); goto finish; } if (L_ABS(angle) < MIN_DESKEW_ANGLE || conf < MIN_ALLOWED_CONFIDENCE) { pixd = pixClone(pixs); goto finish; } /* If the pixel depth of pixs is 1, we need to use a bit-depth * independent rotate instead of the more accurate area mapping rotate. */ if (pixGetDepth(pixs) == 1) { if ((pixd = pixRotateShear(pixs, 0, 0, deg2rad * angle, 0xffffff00)) == NULL) { pixd = pixClone(pixs); } } else { #if defined(COLOR_ROTATE) if ((pixd = pixRotateAMColorFast(pixs, deg2rad * angle)) == NULL) { pixd = pixClone(pixs); } #else if ((pixd = pixRotateAM(pixs, deg2rad * angle, 0xffffff00)) == NULL) { pixd = pixClone(pixs); } #endif } finish: if (pixb != pixs) { pixDestroy(&pixb); } return pixd; }
int main(int argc, char **argv) { char label[512]; l_int32 rval, gval, bval, w, h, i, j, rwhite, gwhite, bwhite, count; l_uint32 pixel; GPLOT *gplot1, *gplot2; NUMA *naseq, *na; NUMAA *naa1, *naa2; PIX *pixs, *pixt, *pixt0, *pixt1, *pixt2; PIX *pixr, *pixg, *pixb; PIXA *pixa; PIXCMAP *cmap; static char mainName[] = "colorspacetest"; if (argc != 2) return ERROR_INT(" Syntax: colorspacetest filein", mainName, 1); if ((pixs = pixRead(argv[1])) == NULL) return ERROR_INT("pixs not made", mainName, 1); /* Generate colors by sampling hue with max sat and value. * This was used to make the color strip 19-colors.png. */ pixa = pixaCreate(19); for (i = 0; i < 19; i++) { convertHSVToRGB((240 * i / 18), 255, 255, &rval, &gval, &bval); composeRGBPixel(rval, gval, bval, &pixel); pixt1 = pixCreate(50, 100, 32); pixSetAllArbitrary(pixt1, pixel); pixaAddPix(pixa, pixt1, L_INSERT); } pixt2 = pixaDisplayTiledInRows(pixa, 32, 1100, 1.0, 0, 0, 0); pixDisplayWrite(pixt2, 1); pixDestroy(&pixt2); pixaDestroy(&pixa); /* Colorspace conversion in rgb */ pixDisplayWrite(pixs, 1); pixt = pixConvertRGBToHSV(NULL, pixs); pixDisplayWrite(pixt, 1); pixConvertHSVToRGB(pixt, pixt); pixDisplayWrite(pixt, 1); pixDestroy(&pixt); /* Colorspace conversion on a colormap */ pixt = pixOctreeQuantNumColors(pixs, 25, 0); pixDisplayWrite(pixt, 1); cmap = pixGetColormap(pixt); pixcmapWriteStream(stderr, cmap); pixcmapConvertRGBToHSV(cmap); pixcmapWriteStream(stderr, cmap); pixDisplayWrite(pixt, 1); pixcmapConvertHSVToRGB(cmap); pixcmapWriteStream(stderr, cmap); pixDisplayWrite(pixt, 1); pixDestroy(&pixt); /* Color content extraction */ pixColorContent(pixs, 0, 0, 0, 0, &pixr, &pixg, &pixb); pixDisplayWrite(pixr, 1); pixDisplayWrite(pixg, 1); pixDisplayWrite(pixb, 1); pixDestroy(&pixr); pixDestroy(&pixg); pixDestroy(&pixb); /* Color content measurement */ pixa = pixaCreate(20); naseq = numaMakeSequence(100, 5, 20); naa1 = numaaCreate(6); naa2 = numaaCreate(6); for (i = 0; i < 6; i++) { na = numaCreate(20); numaaAddNuma(naa1, na, L_COPY); numaaAddNuma(naa2, na, L_INSERT); } pixGetDimensions(pixs, &w, &h, NULL); for (i = 0; i < 20; i++) { rwhite = 100 + 5 * i; gwhite = 200 - 5 * i; bwhite = 150; pixt0 = pixGlobalNormRGB(NULL, pixs, rwhite, gwhite, bwhite, 255); pixaAddPix(pixa, pixt0, L_INSERT); pixt1 = pixColorMagnitude(pixs, rwhite, gwhite, bwhite, L_MAX_DIFF_FROM_AVERAGE_2); for (j = 0; j < 6; j++) { pixt2 = pixThresholdToBinary(pixt1, 30 + 10 * j); pixInvert(pixt2, pixt2); pixCountPixels(pixt2, &count, NULL); na = numaaGetNuma(naa1, j, L_CLONE); numaAddNumber(na, (l_float32)count / (l_float32)(w * h)); numaDestroy(&na); pixDestroy(&pixt2); } pixDestroy(&pixt1); pixt1 = pixColorMagnitude(pixs, rwhite, gwhite, bwhite, L_MAX_MIN_DIFF_FROM_2); for (j = 0; j < 6; j++) { pixt2 = pixThresholdToBinary(pixt1, 30 + 10 * j); pixInvert(pixt2, pixt2); pixCountPixels(pixt2, &count, NULL); na = numaaGetNuma(naa2, j, L_CLONE); numaAddNumber(na, (l_float32)count / (l_float32)(w * h)); numaDestroy(&na); pixDestroy(&pixt2); } pixDestroy(&pixt1); } gplot1 = gplotCreate("/tmp/junkplot1", GPLOT_X11, "Fraction with given color (diff from average)", "white point space for red", "amount of color"); gplot2 = gplotCreate("/tmp/junkplot2", GPLOT_X11, "Fraction with given color (min diff)", "white point space for red", "amount of color"); for (j = 0; j < 6; j++) { na = numaaGetNuma(naa1, j, L_CLONE); sprintf(label, "thresh %d", 30 + 10 * j); gplotAddPlot(gplot1, naseq, na, GPLOT_LINES, label); numaDestroy(&na); na = numaaGetNuma(naa2, j, L_CLONE); gplotAddPlot(gplot2, naseq, na, GPLOT_LINES, label); numaDestroy(&na); } gplotMakeOutput(gplot1); gplotMakeOutput(gplot2); gplotDestroy(&gplot1); gplotDestroy(&gplot2); pixt1 = pixaDisplayTiledAndScaled(pixa, 32, 250, 4, 0, 10, 2); pixWrite("/tmp/junkcolormag", pixt1, IFF_PNG); pixDisplayWithTitle(pixt1, 0, 100, "Color magnitude", 1); pixDestroy(&pixt1); pixaDestroy(&pixa); numaDestroy(&naseq); numaaDestroy(&naa1); numaaDestroy(&naa2); pixDisplayMultiple("/tmp/display/file*"); pixDestroy(&pixs); return 0; }
/* * pixWriteSegmentedPageToPS() * * Input: pixs (all depths; colormap ok) * pixm (<optional> 1 bpp segmentation mask over image region) * textscale (scale of text output relative to pixs) * imagescale (scale of image output relative to pixs) * threshold (threshold for binarization; typ. 190) * pageno (page number in set; use 1 for new output file) * fileout (output ps file) * Return: 0 if OK, 1 on error * * Notes: * (1) This generates the PS string for a mixed text/image page, * and adds it to an existing file if @pageno > 1. * The PS output is determined by fitting the result to * a letter-size (8.5 x 11 inch) page. * (2) The two images (pixs and pixm) are at the same resolution * (typically 300 ppi). They are used to generate two compressed * images, pixb and pixc, that are put directly into the output * PS file. * (3) pixb is the text component. In the PostScript world, we think of * it as a mask through which we paint black. It is produced by * scaling pixs by @textscale, and thresholding to 1 bpp. * (4) pixc is the image component, which is that part of pixs under * the mask pixm. It is scaled from pixs by @imagescale. * (5) Typical values are textscale = 2.0 and imagescale = 0.5. * (6) If pixm == NULL, the page has only text. If it is all black, * the page is all image and has no text. * (7) This can be used to write a multi-page PS file, by using * sequential page numbers with the same output file. It can * also be used to write separate PS files for each page, * by using different output files with @pageno = 0 or 1. */ l_int32 pixWriteSegmentedPageToPS(PIX *pixs, PIX *pixm, l_float32 textscale, l_float32 imagescale, l_int32 threshold, l_int32 pageno, const char *fileout) { l_int32 alltext, notext, d, ret; l_uint32 val; l_float32 scaleratio; PIX *pixmi, *pixmis, *pixt, *pixg, *pixsc, *pixb, *pixc; PROCNAME("pixWriteSegmentedPageToPS"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!fileout) return ERROR_INT("fileout not defined", procName, 1); if (imagescale <= 0.0 || textscale <= 0.0) return ERROR_INT("relative scales must be > 0.0", procName, 1); /* Analyze the page. Determine the ratio by which the * binary text mask is scaled relative to the image part. * If there is no image region (alltext == TRUE), the * text mask will be rendered directly to fit the page, * and scaleratio = 1.0. */ alltext = TRUE; notext = FALSE; scaleratio = 1.0; if (pixm) { pixZero(pixm, &alltext); /* pixm empty: all text */ if (alltext) pixm = NULL; /* treat it as not existing here */ else { pixmi = pixInvert(NULL, pixm); pixZero(pixmi, ¬ext); /* pixm full; no text */ pixDestroy(&pixmi); scaleratio = textscale / imagescale; } } if (pixGetDepth(pixs) == 1) { /* render tiff g4 */ pixb = pixClone(pixs); pixc = NULL; } else { pixt = pixConvertTo8Or32(pixs, 0, 0); /* this can be a clone of pixs */ /* Get the binary text mask. Note that pixg cannot be a * clone of pixs, because it may be altered by pixSetMasked(). */ pixb = NULL; if (notext == FALSE) { d = pixGetDepth(pixt); if (d == 8) pixg = pixCopy(NULL, pixt); else /* d == 32 */ pixg = pixConvertRGBToLuminance(pixt); if (pixm) /* clear out the image parts */ pixSetMasked(pixg, pixm, 255); if (textscale == 1.0) pixsc = pixClone(pixg); else if (textscale >= 0.7) pixsc = pixScaleGrayLI(pixg, textscale, textscale); else pixsc = pixScaleAreaMap(pixg, textscale, textscale); pixb = pixThresholdToBinary(pixsc, threshold); pixDestroy(&pixg); pixDestroy(&pixsc); } /* Get the scaled image region */ pixc = NULL; if (pixm) { if (imagescale == 1.0) pixsc = pixClone(pixt); /* can possibly be a clone of pixs */ else pixsc = pixScale(pixt, imagescale, imagescale); /* If pixm is not full, clear the pixels in pixsc * corresponding to bg in pixm, where there can be text * that is written through the mask pixb. Note that * we could skip this and use pixsc directly in * pixWriteMixedToPS(); however, clearing these * non-image regions to a white background will reduce * the size of pixc (relative to pixsc), and hence * reduce the size of the PS file that is generated. * Use a copy so that we don't accidentally alter pixs. */ if (notext == FALSE) { pixmis = pixScale(pixm, imagescale, imagescale); pixmi = pixInvert(NULL, pixmis); val = (d == 8) ? 0xff : 0xffffff00; pixc = pixCopy(NULL, pixsc); pixSetMasked(pixc, pixmi, val); /* clear non-image part */ pixDestroy(&pixmis); pixDestroy(&pixmi); } else pixc = pixClone(pixsc); pixDestroy(&pixsc); } pixDestroy(&pixt); } ret = pixWriteMixedToPS(pixb, pixc, scaleratio, pageno, fileout); pixDestroy(&pixb); pixDestroy(&pixc); return ret; }
l_int32 main(int argc, char **argv) { l_int32 irval, igval, ibval; l_float32 rval, gval, bval, fract, fgfract; L_BMF *bmf; BOX *box; BOXA *boxa; FPIX *fpix; PIX *pixs, *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7; PIX *pix8, *pix9, *pix10, *pix11, *pix12, *pix13, *pix14, *pix15; PIXA *pixa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixa = pixaCreate(0); pixs = pixRead("breviar38.150.jpg"); /* pixs = pixRead("breviar32.150.jpg"); */ pixaAddPix(pixa, pixs, L_CLONE); regTestWritePixAndCheck(rp, pixs, IFF_JFIF_JPEG); /* 0 */ pixDisplayWithTitle(pixs, 0, 0, "Input image", rp->display); /* Extract the blue component, which is small in all the text * regions, including in the highlight color region */ pix1 = pixGetRGBComponent(pixs, COLOR_BLUE); pixaAddPix(pixa, pix1, L_CLONE); regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG); /* 1 */ pixDisplayWithTitle(pix1, 200, 0, "Blue component", rp->display); /* Do a background normalization, with the background set to * approximately 200 */ pix2 = pixBackgroundNormSimple(pix1, NULL, NULL); pixaAddPix(pixa, pix2, L_COPY); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 2 */ pixDisplayWithTitle(pix2, 400, 0, "BG normalized to 200", rp->display); /* Do a linear transform on the gray pixels, with 50 going to * black and 160 going to white. 50 is sufficiently low to * make both the red and black print quite dark. Quantize * to a few equally spaced gray levels. This is the image * to which highlight color will be applied. */ pixGammaTRC(pix2, pix2, 1.0, 50, 160); pix3 = pixThresholdOn8bpp(pix2, 7, 1); pixaAddPix(pixa, pix3, L_CLONE); regTestWritePixAndCheck(rp, pix3, IFF_JFIF_JPEG); /* 3 */ pixDisplayWithTitle(pix3, 600, 0, "Basic quantized with white bg", rp->display); /* Identify the regions of red text. First, make a mask * consisting of all pixels such that (R-B)/B is larger * than 2.0. This will have all the red, plus a lot of * the dark pixels. */ fpix = pixComponentFunction(pixs, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0); pix4 = fpixThresholdToPix(fpix, 2.0); pixInvert(pix4, pix4); /* red plus some dark text */ pixaAddPix(pixa, pix4, L_CLONE); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 4 */ pixDisplayWithTitle(pix4, 800, 0, "Red plus dark pixels", rp->display); /* Make a mask consisting of all the red and background pixels */ pix5 = pixGetRGBComponent(pixs, COLOR_RED); pix6 = pixThresholdToBinary(pix5, 128); pixInvert(pix6, pix6); /* red plus background (white) */ /* Intersect the two masks to get a mask consisting of pixels * that are almost certainly red. This is the seed. */ pix7 = pixAnd(NULL, pix4, pix6); /* red only (seed) */ pixaAddPix(pixa, pix7, L_COPY); regTestWritePixAndCheck(rp, pix7, IFF_PNG); /* 5 */ pixDisplayWithTitle(pix7, 0, 600, "Seed for red color", rp->display); /* Make the clipping mask by thresholding the image with * the background cleaned to white. */ pix8 = pixThresholdToBinary(pix2, 230); /* mask */ pixaAddPix(pixa, pix8, L_CLONE); regTestWritePixAndCheck(rp, pix8, IFF_PNG); /* 6 */ pixDisplayWithTitle(pix8, 200, 600, "Clipping mask for red components", rp->display); /* Fill into the mask from the seed */ pixSeedfillBinary(pix7, pix7, pix8, 8); /* filled: red plus touching */ regTestWritePixAndCheck(rp, pix7, IFF_PNG); /* 7 */ pixDisplayWithTitle(pix7, 400, 600, "Red component mask filled", rp->display); /* Remove long horizontal and vertical lines from the filled result */ pix9 = pixMorphSequence(pix7, "o40.1", 0); pixSubtract(pix7, pix7, pix9); /* remove long horizontal lines */ pixDestroy(&pix9); pix9 = pixMorphSequence(pix7, "o1.40", 0); pixSubtract(pix7, pix7, pix9); /* remove long vertical lines */ /* Close the regions to be colored */ pix10 = pixMorphSequence(pix7, "c5.1", 0); pixaAddPix(pixa, pix10, L_CLONE); regTestWritePixAndCheck(rp, pix10, IFF_PNG); /* 8 */ pixDisplayWithTitle(pix10, 600, 600, "Components defining regions allowing coloring", rp->display); /* Sanity check on amount to be colored. Only accept images * with less than 10% of all the pixels with highlight color */ pixForegroundFraction(pix10, &fgfract); if (fgfract >= 0.10) { L_INFO("too much highlighting: fract = %6.3f; removing it\n", rp->testname, fgfract); pixClearAll(pix10); pixSetPixel(pix10, 0, 0, 1); } /* Get the bounding boxes of the regions to be colored */ boxa = pixConnCompBB(pix10, 8); /* Get a color to paint that is representative of the * actual highlight color in the image. Scale each * color component up from the average by an amount necessary * to saturate the red. Then divide the green and * blue components by 2.0. */ pixGetAverageMaskedRGB(pixs, pix7, 0, 0, 1, L_MEAN_ABSVAL, &rval, &gval, &bval); fract = 255.0 / rval; irval = lept_roundftoi(fract * rval); igval = lept_roundftoi(fract * gval / 2.0); ibval = lept_roundftoi(fract * bval / 2.0); fprintf(stderr, "(r,g,b) = (%d,%d,%d)\n", irval, igval, ibval); /* Color the quantized gray version in the selected regions */ pix11 = pixColorGrayRegions(pix3, boxa, L_PAINT_DARK, 220, irval, igval, ibval); pixaAddPix(pixa, pix11, L_CLONE); regTestWritePixAndCheck(rp, pix11, IFF_PNG); /* 9 */ pixDisplayWithTitle(pix11, 800, 600, "Final colored result", rp->display); pixaAddPix(pixa, pixs, L_CLONE); /* Test colorization on gray and cmapped gray */ pix12 = pixColorGrayRegions(pix2, boxa, L_PAINT_DARK, 220, 0, 255, 0); pixaAddPix(pixa, pix12, L_CLONE); regTestWritePixAndCheck(rp, pix12, IFF_PNG); /* 10 */ pixDisplayWithTitle(pix12, 900, 600, "Colorizing boxa gray", rp->display); box = boxCreate(200, 200, 250, 350); pix13 = pixCopy(NULL, pix2); pixColorGray(pix13, box, L_PAINT_DARK, 220, 0, 0, 255); pixaAddPix(pixa, pix13, L_CLONE); regTestWritePixAndCheck(rp, pix13, IFF_PNG); /* 11 */ pixDisplayWithTitle(pix13, 1000, 600, "Colorizing box gray", rp->display); pix14 = pixThresholdTo4bpp(pix2, 6, 1); pix15 = pixColorGrayRegions(pix14, boxa, L_PAINT_DARK, 220, 0, 0, 255); pixaAddPix(pixa, pix15, L_CLONE); regTestWritePixAndCheck(rp, pix15, IFF_PNG); /* 12 */ pixDisplayWithTitle(pix15, 1100, 600, "Colorizing boxa cmap", rp->display); pixColorGrayCmap(pix14, box, L_PAINT_DARK, 0, 255, 255); pixaAddPix(pixa, pix14, L_CLONE); regTestWritePixAndCheck(rp, pix14, IFF_PNG); /* 13 */ pixDisplayWithTitle(pix14, 1200, 600, "Colorizing box cmap", rp->display); boxDestroy(&box); /* Generate a pdf of the intermediate results */ lept_mkdir("lept"); L_INFO("Writing to /tmp/lept/colorize.pdf\n", rp->testname); pixaConvertToPdf(pixa, 90, 1.0, 0, 0, "Colorizing highlighted text", "/tmp/lept/colorize.pdf"); pixaDestroy(&pixa); fpixDestroy(&fpix); boxDestroy(&box); boxaDestroy(&boxa); pixDestroy(&pixs); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); pixDestroy(&pix7); pixDestroy(&pix8); pixDestroy(&pix9); pixDestroy(&pix10); pixDestroy(&pix11); pixDestroy(&pix12); pixDestroy(&pix13); pixDestroy(&pix14); pixDestroy(&pix15); /* Test the color detector */ pixa = pixaCreate(7); bmf = bmfCreate("./fonts", 4); pix1 = TestForRedColor(rp, "brev06.75.jpg", 1, bmf); /* 14 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = TestForRedColor(rp, "brev10.75.jpg", 0, bmf); /* 15 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = TestForRedColor(rp, "brev14.75.jpg", 1, bmf); /* 16 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = TestForRedColor(rp, "brev20.75.jpg", 1, bmf); /* 17 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = TestForRedColor(rp, "brev36.75.jpg", 0, bmf); /* 18 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = TestForRedColor(rp, "brev53.75.jpg", 1, bmf); /* 19 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = TestForRedColor(rp, "brev56.75.jpg", 1, bmf); /* 20 */ pixaAddPix(pixa, pix1, L_INSERT); /* Generate a pdf of the color detector results */ L_INFO("Writing to /tmp/lept/colordetect.pdf\n", rp->testname); pixaConvertToPdf(pixa, 45, 1.0, 0, 0, "Color detection", "/tmp/lept/colordetect.pdf"); pixaDestroy(&pixa); bmfDestroy(&bmf); return regTestCleanup(rp); }
/*! * pixOtsuAdaptiveThreshold() * * Input: pixs (8 bpp) * sx, sy (desired tile dimensions; actual size may vary) * smoothx, smoothy (half-width of convolution kernel applied to * threshold array: use 0 for no smoothing) * scorefract (fraction of the max Otsu score; typ. 0.1; * use 0.0 for standard Otsu) * &pixth (<optional return> array of threshold values * found for each tile) * &pixd (<optional return> thresholded input pixs, based on * the threshold array) * Return: 0 if OK, 1 on error * * Notes: * (1) The Otsu method finds a single global threshold for an image. * This function allows a locally adapted threshold to be * found for each tile into which the image is broken up. * (2) The array of threshold values, one for each tile, constitutes * a highly downscaled image. This array is optionally * smoothed using a convolution. The full width and height of the * convolution kernel are (2 * @smoothx + 1) and (2 * @smoothy + 1). * (3) The minimum tile dimension allowed is 16. If such small * tiles are used, it is recommended to use smoothing, because * without smoothing, each small tile determines the splitting * threshold independently. A tile that is entirely in the * image bg will then hallucinate fg, resulting in a very noisy * binarization. The smoothing should be large enough that no * tile is only influenced by one type (fg or bg) of pixels, * because it will force a split of its pixels. * (4) To get a single global threshold for the entire image, use * input values of @sx and @sy that are larger than the image. * For this situation, the smoothing parameters are ignored. * (5) The threshold values partition the image pixels into two classes: * one whose values are less than the threshold and another * whose values are greater than or equal to the threshold. * This is the same use of 'threshold' as in pixThresholdToBinary(). * (6) The scorefract is the fraction of the maximum Otsu score, which * is used to determine the range over which the histogram minimum * is searched. See numaSplitDistribution() for details on the * underlying method of choosing a threshold. * (7) This uses enables a modified version of the Otsu criterion for * splitting the distribution of pixels in each tile into a * fg and bg part. The modification consists of searching for * a minimum in the histogram over a range of pixel values where * the Otsu score is within a defined fraction, @scorefract, * of the max score. To get the original Otsu algorithm, set * @scorefract == 0. */ l_int32 pixOtsuAdaptiveThreshold(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, PIX **ppixth, PIX **ppixd) { l_int32 w, h, nx, ny, i, j, thresh; l_uint32 val; PIX *pixt, *pixb, *pixthresh, *pixth, *pixd; PIXTILING *pt; PROCNAME("pixOtsuAdaptiveThreshold"); if (!ppixth && !ppixd){ return ERROR_INT("neither &pixth nor &pixd defined", procName, 1); LOGE("neither &pixth nor &pixd defined"); } if (ppixth) *ppixth = NULL; if (ppixd) *ppixd = NULL; if (!pixs || pixGetDepth(pixs) != 8){ return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); LOGE("pixs not defined or not 8 bpp"); } if (sx < 16 || sy < 16){ return ERROR_INT("sx and sy must be >= 16", procName, 1); LOGE("sx and sy must be >= 16"); } /* Compute the threshold array for the tiles */ pixGetDimensions(pixs, &w, &h, NULL); nx = L_MAX(1, w / sx); ny = L_MAX(1, h / sy); smoothx = L_MIN(smoothx, (nx - 1) / 2); smoothy = L_MIN(smoothy, (ny - 1) / 2); pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0); pixthresh = pixCreate(nx, ny, 8); for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { pixt = pixTilingGetTile(pt, i, j); pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh, NULL, NULL, 0); pixSetPixel(pixthresh, j, i, thresh); /* see note (4) */ pixDestroy(&pixt); } } /* Optionally smooth the threshold array */ if (smoothx > 0 || smoothy > 0) pixth = pixBlockconv(pixthresh, smoothx, smoothy); else pixth = pixClone(pixthresh); pixDestroy(&pixthresh); /* Optionally apply the threshold array to binarize pixs */ if (ppixd) { pixd = pixCreate(w, h, 1); for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { pixt = pixTilingGetTile(pt, i, j); pixGetPixel(pixth, j, i, &val); pixb = pixThresholdToBinary(pixt, val); pixTilingPaintTile(pixd, i, j, pixb, pt); pixDestroy(&pixt); pixDestroy(&pixb); } } *ppixd = pixd; } if (ppixth) *ppixth = pixth; else pixDestroy(&pixth); pixTilingDestroy(&pt); return 0; }
int main(int argc, char **argv) { char dilateseq[512], erodeseq[512]; char openseq[512], closeseq[512]; char wtophatseq[512], btophatseq[512]; l_int32 w, h; PIX *pixs, *pix1, *pix2, *pix3, *pix4, *pix5; PIXA *pixa; PIXACC *pacc; PIXCMAP *cmap; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixs = pixRead("aneurisms8.jpg"); pixa = pixaCreate(0); /* =========================================================== */ /* -------- Test gray morph, including interpreter ------------ */ pix1 = pixDilateGray(pixs, WSIZE, HSIZE); sprintf(dilateseq, "D%d.%d", WSIZE, HSIZE); pix2 = pixGrayMorphSequence(pixs, dilateseq, 0, 0); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 0 */ regTestComparePix(rp, pix1, pix2); /* 1 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pix1 = pixErodeGray(pixs, WSIZE, HSIZE); sprintf(erodeseq, "E%d.%d", WSIZE, HSIZE); pix2 = pixGrayMorphSequence(pixs, erodeseq, 0, 100); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 2 */ regTestComparePix(rp, pix1, pix2); /* 3 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pix1 = pixOpenGray(pixs, WSIZE, HSIZE); sprintf(openseq, "O%d.%d", WSIZE, HSIZE); pix2 = pixGrayMorphSequence(pixs, openseq, 0, 200); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 4 */ regTestComparePix(rp, pix1, pix2); /* 5 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pix1 = pixCloseGray(pixs, WSIZE, HSIZE); sprintf(closeseq, "C%d.%d", WSIZE, HSIZE); pix2 = pixGrayMorphSequence(pixs, closeseq, 0, 300); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 6 */ regTestComparePix(rp, pix1, pix2); /* 7 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pix1 = pixTophat(pixs, WSIZE, HSIZE, L_TOPHAT_WHITE); sprintf(wtophatseq, "Tw%d.%d", WSIZE, HSIZE); pix2 = pixGrayMorphSequence(pixs, wtophatseq, 0, 400); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 8 */ regTestComparePix(rp, pix1, pix2); /* 9 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pix1 = pixTophat(pixs, WSIZE, HSIZE, L_TOPHAT_BLACK); sprintf(btophatseq, "Tb%d.%d", WSIZE, HSIZE); pix2 = pixGrayMorphSequence(pixs, btophatseq, 0, 500); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 10 */ regTestComparePix(rp, pix1, pix2); /* 11 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); /* ------------- Test erode/dilate duality -------------- */ pix1 = pixDilateGray(pixs, WSIZE, HSIZE); pix2 = pixInvert(NULL, pixs); pix3 = pixErodeGray(pix2, WSIZE, HSIZE); pixInvert(pix3, pix3); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 12 */ regTestComparePix(rp, pix1, pix3); /* 13 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix3); /* ------------- Test open/close duality -------------- */ pix1 = pixOpenGray(pixs, WSIZE, HSIZE); pix2 = pixInvert(NULL, pixs); pix3 = pixCloseGray(pix2, WSIZE, HSIZE); pixInvert(pix3, pix3); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 14 */ regTestComparePix(rp, pix1, pix3); /* 15 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix3); /* ------------- Test tophat duality -------------- */ pix1 = pixTophat(pixs, WSIZE, HSIZE, L_TOPHAT_WHITE); pix2 = pixInvert(NULL, pixs); pix3 = pixTophat(pix2, WSIZE, HSIZE, L_TOPHAT_BLACK); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 16 */ regTestComparePix(rp, pix1, pix3); /* 17 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix3); pix1 = pixGrayMorphSequence(pixs, "Tw9.5", 0, 100); pix2 = pixInvert(NULL, pixs); pix3 = pixGrayMorphSequence(pix2, "Tb9.5", 0, 300); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 18 */ regTestComparePix(rp, pix1, pix3); /* 19 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix3); /* ------------- Test opening/closing for large sels -------------- */ pix1 = pixGrayMorphSequence(pixs, "C9.9 + C19.19 + C29.29 + C39.39 + C49.49", 0, 100); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 20 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixGrayMorphSequence(pixs, "O9.9 + O19.19 + O29.29 + O39.39 + O49.49", 0, 400); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 21 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixaDisplayTiledInColumns(pixa, 4, 1.0, 20, 2); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 22 */ pixDisplayWithTitle(pix1, 0, 0, NULL, rp->display); pixaDestroy(&pixa); pixDestroy(&pix1); /* =========================================================== */ pixa = pixaCreate(0); /* ---------- Closing plus white tophat result ------------ * * Parameters: wsize, hsize = 9, 29 * * ---------------------------------------------------------*/ pix1 = pixCloseGray(pixs, 9, 9); pix2 = pixTophat(pix1, 9, 9, L_TOPHAT_WHITE); pix3 = pixGrayMorphSequence(pixs, "C9.9 + TW9.9", 0, 0); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 23 */ regTestComparePix(rp, pix2, pix3); /* 24 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixMaxDynamicRange(pix2, L_LINEAR_SCALE); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 25 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix3); pix1 = pixCloseGray(pixs, 29, 29); pix2 = pixTophat(pix1, 29, 29, L_TOPHAT_WHITE); pix3 = pixGrayMorphSequence(pixs, "C29.29 + Tw29.29", 0, 0); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 26 */ regTestComparePix(rp, pix2, pix3); /* 27 */ pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixMaxDynamicRange(pix2, L_LINEAR_SCALE); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 28 */ pixaAddPix(pixa, pix1, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix3); /* --------- hdome with parameter height = 100 ------------*/ pix1 = pixHDome(pixs, 100, 4); pix2 = pixMaxDynamicRange(pix1, L_LINEAR_SCALE); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 29 */ regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 30 */ pixaAddPix(pixa, pix1, L_INSERT); pixaAddPix(pixa, pix2, L_INSERT); /* ----- Contrast enhancement with morph parameters 9, 9 -------*/ pixGetDimensions(pixs, &w, &h, NULL); pix1 = pixInitAccumulate(w, h, 0x8000); pixAccumulate(pix1, pixs, L_ARITH_ADD); pixMultConstAccumulate(pix1, 3., 0x8000); pix2 = pixOpenGray(pixs, 9, 9); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 31 */ pixaAddPix(pixa, pix2, L_INSERT); pixAccumulate(pix1, pix2, L_ARITH_SUBTRACT); pix2 = pixCloseGray(pixs, 9, 9); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 32 */ pixaAddPix(pixa, pix2, L_INSERT); pixAccumulate(pix1, pix2, L_ARITH_SUBTRACT); pix2 = pixFinalAccumulate(pix1, 0x8000, 8); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 33 */ pixaAddPix(pixa, pix2, L_INSERT); pixDestroy(&pix1); /* Do the same thing with the Pixacc */ pacc = pixaccCreate(w, h, 1); pixaccAdd(pacc, pixs); pixaccMultConst(pacc, 3.); pix1 = pixOpenGray(pixs, 9, 9); pixaccSubtract(pacc, pix1); pixDestroy(&pix1); pix1 = pixCloseGray(pixs, 9, 9); pixaccSubtract(pacc, pix1); pixDestroy(&pix1); pix1 = pixaccFinal(pacc, 8); pixaccDestroy(&pacc); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 34 */ pixaAddPix(pixa, pix1, L_INSERT); regTestComparePix(rp, pix1, pix2); /* 35 */ pix1 = pixaDisplayTiledInColumns(pixa, 4, 1.0, 20, 2); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 36 */ pixDisplayWithTitle(pix1, 1100, 0, NULL, rp->display); pixaDestroy(&pixa); pixDestroy(&pix1); pixDestroy(&pixs); /* =========================================================== */ pixa = pixaCreate(0); /* ---- Tophat result on feynman stamp, to extract diagrams ----- */ pixs = pixRead("feynman-stamp.jpg"); pixGetDimensions(pixs, &w, &h, NULL); /* Make output image to hold five intermediate images */ pix1 = pixCreate(5 * w + 18, h + 6, 32); /* composite output image */ pixSetAllArbitrary(pix1, 0x0000ff00); /* set to blue */ /* Paste in the input image */ pix2 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); pixRasterop(pix1, 3, 3, w, h, PIX_SRC, pix2, 0, 0); /* 1st one */ regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 37 */ pixaAddPix(pixa, pix2, L_INSERT); /* Paste in the grayscale version */ cmap = pixGetColormap(pixs); if (cmap) pix2 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); else pix2 = pixConvertRGBToGray(pixs, 0.33, 0.34, 0.33); pix3 = pixConvertTo32(pix2); /* 8 --> 32 bpp */ pixRasterop(pix1, w + 6, 3, w, h, PIX_SRC, pix3, 0, 0); /* 2nd one */ regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 38 */ pixaAddPix(pixa, pix3, L_INSERT); /* Paste in a log dynamic range scaled version of the white tophat */ pix3 = pixTophat(pix2, 3, 3, L_TOPHAT_WHITE); pix4 = pixMaxDynamicRange(pix3, L_LOG_SCALE); pix5 = pixConvertTo32(pix4); pixRasterop(pix1, 2 * w + 9, 3, w, h, PIX_SRC, pix5, 0, 0); /* 3rd */ regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 39 */ pixaAddPix(pixa, pix5, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix4); /* Stretch the range and threshold to binary; paste it in */ pix2 = pixGammaTRC(NULL, pix3, 1.0, 0, 80); pix4 = pixThresholdToBinary(pix2, 70); pix5 = pixConvertTo32(pix4); pixRasterop(pix1, 3 * w + 12, 3, w, h, PIX_SRC, pix5, 0, 0); /* 4th */ regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 40 */ pixaAddPix(pixa, pix5, L_INSERT); pixDestroy(&pix2); pixDestroy(&pix3); /* Invert; this is the final result */ pixInvert(pix4, pix4); pix5 = pixConvertTo32(pix4); pixRasterop(pix1, 4 * w + 15, 3, w, h, PIX_SRC, pix5, 0, 0); /* 5th */ regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 41 */ pixaAddPix(pixa, pix5, L_INSERT); pixDestroy(&pix1); pixDestroy(&pix4); pix1 = pixaDisplayTiledInRows(pixa, 32, 1700, 1.0, 0, 20, 2); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 42 */ pixDisplayWithTitle(pix1, 0, 800, NULL, rp->display); pixaDestroy(&pixa); pixDestroy(&pix1); pixDestroy(&pixs); return regTestCleanup(rp); }
/*! * pixMaskedThreshOnBackgroundNorm() * * Input: pixs (8 bpp grayscale; not colormapped) * pixim (<optional> 1 bpp 'image' mask; can be null) * sx, sy (tile size in pixels) * thresh (threshold for determining foreground) * mincount (min threshold on counts in a tile) * smoothx (half-width of block convolution kernel width) * smoothy (half-width of block convolution kernel height) * scorefract (fraction of the max Otsu score; typ. ~ 0.1) * &thresh (<optional return> threshold value that was * used on the normalized image) * Return: pixd (1 bpp thresholded image), or null on error * * Notes: * (1) This begins with a standard background normalization. * Additionally, there is a flexible background norm, that * will adapt to a rapidly varying background, and this * puts white pixels in the background near regions with * significant foreground. The white pixels are turned into * a 1 bpp selection mask by binarization followed by dilation. * Otsu thresholding is performed on the input image to get an * estimate of the threshold in the non-mask regions. * The background normalized image is thresholded with two * different values, and the result is combined using * the selection mask. * (2) Note that the numbers 255 (for bgval target) and 190 (for * thresholding on pixn) are tied together, and explicitly * defined in this function. * (3) See pixBackgroundNorm() for meaning and typical values * of input parameters. For a start, you can try: * sx, sy = 10, 15 * thresh = 100 * mincount = 50 * smoothx, smoothy = 2 */ PIX * pixMaskedThreshOnBackgroundNorm(PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, l_int32 *pthresh) { l_int32 w, h; l_uint32 val; PIX *pixn, *pixm, *pixd, *pixt1, *pixt2, *pixt3, *pixt4; PROCNAME("pixMaskedThreshOnBackgroundNorm"); if (pthresh) *pthresh = 0; if (!pixs || pixGetDepth(pixs) != 8) return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); if (pixGetColormap(pixs)) return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); if (sx < 4 || sy < 4) return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); if (mincount > sx * sy) { L_WARNING("mincount too large for tile size\n", procName); mincount = (sx * sy) / 3; } /* Standard background normalization */ pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh, mincount, 255, smoothx, smoothy); if (!pixn) return (PIX *)ERROR_PTR("pixn not made", procName, NULL); /* Special background normalization for adaptation to quickly * varying background. Threshold on the very light parts, * which tend to be near significant edges, and dilate to * form a mask over regions that are typically text. The * dilation size is chosen to cover the text completely, * except for very thick fonts. */ pixt1 = pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 20); pixt2 = pixThresholdToBinary(pixt1, 240); pixInvert(pixt2, pixt2); pixm = pixMorphSequence(pixt2, "d21.21", 0); pixDestroy(&pixt1); pixDestroy(&pixt2); /* Use Otsu to get a global threshold estimate for the image, * which is stored as a single pixel in pixt3. */ pixGetDimensions(pixs, &w, &h, NULL); pixOtsuAdaptiveThreshold(pixs, w, h, 0, 0, scorefract, &pixt3, NULL); if (pixt3 && pthresh) { pixGetPixel(pixt3, 0, 0, &val); *pthresh = val; } pixDestroy(&pixt3); /* Threshold the background normalized images differentially, * using a high value correlated with the background normalization * for the part of the image under the mask (i.e., near the * darker, thicker foreground), and a value that depends on the Otsu * threshold for the rest of the image. This gives a solid * (high) thresholding for the foreground parts of the image, * while allowing the background and light foreground to be * reasonably well cleaned using a threshold adapted to the * input image. */ pixd = pixThresholdToBinary(pixn, val + 30); /* for bg and light fg */ pixt4 = pixThresholdToBinary(pixn, 190); /* for heavier fg */ pixCombineMasked(pixd, pixt4, pixm); pixDestroy(&pixt4); pixDestroy(&pixm); pixDestroy(&pixn); if (!pixd) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); else return pixd; }
/*! * convertFilesTo1bpp() * * Input: dirin * substr (<optional> substring filter on filenames; can be NULL) * upscaling (1, 2 or 4; only for input color or grayscale) * thresh (global threshold for binarization; use 0 for default) * firstpage * npages (use 0 to do all from @firstpage to the end) * dirout * outformat (IFF_PNG, IFF_TIFF_G4) * Return: 0 if OK, 1 on error * * Notes: * (1) Images are sorted lexicographically, and the names in the * output directory are retained except for the extension. */ l_int32 convertFilesTo1bpp(const char *dirin, const char *substr, l_int32 upscaling, l_int32 thresh, l_int32 firstpage, l_int32 npages, const char *dirout, l_int32 outformat) { l_int32 i, nfiles; char buf[512]; char *fname, *tail, *basename; PIX *pixs, *pixg1, *pixg2, *pixb; SARRAY *safiles; PROCNAME("convertFilesTo1bpp"); if (!dirin) return ERROR_INT("dirin", procName, 1); if (!dirout) return ERROR_INT("dirout", procName, 1); if (upscaling != 1 && upscaling != 2 && upscaling != 4) return ERROR_INT("invalid upscaling factor", procName, 1); if (thresh <= 0) thresh = 180; if (firstpage < 0) firstpage = 0; if (npages < 0) npages = 0; if (outformat != IFF_TIFF_G4) outformat = IFF_PNG; safiles = getSortedPathnamesInDirectory(dirin, substr, firstpage, npages); if (!safiles) return ERROR_INT("safiles not made", procName, 1); if ((nfiles = sarrayGetCount(safiles)) == 0) { sarrayDestroy(&safiles); return ERROR_INT("no matching files in the directory", procName, 1); } for (i = 0; i < nfiles; i++) { fname = sarrayGetString(safiles, i, L_NOCOPY); if ((pixs = pixRead(fname)) == NULL) { L_WARNING("Couldn't read file %s\n", procName, fname); continue; } if (pixGetDepth(pixs) == 32) pixg1 = pixConvertRGBToLuminance(pixs); else pixg1 = pixClone(pixs); pixg2 = pixRemoveColormap(pixg1, REMOVE_CMAP_TO_GRAYSCALE); if (pixGetDepth(pixg2) == 1) { pixb = pixClone(pixg2); } else { if (upscaling == 1) pixb = pixThresholdToBinary(pixg2, thresh); else if (upscaling == 2) pixb = pixScaleGray2xLIThresh(pixg2, thresh); else /* upscaling == 4 */ pixb = pixScaleGray4xLIThresh(pixg2, thresh); } pixDestroy(&pixs); pixDestroy(&pixg1); pixDestroy(&pixg2); splitPathAtDirectory(fname, NULL, &tail); splitPathAtExtension(tail, &basename, NULL); if (outformat == IFF_TIFF_G4) { snprintf(buf, sizeof(buf), "%s/%s.tif", dirout, basename); pixWrite(buf, pixb, IFF_TIFF_G4); } else { snprintf(buf, sizeof(buf), "%s/%s.png", dirout, basename); pixWrite(buf, pixb, IFF_PNG); } pixDestroy(&pixb); FREE(tail); FREE(basename); } sarrayDestroy(&safiles); return 0; }
char *_process_frame_color_basic(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int index) { char *subtitle_text=NULL; PIX *im; im = pixCreate(width,height,32); PIX *hue_im = pixCreate(width,height,32); int i,j; for(i=0;i<height;i++) { for(j=0;j<width;j++) { int p=j*3+i*frame->linesize[0]; int r=frame->data[0][p]; int g=frame->data[0][p+1]; int b=frame->data[0][p+2]; pixSetRGBPixel(im,j,i,r,g,b); float H,S,V; rgb_to_hsv((float)r,(float)g,(float)b,&H,&S,&V); if(abs(H-ctx->hue)<20) { pixSetRGBPixel(hue_im,j,i,r,g,b); } } } PIX *edge_im = pixCreate(width,height,8),*edge_im_2 = pixCreate(width,height,8); edge_im = pixConvertRGBToGray(im,0.0,0.0,0.0); edge_im = pixSobelEdgeFilter(edge_im, L_VERTICAL_EDGES); edge_im = pixDilateGray(edge_im, 21, 1); edge_im = pixThresholdToBinary(edge_im,50); PIX *pixd = pixCreate(width,height,1); pixSauvolaBinarize(pixConvertRGBToGray(hue_im,0.0,0.0,0.0), 15, 0.3, 1, NULL, NULL, NULL, &pixd); edge_im_2 = pixConvertRGBToGray(hue_im,0.0,0.0,0.0); edge_im_2 = pixDilateGray(edge_im_2, 5, 5); PIX *feat_im = pixCreate(width,height,32); for(i=3*(height/4);i<height;i++) { for(j=0;j<width;j++) { unsigned int p1,p2,p3,p4; pixGetPixel(edge_im,j,i,&p1); pixGetPixel(pixd,j,i,&p2); // pixGetPixel(hue_im,j,i,&p3); pixGetPixel(edge_im_2,j,i,&p4); if(p1==0&&p2==0&&p4>0)//if(p4>0&&p1==0)//if(p2==0&&p1==0&&p3>0) { pixSetRGBPixel(feat_im,j,i,255,255,255); } } } if(ctx->detect_italics) { ctx->ocr_mode = HARDSUBX_OCRMODE_WORD; } // TESSERACT OCR FOR THE FRAME HERE switch(ctx->ocr_mode) { case HARDSUBX_OCRMODE_WORD: if(ctx->conf_thresh > 0) subtitle_text = get_ocr_text_wordwise_threshold(ctx, feat_im, ctx->conf_thresh); else subtitle_text = get_ocr_text_wordwise(ctx, feat_im); break; case HARDSUBX_OCRMODE_LETTER: if(ctx->conf_thresh > 0) subtitle_text = get_ocr_text_letterwise_threshold(ctx, feat_im, ctx->conf_thresh); else subtitle_text = get_ocr_text_letterwise(ctx, feat_im); break; case HARDSUBX_OCRMODE_FRAME: if(ctx->conf_thresh > 0) subtitle_text = get_ocr_text_simple_threshold(ctx, feat_im, ctx->conf_thresh); else subtitle_text = get_ocr_text_simple(ctx, feat_im); break; default: fatal(EXIT_MALFORMED_PARAMETER,"Invalid OCR Mode"); } pixDestroy(&feat_im); pixDestroy(&im); pixDestroy(&edge_im); pixDestroy(&hue_im); return subtitle_text; }
int main(int argc, char **argv) { char dilateseq[BUF_SIZE], erodeseq[BUF_SIZE]; char openseq[BUF_SIZE], closeseq[BUF_SIZE]; char wtophatseq[BUF_SIZE], btophatseq[BUF_SIZE]; char *filein; l_int32 w, h, d; PIX *pixs, *pixt, *pixt2, *pixt3, *pixt3a, *pixt4; PIX *pixg, *pixd, *pixd1, *pixd2, *pixd3; PIXACC *pacc; PIXCMAP *cmap; static char mainName[] = "graymorph1_reg"; if (argc != 2) return ERROR_INT(" Syntax: graymorph1_reg filein", mainName, 1); filein = argv[1]; if ((pixs = pixRead(filein)) == NULL) return ERROR_INT("pixs not made", mainName, 1); pixGetDimensions(pixs, &w, &h, &d); if (d != 8) return ERROR_INT("pixs not 8 bpp", mainName, 1); /* -------- Test gray morph, including interpreter ------------ */ pixd = pixDilateGray(pixs, WSIZE, HSIZE); sprintf(dilateseq, "D%d.%d", WSIZE, HSIZE); pixg = pixGrayMorphSequence(pixs, dilateseq, HORIZ_SEP, 0); pixCompare(pixd, pixg, "results are the same", "results are different"); pixDestroy(&pixg); pixDestroy(&pixd); pixd = pixErodeGray(pixs, WSIZE, HSIZE); sprintf(erodeseq, "E%d.%d", WSIZE, HSIZE); pixg = pixGrayMorphSequence(pixs, erodeseq, HORIZ_SEP, 100); pixCompare(pixd, pixg, "results are the same", "results are different"); pixDestroy(&pixg); pixDestroy(&pixd); pixd = pixOpenGray(pixs, WSIZE, HSIZE); sprintf(openseq, "O%d.%d", WSIZE, HSIZE); pixg = pixGrayMorphSequence(pixs, openseq, HORIZ_SEP, 200); pixCompare(pixd, pixg, "results are the same", "results are different"); pixDestroy(&pixg); pixDestroy(&pixd); pixd = pixCloseGray(pixs, WSIZE, HSIZE); sprintf(closeseq, "C%d.%d", WSIZE, HSIZE); pixg = pixGrayMorphSequence(pixs, closeseq, HORIZ_SEP, 300); pixCompare(pixd, pixg, "results are the same", "results are different"); pixDestroy(&pixg); pixDestroy(&pixd); pixd = pixTophat(pixs, WSIZE, HSIZE, L_TOPHAT_WHITE); sprintf(wtophatseq, "Tw%d.%d", WSIZE, HSIZE); pixg = pixGrayMorphSequence(pixs, wtophatseq, HORIZ_SEP, 400); pixCompare(pixd, pixg, "results are the same", "results are different"); pixDestroy(&pixg); pixDestroy(&pixd); pixd = pixTophat(pixs, WSIZE, HSIZE, L_TOPHAT_BLACK); sprintf(btophatseq, "Tb%d.%d", WSIZE, HSIZE); pixg = pixGrayMorphSequence(pixs, btophatseq, HORIZ_SEP, 500); pixCompare(pixd, pixg, "results are the same", "results are different"); pixDestroy(&pixg); /* ------------- Test erode/dilate duality -------------- */ pixd = pixDilateGray(pixs, WSIZE, HSIZE); pixInvert(pixs, pixs); pixd2 = pixErodeGray(pixs, WSIZE, HSIZE); pixInvert(pixd2, pixd2); pixCompare(pixd, pixd2, "results are the same", "results are different"); pixDestroy(&pixd); pixDestroy(&pixd2); /* ------------- Test open/close duality -------------- */ pixd = pixOpenGray(pixs, WSIZE, HSIZE); pixInvert(pixs, pixs); pixd2 = pixCloseGray(pixs, WSIZE, HSIZE); pixInvert(pixd2, pixd2); pixCompare(pixd, pixd2, "results are the same", "results are different"); pixDestroy(&pixd); pixDestroy(&pixd2); /* ------------- Test tophat duality -------------- */ pixd = pixTophat(pixs, WSIZE, HSIZE, L_TOPHAT_WHITE); pixInvert(pixs, pixs); pixd2 = pixTophat(pixs, WSIZE, HSIZE, L_TOPHAT_BLACK); pixCompare(pixd, pixd2, "Correct: images are duals", "Error: images are not duals"); pixDestroy(&pixd); pixDestroy(&pixd2); pixInvert(pixs, pixs); pixd = pixGrayMorphSequence(pixs, "Tw9.5", HORIZ_SEP, 100); pixInvert(pixs, pixs); pixd2 = pixGrayMorphSequence(pixs, "Tb9.5", HORIZ_SEP, 300); pixCompare(pixd, pixd2, "Correct: images are duals", "Error: images are not duals"); pixDestroy(&pixd); pixDestroy(&pixd2); /* ------------- Test opening/closing for large sels -------------- */ pixd = pixGrayMorphSequence(pixs, "C9.9 + C19.19 + C29.29 + C39.39 + C49.49", HORIZ_SEP, 100); pixDestroy(&pixd); pixd = pixGrayMorphSequence(pixs, "O9.9 + O19.19 + O29.29 + O39.39 + O49.49", HORIZ_SEP, 400); pixDestroy(&pixd); /* ---------- Closing plus white tophat result ------------ * * Parameters: wsize, hsize = 9, 29 * * ---------------------------------------------------------*/ pixd = pixCloseGray(pixs, 9, 9); pixd1 = pixTophat(pixd, 9, 9, L_TOPHAT_WHITE); pixd2 = pixGrayMorphSequence(pixs, "C9.9 + TW9.9", HORIZ_SEP, 0); pixCompare(pixd1, pixd2, "correct: same", "wrong: different"); pixd3 = pixMaxDynamicRange(pixd1, L_LINEAR_SCALE); pixDisplayWrite(pixd3, 1); pixDestroy(&pixd); pixDestroy(&pixd1); pixDestroy(&pixd2); pixDestroy(&pixd3); pixd = pixCloseGray(pixs, 29, 29); pixd1 = pixTophat(pixd, 29, 29, L_TOPHAT_WHITE); pixd2 = pixGrayMorphSequence(pixs, "C29.29 + Tw29.29", HORIZ_SEP, 0); pixCompare(pixd1, pixd2, "correct: same", "wrong: different"); pixd3 = pixMaxDynamicRange(pixd1, L_LINEAR_SCALE); pixDisplayWrite(pixd3, 1); pixDestroy(&pixd); pixDestroy(&pixd1); pixDestroy(&pixd2); pixDestroy(&pixd3); /* --------- hdome with parameter height = 100 ------------*/ pixd = pixHDome(pixs, 100, 4); pixd2 = pixMaxDynamicRange(pixd, L_LINEAR_SCALE); pixDisplayWrite(pixd2, 1); pixDestroy(&pixd2); /* ----- Contrast enhancement with morph parameters 9, 9 -------*/ pixd1 = pixInitAccumulate(w, h, 0x8000); pixAccumulate(pixd1, pixs, L_ARITH_ADD); pixMultConstAccumulate(pixd1, 3., 0x8000); pixd2 = pixOpenGray(pixs, 9, 9); pixAccumulate(pixd1, pixd2, L_ARITH_SUBTRACT); pixDestroy(&pixd2); pixd2 = pixCloseGray(pixs, 9, 9); pixAccumulate(pixd1, pixd2, L_ARITH_SUBTRACT); pixDestroy(&pixd2); pixd = pixFinalAccumulate(pixd1, 0x8000, 8); pixDisplayWrite(pixd, 1); pixDestroy(&pixd1); /* Do the same thing with the Pixacc */ pacc = pixaccCreate(w, h, 1); pixaccAdd(pacc, pixs); pixaccMultConst(pacc, 3.); pixd1 = pixOpenGray(pixs, 9, 9); pixaccSubtract(pacc, pixd1); pixDestroy(&pixd1); pixd1 = pixCloseGray(pixs, 9, 9); pixaccSubtract(pacc, pixd1); pixDestroy(&pixd1); pixd2 = pixaccFinal(pacc, 8); pixaccDestroy(&pacc); pixDisplayWrite(pixd2, 1); pixCompare(pixd, pixd2, "Correct: same", "Wrong: different"); pixDestroy(&pixd); pixDestroy(&pixd2); /* ---- Tophat result on feynman stamp, to extract diagrams ----- */ pixDestroy(&pixs); pixs = pixRead("feynman-stamp.jpg"); /* Make output image to hold five intermediate images */ w = pixGetWidth(pixs); h = pixGetHeight(pixs); pixd = pixCreate(5 * w + 18, h + 6, 32); /* composite output image */ pixSetAllArbitrary(pixd, 0x0000ff00); /* set to blue */ /* Paste in the input image */ pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); pixRasterop(pixd, 3, 3, w, h, PIX_SRC, pixt, 0, 0); /* 1st one */ /* pixWrite("/tmp/junkgray.jpg", pixt, IFF_JFIF_JPEG); */ pixDestroy(&pixt); /* Paste in the grayscale version */ cmap = pixGetColormap(pixs); if (cmap) pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); else pixt = pixConvertRGBToGray(pixs, 0.33, 0.34, 0.33); pixt2 = pixConvertTo32(pixt); /* 8 --> 32 bpp */ pixRasterop(pixd, w + 6, 3, w, h, PIX_SRC, pixt2, 0, 0); /* 2nd one */ pixDestroy(&pixt2); /* Paste in a log dynamic range scaled version of the white tophat */ pixt2 = pixTophat(pixt, 3, 3, L_TOPHAT_WHITE); pixt3a = pixMaxDynamicRange(pixt2, L_LOG_SCALE); pixt3 = pixConvertTo32(pixt3a); pixRasterop(pixd, 2 * w + 9, 3, w, h, PIX_SRC, pixt3, 0, 0); /* 3rd */ /* pixWrite("/tmp/junktophat.jpg", pixt2, IFF_JFIF_JPEG); */ pixDestroy(&pixt3); pixDestroy(&pixt3a); pixDestroy(&pixt); /* Stretch the range and threshold to binary; paste it in */ pixt3a = pixGammaTRC(NULL, pixt2, 1.0, 0, 80); pixt3 = pixThresholdToBinary(pixt3a, 70); pixt4 = pixConvertTo32(pixt3); pixRasterop(pixd, 3 * w + 12, 3, w, h, PIX_SRC, pixt4, 0, 0); /* 4th */ /* pixWrite("/tmp/junkbin.png", pixt3, IFF_PNG); */ pixDestroy(&pixt2); pixDestroy(&pixt3a); pixDestroy(&pixt4); /* Invert; this is the final result */ pixInvert(pixt3, pixt3); pixt4 = pixConvertTo32(pixt3); pixRasterop(pixd, 4 * w + 15, 3, w, h, PIX_SRC, pixt4, 0, 0); /* 5th */ pixWrite("/tmp/junkbininvert.png", pixt3, IFF_PNG); pixDisplayWrite(pixd, 1); /* pixWrite("/tmp/junkall.jpg", pixd, IFF_JFIF_JPEG); */ pixDestroy(&pixt3); pixDestroy(&pixt4); pixDestroy(&pixd); pixDisplayMultiple("/tmp/display/file*"); pixDestroy(&pixs); 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); }