从内存缓冲区保存 QImage
Posted
技术标签:
【中文标题】从内存缓冲区保存 QImage【英文标题】:Saving a QImage from a memory buffer 【发布时间】:2016-08-04 10:34:43 【问题描述】:这基本上是我正在做的事情:
我有一个视频流(YUV 格式)。每一帧都被提取到一个缓冲区中(frameBytes
)。后来这个buffer用来做YUV->RGB的转换,然后转入一个IplImage
。然后将IplImage
传输到cv::Mat
并显示在OpenGL 上下文中。一切正常。
我想做的是绕过IplImage
和cv::Mat
部分,直接使用OpenGL 中的frameBytes
缓冲区并在着色器中进行转换。
此解释仅针对上下文,因为我遇到的问题更简单。
要查看是否可以更早地使用缓冲区,我尝试使用 memcpy 复制它,然后将其保存在 QImage
中,然后保存在文件中。
这是我这部分的代码:
unsigned char *mycopy = new unsigned char[1920*1080*3];
memcpy(mycopy, frameBytes, sizeof(1920*1080*3));
QImage *img = new QImage(mycopy, 1920, 1080, QImage::Format_RGB888);
img->save("image.jpg",0,-1);
frameBytes 包含来自视频流的 YUV 数据。我知道它是 YUV,我正在尝试创建一个 RGB888 格式的 QImage,但由于 QImage 不支持该格式,我没有在那里进行转换,我认为它仍然会保存图像但颜色错误所以我不在乎这一刻(也许这个假设是错误的?)。
问题是,保存的图片是黑色的。
只是为了了解更多信息,这里有一个示例,我使用frameBytes
进行 YUV->RGB 转换。
void DeckLinkCaptureDelegate::convertFrameToOpenCV(void* frameBytes, IplImage * m_RGB)
if(!m_RGB) m_RGB = cvCreateImage(cvSize(1920, 1080), IPL_DEPTH_8U, 3);
unsigned char* pData = (unsigned char *) frameBytes;
for(int i = 0, j=0; i < 1920 * 1080 * 3; i+=6, j+=4)
unsigned char u = pData[j];
unsigned char y = pData[j+1];
unsigned char v = pData[j+2];
//fprintf(stderr, "%d\n", v);
m_RGB->imageData[i+2] = 1.0*y + 8 + 1.402*(v-128); // r
m_RGB->imageData[i+1] = 1.0*y - 0.34413*(u-128) - 0.71414*(v-128); // g
m_RGB->imageData[i] = 1.0*y + 1.772*(u-128) + 0; // b
y = pData[j+3];
m_RGB->imageData[i+5] = 1.0*y + 8 + 1.402*(v-128); // r
m_RGB->imageData[i+4] = 1.0*y - 0.34413*(u-128) - 0.71414*(v-128); // g
m_RGB->imageData[i+3] = 1.0*y + 1.772*(u-128) + 0;
【问题讨论】:
为什么是 sizeof(1920*1080*3) 而不仅仅是 1920*1080*3? sizeof(1920*1080*3) 和 sizeof(int) 一样 它解决了这个问题。谢谢。 :) 【参考方案1】:修复错误
您没有将所有数据复制到新缓冲区:
unsigned char *mycopy = new unsigned char[1920*1080*3];
memcpy(mycopy, frameBytes, sizeof(1920*1080*3));
其中的sizeof
意味着您只是在复制int
大小的块,而不是6MB。看起来像是使用静态数组的意外保留?替换为
const size_t bufsize = 1920*1080*3;
auto *mycopy = new unsigned char[bufsize];
memcpy(mycopy, frameBytes, bufsize);
更简单的方法
或者,您可以复制图像,而不是自己进行内存分配(并在QImage
被破坏后负责delete[]
ing),而是复制图像:
const unsigned char *bytes = frameBytes;
QImage img = QImage(bytes, 1920, 1080, QImage::Format_RGB888).copy();
其工作方式是我们使用frameBytes
作为其源创建一个临时QImage
(我们将其作为指向const
的指针传递以坚持它是只读的)。然后我们将copy()
全部转移到一个新的QImage
,丢弃临时的。 copy()
执行的操作与您上面的代码类似,但我们现在不必进行计算,从而消除了一些随之而来的潜在错误。
还要注意,我更喜欢按值传递QImage
。尽管这看起来效率低下,但大多数(可复制的)Qt 类型被设计为引用计数的写时复制结构,并且可以以这种方式安全地使用,从而消除了另一类错误(内存管理)。 Qt 的集合类型也是如此,而且非常有用。
【讨论】:
以上是关于从内存缓冲区保存 QImage的主要内容,如果未能解决你的问题,请参考以下文章