我正在尝试使用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
之前的frameRGBA
和cv::cvtColor
之后的frameRGBA
确认使用了相同的内存区域,并且没有新的内存分配给结果。
然而......转换本身没有正确完成,结果如下所示,当显示在屏幕上时:
此时,我没有想法。我的最后一种方法本质上是错误的,还是没有办法实现这一目标?
答案 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,因此你会在奇怪的位置看到错误(可能你会有一些正确转换的横条纹和一些完全错误的条纹)。所以你需要有一个不同的缓冲区。
转换可以在原地进行,但速度要慢得多,因为您需要重新排列数据。