cv :: cvtColor没有分配内存问题

时间:2017-08-10 07:13:31

标签: android c++ opencv memory memory-management

我正在尝试使用cv :: cvtColor将<320>从NV21转换为RGBA图像,不用分配新内存。

所以我尝试了各种方法来实现这一目标。我知道cv :: cvtColor调用cv :: Mat.create(..)为输出Mat分配内存,我发现cv :: Mat.create(...)只分配新内存大小&amp;请求的Mat的类型与其当前值不同。

鉴于,这些,以下内容:

//(1)
cv::Mat frame(height + height / 2, width, CV_8UC1, pointerToNV21Data);
//(2)
cv::cvtColor(frame, frame, cv::COLOR_YUV2RGBA_NV21);

分配320 * 240 * 4 channels = 307200字节的新连续内存区域(以前的空闲内存),并丢弃先前分配的320 * 360 * 1 channel = 115200字节。

cv::cvtColor内部调用cv::Mat.create(...)时,新内存块的分配是因为:

  • cv::cvtColor之前,框架cv::Mat,尺寸为320x360,有1个频道
  • cv::cvtColor要求它是cv::Mat,尺寸为320x240,包含4个频道

因此,我最乐观的解决方法是做以下事情:

//allocate a byte buffer of 307200 bytes
uchar largeBuffer[307200];

//make sure the 115200 NV21 image bytes are written to the buffer
writeNV21DataToBuffer(largeBuffer);

//create cv::Mat that wraps "largeBuffer" as a NV21 image
cv::Mat frameNV21(height + height / 2, width, CV_8UC1, largeBuffer);

//create a second cv::Mat that wraps around the same "largeBuffer", 
//but this time consider it as pointing to a RGBA image
cv::Mat frameRGBA(height, width, CV_8UC4, largeBuffer);

//call cv::cvtColor
cv::cvtColor(frameNV21, frameRGBA, cv::COLOR_YUV2RGBA_NV21);

通过这样做,确实没有分配新内存。打印cv::Mat.dataStart&amp; cv::Mat.dataStart的{​​{1}},frameNV21之前的frameRGBAcv::cvtColor之后的frameRGBA确认使用了相同的内存区域,并且没有新的内存分配给结果。

然而......转换本身没有正确完成,结果如下所示,当显示在屏幕上时:

enter image description here

此时,我没有想法。我的最后一种方法本质上是错误的,还是没有办法实现这一目标?

2 个答案:

答案 0 :(得分:1)

这更像是评论而不是答案,但我无法在评论中加以评论。所以我们走了: 我在OpenCV中没有NV21编码器它只能解码,所以我尝试了这段代码:

cv::Mat ssOrg = cv::imread( "/home/user/ss.png");

cv::Mat ssGray;
cv::cvtColor( ssOrg, ssGray, CV_BGR2YUV_I420);
cv::resize( ssGray, ssGray, cv::Size(320, 360));

uchar largeBuffer[307200];
cv::Mat frameNV21( 360, 320, CV_8UC1, largeBuffer);
cv::Mat frameRGBA( 320, 240, CV_8UC4, largeBuffer);

const uchar * nv21Ptr = ssGray.ptr();
for ( int i = 0; i < 360 * 320; ++i){
    largeBuffer[i] = *nv21Ptr++;
}
cv::cvtColor(frameNV21, frameRGBA, CV_YUV2BGRA_I420);
cv::imshow( "rgb", frameRGBA);

cv::waitKey();

此代码确实有效,所以我认为您的方法是正确的。但是当我将行cv::cvtColor(frameNV21, frameRGBA, CV_YUV2BGRA_I420);更改为cv::cvtColor(frameNV21, frameRGBA, CV_YUV2BGRA_NV21);时,我在图像上得到了一些文物。你能检查一下你是否可以从NV21格式转换回来吗?此外,我正在测试PC,你正在测试Android。

答案 1 :(得分:1)

是的,您可以在不分配新内存的情况下使用cvtColor:只需使用更大的缓冲区:

//allocate a byte buffer of 307200 + 115200 bytes
uchar largeBuffer[307200 + 115200];

//make sure the 115200 NV21 image bytes are written to the buffer
writeNV21DataToBuffer(largeBuffer + 307200);

//create cv::Mat that wraps "largeBuffer + 307200" as a NV21 image
cv::Mat frameNV21(height + height / 2, width, CV_8UC1, largeBuffer + 307200);

//create a second cv::Mat that wraps around the same "largeBuffer", 
//but this time consider it as pointing to a RGBA image
cv::Mat frameRGBA(height, width, CV_8UC4, largeBuffer);

//call cv::cvtColor
cv::cvtColor(frameNV21, frameRGBA, cv::COLOR_YUV2RGBA_NV21);

如果您的问题是:&#34;我可以cvtColor在原地工作吗?&#34;,意味着没有额外的内存来存储源位图,答案是否定的。至少不在cvtColor

问题是您需要使用cvtColor覆盖的数据。实际上,如果所有内容都按顺序完成,您可以将图像的UV部分放在RGBA数据之外并保存width * height个字节。 (联合国)幸运的是,OpenCV正在并行执行cvtColor,因此你会在奇怪的位置看到错误(可能你会有一些正确转换的横条纹和一些完全错误的条纹)。所以你需要有一个不同的缓冲区。

转换可以在原地进行,但速度要慢得多,因为您需要重新排列数据。