Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

SIGSEV on ~QColorSpace Destructor



  • Hello!

    I'm currently in the process of implementing an opencv based object tracker and I want to show the cv::Mats on some forms using QT. So far I've implemented a QTimer to poll a buffer which gets filled by a pipeline, running in another thread. On every timer tick I convert the cv::Mats to QPixmaps using QImage (see Code #1 below). Now after roughly 1 minute and 15-20 seconds the whole programm crashes at ~QColorSace (line 479). Even if I don't convert the images and just let the program run it'll crash at the same exact spot, giving me following stack:

    1  QColorSpace::~QColorSpace    qcolorspace.cpp           479  0x7fffe40e9ebc 
    2  QImageData::~QImageData      qimage.cpp                169  0x7fffe3f565de 
    3  QImage::~QImage              qimage.cpp                1027 0x7fffe3f56ab7 
    4  QRasterPlatformPixmap::~QRasterPlatformPixmap     
    qpixmap_raster.cpp        88   0x7fffe3f9085c 
    
    5  QRasterPlatformPixmap::~QRasterPlatformPixmap     
    qpixmap_raster.cpp        90   0x7fffe3f90879 
    
    6  QExplicitlySharedDataPointer<QPlatformPixmap>::~QExplicitlySharedDataPointer qshareddata.h  184 0x7fffe3f86411 
    
    7  QPixmap::~QPixmap     qpixmap.cpp               263  0x7fffe3f86411 
    8  QPixmapCacheEntry::~QPixmapCacheEntry  qpixmapcache.cpp          480  0x7fffe3f8ae1f 
    9  QPixmapCacheEntry::~QPixmapCacheEntry qpixmapcache.cpp          483  0x7fffe3f8ae1f 
    10 QCache<QPixmapCache::Key, QPixmapCacheEntry>::unlink qcache.h                  69   0x7fffe3f8ae1f 
    11 QCache<QPixmapCache::Key, QPixmapCacheEntry>::trim     qcache.h                  193  0x7fffe3f8ae1f 
    12 QCache<QPixmapCache::Key, QPixmapCacheEntry>::setMaxCost  qcache.h                  129  0x7fffe3f8ae1f 
    13 QPMCache::flushDetachedPixmaps           qpixmapcache.cpp          290  0x7fffe3f8ae1f 
    14 QPMCache::timerEvent                     qpixmapcache.cpp          312  0x7fffe3f8b4e5 
    15 QObject::event                           qobject.cpp               1336 0x7fffe31f6cab 
    16 QApplicationPrivate::notify_helper       qapplication.cpp          3671 0x7fffe483413c 
    17 QApplication::notify                     qapplication.cpp          3417 0x7fffe483ad10 
    18 QCoreApplication::notifyInternal2        qcoreapplication.cpp      1061 0x7fffe31c78f8 
    19 QCoreApplication::sendEvent              qcoreapplication.cpp      1456 0x7fffe31c7aae 
    20 QTimerInfoList::activateTimers           qtimerinfo_unix.cpp       643  0x7fffe32225d9 
    21 timerSourceDispatch                      qeventdispatcher_glib.cpp 183  0x7fffe3222db1 
    22 g_main_context_dispatch                  0x7fffe52db417 
    23 ??                                       0x7fffe52db650 
    24 g_main_context_iteration                 0x7fffe52db6dc 
    25 QEventDispatcherGlib::processEvents      qeventdispatcher_glib.cpp 423  0x7fffe322311c 
    26 QEventLoop::exec                         qeventloop.cpp            232  0x7fffe31c630a 
    27 QCoreApplication::exec                   qcoreapplication.cpp      1369 0x7fffe31cf2b3 
    28 main                                     App_main.cpp              26   0x555555579b99 
    
    

    With d_ptr:

    this	@0x7fffb400ab18	QColorSpace
      d_ptr	    0xb3baf80208b	  QColorSpacePrivate*
      staticMetaObject	@0x7fffe46b4020	QMetaObject
    
    

    I've tried to implement a new PixmapCache for said image but that didn't work either.
    Code#1:

    void  frm_Main::C_frm_Main::FillMat2Lbl(cv::Mat& img, QLabel& label)
      {
      cv::Mat* imgPtr = &img;
      if(imgPtr == nullptr)
        return;
      else if(img.empty())
        return;
      else if(this->PixmapCache->find(*this->PixmapKey, this->QPixImg))
        {
        *this->Qimg = this->cvMatToQImage(img);
        this->QPixImg->convertFromImage(*this->Qimg);
        label.setPixmap(QPixImg->scaled(label.size(), Qt::KeepAspectRatio));
        }
      else
          return;
    
    inline QImage   frm_Main::C_frm_Main::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( (uchar*)inMat.data, inMat.cols, inMat.rows, inMat.step, QImage::Format_RGB888 );
           return image.rgbSwapped().copy();
           }
         // 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() << "***ERROR*** ::cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
           break;
          }
          return QImage();
       }
    

    I feel like that error has nothing to do with the FillMat2Lbl but rather with some wonky implementation of the QTimer on my end, but as it stands I'm completely clueless on what to do.
    Also I'm fairly new to C++ and an absolute beginner in QT, so every hint is greatly appreciated!

    Best


  • Lifetime Qt Champion

    When you say you poll a thread? Where does FillMat2Lbl() get it's data from? Are you sure you're in the main thread here?
    Why do you need an own pixmap cache what do you think you gain with it?
    Why do you use QImage and QPixmap as pointer even you never need it at all?
    This can never be true since you can't pass a null reference.

      cv::Mat* imgPtr = &img;
      if(imgPtr == nullptr)
        return;
    


  • @Christian-Ehrlicher First of, thanks for you response!
    **When you say you poll a thread? Where does FillMat2Lbl() get it's data from? **
    I have a tbb pipeline running in the background which grabs new images from webcams, does some work on it(filter, calculations, etc) and the pushes the whole data package as a payload into tbb Que. The timer tries to pop a payload from said Que and hands it over to FillMat2Lbl():

    void C_frm_Object_Calibration::Taktgeber_Tick()
      {
      this->Ui->txb_zaehler->setText(QString::number(this->Zaehler++));
        if(this->Main->cameraManager->pipelineQue->try_pop(pData))
          {
          this->Main->frm_Main->FillMat2Lbl(pData->cpuSrcImg[0], *this->Ui->lbl_src_img);
          this->Main->frm_Main->FillMat2Lbl(pData->cpuGrayImg[0], *this->Ui->lbl_img_gray);
          this->Main->frm_Main->FillMat2Lbl(pData->cpuUndistortedImg[0], *this->Ui->lbl_imgFinal);
          this->Ui->txb_fps->setText(QString::number(pData->fps));
          this->Ui->txb_frametime->setText(QString::number(pData->frametime.count()));
          this->Ui->txb_worker_1->setText(QString::number(pData->executionTime[0].count()));
          this->Ui->txb_worker_2->setText(QString::number(pData->executionTime[1].count()));
          this->Ui->txb_worker_3->setText(QString::number(pData->executionTime[2].count()));
          this->Ui->txb_worker_4->setText(QString::number(pData->executionTime[3].count()));
          this->Ui->txb_worker_5->setText(QString::number(pData->executionTime[4].count()));
          this->Ui->txb_worker_6->setText(QString::number(pData->executionTime[5].count()));
          this->Ui->txb_worker_7->setText(QString::number(pData->executionTime[6].count()));
          this->Ui->txb_worker_8->setText(QString::number(pData->executionTime[7].count()));
        delete(pData);
        }
      }
    
    

    ** Are you sure you're in the main thread here?**
    I'm pretty sure, yes. In theory there is just the main thread + tbb pipeline threads. Is there a way to check if its running in main thread?
    Why do you need an own pixmap cache what do you think you gain with it?
    Well I thought the crash has something to do with the default cache being to small so I've tried to implement a bigger one but that didn't do anything unfortunately.
    Why do you use QImage and QPixmap as pointer even you never need it at all?
    I'm don't really get whats wrong with using pointers, could you elaborate please? I've defined those Pointers in my .h and declared them with new. That might be a bad design choice but unfortunately that is how my Professor wants me to do it.
    This can never be true since you can't pass a null reference.
    Thanks for pointing out! Gonna delete that part

    EDIT
    According to that small code snipped the timer runs in main thread:

    Main.cpp
      this->MAIN_THREAD_ID = std::this_thread::get_id();
    
    frm_camera_calibration.cpp
    void C_frm_Camera_Calibration::Taktgeber_Tick()
    {
       if (std::this_thread::get_id() == this->Main->MAIN_THREAD_ID)
         {
         std::cout << "C_frm_Object_Calibration::Taktgeber_Tick main thread\n";
         }
       else
         {
         std::cout << "C_frm_Object_Calibration::Taktgeber_Tick not main thread\n";
         }
    }
      
    

  • Lifetime Qt Champion

    @GrafZ4hl said in SIGSEV on ~QColorSpace Destructor:

    That might be a bad design choice but unfortunately that is how my Professor wants me to do it.

    But that's not the Qt way of handling QImage and QPixmap. Esp. since you don't need either of them:

    auto image = cvMatToQImage(img);
    auto pixmap = QPixmap::fromImage(image);
    label.setPixmap(pixmap.scaled(label.size(), Qt::KeepAspectRatio));
    

    On the other hand you pass references where Qt gives you pointer (e.g. the QLabels) - this somehow looks very inconsistent.
    And no, "this->" is no c++ way either - it's java-style and is just for people who are paid by characters :)

    This avoid some (de)allocations.

    Now to your problem - you said it also crashes when you don't convert anything. So you mean you don't call cvMatToQImage()?

    Are you sure cameraManager->pipelineQue() doesn't overflow?

    If you're on linux I would say use AdressSanitizer or valgrind to see what goes wrong.



  • @Christian-Ehrlicher
    But that's not the Qt way of handling QImage and QPixmap. Esp. since you don't need either of them:
    That sounds like a fair point! My usecase is highly time critical, that why I thought I could avoid some unnecessary overhead by using pointer instead of allocating new QImages/QPixmaps on every function call.

    And no, "this->" is no c++ way either - it's java-style and is just for people who are paid by characters :)
    Oh well I'm not gonna tell him! That'd surely be noticable in my final grade

    Are you sure cameraManager->pipelineQue() doesn't overflow?
    Intel tbb has some build in thread safe overflow protections to keep the Que in a fixed size so I recon it won't be that function.

    you said it also crashes when you don't convert anything. So you mean you don't call cvMatToQImage()?
    Exactly! I've commented every line on my timer_tick function but it still crashes after 1-1,5 minutes. So I think theres nothing wrong with my image conversion but rather with the timer. As before I've declared a pointer in frm_camera_calibration.h

    frm_camera_calibration.h
    
      QTimer*  Taktgeber;
    
    

    Constructed the timer in frm_camera_calibration.cpp

    C_frm_Camera_Calibration::C_frm_Camera_Calibration(C_GlobalObjects* GlobalObjects, C_Main* Main, QWidget *parent) :
        QMainWindow(parent)
    {
        this->Taktgeber  = new QTimer(this);
    }
    
    

    And started the timer in showEvent()

    void C_frm_Camera_Calibration::showEvent(QShowEvent* ShowEvent)
    {
    Q_UNUSED(ShowEvent)    
    this->Zaehler                     = 0;
    this->Taktgeber_Intervall         = 25;
    connect                           (this->Taktgeber, &QTimer::timeout, this, &C_frm_Camera_Calibration::Taktgeber_Tick);
    this->Taktgeber->start            (this->Taktgeber_Intervall);
    this->installEventFilter          (this);
    }
    
    

    Again abundant usage of "this->".. Its a thing I have to get used to first.

    If you're on linux I would say use AdressSanitizer or valgrind to see what goes wrong.
    I'll give Valgrind a go. Do you, by any chance, have some good guides at hand on how to use it?


  • Lifetime Qt Champion

    I would not set up the timer in the showEvent() since this can be called more than once.
    And does it crash when the timer does not run?



  • @Christian-Ehrlicher
    I would not set up the timer in the showEvent() since this can be called more than once.
    So a better way to set it up would be using an external call on frm->show?

    And does it crash when the timer does not run?
    Yeah unfortunately its the same behaviour even if I comment it out

    //connect (this->Taktgeber, &QTimer::timeout, this, &C_frm_Camera_Calibration::Taktgeber_Tick);
    //this->Taktgeber->start            (this->Taktgeber_Intervall);
    //this->installEventFilter          (this);
    
    

    So in my program frm_main is like a main menu which calls frm_camera_positioning, frm_camera_calibration and so on but every new form has its own QTimer and EventFilter. Is that the right way to do it? Also, every window is declared as

    C_frm_Camera_Calibration : public QMainWindow
    C_frm_Camera_Positioning : public QMainWindow
    C_frm_Main : public QMainWindow
    
    C_frm_Camera_Calibration::C_frm_Camera_Calibration(C_GlobalObjects* GlobalObjects, C_Main* Main, QWidget *parent) : QMainWindow(parent)
    
    C_frm_Camera_Positioning::C_frm_Camera_Positioning(C_GlobalObjects* GlobalObjects, C_Main* Main, QWidget *parent) : QMainWindow(parent)
    
    C_frm_Main::C_frm_Main(C_GlobalObjects* GlobalObjects, C_Main* Main, QWidget *parent) : (QMainWindow(parent)
    

    Well another try with commented QTimer and installEventFilter on every form failed with the same issue...


  • Lifetime Qt Champion

    The setup should be done in the ctor or separate init() function.

    For the crash - reduce your program until it no longer crashes :)


  • Qt Champions 2017

    @GrafZ4hl said in SIGSEV on ~QColorSpace Destructor:

    connect (this->Taktgeber, &QTimer::timeout, this, &C_frm_Camera_Calibration::Taktgeber_Tick);

    What is this?

    if(this->Main->cameraManager->pipelineQue->try_pop(pData))

    What is pData?
    What is pipelineQue?
    What's the implementation of try_pop?

    I'd hazard that your pData's containing garbage or doesn't live long enough for whatever it is you're trying to do.


Log in to reply