我正在通过网络(从Python)发送图像,并希望在接收端(在C ++中)从它们创建OpenCV Mat
。
它们是这样创建的:
image = self.camera.capture_image() # np.array of dtype np.uint8
h, w, c = image.shape # 4 channels
image = np.transpose(image, (2, 0, 1)) # transpose because channels come first in OpenCV (?)
image = np.ascontiguousarray(image, dtype='>B') # big-endian bytes
bytess = image.tobytes(order='C')
在此之后,我应该有一个数组,其中3个维度被展平,以便每个通道的各个行附加在一起,然后附加通道以形成最终的字节缓冲区。我已经证实我的理解是正确的,并且以下情况成立
bytess[channel*height*width + i*wwidth + j] == image[channel, i, j]
[我认为上面的部分实际上并不重要,因为如果不正确,我会得到不正确显示的图像,但是至少我会得到一个图像,比我现在的距离再远了一步。]
现在,我正在尝试这样做:
char* pixel_data = … // retrieve array of bytes from message
// assume height, width and channels are known
const int sizes[3] = {channels, width, height};
const size_t steps[3] = {(size_t)height * (size_t)width, (size_t)height};
cv::Mat image(3, sizes, CV_8UC1, pixel_data, steps);
因此,我创建了一个具有三个维度的矩阵,其中元素类型为byte
。我不确定我是否正确确定了steps
,但我认为它与documentation匹配。
但是运行它只会崩溃
error: (-5:Bad argument) Unknown array type in function 'cvarrToMat'
将RGBA(或OpenGRAV的BGRA)图像序列化为字节缓冲区并使用C ++ API从中创建cv::Mat
的正确方法是什么?
答案 0 :(得分:0)
我有一个解决方案,可以解决这个问题。此行:
cv::Mat image(3, sizes, CV_8UC1, pixel_data, steps);
假设我可以通过单个字节传递三个维度的大小,但是我无法完成这项工作。
cv::Mat image(height, width CV_8UC4, pixel_data);
我可以将图像视为二维图像,但具有矢量数据类型(4字节元素大小而不是标量字节)。如果pixel_data
指针的布局正确,则可以使用。
正确的布局没有真正明确地记录在案,但是可以从official tutorials之一推论得出
因此,数据被存储为使得一行排在另一行之后,并且一行的每个元素都被拆分为n_channels
个元素。使用诸如CV_8UC4
之类的数据类型可使矩阵在原始数据数组的每个位置读取4个字节,并将指针前进4个字节。
因此,在这种情况下,我只需要将numpy数组重新排列为适当的顺序:将行附加在一起,但交错通道。我是这样做的,但我希望有一种不循环的方法。
def array_to_cv_bytes(ary):
assert ary.ndim == 3, 'Array must have 3 dimensions'
h, w, c = ary.shape
ary = ary[..., (2, 1, 0, 3)]
output = np.empty(h * c * w, dtype=np.uint8)
for channeld_idx in range(c):
output[channeld_idx::c] = ary[..., channeld_idx].reshape(h*w)
return output.tobytes(order='C')