0

I am trying to send an image from Python program that returns ndarray to C++ (OpenCV Mat) in the simple way: saving image to the buffer and then passing the buffer contents as string to C++ and using cv::imdecode there.

The error is thrown in c++ by cv::imshow:

OpenCV Error: Assertion failed (size.width>0 && size.height>0) in imshow, 
file /home/hcl/opencv-3.1.0/modules/highgui/src/window.cpp, line 281
terminate called after throwing an instance of 'cv::Exception'
  what():  /home/hcl/opencv-3.1.0/modules/highgui/src/window.cpp:281: 
error: (-215) size.width>0 && size.height>0 in function imshow

Aborted (core dumped)

EDIT: it works.

Before this I've read a number of approaches of sending ndarray from Python to C++ here on StackOverflow and didn't manage them to work. Also I checked two advised converters on Github (1,2), but unfortunately they are incompatible with OpenCV 3. Being unable to troubleshoot problems, I wish to make it working just in this simplest possible way to avoid possible porting problems in the future.

Python script that returns image(imgread.py):

import scipy.misc
import click as _click

import base64
from io import BytesIO

def get_my_image(image_name):

    # using click package to make sure that path is interpreted correctly
    _click.command()
    _click.argument('image_name',type=_click.Path(exists=True,
                                dir_okay=True, readable=True))

    # just reading some image (ndarray format is essential)
    image = scipy.misc.imread(image_name)

    # processing data ....

    # sending it back-----------------------------------

    # I've heard that scipy version started to normalize all images, 
    # but at the moment I just want to start it working
    img_to_send = scipy.misc.toimage(image) 

    # https://stackoverflow.com/questions/31826335/
    # how-to-convert-pil-image-image-object-to-base64-string
    # preparing buffer and writing image to it
    mybuffer = BytesIO()
    img_to_send.save(mybuffer, format="JPEG")

    # converting buffer contents to string
    img_str = base64.b64encode(mybuffer.getvalue())  

    return(img_str) 

The original working *.cpp example is here and I have only changed the part that corresponds to processing the data.

Main C++ program : (EDITED. base64 conversion code taken from here)

// call_function.c - A sample of calling 
// python functions from C code

// original code from

// https://stackoverflow.com/questions/215752/
// python-embedded-in-cpp-how-to-get-data-back-to-cpp

#include <Python.h>

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"

#include <iostream>

#include <string>
#include "base64.cpp"

using namespace cv;
using namespace std;

int
main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pArg, *pResult;
    int i;

    Py_Initialize();
    pName = PyString_FromString("imgread");
    // (no .py, error "ImportError: No module named py" otherwise)
    /* Error checking of pName left out as exercise */

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, "get_my_image");
        /* pFunc is a new reference */

        if (pFunc) {
            pArgs = PyTuple_New(1);
            pArg = PyString_FromString("1.jpg");
            /* pArg reference stolen here: */
            PyTuple_SetItem(pArgs, 0, pArg);
            pResult = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pResult != NULL) {

                //------------------------------- my part

            // see https://stackoverflow.com/questions/14727267/
            // opencv-read-jpeg-image-from-buffer


            char* str = PyString_AsString(pResult);

            // converting back from base64
            const std::string base64_chars = string(str);

            string s = base64_decode(base64_chars);                

            char* str3 = (char*)s.c_str();

            //taking NULLs into account and using s.size()
            size_t len = s.size();
            uchar* str2 = reinterpret_cast<unsigned char*>(str3);

                //attempting to display the image
                Mat rawData(1, len*sizeof(uchar), CV_8UC1, str2);
                Mat decodedImage  =  imdecode(rawData,CV_LOAD_IMAGE_COLOR);
                imshow( "Display window", decodedImage ); 

                //------------------------------- 

                Py_DECREF(pResult);

            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr,"Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function");
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load module");
        return 1;
    }
    Py_Finalize();

    waitKey(0);
    return 0;
}

EDIT: To get rid of normalization performed by scipy.misc.toimage I try to use img_to_send = scipy.misc.toimage(image,high=np.max(image), low=np.min(image)), but I don't know whether it works as intended or not.

Community
  • 1
  • 1
Slowpoke
  • 1,069
  • 1
  • 13
  • 37
  • 1
    I don't see you decoding the base64 on the c++ side. – Dan Mašek Nov 03 '16 at 17:51
  • @DanMašek Fixed, the error is still the same and the `out.jpg` is 4 bytes (was ~ half-size of the original) – Slowpoke Nov 03 '16 at 18:15
  • 2
    `strlen(str3);` This will treat it as a c-string, with NULL byte acting as a terminator. You definitely don't want that, as the JPEG data is bound to contain many NULL bytes -- it's a binary format. You should use `s.size()` to get the correct length. – Dan Mašek Nov 03 '16 at 18:31
  • @DanMašek I fixed the main part (except `cout` that still writes 4 bytes), now I get additional error before OpenCV exception: `Corrupt JPEG data: 423 extraneous bytes before marker 0xfe`. – Slowpoke Nov 03 '16 at 18:45
  • @DanMašek Oops, looks like I a bit overdid with that. Now everything works at last! Thank you very much! – Slowpoke Nov 03 '16 at 18:49

0 Answers0