Конвертировать QVideoFrame в QImage
Я хочу получить все кадры из QMediaPlayer
и преобразовать его в QImage
(или же cv::Mat
)
поэтому я использовал videoFrameProbed
сигнал от QVideoProbe
:
connect(&video_probe_, &QVideoProbe::videoFrameProbed,
[this](const QVideoFrame& currentFrame){
//QImage img = ??
}
Но я не нашел способа получить QImage
от QVideoFrame
!
Как я могу конвертировать QVideoFrame
в QImage
?!
2 ответа
Вы можете использовать конструктор QImage:
QImage img( currentFrame.bits(),
currentFrame.width(),
currentFrame.height(),
currentFrame.bytesPerLine(),
imageFormat);
Где вы можете получить imageFormat
от pixelFormat
из QVideoFrame
:
QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(currentFrame.pixelFormat());
Для вывода QCamera этот метод не всегда работает. В частности, QVideoFrame::imageFormatFromPixelFormat() возвращает QImage::Format_Invalid при получении QVideoFrame::Format_Jpeg, что и есть в моей 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;
}
Qt имеет частную функцию qt_imageFromVideoFrame
для преобразования QVideoFrame
к QImage
. Если вы хотите его использовать, вам необходимо:
- добавлять
QT += multimedia multimedia-private
на ваш.pro
файл - добавлять
#include "private/qvideoframe_p.h"
на ваш.cpp
файл Тогда вы можете использовать
qt_imageFromVideoFrame
со следующей подписью:QImage qt_imageFromVideoFrame (const QVideoFrame& f);
Обратите внимание на предупреждение в шапке:
//
// 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 для извлечения VideoFrame. Также на 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
Для всех, кто оказался здесь после того, как обнаружил, что приватная функция qt "qt_imageFromVideoFrame" больше не существует в Qt5.15.0, теперь вы можете использовать недавно реализованный метод QImage QVideoFrame::image().
QImage img = frame.image();
Я тестировал это на Ubuntu, и он работает.