how to convert python ndarray to c++ char*? - python

I'm using swig to wrap a c++ library, it needs to get image data as char*. I can read the image in python. But how can i trans it to c++?
I konw I might need to use typemap. I tried several ways, but I always get a picture with only stripes.
This is my interface file:
/* np2char */
%module np2char
%{
#define SWIG_FILE_WITH_INIT
#include <opencv2/opencv.hpp>
using namespace cv;
%}
%inline %{
typedef char* Image_Data_Type;
typedef int Image_Width_Type;
typedef int Image_Height_Type;
struct Image_Info {
Image_Data_Type imageData;
Image_Width_Type imageWidth;
Image_Height_Type imageHeight;
};
int Imageshow(Image_Info ImageInfo) {
Mat img(ImageInfo.imageHeight, ImageInfo.imageWidth, CV_8UC3, ImageInfo.imageData);
imshow("img_in_cpp", img);
waitKey(0);
destroyAllWindows();
return 0;
}
%}
This is my setup.py:
"""
setup.py
"""
from distutils.core import setup,Extension
module1 = Extension('_np2char',
sources=['np2char_wrap.cxx'],
include_dirs=['include'],
libraries = ["opencv_world342"],
library_dirs=["lib"],
)
setup(name = "np2char",
version = "1.0",
description = 'This package is used to trans ndarray to char*',
ext_modules = [module1],
py_modules=['np2char'])
and this is my python file:
import np2char
import cv2
img1 = cv2.imread("1.jpg")
img_info = np2char.Image_Info()
img_info.imageData = img1.data
img_info.imageWidth = img1.shape[1]
img_info.imageHeight = img1.shape[0]
np2char.Imageshow(img_info)
I have tried
%typemap(in) Image_Data_Type{
$1 = reinterpret_cast<char*>(PyLong_AsLongLong($input));
}
, and in python side
img_info.imageData=img1.ctypes.data
But still I got only stripes. It seems that imagedata is copied to other places in memory. In the process, it was truncated by '\0'.

haha, I figured it out myself.
In SWIG Documentation 5.5.2,
SWIG assumes that all members of type char * have been dynamically allocated using malloc() and that they are NULL-terminated ASCII strings.
If this behavior differs from what you need in your applications, the SWIG "memberin" typemap can be used to change it.
So, what I need is "typemap(memberin)":
%typemap(in) Image_Data_Type{
$1 = reinterpret_cast<Image_Data_Type>(PyLong_AsLongLong($input));
}
%typemap(memberin) Image_Data_Type{
$1 = $input;
}
%typemap(out) Image_Data_Type{
$result = PyLong_FromLongLong(reinterpret_cast<__int64>($1));
}
It's a bit ugly using integer to transfer pointer. Is there a better way?

Related

SWIG Attribute Error: module has no attribute 'delete_...'

