【问题标题】:QT QLabel setPixmap from QAbstractVideoSurface来自 QAbstractVideoSurface 的 QT QLabel setPixmap
【发布时间】:2018-12-09 20:30:09
【问题描述】:

我正在实现自己的类,用于在 Windows 下探测来自 QCamera 的视频帧。它是 QAbstractVideoSurface 的子类。因此,我的探针生成了我试图在 QLabel(作为取景器)上绘制的 QPixmap。而且我在 QLabel setPixmap 调用时遇到了分段错误。

我确信我的 Qpixmap 制作精良,因为我可以使用 save() 将它保存在磁盘上。我的 QLabel 已初始化并且运行良好,因为我可以从磁盘加载 QPixmap 并将其设置为 QLabel。我猜像素图的格式有问题,但不知道如何纠正它:(

我的代码

frameprobe.h

#ifndef FRAMEPROBE_H
#define FRAMEPROBE_H
#include <QAbstractVideoSurface>
#include <QList>
#include <QPixmap>

class FrameProbe : public QAbstractVideoSurface
{
    Q_OBJECT
    public:
        explicit FrameProbe(QObject *parent = 0);

        QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;

        bool present(const QVideoFrame &frame);

    signals:
        void frameAvailable(QImage frame);
        void frameReady(QPixmap frame);

    public slots:
};

#endif // FRAMEPROBE_H

frameprobe.cpp

#include "frameprobe.h"

FrameProbe::FrameProbe(QObject *parent) :
    QAbstractVideoSurface(parent)
{
}

QList<QVideoFrame::PixelFormat> FrameProbe::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
    Q_UNUSED(handleType);
    return QList<QVideoFrame::PixelFormat>()
        << QVideoFrame::Format_ARGB32
        << QVideoFrame::Format_ARGB32_Premultiplied
        << QVideoFrame::Format_RGB32
        << QVideoFrame::Format_RGB24
        << QVideoFrame::Format_RGB565
        << QVideoFrame::Format_RGB555
        << QVideoFrame::Format_ARGB8565_Premultiplied
        << QVideoFrame::Format_BGRA32
        << QVideoFrame::Format_BGRA32_Premultiplied
        << QVideoFrame::Format_BGR32
        << QVideoFrame::Format_BGR24
        << QVideoFrame::Format_BGR565
        << QVideoFrame::Format_BGR555
        << QVideoFrame::Format_BGRA5658_Premultiplied
        << QVideoFrame::Format_AYUV444
        << QVideoFrame::Format_AYUV444_Premultiplied
        << QVideoFrame::Format_YUV444
        << QVideoFrame::Format_YUV420P
        << QVideoFrame::Format_YV12
        << QVideoFrame::Format_UYVY
        << QVideoFrame::Format_YUYV
        << QVideoFrame::Format_NV12
        << QVideoFrame::Format_NV21
        << QVideoFrame::Format_IMC1
        << QVideoFrame::Format_IMC2
        << QVideoFrame::Format_IMC3
        << QVideoFrame::Format_IMC4
        << QVideoFrame::Format_Y8
        << QVideoFrame::Format_Y16
        << QVideoFrame::Format_Jpeg
        << QVideoFrame::Format_CameraRaw
        << QVideoFrame::Format_AdobeDng;
}

bool FrameProbe::present(const QVideoFrame &frame)
{
    if (frame.isValid()) {
        QVideoFrame cloneFrame(frame);
        cloneFrame.map(QAbstractVideoBuffer::ReadOnly);

        QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat());

        const QImage image(cloneFrame.bits(),
                           cloneFrame.width(),
                           cloneFrame.height(),
                           imageFormat);
        emit frameAvailable(image);
        emit frameReady(QPixmap::fromImage(image));
        cloneFrame.unmap();
        return true;
    }
    return false;
}

初始化代码

  QCamera * MyCamera= new QCamera(CameraDeviceName);
    MyCamera->setCaptureMode( QCamera::CaptureVideo );

    Label = new QLabel();
    Label->setText("Label");

    FrameProbe * VSurface = new FrameProbe();

    MyCamera->setViewfinder(VSurface);

    connect(VSurface, SIGNAL(frameAvailable(QImage)), this, SLOT(video_probed(QImage)));
    connect(VSurface, SIGNAL(frameReady(QPixmap)), this,SLOT(video_forward(QPixmap)));

    ui->gridLayout->addWidget(Label,0,0);

    MyCamera->start();

和插槽

void MainWindow::video_probed(QImage InVideoFrame){

  FramesProbed++;
  std::cout<<FramesProbed<<std::endl;

}
void MainWindow::video_forward(QPixmap InVideoFrame){

    Label->setPixmap(InVideoFrame); //<<<<<<<<< Segmentation Fault here
}

如果我将 video_forward 插槽更改为这样的内容

void MainWindow::video_forward(QPixmap InVideoFrame){

    InVideoFrame.save("c:\\temp\\a.jpg",0,-1);
    QPixmap a;
    a.load("c:\\temp\\a.jpg");

    Label->setPixmap(a);
}

它工作。当然慢 :) 但工作......

PS:在FrameProbe::present图像中有QImage::Format_RGB32格式。

【问题讨论】:

  • 你试过使用 valgrind 吗?在未分配的情况下准确查看访问了哪些资源非常有帮助。
  • 问题可能与 QPixmap 内部数据的隐式共享有关:doc.qt.io/qt-5/implicit-sharing.html。查看 video_forward 函数,您在离开该函数时已销毁 QPixmap 对象并与标签共享。除非您确定,否则请避免按值发送对象。如果可能,最好像 Qt 那样使用 const T&。
  • @AlexanderV 是的,这是我的问题。任何制作像素图真实副本的操作都可以解决问题。

标签: c++ qt qlabel qcamera


【解决方案1】:

解决方案:

bool FrameProbe::present(const QVideoFrame &frame)
{
   ...
   emit frameAvailable(image.copy());
   ...
}

// slot video_probed
connect(VSurface, &FrameProbe::frameAvailable, [=](QImage a){
    Label->setPixmap(QPixmap::fromImage(a));
});

那么为什么会崩溃:

因为你使用下面来构造QImage

QImage::QImage(uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = Q_NULLPTR, void *cleanupInfo = Q_NULLPTR)

第一个参数data 是从cloneFrame 设置的,它将在cloneFrame.unmap(); 之后释放。 data 不会存在于 QLabel::paintEvent 堆栈中。

【讨论】:

  • 谢谢!这只是沙盒,不是真正的应用程序。我只是将 QImage 分开用于以后的图像处理和 QPixmap 用于在不同的插槽中显示为取景器。
  • @AbbeyRoad 我明白了,发送image.copy() 的解决方案不起作用?
  • image.copy() 就像一个魅力。以及制作真实数据副本的任何其他解决方案(例如,非隐式共享副本)。
  • @AbbeyRoad image.copy()是复制QImage的数据...我没有看到其他方法...
  • 你可以做这样的事情 void MainWindow::video_forward(QPixmap InVideoFrame){ InVideoFrame.setMask(InVideoFrame.mask());标签->setPixmap(InVideoFrame);标签->更新(); } 没有 image.copy() ,它会工作
猜你喜欢
  • 2012-09-17
  • 2023-03-07
  • 1970-01-01
  • 2017-03-03
  • 2014-09-04
  • 2015-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多