nguyenq / lept4j

Java JNA Wrapper for Leptonica Image Processing Library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

LeptUtils.convertPixToImage() results in exit code -1073740940 (0xC0000374)

bugfoot opened this issue · comments

When trying to convert a Pix (which was originally converted from a valid BufferedImage using LeptUtils.convertImageToPix(),) to BufferedImage using LeptUtils.convertPixToImage() my program ends prematurely with exit code -1073740940 (0xC0000374), whether I do some interim image manipulation or not.
The same happens when running LeptUtilsTest.java.
Lept4J version: 1.3.0.

We could not reproduce the issue. What's your development configuration -- Java, OS, etc.?

Scala (2.12) SBT project in IntelliJ IDEA (Community Edition 2016.3.4)
OS: 64-bit Win7
Java: jdk1.8.0_121

Here is the problematic Scala code:

import java.io.File
import javax.imageio.ImageIO
import net.sourceforge.lept4j.util.LeptUtils

object Test extends App {
  val imageFile = new File("FullPathToMyFile.png")
  val bi = ImageIO.read(imageFile)
  val pix = LeptUtils.convertImageToPix(bi) // this is also OK
  val grayBi = LeptUtils.convertPixToImage(pix) // this is where I the exit happens
}

We replicated your test case in Java and had no issue.

@Test
public void testConvert() throws Exception {
    System.out.println("testConvert");
    File input = new File(testResourcesPath, "eurotext.png");
    BufferedImage image = ImageIO.read(new FileInputStream(input));
    Pix pix = LeptUtils.convertImageToPix(image);
    assertEquals(image.getWidth(), pix.w);
    assertEquals(image.getHeight(), pix.h);
    assertEquals(image.getColorModel().getPixelSize(), pix.d);
    System.out.println(String.format("Image properties: width=%d, height=%d, depth=%d", pix.w, pix.h, pix.d));
    
    BufferedImage result = LeptUtils.convertPixToImage(pix);
    assertEquals(pix.w, result.getWidth());
    assertEquals(pix.h, result.getHeight());
    assertEquals(pix.d, result.getColorModel().getPixelSize());
    
    PointerByReference pRef = new PointerByReference();
    pRef.setValue(pix.getPointer());
    Leptonica1.pixDestroy(pRef);
}

Unfortunately I get the same premature exit with your Java code right at LeptUtils.convertPixToImage(pix). Based on this entry, exception code 0xc0000374 indicates a heap corruption. I will try to create a Java only project from scratch using your code and see how I fare.

When I reproduce your example code above using instructions for command line found here, the command line crashes, and I get the same exception, but this time coming from ntdll.dll (Unhandled exception at 0x000000007717F3E2 (ntdll.dll) in java.exe: 0xC0000374: corrupted heap). I'll investigate it further using this SO question.

Can you try on another machine?

Yes, different machine, 64-bit Windows 10, 64-bit jdk1.8.0_121, same error. Will try with a 32-bit JDK.

Same error at the same place (LeptUtils.convertPixToImage(pix)) with the new Windows 10 machine using 32-bit jdk1.8.0_121 as well.

A last grasp at straws, did you have VC++ 2015 runtime installed?

I had version 14.0.24215 installed, so I uninstalled that, and then installed the version you linked to (14.0.24212), then ran again your Java test, clenched my teeth, but to no avail... same place, same exit code. I will try a third machine, this heap corruption seems to be a really deep issue...

You may want to try with an older version, 1.2.4, for instance.

Or try your own method of conversion by copying the underlying pixel data.

Without intention to cause frustration, just reporting as a follow-up :-)
I tried on a third machine (64-bit Windows 10, installed the linked VC++ 2015 runtime, ran command line instructions for both 32-bit and 64-bit JDK), and the same crash at the same code lines happened.
I will try it with the previous version.

All other test cases passed except this one?

Yes, all others passed (LeptUtilsTest, MemoryTest, Leptonica1Test, LeptonicaTest, LineRemovalTest, OtsuTest, DewarpTest).

And to be precise: your test Java test case passed with eurotext.png, but not with my (I think otherwise completely normal) png file... I will try several other png files to see how it fares...

