Leptonica - unable to record image after otsu threshold applied
I am trying to save an image in jpeg after processing with leptonica. I am using python with ctypes and my code is:
import ctypes leptlib = "liblept.so" leptonica = ctypes.cdll.LoadLibrary(leptlib) filename = "IMAG0724.jpg" img = leptonica.pixRead(filename) leptonica.pixConvertTo8.argtypes = [ctypes.c_void_p, ctypes.c_int32] pix_image = leptonica.pixConvertTo8(img, False) leptonica.pixOtsuAdaptiveThreshold.argtypes = [ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_float] otsu = leptonica.pixOtsuAdaptiveThreshold(pix_image,20,20,0,0,0.1) leptonica.pixWriteJpeg.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32] leptonica.pixWriteJpeg("otsu-lept", otsu, 75, 0)
This code throws an error:
Error in pixWriteJpeg: pix not defined
I believe this is because I need to do something after applying otsu, but before writing a new image. What am I missing?
I have now made changes to the following leptonica docs http://tpgit.github.io/Leptonica/binarize_8c.html :
leptonica.pixOtsuAdaptiveThreshold.argtypes = leptonica.pixOtsuAdaptiveThreshold(pix_image,20,20,0,0,0.1, img) leptonica.pixWriteJpeg("otsu-lept", img, 75, 0)
Now a new error occurs:
Maximum supported image size is 65500 pixels Error in pixWriteStreamJpeg: internal jpeg error Error in pixWriteJpeg: pix is not written to the stream
My image resolution is 1552 x 2592 and leptonica.pixWriteJpeg works when the otsu function line is commented out, so it seems that the problem is still with the otsu function returning the image.
**** EDIT 2 ****
When I check the img output with leptonica it tells me the width is some big number that seems to change every time I run the function (like 149996048) and the height stays true with the same value as input image. Apparently the otsu function changes the image width to this large value for some reason.
jsbueno below provided me with a solution to this problem, which I will share here. The problem is that I was passing an image directly to a function when it actually needed to pass a pointer to a pointer to a function, which then works. The final working code is shown below:
import ctypes leptlib = "liblept.so" leptonica = ctypes.cdll.LoadLibrary(leptlib) filename = "IMAG0724.jpg" img = leptonica.pixRead(filename) leptonica.pixConvertTo8.argtypes = [ctypes.c_void_p, ctypes.c_int32 ] pix_image = leptonica.pixConvertTo8(img, False) w = leptonica.pixGetWidth(img) h = leptonica.pixGetHeight(img) pixa_out = leptonica.pixCreate(w,h,8) pixa = ctypes.c_void_p(pixa_out) leptonica.pixOtsuAdaptiveThreshold.argtypes = [ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_float, ctypes.c_void_p, ctypes.c_void_p ] otsu = leptonica.pixOtsuAdaptiveThreshold(pix_image, 20, 20, 0, 0, 0.1, None, ctypes.addressof(pixa) ) leptonica.pixWritePng("otsu-lept", pixa, 8)
source to share
I figured out what you are doing wrong - if you check the function signature, it takes an optional 2 exclusive muttualy parameters at the end - one of them is an array to return the coefficients, not an image. the last parameter is a blank image where the algorithm application can be applied.
In addition, the last 2 parameters are "pointers to pointer" of the leptonica PIX object - you can create a Python variable containing the ctypes.c_void_p object and pass it
to the Leptonica function.
The documents for the outsu function are as follows:
/*------------------------------------------------------------------* * Adaptive Otsu-based thresholding * *------------------------------------------------------------------*/ /*! * 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 *
PS. By researching this, I was able to build python-leptonica bindings for leptonica 1.7.1 - once I clean up the mess I made to get there, I have to do another release.
For anyone who can run python-leptonica as it is now (limited for leptonica 1.6.0 without hacking) - the code to use this feature would be something like this:
import leptonica import ctypes img = leptonica.functions.pixRead("t1.png") imggray = leptonica.functions.pixConvertRGBToGrayMinMax(img, 1) img = leptonica.functions.pixRead("t1.png") output = leptonica.functions.pixCreate(imggray.w, imggray.h, 1) a = ctypes.c_voidp() leptonica.functions.pixOtsuAdaptiveThreshold(imggray, 20, 20, 0, 0, .1, None, ctypes.addressof(a)) output = leptonica.PIX(from_address=a) leptonica.functions.pixWritePng("t3.png", c, 1)
source to share