从内存缓冲区保存 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 上下文中。一切正常。

我想做的是绕过IplImagecv::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的主要内容,如果未能解决你的问题,请参考以下文章

PySide.QtGui.QImage to Base64

#yyds干货盘点#Android C++系列:Linux Socket编程预备知识

c++缓冲池循环队列实现

Linux中的buff/cache内存

TCP之socket编程

Linux网络编程 --- socket编程前传