I think I have the culprit... I opened and saved eurotext.png with Paint with another name, re-run your test, and voilà, the same dreaded exit code. I guess Microsoft should rename it to Pain instead. It's pretty mind-boggling, I'll try with other pictures.

It seems that not all PNGs are created equal, at least when depths are concerned. For example, for this publicly available PNG file, your test throws the following error:


Image properties: width=220, height=165, depth=32
Exception in thread "main" java.lang.AssertionError: expected:<32> but was:<24>
	at org.junit.Assert.fail(Assert.java:88)
	at org.junit.Assert.failNotEquals(Assert.java:834)
	at org.junit.Assert.assertEquals(Assert.java:645)
	at org.junit.Assert.assertEquals(Assert.java:631)
	at test2.main(test2.java:36)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

Process finished with exit code 1

Hi,

I also having issues when converting back. Is this a compareable issue?

Edit: I copied the convertPixToImage and translated it into scala. The error seems to be connected with the freeing of the memory because when I exclude

Leptonica1.bbufferDestroy(pdata);

it is working.

Environment:
CentOs 7 - Cloudera - Spark2

The code I'm executing:

        val pix = LeptUtils.convertImageToPix(image);
        val binaryImage = leptInstance.pixConvertTo1(pix, 250);

The image is defined as
image: (java.awt.image.BufferedImage)

This is the pix:

(Pix ,Pix(native@0x7fb9aebb17f0) (64 bytes) {
  int w@0=9af
  int h@4=db4
  int d@8=20
  int spp@c=3
  int wpl@10=9af
  int refcount@14=1
  int xres@18=1
  int yres@1c=1
  int informat@20=4
  int special@24=0
  Pointer text@28=null
  PixColormap$ByReference colormap@30=null
  IntByReference data@38=native@0x7fb984ec3010 (com.sun.jna.ptr.IntByReference@84ecafc9)
})

In a previous test I used the pix with tesseract and this was working fine. So I'm guessing the pix is fine.

The error I get is:

# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007fdca6ea738c, pid=100350, tid=140585127823104
#
# JRE version: Java(TM) SE Runtime Environment (7.0_67-b01) (build 1.7.0_67-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.65-b04 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [libc.so.6+0x8038c]  cfree+0x1c
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /yarn/nm/usercache/sagreven/appcache/application_1491314070109_0182/container_e474_1491314070109_0182_01_000003/hs_err_pid100350.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.sun.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

@sagreven The problem is indeed with Leptonica1.bbufferDestroy(pdata); in convertPixToImage. If I I replace it with Leptonica1.pixDestroy(pdata), the program prematurely exits with the same exit code (0xC0000374).

* Original signature : <code>void bbufferDestroy(L_BBUFFER**)</code><br>

is a JNA call to bbufferDestroy in Leptonica (see line 00147 here).
Some illegal memory access happens there I guess.

I think that this issue is related to Letp4J issue #2 and Leptonica issue #195.

We can't reproduce the issue. If bbufferDestroy is taken out, it will cause memory leaks, as was mentioned in Issue #2 .

@nguyenq If I am running your test case using a randomly selected publicly available "legal" PNG file downloaded from a web URL, e.g.

public void testConvert() throws Exception {
        System.out.println("testConvert");
        BufferedImage image = ImageIO.read(new URL("https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png"));
        Pix pix = LeptUtils.convertImageToPix(image);
        assertEquals(image.getWidth(), pix.w);
        assertEquals(image.getHeight(), pix.h);
        assertEquals(image.getColorModel().getPixelSize(), pix.d);
        System.out.println(String.format("Image properties: width=%d, height=%d, depth=%d", pix.w, pix.h, pix.d));

        BufferedImage result = LeptUtils.convertPixToImage(pix);
        assertEquals(pix.w, result.getWidth());
        assertEquals(pix.h, result.getHeight());
        assertEquals(pix.d, result.getColorModel().getPixelSize());

        PointerByReference pRef = new PointerByReference();
        pRef.setValue(pix.getPointer());
        Leptonica1.pixDestroy(pRef);
    }

