我正在开发一个涉及通过深度学习进行对象检测的项目,底层检测代码用C语言编写。由于项目的要求,这段代码有一个Python包装器,它通过它连接所需的C函数ctypes的。图像从Python读取,然后传输到C中作为批处理。
在当前状态下,代码非常不优化:使用cv2.imread
读取图像(每个640x360x3),然后堆叠成numpy数组。例如,对于批量大小为16,此阵列的尺寸为(16,360,640,3)。完成此操作后,指向此数组的指针将通过ctypes传递到C,在此处解析数组,将像素值标准化并重新排列为2D数组。 2D阵列的尺寸为16x691200(16x(640 * 360 * 3)),排列如下。
row [0]: Image 0: (B)r0(B)r1(B)r2.... (G)r0(G)r1(G)r2.... (R)r0(R)r1(R)r2....
row [1]: Image 1: (B)r0(B)r1(B)r2.... (G)r0(G)r1(G)r2.... (R)r0(R)r1(R)r2....
.
.
row [15]: Image 15: (B)r0(B)r1(B)r2.... (G)r0(G)r1(G)r2.... (R)r0(R)r1(R)r2....
`
用于执行此操作的C代码当前看起来像这样,其中通过步幅访问像素值并按顺序排列每个图像。 nb是批次中的图像总数(通常为16); h,w,c分别为360,640和3。
matrix ndarray_to_matrix(unsigned char* src, long* shape, long* strides)
{
int nb = shape[0];
int h = shape[1];
int w = shape[2];
int c = shape[3];
matrix X = make_matrix(nb, h*w*c);
int step_b = strides[0];
int step_h = strides[1];
int step_w = strides[2];
int step_c = strides[3];
int b, i, j, k;
int index1, index2 = 0;
for(b = 0; b < nb ; ++b) {
for(i = 0; i < h; ++i) {
for(k= 0; k < c; ++k) {
for(j = 0; j < w; ++j) {
index1 = k*w*h + i*w + j;
index2 = step_b*b + step_h*i + step_w*j + step_c*k;
X.vals[b][index1] = src[index2]/255.;
}
}
}
}
return X;
}
调用此函数的相应Python代码:( array是原始的numpy数组)
for i in range(start, end):
imgName = imgDir + '/' + allImageName[i]
img = cv2.imread(imgName, 1)
batchImageData[i-start,:,:] = img[:,:]
data = batchImageData.ctypes.data_as(POINTER(c_ubyte))
resmatrix = self.ndarray_to_matrix(data, batchImageData.ctypes.shape, batchImageData.ctypes.strides)
截至目前,对于一批16张图像,此ctypes实现大约需要35毫秒。我正在开发一个非常FPS的关键图像处理管道,那么有更有效的方法来执行这些操作吗?具体做法是:
np.ascontiguousarray(img.transpose(2,0,1).flat, dtype=float)/255.
应该实现类似的东西,但这实际上花费了更多的时间,因为它是用Python调用的。答案 0 :(得分:2)
关于ascontiguousarray
方法,我假设它非常慢,因为python必须做一些内存工作才能返回类似C的连续数组。
编辑1:
我看到this answer,显然openCV的imread
函数应该已经返回一个连续的数组。
我对ctypes
不太熟悉,但碰巧使用PyBind library并且只能推荐使用它。它实现了Python的buffer protocol,因此允许您与python数据进行交互,几乎没有开销。
我已经回答了question解释如何将numpy
数组从Python传递到C / C ++,在C ++中做一些虚拟的事情并将动态创建的数组返回给Python。
编辑2:我添加了一个接收Numpy数组的简单示例,将其发送到C并从C打印出来。您可以找到它here。希望它有所帮助!
编辑3: 要回答你的最后评论,是的,你绝对可以做到这一点。 您可以修改代码,以便(1)在C ++中实例化2D numpy数组,(2)将对数据的引用传递给C函数,该函数将修改它而不是声明Matrix并且(3)通过引用将该实例返回给Python
你的职能将成为:
void ndarray_to_matrix(unsigned char* src, double * x, long* shape, long* strides)
{
int nb = shape[0];
int h = shape[1];
int w = shape[2];
int c = shape[3];
int step_b = strides[0];
int step_h = strides[1];
int step_w = strides[2];
int step_c = strides[3];
int b, i, j, k;
int index1, index2 = 0;
for(b = 0; b < nb ; ++b) {
for(i = 0; i < h; ++i) {
for(k= 0; k < c; ++k) {
for(j = 0; j < w; ++j) {
index1 = k*w*h + i*w + j;
index2 = step_b*b + step_h*i + step_w*j + step_c*k;
X.vals[b][index1] = src[index2]/255.;
}
}
}
}
}
您要在C ++包装器代码中添加
// Instantiate the output array, assuming we know b, h, c,w
py::array_t<double> x = py::array_t<double>(b*h*c*w);
py::buffer_info bufx = x.request();
double*ptrx = (double *) bufx.ptr;
// Call to your C function with ptrx as input
ndarray_to_matrix(src, ptrx, shape, strides);
// now reshape x
x.reshape({b, h*c*w});
不要忘记修改C ++包装函数的原型以返回numpy
数组,如:
py::array_t<double> read_matrix(...){}...
这应该有用,但我没有测试过它:)