3

I am trying to use Leptonica (C library) from within python. The library has a pixRead method that takes absolute path to an image file as a parameter. Currently I'm successfully calling this from python like this:

leptonica = ctypes.cdll.LoadLibrary("/path/to/lept.so")
pix_image = leptonica.pixRead("/path/to/image.jgp")

However, I would like to call the method pixReadStream that takes in file stream as an input parameter. In my python program, I have access to the image as a numpy array using OpenCV.

Question

Is there any way to pass an image I have as a numpy array in my Python program to the pixReadStream method in leptopnica C library that takes file stream as an input parameter?

roi #OpenCV image numpy array
leptonica = ctypes.cdll.LoadLibrary("/path/to/lept.so")
pix_image = leptonica.pixReadStream(##any way to pass roi here?##)
Adam
  • 109
  • 1
  • 6
  • pix_image will be of type `int` and not of type `PIX*`, so you cannot really do anything with it unless you [tell python how to handle the struct PIX](https://docs.python.org/2/library/ctypes.html#structures-and-unions). Anyway, to answer your initial question, you could try to create a file-like object by using [`numpy.ndarray.tostring`](http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.ndarray.tostring.html) and [`StringIO`](https://docs.python.org/2/library/stringio.html) – Jan Christoph Terasa Apr 19 '16 at 06:16
  • Modelling the PIX struct is not really necessary as Leptonica has accessor functions for all fields considered public. So you can get away with that number and using it as address for a void pointer. `StringIO` won't work as that isn't a real operating system level file but just a Python object with methods expected from a file from Python's point of view. – BlackJack May 18 '16 at 22:32

3 Answers3

0

Creating a Leptonica PIX structure from a Numpy array can be done without encoding the data as a image format meant for storage and channeling that somehow via the kernel as file within the same process. Convert the array data from OpenCV to RGBA data and wrap enough from Leptonica to create an empty PIX structure of the appropriate size and then copy the data from the array into the PIX.

Here is a small example how to load an image with OpenCV, convert it into a Python object wrapping a PIX structure, and save the image data with Leptonica into a file again:

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from ctypes import c_char_p, c_uint32, c_void_p, CDLL, memmove, pointer, POINTER
from ctypes.util import find_library
import cv2


LEPTONICA = CDLL(find_library('lept'))

_pix_create = LEPTONICA.pixCreate
_pix_create.argtypes = [c_uint32, c_uint32, c_uint32]
_pix_create.restype = c_void_p

_pix_destroy = LEPTONICA.pixDestroy
_pix_destroy.argtypes = [POINTER(c_void_p)]
_pix_destroy.restype = None

_pix_get_data = LEPTONICA.pixGetData
_pix_get_data.argtypes = [c_void_p]
_pix_get_data.restype = POINTER(c_uint32)

_pix_endian_byte_swap = LEPTONICA.pixEndianByteSwap
_pix_endian_byte_swap.argtypes = [c_void_p]
_pix_endian_byte_swap.restype = c_uint32

_pix_write_implied_format = LEPTONICA.pixWriteImpliedFormat
_pix_write_implied_format.argtypes = [c_char_p, c_void_p, c_uint32, c_uint32]
_pix_write_implied_format.restype = c_uint32


class Pix(object):

    def __init__(self, width, height, depth):
        self._as_parameter_ = _pix_create(width, height, depth)
        self._pointer = pointer
        self._pix_destroy = _pix_destroy

    def __del__(self):
        pix_pointer = self._pointer(c_void_p(self._as_parameter_))
        self._pix_destroy(pix_pointer)
        assert pix_pointer[0] is None

    @property
    def data(self):
        return _pix_get_data(self)

    def endian_byte_swap(self):
        _pix_endian_byte_swap(self)

    def save(self, filename, quality=0, progessive=False):
        _pix_write_implied_format(filename, self, quality, progessive)

    @classmethod
    def from_rgba(cls, array):
        width, height, depth = array.shape
        if depth != 4 and array.itemsize != 1:
            raise ValueError('array has wrong format')
        result = cls(width, height, 32)
        memmove(result.data, array.ctypes.data, array.size * array.itemsize)
        result.endian_byte_swap()
        return result


def main():
    image = cv2.imread('test.jpg')
    image = cv2.cvtColor(image, cv2.cv.CV_BGR2RGBA)

    pix = Pix.from_rgba(image)
    pix.save('test.png')


if __name__ == '__main__':
    main()
BlackJack
  • 4,476
  • 1
  • 20
  • 25
-1

Does this help you maybe?

There is an answer given which says that "You can use PyFile_AsFile from it."

rocksteady
  • 2,320
  • 5
  • 24
  • 40
-1
  1. For the conversion you can have numpy.load function...http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.load.html

https://github.com/numpy/numpy/blob/v1.10.1/numpy/lib/npyio.py#L257-L419

  1. For communicating with c...you can have ctypes or cffi

https://kogs-www.informatik.uni-hamburg.de/~seppke/content/teaching/wise1314/20131107_pridoehl-cffi.pdf

BusyTraveller
  • 183
  • 3
  • 14
  • Ad 1: Adam already has a numpy array, so `numpy.load()` makes no sense. Ad 2: Adam already knows about ctypes and with cffi there's still the same problem/question. – BlackJack May 18 '16 at 22:36