У меня есть бэкэнд OpenCV, который извлекает видеокадр с устройства камеры через cv::VideoCapture
, выполняет некоторую обработку и затем передает кадр cv::Mat
в приложение Qt5 для отображения в QML VideoOutput
.
Проблема в том, что нарисованные кадры пустые / белые.
Класс CameraService
получает cv::Mat
от серверного объекта, который выполняется в собственном потоке с использованием сигнала Qt::QueuedConnection
. Затем я конвертирую его в QImage
, который использую для инициализации QVideoFrame
и передаю его в QAbstractVideoSurface
, полученное из QML VideoOutput
, после установки для него формата пикселей.
Я проверил, есть ли у cv::Mat
действительный контент перед преобразованием в QVideoFrame
, так что это не так.
Или я делаю это совершенно неправильно и вместо этого должен рисовать изображение?
Соответствующий код:
CameraService.cpp
CameraService::CameraService(Video::Backend *backend)
: QObject(),
surface(nullptr),
isFormatSet(false) {
this->backend = backend;
connect(
backend, &Video::Backend::onFrameReady,
this, &CameraService::onVideoFrameReady,
Qt::QueuedConnection);
}
CameraService::~CameraService() {
backend->deleteLater();
}
QAbstractVideoSurface *CameraService::getVideoSurface() const {
return surface;
}
void CameraService::setVideoSurface(QAbstractVideoSurface *surface) {
if (!this->surface && surface)
backend->start();
if (this->surface && this->surface != surface && this->surface->isActive())
this->surface->stop();
this->surface = surface;
if (this->surface && format.isValid()) {
format = this->surface->nearestFormat(format);
this->surface->start(format);
}
}
void CameraService::setFormat(
int width,
int height,
QVideoFrame::PixelFormat frameFormat
){
QSize size(width, height);
QVideoSurfaceFormat format(size, frameFormat);
this->format = format;
if (surface) {
if (surface->isActive())
surface->stop();
this->format = surface->nearestFormat(this->format);
surface->start(this->format);
}
}
void CameraService::onVideoFrameReady(cv::Mat currentFrame) {
if (!surface || currentFrame.empty())
return;
cv::Mat continuousFrame;
if (!currentFrame.isContinuous())
continuousFrame = currentFrame.clone();
else
continuousFrame = currentFrame;
if (!isFormatSet) {
setFormat(
continuousFrame.cols,
continuousFrame.rows,
QVideoFrame::PixelFormat::Format_BGR32);
isFormatSet = true;
}
frame = QImage(
(uchar *)continuousFrame.data,
continuousFrame.cols,
continuousFrame.rows,
continuousFrame.step,
QVideoFrame::imageFormatFromPixelFormat(
QVideoFrame::PixelFormat::Format_BGR32));
surface->present(QVideoFrame(frame));
}
Объект QML:
VideoOutput {
objectName: "videoOutput";
anchors.fill: parent;
fillMode: VideoOutput.PreserveAspectCrop;
source: CameraService;
}
Объект CameraService
становится доступным как одноэлемент для QML с помощью этого оператора:
qmlRegisterSingletonInstance<Application::CameraService>("Application.CameraService", 1, 0, "CameraService", service);