I have been trying to get this to work for a while now. I am trying to wrap a LOT of c++ classes in swig, but I can't even get the first one to work. The error is at the bottom. Here is my interface file, setup.py, and class file.
Interface
//This file is automatically generated from "build_swig_files.py
//Makes changes to build_swig_files.py to edit jcm.i
%module jcm
%{
#include "jtag/GenericJTAGDevice.h"
typedef unsigned int u32;
%}
class GenericJTAGDevice {
public:
virtual ~GenericJTAGDevice();
GenericJTAGDevice(int irLength, int idCode);
unsigned int getIrLength();
unsigned int getIdCode();
private:
unsigned int idCode;
unsigned int irLength;
};
typedef unsigned int u32;
%include <std_string.i>
using std::string;
%include "cpointer.i"
%pointer_functions(u32, u32p);
%include "carrays.i"
%array_class(u32, u32a);
%include "std_vector.i"
namespace std {
%template(IntVector) vector<int>;
}
setup.py
from distutils.core import setup, Extension
jcm_sources = [
"jcm.i",
"/root/git/jcm/jcm_source/base/src/jtag/GenericJTAGDevice.cpp"
]
jcm_module = Extension('_jcm',
sources=jcm_sources,
swig_opts=[ '-I/root/git/jcm/jcm_source/base/include',
'-I/root/git/jcm/jcm_source/base/include/jtag',
'-I/root/git/jcm/jcm_source/base/include/util',
'-I/root/git/jcm/jcm_source/base/include/xilinx',
'-c++'],
include_dirs=[ '/root/git/jcm/jcm_source/base/include',
'/root/git/jcm/jcm_source/base/include/jtag',
'/root/git/jcm/jcm_source/base/include/util',
'/root/git/jcm/jcm_source/base/include/xilinx'],
libraries=['supc++'])
setup (name = 'jcm', version = '0.3', author = 'BYUCCL', ext_modules = [jcm_module], py_modules = ["jcm"])
Class Header
#ifndef GENERIC_JTAG_DEVICE_H
#define GENERIC_JTAG_DEVICE_H
#include <string>
#include <vector>
//#include "JTAGDevice.h"
using namespace std;
/**
* #brief Basic implementation of a JTAGDevice
*
* \class GenericJTAGDevice
*
**/
class GenericJTAGDevice {
public:
virtual ~GenericJTAGDevice();
GenericJTAGDevice(int irLength, int idCode);
unsigned int getIrLength();
unsigned int getIdCode();
private:
unsigned int idCode;
unsigned int irLength;
};
#endif // GENERIC_JTAG_DEVICE_H
Here is the error:
>>> import sys
>>> sys.path.insert(0, '/root/git/JCM/jcm_source/python/swig')
>>> import jcm
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/root/git/jcm/jcm_source/python/swig/jcm.py", line 64, in <module>
class GenericJTAGDevice(object):
File "/root/git/jcm/jcm_source/python/swig/jcm.py", line 67, in GenericJTAGDevice
__swig_destroy__ = _jcm.delete_GenericJTAGDevice
AttributeError: module '_jcm' has no attribute 'delete_GenericJTAGDevice'
I have tried a couple variations in the interface file, such as not having the whole class definition and just doing %include GenericJTAGDevice.h. I have a feeling it has to do with the virtual destructor, but I don't know how to fix that because I need the destructor.
Edit: I tried with another class and it did the same thing. So perhaps I am understanding the interface file wrong.
Edit: All I am running is python3 setup.py build
So I saw the answer in the link below before but didn't understand what it was saying. Basically, my process to build swig didn't include making a new _jcm.so. So pretty much the first time I ran it was it, and after that all the changes I made to the .i or the code or setup.py didn't mean anything because the _jcm.so wasn't being rewritten. In my case, I run a "make clean" from my make file and it deletes the _jcm.so. After that I build the _jcm.so again, and then run setup.py.
Simple, but hard to find.
http://swig.10945.n7.nabble.com/Req-module-object-has-no-attribute-delete-TSP-CA-td2271.html

Return vector from C++ to Python without copying using SWIG