I get an AssertionError: after converting the Pix to BufferedImage, the pixel depth is expected to be 32, but it is 24:

Image properties: width=280, height=210, depth=32

java.lang.AssertionError: 
Expected :32
Actual   :24
 <Click to see difference>

	at org.junit.Assert.fail(Assert.java:88)
	at org.junit.Assert.failNotEquals(Assert.java:834)
	at org.junit.Assert.assertEquals(Assert.java:645)
	at org.junit.Assert.assertEquals(Assert.java:631)
	at PixBufferedImageConversionTest.testConvert(PixBufferedImageConversionTest.java:29)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Process finished with exit code -1

Couldn't this be somehow related to the premature exit we are experiencing with @sagreven ?

If I read this publicly available PNG from the web, then the test fails right at assertEquals(image.getColorModel().getPixelSize(), pix.d);:

testConvert

java.lang.AssertionError: 
Expected :24
Actual   :32
 <Click to see difference>

	at org.junit.Assert.fail(Assert.java:88)
	at org.junit.Assert.failNotEquals(Assert.java:834)
	at org.junit.Assert.assertEquals(Assert.java:645)
	at org.junit.Assert.assertEquals(Assert.java:631)
	at PixBufferedImageConversionTest.testConvert(PixBufferedImageConversionTest.java:23)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Process finished with exit code -1

This means in my interpretation that an image with 24-bit pixel size somehow gets converted to a Pix with 32-bit pixel size by LeptUtils.convertImageToPix.

I think this is where our premature exit comes from, because if run the below test case:

@Test
    public void testConvert() throws Exception {
        System.out.println("testConvert");
        BufferedImage image = ImageIO.read(new URL("https://upload.wikimedia.org/wikipedia/en/7/7d/IOS_6_Home_Screen.png"));
        Pix pix = LeptUtils.convertImageToPix(image);
        assertEquals(image.getWidth(), pix.w);
        assertEquals(image.getHeight(), pix.h);
        // Commenting out the below two lines so that we reach LeptUtils.convertPixToImage.
        //assertEquals(image.getColorModel().getPixelSize(), pix.d);
        //System.out.println(String.format("Image properties: width=%d, height=%d, depth=%d", pix.w, pix.h, pix.d));

        BufferedImage result = LeptUtils.convertPixToImage(pix);
        assertEquals(pix.w, result.getWidth());
        assertEquals(pix.h, result.getHeight());
        assertEquals(pix.d, result.getColorModel().getPixelSize());

        PointerByReference pRef = new PointerByReference();
        pRef.setValue(pix.getPointer());
        Leptonica1.pixDestroy(pRef);
    }

The result is the premature exit I am experiencing, this time in a reproducible manner:

testConvert

Process finished with exit code -1073740940 (0xC0000374)

In my interpretation this example suggests that even if a BufferedImage has 24-bit pixel depth (whatever that means), then convertImageToPix automatically converts it to a 32-bit pixel depth Pix. Then, in my naive understanding, in the remaining 32-24=8bits there is nothing meaningful (for lack of a better expression). This then causes a problem for convertPixToImage when it tries to convert the 32-bit pixel depth Pix, where only 24-bit information is meaningful, and the remaining 8bit (perhaps representing the alpha channel) is not.

Now given that ImageIO.read reads this particular image as a BufferedImage with 24-bit pixel depth (RGB channels only), I tried to convert it to a 32-bit pixel depth BufferedImage with:

BufferedImage tempImage = ImageIO.read(new URL("https://upload.wikimedia.org/wikipedia/en/7/7d/IOS_6_Home_Screen.png"));
        BufferedImage image = new BufferedImage(tempImage.getWidth(), tempImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        image.getGraphics().drawImage(tempImage, 0, 0, null);

However, no matter what ARGB BufferedImage static field I set to convert the RGB BufferedImage to, e.g. TYPE_4BYTE_ABGR_PRE, TYPE_INT_ARGB, TYPE_INT_ARGB_PRE, etc., I always get the premature exit code when trying to convert the now freshly converted ARGB BufferedImage converted Pix back to a BufferedImage.
So for some reason it's not a feasible workaround.

Thanks for the analysis. I'm seeing what you're seeing: a discrepancy in image bit depth. I will dig deeper, but it may take some time.

Leptonica supports 1, 2, 4, 8, 16 and 32 bpp, not 24 (reference).

Then I believe pixConvert24To32 should be used first, since in the Leptonica C source code it says

02652 * Input: pixs (24 bpp rgb)
02653 * Return: pixd (32 bpp rgb), or null on error
02654 *
02655 * Notes:
02656 * (1) 24 bpp rgb pix are not supported in leptonica.
02657 * The data is a byte array, with pixels in order r,g,b, and
02658 * padded to 32 bit boundaries in each line.

@parazs Have you tried that out yourself? If it works, please submit a PR. Thanks.

Yes I have but it didn't work. I am completely in the dark. I get the exit code even if I convert a 24 bpp BufferedImage tempImage using

BufferedImage image = new BufferedImage(tempImage.getWidth(), tempImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
// Same exit code if I use BufferedImage.TYPE_INT_ABGR above
ColorConvertOp cco = new ColorConvertOp(tempImage.getColorModel().getColorSpace(),
                image.getColorModel().getColorSpace(), null);
cco.filter(tempImage, image);

before using LeptUtils.convertPixToImage. The problem seems to be that I can't read or convert a PNG image that was saved in 24 bpp depth as a 32 bpp BufferedImage converted to Pix for Leptonica to properly treat it so.

If I use Leptonica1.pixDestroy instead of Leptonica1.bbufferDestroy in convertPixToImage:

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import javax.imageio.ImageIO;

import org.junit.Test;

import com.ochafik.lang.jnaerator.runtime.NativeSizeByReference;
import com.sun.jna.ptr.PointerByReference;
import net.sourceforge.lept4j.Leptonica1;
import net.sourceforge.lept4j.Pix;
import net.sourceforge.lept4j.util.LeptUtils;
import static net.sourceforge.lept4j.ILeptonica.IFF_TIFF;

public class PixBufferedImageConversionTest {
    // Source: https://stackoverflow.com/a/29886786/8762739
    public static boolean compareImages(BufferedImage imgA, BufferedImage imgB) {
        // The images must be the same size.
        if (imgA.getWidth() == imgB.getWidth() && imgA.getHeight() == imgB.getHeight()) {
            int width = imgA.getWidth();
            int height = imgA.getHeight();

            // Loop over every pixel.
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    // Compare the pixels for equality.
                    if (imgA.getRGB(x, y) != imgB.getRGB(x, y)) {
                        return false;
                    }
                }
            }
        } else {
            return false;
        }

        return true;
    }

    @Test
    public void testConvert() throws Exception {
        System.out.println("testConvert");
        BufferedImage bi =
                ImageIO.read(new URL("https://upload.wikimedia.org/wikipedia/en/7/7d/IOS_6_Home_Screen.png"));
        System.out.println(String.format("Original BufferedImage properties: width=%d, height=%d, depth=%d",
                bi.getWidth(), bi.getHeight(), bi.getColorModel().getPixelSize()));

        Pix bi2Pix = LeptUtils.convertImageToPix(bi);
        System.out.println(String.format("Converted Pix properties: width=%d, height=%d, depth=%d",
                bi2Pix.w, bi2Pix.h, bi2Pix.d));

        // Rewriting convertPixToImage to use pixDestroy instead of bbufferDestroy to free memory of pdata
        PointerByReference pdata = new PointerByReference();
        NativeSizeByReference psize = new NativeSizeByReference();
        int format = IFF_TIFF;
        Leptonica1.pixWriteMem(pdata, psize, bi2Pix, format);
        byte[] b = pdata.getValue().getByteArray(0, psize.getValue().intValue());
        InputStream in = new ByteArrayInputStream(b);
        BufferedImage bi2Pix2Bi = ImageIO.read(in);
        in.close();
        //Instead of using Leptonica1.bbufferDestroy(pdata):
        Leptonica1.pixDestroy(pdata);

        System.out.println(String.format("Converted BufferedImage properties: width=%d, height=%d, depth=%d",
                bi2Pix2Bi.getWidth(), bi2Pix2Bi.getHeight(), bi2Pix2Bi.getColorModel().getPixelSize()));

        // Writing the BufferedImage back results in a similar-looking, but smaller file.
        File outputFile = new File("C:\\output.png");
        ImageIO.write(bi2Pix2Bi, "png", outputFile);

        // Now let's see whether the PNG is the same after conversions:
        BufferedImage bi2Pix2Bi2PNG2Bi = ImageIO.read(outputFile);
        System.out.println(String.format("Read converted BufferedImage properties: width=%d, height=%d, depth=%d",
                bi2Pix2Bi2PNG2Bi.getWidth(), bi2Pix2Bi2PNG2Bi.getHeight(), bi2Pix2Bi2PNG2Bi.getColorModel().getPixelSize()));
        System.out.println("24-bpp PNG read as BufferedImage converted to Pix, then converted back to BufferedImage,\n\t" +
                "then saved as PNG, eventually read as BufferedImage are the same: " + compareImages(bi, bi2Pix2Bi2PNG2Bi));

        PointerByReference pRef = new PointerByReference();
        pRef.setValue(bi2Pix.getPointer());
        Leptonica1.pixDestroy(pRef);
    }
}

