nguyenq / lept4j

Java JNA Wrapper for Leptonica Image Processing Library
Apache License 2.0
27 stars 14 forks source link

Porting Leptonica C code to Java #15

Closed Artgit closed 6 years ago

Artgit commented 6 years ago

I'm trying to port the following C code https://github.com/DanBloomberg/leptonica/blob/master/prog/speckle_reg.c to Java lept4j

This is my current version:

public static final String selstr2 = "oooooC oo  ooooo";
public static final String selstr3 = "ooooooC  oo   oo   oooooo";

private static Pix clearNoise(Pix pixs) {

    Pix pix1, pix2, pix3, pix4, pix5;
    Pix pix6, pix7, pix8, pix9, pix10;
    Pixa pixa1;
    Sel sel1, sel2, sel3, sel4;
    L_RegParams rp = new L_RegParams();

    /*  Normalize for rapidly varying background */
    pixa1 = Leptonica1.pixaCreate(0);
    Leptonica1.pixaAddPix(pixa1, pixs, ILeptonica.L_INSERT);
    Leptonica1.regTestWritePixAndCheck(rp, pixs, ILeptonica.IFF_JFIF_JPEG);  /* 0 */
    pix1 = Leptonica1.pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 10);
    Leptonica1.pixaAddPix(pixa1, pix1, ILeptonica.L_INSERT);
    Leptonica1.regTestWritePixAndCheck(rp, pix1, ILeptonica.IFF_JFIF_JPEG); /* 1 */

    /* Remove the background */
    pix2 = Leptonica1.pixGammaTRCMasked(null, pix1, null, 1.0f, 100, 175);
    Leptonica1.regTestWritePixAndCheck(rp, pix2, ILeptonica.IFF_JFIF_JPEG); /* 2 */

    /* Binarize */
    pix3 = Leptonica1.pixThresholdToBinary(pix2, 180);
    Leptonica1.pixaAddPix(pixa1, pix3, ILeptonica.L_INSERT);
    Leptonica1.regTestWritePixAndCheck(rp, pix3, ILeptonica.IFF_PNG); /* 3 */

    /* Remove the speckle noise up to 2x2 */
    sel1 = Leptonica1.selCreateFromString(selstr2, 4, 4, "speckle2");
    pix4 = Leptonica1.pixHMT(null, pix3, sel1.getPointer());
    Leptonica1.pixaAddPix(pixa1, pix4, ILeptonica.L_INSERT);
    sel2 = Leptonica1.selCreateBrick(2, 2, 0, 0, ILeptonica.SEL_HIT);
    pix5 = Leptonica1.pixDilate(null, pix4, sel2.getPointer());
    Leptonica1.pixaAddPix(pixa1, pix5, ILeptonica.L_INSERT);
    Leptonica1.regTestWritePixAndCheck(rp, pix5, ILeptonica.IFF_PNG);  /* 4 */
    pix6 = Leptonica1.pixSubtract(null, pix3, pix5);
    Leptonica1.pixaAddPix(pixa1, pix6, ILeptonica.L_INSERT);
    Leptonica1.regTestWritePixAndCheck(rp, pix6, ILeptonica.IFF_PNG); /* 5 */

    /* Remove the speckle noise up to 3x3 */
    sel3 = Leptonica1.selCreateFromString(selstr3, 5, 5, "speckle3");
    pix7 = Leptonica1.pixHMT(null, pix3, sel3.getPointer());
    Leptonica1.pixaAddPix(pixa1, pix7, ILeptonica.L_INSERT);
    sel4 = Leptonica1.selCreateBrick(3, 3, 0, 0, ILeptonica.SEL_HIT);
    pix8 = Leptonica1.pixDilate(null, pix7, sel4.getPointer());
    Leptonica1.pixaAddPix(pixa1, pix8, ILeptonica.L_INSERT);
    Leptonica1.regTestWritePixAndCheck(rp, pix8, ILeptonica.IFF_PNG);  /* 6 */
    pix9 = Leptonica1.pixSubtract(null, pix3, pix8);
    Leptonica1.pixaAddPix(pixa1, pix9, ILeptonica.L_INSERT);
    Leptonica1.regTestWritePixAndCheck(rp, pix9, ILeptonica.IFF_PNG); /* 7 */

    /*
    pix10 = Leptonica1.pixaDisplayTiledInColumns(pixa1, 3, 1.0f, 30, 2);
    Leptonica1.pixDisplayWithTitle(pix10, 0, 0, null, rp.display);
    Leptonica1.regTestWritePixAndCheck(rp, pix10, ILeptonica.IFF_JFIF_JPEG);   8  
    Leptonica1.selDestroy(new PointerByReference(sel1.getPointer()));
    Leptonica1.selDestroy(new PointerByReference(sel2.getPointer()));
    Leptonica1.selDestroy(new PointerByReference(sel3.getPointer()));
    Leptonica1.selDestroy(new PointerByReference(sel4.getPointer()));
    Leptonica1.pixDestroy(new PointerByReference(pix2.getPointer()));
    Leptonica1.pixDestroy(new PointerByReference(pix10.getPointer()));
    Leptonica1.pixaDestroy(new PointerByReference(pixa1.getPointer()));
    Leptonica1.regTestCleanup(rp);
   */       
    return pix9;
}

