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
-
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 partEDIT
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"; } }
-
@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 gradeAre 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.hfrm_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? -
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...
-
The setup should be done in the ctor or separate init() function.
For the crash - reduce your program until it no longer crashes :)
-
@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 ispipelineQue
?
What's the implementation oftry_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.