Then what I get is quite intriguing:

testConvert
Original BufferedImage properties: width=250, height=444, depth=24
Converted Pix properties: width=250, height=444, depth=32
Converted BufferedImage properties: width=250, height=444, depth=24
Read converted BufferedImage properties: width=250, height=444, depth=24
24-bpp PNG read as BufferedImage converted to Pix, then converted back to BufferedImage,
	then saved as PNG, eventually read as BufferedImage are the same: true

Process finished with exit code 0

First off, I don't get a premature exit code, which is great! Plus convertPixToImage can somehow intuitively convert a 32-bpp Pix that was created from a 24-bpp BufferedImage back to a 24-bpp BufferedImage, which, if saved as a PNG, results in a smaller file (222 KB vs 252 KB), but when read back as a BufferedImage˛ is identical to the original one both in pixel depth and pixel by pixel.
I do not know why Leptonica1.pixDestroy works but Leptonica1.bbufferDestroy doesn't, at least for 24-bpp BufferedImages, I guess answering it would require some digging in the original Leptonica C code.
I am also unsure whether using Leptonica1.pixDestroy creates any memory leakage, and whether it works for non-24-bpp BufferedImages, but at least it's a progress.

I would attribute the smaller file size to the absence of the meta data for the image, such as resolution, bit depth, compression type, etc.

If I remember correctly, I did try Leptonica1.pixDestroy(pdata); during development of the method, but it did not seem to release the memory.

@nguyenq you are right about the smaller file size.
Another interesting outcome is if I try the above test case with this 32-bpp PNG file:

testConvert
Original BufferedImage properties: width=220, height=165, depth=32
Converted Pix properties: width=220, height=165, depth=32
Converted BufferedImage properties: width=220, height=165, depth=24
Read converted BufferedImage properties: width=220, height=165, depth=24
24-bpp PNG read as BufferedImage converted to Pix, then converted back to BufferedImage,
	then saved as PNG, eventually read as BufferedImage are the same: false

I get the same output even if I replace the changed convertPixToImage implementation with:
BufferedImage bi2Pix2Bi = LeptUtils.convertPixToImage(bi2Pix);
So for some reason convertPixToImage converts a 32-bpp Pix which was converted from a true 32-bpp BufferedImage to a 24-bpp BufferedImage, losing the alpha channel on the way. This is clearly undesirable, and perhaps related to the original issue of premature exit.

I found two methods for converting BufferedImage to and from Pix in the unmaintained JLeptonica library here. This might be helpful going forward.

Some more help with the investigation. It seems that not only 24-bpp PNG turned BufferedImages give the premature exit code when reaching Leptonica1.bbufferDestroy in LeptUtils.convertPixToImage. Trying to convert this 32-bpp PNG does so as well, while the BufferedImage converted from the Pix (which was converted from the original BufferedImage) becomes 24-bpp in the meantime.

A fix ff93f30 has been committed. Release 1.9.1 has been published as a result.