I am trying to build a python module in C++ using SWIG. I have the following code:
struct ContainerElement
{
uint8_t i;
double d;
double d2;
};
class Container
{
private:
std::vector<uint8_t> ints;
std::vector<double> doubles;
std::vector<double> doubles2;
public:
std::vector<uint8_t>& getInts() { return ints; }
std::vector<double>& getDoubles() { return doubles; }
std::vector<double>& getDoubles2() { return doubles2; }
void addElement(ContainerElement element)
{
ints.emplace_back(element.i);
doubles.emplace_back(element.d);
doubles2.emplace_back(element.d2);
}
};
void fillContainer(Container& container)
{
for (int i = 0; i < 1e6; ++i)
{
container.addElement({(uint8_t)i, (double)i,(double)i });
}
}
And an interface file container.i:
%module container
%{
#include "container.h"
%}
%include container.h
%include "std_vector.i"
%include "stdint.i"
%template(DoubleVector) std::vector<double>;
%template(IntVector) std::vector<uint8_t>;
When I call this code in python:
import psutil
import os
import container
import numpy as np
print(psutil.Process(os.getpid()).memory_info().rss)
cont = container.Container()
container.fillContainer(cont)
print(psutil.Process(os.getpid()).memory_info().rss)
a = np.array(cont.getInts(), copy=False)
b = np.array(cont.getDoubles(), copy=False)
c = np.array(cont.getDoubles2(), copy=False)
print(psutil.Process(os.getpid()).memory_info().rss)
This works, however when I check the memory usage of the program (using psutil.Process(os.getpid()).memory_info().rss) it seems to make a copy when I call the functions getInts, getDoubles and getDoubles2. Is there a way to avoid this?
Numpy can't do anything with a vector object: it needs the underlying memory. The easiest would have been if you could call the vector's data(), but SWIG does not expose that function. Just for reference, here's cppyy (http://cppyy.org) which does expose data() to show that that simply works, i.e. that the problem isn't with numpy:
import cppyy
cppyy.include("container.h")
import psutil
import os
import numpy as np
print(psutil.Process(os.getpid()).memory_info().rss)
cont = cppyy.gbl.Container()
cppyy.gbl.fillContainer(cont)
print(psutil.Process(os.getpid()).memory_info().rss)
a = np.array(cont.getInts().data(), copy=False)
b = np.array(cont.getDoubles().data(), copy=False)
c = np.array(cont.getDoubles2().data(), copy=False)
print(psutil.Process(os.getpid()).memory_info().rss)
(Of course cppyy takes more memory straight of the bat, b/c it loads LLVM; incrementally it will win however.)
So first you need to expose that data() call through SWIG and then I think it's easiest to add a python layering on top of that, to make the usage clean to client code (with the caveat that you may run into memory issues if the original vectors get deleted or resized, but that's always a problem for any code that does not copy).
Start by modifying your container code:
// include the numpy header; find its location with:
// python -c 'import numpy; print(numpy.get_include())'
#include "numpy/arrayobject.h"
// ... rest of your container code here ...
// data to numpy helpers
PyObject* vec_data_uint8_t(std::vector<uint8_t>& d) {
import_array();
npy_intp dims[] = {(npy_intp)d.size()};
npy_intp strides[] = {sizeof(std::vector<uint8_t>::value_type)};
return PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(NPY_UINT8),
1, dims, strides, d.data(), NPY_ARRAY_C_CONTIGUOUS, nullptr);
}
PyObject* vec_data_double(std::vector<double>& d) {
import_array();
npy_intp dims[] = {(npy_intp)d.size()};
npy_intp strides[] = {sizeof(std::vector<double>::value_type)};
return PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(NPY_DOUBLE),
1, dims, strides, d.data(), NPY_ARRAY_C_CONTIGUOUS, nullptr);
}
SWIG will automatically pick those up, so container.i is fine. Now add a mycontainer.py helper module like so:
import numpy
from container import *
IntVector.__array__ = vec_data_uint8_t
DoubleVector.__array__ = vec_data_double
and in your original python code, replace import container with:
import mycontainer as container
Now you can use your vectors as always but when handed to a numpy array, the custom __array__ function will be called, given it access to the internal data and will not copy if copy=False.

How to implement Array in python Swig.?

I am wrote sample C++ application. Now I am going communicate with Python through Swig.
What I did
Generated interface file for my function
classify.i
%module example
%{
#include "Facdetect.h"
%}
%include "typemaps.i"
%include "cstring.i"
%include "cdata.i"
%include <std_string.i>
%apply unsigned char *{unsigned char *x};
%apply double *INOUT{double *y};
%include "Facdetect.h"
Facdetect.h
#include <iostream>
class facedetect
{
public:
void display(unsigned char *x,double *y);
};
sample.cpp
#include <cstdlib>
#include "Facdetect.h"
using namespace std;
void facedetect::display( unsigned char *x,double *y)
{
cv::Mat in(512,512,CV_8UC3,x);
cv::Mat res=imdecode(in,1);
cv::cvtColor(res,res,cv::COLOR_RGB2GRAY);
std::vector<uchar> buff;
cv::imencode(".jpg",res,buff);
int k=0;
std::cout<<" buffer size "<<buff.size()<<std::endl;
for(int j=0;j<10;j++)
{
y[k++]=j;
}
}
sample.py
import os
import io
import sys
import time
import example
from PIL import Image
import struct
from array import array
def readimage(path):
count = os.stat(path).st_size / 2
with open(path, "rb") as f:
return bytearray(f.read())
bytes = readimage("sample.jpg")
print len(bytes)
c=example.facedetect()
ret = array(10)
ret=c.display(bytes,ret)
print ret
I had successfully passed byte array from Python to C++.
Now i am passing double array values into python fro C++.
I did not get double array values in python from C++.
What I need:
How to get double array values from C++ to python.?
Is any typemaps i missed for generating swig wrapper.?

how do i get a structure in c++ from python?