Everything works fine except the last code block(commented). rp variable is null and code fails. How to properly convert this code?

nguyenq commented 6 years ago

I think you would need to call regTestSetup to properly set up L_RegParams rp. That may be tricky to implement. If all that you try fail, I would suggest not using L_RegParams variable and replacing regTestWritePixAndCheck call with simpler pixWrite method.

nguyenq commented 6 years ago

Doc on regTestSetup API:

http://misc.voidlinux.de/html/regutils_8c.html#a1ed38b553c1fd2997ba6797fc359bf5a

DanBloomberg commented 6 years ago

Exactly correct. This was set up as a regression test which uses the leptonica regression framework; in particular, the L_RegParams struct. It is not required for using the method.

Artgit commented 6 years ago

Thanks for your answers. I have removed logic with L_RegParams. But I still having issue with the following code:

    Leptonica1.selDestroy(new PointerByReference(sel1.getPointer()));
    Leptonica1.selDestroy(new PointerByReference(sel2.getPointer()));
    Leptonica1.selDestroy(new PointerByReference(sel3.getPointer()));
    Leptonica1.selDestroy(new PointerByReference(sel4.getPointer()));
    Leptonica1.pixDestroy(new PointerByReference(pix2.getPointer()));
    Leptonica1.pixaDestroy(new PointerByReference(pixa1.getPointer()));

I'm doing something wrong here because when this code is uncommented I have the following exception: Exception in thread "main" java.lang.Error: Invalid memory access How to correctly destroy Sels objects?

nguyenq commented 6 years ago

I experienced no issue with running the code.

Artgit commented 6 years ago

Sorry, I forgot to add the code that causes the issue:

Leptonica instance = Leptonica.INSTANCE;
instance.pixWrite("result.png", pixs9, ILeptonica.IFF_PNG);

@nguyenq could you please try with these lines?

nguyenq commented 6 years ago

New despeckle method, based on Leptonica's speckle_reg.c example, and its test case have been incorporated into the baseline. You can try it out.

Artgit commented 6 years ago

@nguyenq thanks! I tested despecklemethod.

The following code works fine:

Leptonica instance = Leptonica.INSTANCE;
Pix pixs = instance.pixRead("w91frag.jpg");
Leptonica1.pixDisplayWrite(pixs, 1);
pixs = LeptUtils.despeckle(pixs, LeptUtils.SEL_STR2, 2);
instance.pixWrite("result.png", pixs, ILeptonica.IFF_PNG);

The following code doesn't work(with the second invocation of despeckle method the result.png has 0 bytes):

Leptonica instance = Leptonica.INSTANCE;
Pix pixs = instance.pixRead("w91frag.jpg");
Leptonica1.pixDisplayWrite(pixs, 1);
pixs = LeptUtils.despeckle(pixs, LeptUtils.SEL_STR2, 2);
pixs = LeptUtils.despeckle(pixs, LeptUtils.SEL_STR3, 3);
instance.pixWrite("result.png", pixs, ILeptonica.IFF_PNG)

What am I doing wrong?

nguyenq commented 6 years ago

Based on the original C example, the second invocation should not be called on the result pix of a previous invocation but rather on the original pix.

Also, reassigning pix variables is not a recommended practice for Leptonica. It leads to memory leaks as the previous pix's resource is not released; it needs to be disposed. Use a new instance of pix, instead.

Artgit commented 6 years ago

Thanks!