【问题标题】:Qt OpenCV - SIGSEGV when displaying transformed framesQt OpenCV - 显示转换帧时的 SIGSEGV
【发布时间】:2015-05-08 10:46:00
【问题描述】:

我有一个应用程序,它必须从视频中提取帧,稍微变换一个,大量变换一个,并同时在 GUI 中显示它们。在工作线程中,有一个 OpenCV 循环:

while(1) {
    cv::VideoCapture kalibrowanyPlik; 
    kalibrowanyPlik.open(kalibracja.toStdString()); //open file from url
    int maxFrames = kalibrowanyPlik.get(CV_CAP_PROP_FRAME_COUNT);
    for(int i=0; i<maxFrames; i++) //I thought it crashed when finished reading the first time around
    {
        cv::Mat frame;
        cv::Mat gray;
        cv::Mat color;

        kalibrowanyPlik.read(frame);
        cv::cvtColor(frame, gray, CV_BGR2GRAY);
        cv::cvtColor(frame, color, CV_BGR2RGB);

        QImage image((uchar*)color.data, color.cols, color.rows,QImage::Format_RGB888);
        QImage processedImage((uchar*)gray.data, gray.cols, gray.rows,QImage::Format_Indexed8);

        emit progressChanged(image, processedImage);
        QThread::msleep(50);
    }
}

这就是框架在 GUI 中的放置方式

void MainWindow::onProgressChagned(QImage image, QImage processedImage) {
    QPixmap processed = QPixmap::fromImage(processedImage);
    processed = processed.scaledToHeight(379);
    ui->labelHsv->clear();
    ui->labelHsv->setPixmap(processed);

    QPixmap original = QPixmap::fromImage(image); //debug points SIGSEGV here
    original = original.scaledToHeight(379);
    ui->labelKalibracja->clear();
    ui->labelKalibracja->setPixmap(original);
}

RGB 图像总是崩溃,灰度图像永远不会崩溃(已测试)。为什么 RGB 图像会崩溃?

编辑:我刚刚发现,如果我将msleep(50) 更改为msleep(100),它会完美执行。但我不想那样。我需要每秒至少 25 帧,10 帧是不可接受的......为什么会导致 SIGSEGV

【问题讨论】:

  • 不可能从您向我们展示的内容中说出来。但是,在此代码中可能发生段错误的唯一合理位置是 QImage image(...) 中的 (uchar*)color.data 指针。很可能 color.cols、color.rows 与 color.data 后面的内存大小不一致。但这只是一种猜测。
  • @Greenflow 我不知道要向您展示什么,实际上(还)没有其他东西可以对这些图像做任何事情编辑:添加更多信息
  • 我只能告诉你。尝试注释掉 QImage image((uchar*)color.data, color.cols, color.rows,QImage::Format_RGB888); 还是崩溃?你说它只有在涉及颜色时才会崩溃。这个函数是我看到的唯一一个,它使用指针。所以也许 QImage::Format_RGB888 是错误的。可能您的颜色格式实际上更小,并且 QImage 尝试访问 color.data 之外的数据。
  • RGB888 一定是错的。我已经用 jpg 文件替换了 original 并将 msleep 设置回 50,它循环良好。编辑:RGB888 是唯一不会在第 1 帧上使应用程序崩溃
  • 视频中的帧通常不是 RGB 或 BGR...它们通常是 yuv 格式。

标签: c++ qt opencv segmentation-fault


【解决方案1】:

标准问题。问题是内存管理! 见我的另一个answer。在 cmets 中有is a good link

因此,在您的代码中,QImage 不会复制,也不会获得矩阵内存的所有权。稍后当矩阵被销毁并且QImage 尝试访问此内存(QImage 通过创建浅拷贝来复制)时,您有一个段错误。


这是this link 的代码形式(我稍微调整了一下),由于某种原因,该站点存在一些管理问题(超出了一些配额),这就是我将其粘贴在这里的原因。
inline QImage  cvMatToQImage( const cv::Mat &inMat )
   {
      switch ( inMat.type() )
      {
         // 8-bit, 4 channel
         case CV_8UC4:
         {
            QImage image( inMat.data, inMat.cols, inMat.rows, inMat.step, QImage::Format_RGB32 );

            QImage copy(image);
            copy.bits(); //enforce deep copy
            return copy;
         }

         // 8-bit, 3 channel
         case CV_8UC3:
         {
            QImage image( inMat.data, inMat.cols, inMat.rows, inMat.step, QImage::Format_RGB888 );

            return image.rgbSwapped();
         }

         // 8-bit, 1 channel
         case CV_8UC1:
         {
            static QVector<QRgb>  sColorTable;

            // only create our color table once
            if ( sColorTable.isEmpty() )
            {
               for ( int i = 0; i < 256; ++i )
                  sColorTable.push_back( qRgb( i, i, i ) );
            }

            QImage image( inMat.data, inMat.cols, inMat.rows, inMat.step, QImage::Format_Indexed8 );
            image.setColorTable( sColorTable );

            QImage copy(image);
            copy.bits(); //enforce deep copy
            return copy;
         }

         default:
            qWarning() << "ASM::cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
            break;
      }

      return QImage();
   }

你的代码应该像这样使用这个函数:

while(1) {
    cv::VideoCapture kalibrowanyPlik; 
    kalibrowanyPlik.open(kalibracja.toStdString()); //open file from url
    int maxFrames = kalibrowanyPlik.get(CV_CAP_PROP_FRAME_COUNT);
    for(int i=0; i<maxFrames; i++) //I thought it crashed when finished reading the first time around
    {
        cv::Mat frame;
        cv::Mat gray;

        kalibrowanyPlik.read(frame);
        cv::cvtColor(frame, gray, CV_BGR2GRAY);

        QImage image(cvMatToQImage(frame));
        QImage processedImage(cvMatToQImage(gray));

        emit progressChanged(image, processedImage);
        QThread::msleep(10); // this is bad see comments below
    }
}

在 95% 的情况下使用 msleep 是错误的!删除此循环并创建将由来自QTimer 的信号调用的槽。

【讨论】:

  • 当 msleep() 设置为低于 100 时仍然会崩溃
  • 按照链接中的说明进行操作!玩弄 sleep 作为同步是非常糟糕的主意,在这里绝对无济于事。只需复制粘贴转换方法并使用它们。
  • 更新的版本就像一个魅力。谢谢!而关于 QTimer 的建议可能会解决我最大的问题
【解决方案2】:

另一种解决方案是使用计时器:

void ??::timerEvent(QTimerEvent*){

 if(kalibrowanssky.isOpened()) 
        cv::Mat frame;
        cv::Mat gray;
        cv::Mat color;

        kalibrowanyPlik.read(frame);
        cv::cvtColor(frame, gray, CV_BGR2GRAY);
        cv::cvtColor(frame, color, CV_BGR2RGB);

        ui->labelHsv->setPixmap(QPixmap::fromImage(Mat2QImage(color)));
        ui->labelKalibracja->setPixmap(QPixmap::fromImage(Mat2QImage(gray)));
}

在你的主要:

cv::VideoCapture kalibrowanyPlik; 
startTimer(1000/25); // 25 frames by second

还有 Mat2QImage 函数(我在这里找到它:how to convert an opencv cv::Mat to qimage):

QImage ??::Mat2QImage(cv::Mat const& src) {

     cv::Mat temp; 
     cvtColor(src, temp,CV_BGR2RGB); 
     QImage dest((const uchar *) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
     dest.bits();

     return dest;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多