My C++ program:
#include <iostream>
using namespace std;
struct FirstStructure
{
public:
int first_int;
int second_int;
};
struct SecondStructure
{
public:
int third_int;
FirstStructure ft;
};
int test_structure(SecondStructure ss)
{
int sum = ss.ft.first_int + ss.ft.second_int + ss.third_int;
return sum;
}
extern "C"
{
int test(SecondStructure ss)
{
return test_structure(ss);
}
}
And I compile the cpp file use this command "g++ -fPIC -shared -o array.so array.cpp".
Then I call the file array.so use python,my python program as these:
#coding=utf-8
import ctypes
from ctypes import *
class FirstStructure(Structure):
_fields_ = [
("first_int", c_int),
("second_int", c_int)
]
class SecondStructure(Structure):
_fields_ = [
("third_int", c_int),
("ft", FirstStructure)
]
if __name__ == '__main__':
fs = FirstStructure(1, 2)
ss = SecondStructure(3, fs)
print ss.ft.first_int
lib = ctypes.CDLL("./array.so")
print lib.test(ss)
When I run the python program,the console show an error, the error is "segmentation fault".I read the documentation from the url "https://docs.python.org/2/library/ctypes.html",how to fix the bug.
You have to declare a function's argument and return types in python, in order to be able to call it properly.
So, insert the following before calling the test function:
lib.test.argtypes = [SecondStructure]
lib.test.restype = ctypes.c_int
Things should work then, as far as I can see...
As long as the amount of C-to-python interfaces remains "small" (whatever that is), I think ctypes is just fine.
ok,I got it,modified code as these:
#include <iostream>
using namespace std;
extern "C"
{
struct FirstStructure
{
public:
int first_int;
int second_int;
};
struct SecondStructure
{
public:
int third_int;
FirstStructure ft;
};
int test_structure(SecondStructure *ss)
{
int sum = ss->ft.first_int + ss->ft.second_int + ss->third_int;
return sum;
}
int test(SecondStructure *ss)
{
return test_structure(ss);
}
}
and then,I fixed the bug.
Well if you are intending to design communication medium between C++ and python then I would suggest go for combination zmq and google protocol buffers.
where proto buf would serve for serialization/deserialization and zmq for communication medium.
You might want to have a look at Boost.python
https://wiki.python.org/moin/boost.python/SimpleExample
It will allow you to compile python modules from C++ and define how the python is allowed to access the c++ code in an easy to understand fashion

typedef does not work with SWIG (python wrapping C++ code)

Hello and thanks for your help in advance !
I am writing a python wrapper (SWIG 2.0 + Python 2.7) for a C++ code. The C++ code has typedef which I need to access in python wrapper. Unfortunately, I am getting following error when executing my Python code:
tag = CNInt32(0)
NameError: global name 'CNInt32' is not defined
I looked into SWIG documentation section 5.3.5 which explains size_t as typedef but I could not get that working too.
Following is simpler code to reproduce the error:
C++ header:
#ifndef __EXAMPLE_H__
#define __EXAMPLE_H__
/* File: example.h */
#include <stdio.h>
#if defined(API_EXPORT)
#define APIEXPORT __declspec(dllexport)
#else
#define APIEXPORT __declspec(dllimport)
#endif
typedef int CNInt32;
class APIEXPORT ExampleClass {
public:
ExampleClass();
~ExampleClass();
void printFunction (int value);
void updateInt (CNInt32& var);
};
#endif //__EXAMPLE_H__
C++ Source:
/* File : example.cpp */
#include "example.h"
#include <iostream>
using namespace std;
/* I'm a file containing use of typedef variables */
ExampleClass::ExampleClass() {
}
ExampleClass::~ExampleClass() {
}
void ExampleClass::printFunction (int value) {
cout << "Value = "<< value << endl;
}
void ExampleClass::updateInt(CNInt32& var) {
var = 10;
}
Interface file:
/* File : example.i */
%module example
typedef int CNInt32;
%{
#include "example.h"
%}
%include <windows.i>
%include "example.h"
Python Code:
# file: runme.py
from example import *
# Try to set the values of some typedef variables
exampleObj = ExampleClass()
exampleObj.printFunction (20)
var = CNInt32(5)
exampleObj.updateInt (var)
Thanks again for your help.
Santosh
I got it working. I had to use typemaps in the interface file, see below:
- Thanks a lot to "David Froger" on Swig mailing lists.
- Also, thanks to doctorlove for initial hints.
%include typemaps.i
%apply CNInt32& INOUT { CNInt32& };
And then in python file:
var = 5 # Note: old code problematic line: var = CNInt32(5)
print "Python value = ",var
var = exampleObj.updateInt (var) # Note: 1. updated values returned automatically by wrapper function.
# 2. Multiple pass by reference also work.
# 3. It also works if your c++ function is returning some value.
print "Python Updated value var = ",var
Thanks again !
Santosh

Categories