Python代码:
image = urllib2.urlopen('http://localhost/test.png').read()
bytes = bytearray(image)
print [myext.do_stuff(bytes, mode=1)]
C ++代码:
static PyObject *
do_stuff(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyByteArrayObject *imgdata;
char *image;
int mode;
char *keywords[] = { "image", "mode", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i", keywords, &imgdata, &mode))
return NULL;
image = PyByteArray_AsString((PyObject*) imgdata);
char *result = do_something_more(image, mode);
return Py_BuildValue("s", result);
}
添加了:
char * do_something_more(char imagebuffer[], int mode)
{
vector<char> vec(imagebuffer, imagebuffer + sizeof(imagebuffer));
Mat input = imdecode(vec, 1);
}
答案 0 :(得分:2)
typeerror只是因为Y
格式说明符不在python2中存在但仅在python3中存在。如果要在python2中传递bytearray
,则必须使用O
格式说明符。
结果字符串只是实际事件的前几个字节这一事实非常简单:
strlen
是一个处理C null终止字符串的C函数。您的图像数据包含一些空字节,因此该函数不返回实际大小。 PyBuild_Value
的s
格式说明符采用C null终止字符串并返回python字符串对象。由于您的数据包含空字节,因此并非所有内容都放在结果中。在您的C ++代码中,char *image
指针 指向所有数据,但您不依赖于C的字符串函数,如果您的字符串包含空字节。您必须始终跟踪字符串的长度。
更明确我的意思。这是一个独立的C扩展,可用于演示您的问题:
#include <Python.h>
static PyObject *
do_stuff(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyByteArrayObject *imgdata;
char *image;
int mode;
char *keywords[] = { "image", "mode", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i", keywords, &imgdata, &mode))
return NULL;
image = PyByteArray_AsString((PyObject*) imgdata);
return Py_BuildValue("s", image);
}
static PyMethodDef noddy_methods[] = {
{"do_stuff", do_stuff, METH_VARARGS | METH_KEYWORDS, "Does stuff"},
{NULL, NULL, 0, NULL} /* Sentinel */
};
void
initdemo(void)
{
(void) Py_InitModule("demo", noddy_methods);
}
使用setup.py
:
from distutils.core import setup, Extension
module1 = Extension('demo',
sources = ['demo_ext.c'])
setup (name = 'Demo',
version = '1.0',
description = 'This is a demo package',
ext_modules = [module1])
用作:
>>> import demo
>>> with open('/Path/to/A/PNG/Image.png', 'rb') as f:
... contents = f.read()
...
>>> byt = bytearray(contents)
>>> byt[:20]
bytearray(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02V')
>>> demo.do_stuff(byt) # "truncates" the data
'\x89PNG\r\n\x1a\n'
现在,如果您将do_stuff
功能更改为:
static PyObject *
do_stuff(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *imgdata;
char *image;
int mode;
Py_ssize_t length;
char *keywords[] = { "image", "mode", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i", keywords, &imgdata, &mode))
return NULL;
image = PyByteArray_AsString(imgdata);
length = PyObject_Length(imgdata);
PyObject *res = PyString_FromStringAndSize(image, length);
return res;
}
你得到:
>>> import demo
>>> with open('/home/giacomo/Immagini/bad_grouping.png', 'rb') as f:
... contents = f.read()
...
>>> byt = bytearray(contents)
>>> byt[:20]
bytearray(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02V')
>>> demo.do_stuff(byt)[:20]
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02V'
正如您所见,do_stuff
不再截断数据。
strlen
之类的所有函数都假设字符串中没有空字节,并且当这不是真时(例如在这种情况下)将会出错。另外一些python的API调用假设C字符串,例如Py_BuildValue
。您可以看到char *image
确实包含所有数据。问题是您正在使用无法正确处理它的函数。