3

I am trying to save an image to 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 produces the error:

Error in pixWriteJpeg: pix not defined

I believe this is because I need to do something after applying the otsu but before writing the new image. What am I missing?

EDIT-

I have now ammended the following per leptonica docs http://tpgit.github.io/Leptonica/binarize_8c.html:

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]

leptonica.pixOtsuAdaptiveThreshold(pix_image,20,20,0,0,0.1, img)

leptonica.pixWriteJpeg("otsu-lept", img, 75, 0)

A new error now occurs:

Maximum supported image dimension is 65500 pixels Error in pixWriteStreamJpeg: internal jpeg error Error in pixWriteJpeg: pix not written to 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 image being returned by the otsu function.

**** EDIT 2 ****

When I check the output img using leptonica it is telling me the width is some large number which seems to vary each time I run the function (eg 149996048) and the height remains correct at the same value as input image. It looks like the otsu function changing the image width to this large value for some reason.

EDIT 3

jsbueno below provided me with the solution to this problem which I will share here. The issue is because I was passing the image directly to the function when it is actually necessary to pass a pointer of a pointer to the function which then works. Final working code is 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)
ajrasp
  • 75
  • 1
  • 2
  • 10
  • 1
    Can you check the type of `otsu`? It looks like it's an int with an error code, not a pix pointer. – 101 Oct 01 '14 at 11:45
  • Yes the type is int. Thanks for that, I have just realized why that is and have amended function to reflect this by adding ctypes.c_void_p on to the end of the argtypes list for otsu and supplying an output image to function. It no returns a different error. Please see amendments to question above. – ajrasp Oct 01 '14 at 13:22
  • Does `pixOtsuAdaptiveThreshold()` return a pass or error? It looks like you have one less arguments than needed. – 101 Oct 01 '14 at 23:21
  • The function returns a zero which according to the documentation is a success. The last two arguments are optional returns according to the documentation. I have included one to return "thresholded input pixs, based on the threshold array" as I want the output to be the original image with the threshold applied. – ajrasp Oct 02 '14 at 20:36

1 Answers1

2

I found out what you are doing wrong - if you check on the function signature, it takes optionally 2 muttualy exclusive parameters at the end - one of which is an array for it to return the coefficients, not an image - the very last parameter is an empty image where to draw the application of the algorithm.

Moreover, the 2 last parameters are "pointers to a pointer" of a leptonica PIX object - You can create a Python variable containing a ctypes.c_void_p object, and pass the ctypes.addressof it to the leptonica function.

The docs 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. While researching this, I managed to build the python-leptonica bindings for leptonica 1.7.1 - as soon as I clean up the mess I did to get there, I should make another release.

For anyone able to run python-leptonica, as it is now (limted to leptonica 1.6.0 without hacking)- the code for using this function would be something along:

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)
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • Thanks very much for this! It's great but I couldn't get it to work yet. I added the extra c_void_p in the argtpyes and also I added ctypes.cvoid_p(0) to the function. New error message said 'module object has no attribute cvoid_p'. I guess this was a typing error in the above so I changed it to ctypes.c_void_p(0) and now the error says 'ctypes.ArgumentError: argument 6: : wrong type'. Any idea what is wrong now? Thanks again. – ajrasp Oct 07 '14 at 19:46
  • Ok I just fixed the error, I had deleted the c_float by mistake. Now have the original error again - "Maximum supported image dimension is 65500 pixels Error in pixWriteStreamJpeg: internal jpeg error Error in pixWriteJpeg: pix not written to stream". Have tried switching the positions of the last two variables just in case but the same result occurs. – ajrasp Oct 08 '14 at 19:45