c++ 将QVideoFrame转换为QImage

xqkwcwgp  于 2022-11-27  发布在  其他
关注(0)|答案(4)|浏览(404)

我想从QMediaPlayer获取每一帧,并将其转换为QImage(或cv::Mat
所以我使用QVideoProbe中的videoFrameProbed信号:

connect(&video_probe_, &QVideoProbe::videoFrameProbed, 
         [this](const QVideoFrame& currentFrame){
   //QImage img = ??
}

但是我没有找到任何从QVideoFrame得到QImage的方法!
如何将QVideoFrame转换为QImage?!

tez616oj

tez616oj1#

您可以使用QImage的构造函数:

QImage img( currentFrame.bits(),
             currentFrame.width(),
             currentFrame.height(),
             currentFrame.bytesPerLine(),
             imageFormat);

QVideoFramepixelFormat可以得到imageFormat

QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(currentFrame.pixelFormat());
5n0oy7gb

5n0oy7gb2#

对于QCamera输出,这个方法并不总是有效的。特别是,当给定QVideoFrame::Format_Jpeg时,QVideoFrame::imageFormatFrom泛指elFormat()返回QImage::Format_Invalid,这是从我的QCamera输出的。但是这个方法是有效的:

QImage Camera::imageFromVideoFrame(const QVideoFrame& buffer) const
{
    QImage img;
    QVideoFrame frame(buffer);  // make a copy we can call map (non-const) on
    frame.map(QAbstractVideoBuffer::ReadOnly);
    QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(
                frame.pixelFormat());
    // BUT the frame.pixelFormat() is QVideoFrame::Format_Jpeg, and this is
    // mapped to QImage::Format_Invalid by
    // QVideoFrame::imageFormatFromPixelFormat
    if (imageFormat != QImage::Format_Invalid) {
        img = QImage(frame.bits(),
                     frame.width(),
                     frame.height(),
                     // frame.bytesPerLine(),
                     imageFormat);
    } else {
        // e.g. JPEG
        int nbytes = frame.mappedBytes();
        img = QImage::fromData(frame.bits(), nbytes);
    }
    frame.unmap();
    return img;
}
kse8i1jr

kse8i1jr3#

Qt有一个私有函数qt_imageFromVideoFrame,用于将QVideoFrame转换为QImage。如果要使用它,您需要:
1.将QT += multimedia multimedia-private添加到.pro文件
1.将#include "private/qvideoframe_p.h"添加到.cpp文件
1.然后您可以使用qt_imageFromVideoFrame搭配下列签章:
(c)将图像转换为视频帧;
请注意标题中的警告:

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

该实现使用QVideoFrame::map(),并对位进行低级操作。转换器处理包括YUV在内的各种条件。
qt_imageFromVideoFrame可以在几乎所有的平台上运行,例如Windows、macOS、Linux和iOS。唯一一个运行不稳定的平台是Android。在那里,你需要使用OpenGL调用来提取视频帧。同样,在Android上,你需要在最后调用QImage:rgbSwapped(),因为QImage将图像存储为ARGB,而OpenGL将图像检索为ABGR。

QImage QVideoFrameToQImage( const QVideoFrame& videoFrame )
{
    if ( videoFrame.handleType() == QAbstractVideoBuffer::NoHandle )
    {
        QImage image = qt_imageFromVideoFrame( videoFrame );
        if ( image.isNull() ) return QImage();
        if ( image.format() != QImage::Format_ARGB32 ) return image.convertToFormat( QImage::Format_ARGB32 );
        return image;
    }
    if ( videoFrame.handleType() == QAbstractVideoBuffer::GLTextureHandle )
    {
        QImage image( videoFrame.width(), videoFrame.height(), QImage::Format_ARGB32 );
        GLuint textureId = static_cast<GLuint>( videoFrame.handle().toInt() );
        QOpenGLContext* ctx = QOpenGLContext::currentContext();
        QOpenGLFunctions* f = ctx->functions();
        GLuint fbo;
        f->glGenFramebuffers( 1, &fbo );
        GLint prevFbo;
        f->glGetIntegerv( GL_FRAMEBUFFER_BINDING, &prevFbo );
        f->glBindFramebuffer( GL_FRAMEBUFFER, fbo );
        f->glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,  GL_TEXTURE_2D, textureId, 0 );
        f->glReadPixels( 0, 0,  videoFrame.width(),  videoFrame.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.bits() );
        f->glBindFramebuffer( GL_FRAMEBUFFER, static_cast<GLuint>( prevFbo ) );
        return image.rgbSwapped();
    }
    return QImage();
}

我在GitHub上有一个完整的工作应用程序,其中包括上述函数的实现:
https://github.com/stephenquan/MyVideoFilterApp/blob/master/QVideoFrameToQImage.cpp

gpnt7bae

gpnt7bae4#

对于那些在Qt5.15.0中发现qt私有函数“qt_imageFromVideoFrame”已不存在的人,现在可以使用新实现的方法QImage QVideoFrame::image()。

QImage img = frame.image();

我已经在Ubuntu上测试过了,效果很好。
对于Ubuntu 6.4版,请用途:QImage img = frame.toImage();

相关问题