2016-05-31 63 views
0

我正在進行生物識別登錄項目,其中人臉識別是其中的一部分。我們正在使用OpenCV 2.4.13。我們有一個Qt的GUI應用程序,它產生一個線程,並通過它的照片來驗證這樣的:OpenCV/QThread段落

void MainWindow::on_button_test_auth_clicked() 
{ 
    statusLabel->setText("Authenticating..."); 
    statusLabel->repaint(); 

    Mat* takenImage = cam->takePicture(); 

    AuthThread *authThread = new AuthThread(); 
    connect(authThread, SIGNAL(resultReady(const QString&)), this, SLOT(setLabelText(const QString&))); 
    connect(authThread, &AuthThread::finished, authThread, &QObject::deleteLater); 

    authThread->passTakenImage(*takenImage); 
    authThread->start(); 

    delete takenImage; 
} 

AuthThread樣子:

class AuthThread : public QThread 
{ 
    Q_OBJECT 
    void run() Q_DECL_OVERRIDE { 
     Ptr<FaceRecognizer> model = createLBPHFaceRecognizer(4,8,8,8); 

     model->load(Config::getModelFile().toStdString()); 

     int label = -1; 
     double distance = 0.0; 

     model->predict(takenImage, label, distance); 

     QString labelString; 
     QTextStream labelStream(&labelString); 

     double threshold = Config::getThreshold(); 

     if(distance < threshold) { 
      labelStream << "Authenticated as " << label << "! " << QString::number((threshold - distance), 'f', 2) << " under threshold."; 
     } else { 
      labelStream << "Not authenticated! " << QString::number((distance - threshold), 'f', 2) << "over threshold."; 
     } 

     emit resultReady(labelString); 
    } 

public: 
    void passTakenImage(Mat& img) { 
     takenImage = img; 
    } 

private: 
    Mat takenImage; 
signals: 
    void resultReady(const QString &s); 
}; 

這一切工作正常上首次on_button_test_auth_clicked()被調用,但第二次它段錯誤上model->predict(takenImage, label, distance);

我試着運行valgrind,看看究竟是怎麼回事,但我相當新的C++,所以我不能有多大意義的輸出,這是:

==6732== Thread 12 AuthThread: 
==6732== Invalid read of size 8 
==6732== at 0x8C94262: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C9432A: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C95E27: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C94C21: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C8F50F: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C8D724: tbb::internal::allocate_root_with_context_proxy::allocate(unsigned long) const (in /usr/lib/libtbb.so.2) 
==6732== by 0x530D7DA: tbb::interface9::internal::start_for<tbb::blocked_range<int>, cv::calcHist1D_Invoker<float>, tbb::auto_partitioner const>::run(tbb::blocked_range<int> const&, cv::calcHist1D_Invoker<float> const&, tbb::auto_partitioner const&) (in /usr/lib/libopencv_imgproc.so.2.4.13) 
==6732== by 0x531378A: cv::calcHist(cv::Mat const*, int, int const*, cv::_InputArray const&, cv::_OutputArray const&, int, int const*, float const**, bool, bool) (in /usr/lib/libopencv_imgproc.so.2.4.13) 
==6732== by 0x5DABFA5: ??? (in /usr/lib/libopencv_contrib.so.2.4.13) 
==6732== by 0x5DB5899: ??? (in /usr/lib/libopencv_contrib.so.2.4.13) 
==6732== by 0x5DB64DD: cv::LBPH::predict(cv::_InputArray const&, int&, double&) const (in /usr/lib/libopencv_contrib.so.2.4.13) 
==6732== by 0x428940: AuthThread::run() (auththread.h:28) 
==6732== Address 0xfffffffffffffff7 is not stack'd, malloc'd or (recently) free'd 
==6732== 
==6732== 
==6732== Process terminating with default action of signal 11 (SIGSEGV): dumping core 
==6732== Access not within mapped region at address 0xFFFFFFFFFFFFFFF7 
==6732== at 0x8C94262: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C9432A: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C95E27: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C94C21: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C8F50F: ??? (in /usr/lib/libtbb.so.2) 
==6732== by 0x8C8D724: tbb::internal::allocate_root_with_context_proxy::allocate(unsigned long) const (in /usr/lib/libtbb.so.2) 
==6732== by 0x530D7DA: tbb::interface9::internal::start_for<tbb::blocked_range<int>, cv::calcHist1D_Invoker<float>, tbb::auto_partitioner const>::run(tbb::blocked_range<int> const&, cv::calcHist1D_Invoker<float> const&, tbb::auto_partitioner const&) (in /usr/lib/libopencv_imgproc.so.2.4.13) 
==6732== by 0x531378A: cv::calcHist(cv::Mat const*, int, int const*, cv::_InputArray const&, cv::_OutputArray const&, int, int const*, float const**, bool, bool) (in /usr/lib/libopencv_imgproc.so.2.4.13) 
==6732== by 0x5DABFA5: ??? (in /usr/lib/libopencv_contrib.so.2.4.13) 
==6732== by 0x5DB5899: ??? (in /usr/lib/libopencv_contrib.so.2.4.13) 
==6732== by 0x5DB64DD: cv::LBPH::predict(cv::_InputArray const&, int&, double&) const (in /usr/lib/libopencv_contrib.so.2.4.13) 
==6732== by 0x428940: AuthThread::run() (auththread.h:28) 
==6732== If you believe this happened as a result of a stack 
==6732== overflow in your program's main thread (unlikely but 
==6732== possible), you can try to increase the size of the 
==6732== main thread stack using the --main-stacksize= flag. 
==6732== The main thread stack size used in this run was 8388608. 

這似乎表明在libtbb.so,線程構建塊中出現了問題,我認爲這是爲QThread提供動力的。

奇怪的是,當我將AuthThread代碼移動到on_button_test_auth_clicked()時,無論被調用多少次,它都可以正常工作。也許來自舊線程的東西繼續存在,第二次弄亂了東西?

opencv Ptr的工作方式與shared_ptr類似,應該在模型超出範圍後明確清理模型(明確釋放它並沒有幫助)。所有其他的東西都是堆棧分配的,所以他們不應該導致任何內存問題,據我所知。

我試過的另一件事是克隆圖像墊,以防它在需要之前被意外刪除。這也沒有幫助。

爲了增加趣味性,這個確切的代碼在我正在使用的傢伙的計算機上運行良好。任何幫助/提示將非常感激。

+0

這是比較普遍的,但在解除引用之前始終要求空指針是一種很好的做法。 I.e .:在你嘗試去除之前,在任何地方放置一個「if(model)」。例如:'模型 - >預測(拍攝圖像,標籤,距離);'。這應該避免你遇到的段錯誤。希望這有助於調試。 – tobilocker

+0

沒讀過你的代碼,你是否用互斥或原子操作來保護你的關鍵部分?也許一些線程正在讀取一個變量,而另一個線程正在寫它=>很大的麻煩 – Micka

+0

@Micka我沒有寫這個代碼,但我會檢查是否是這種情況。似乎所有變量都是線程本身的本地,除了可能被拍攝,但我認爲一個是複製的。我認爲最奇怪的是這個代碼在其他兩臺運行它的機器上始終正常工作。 – Tom

回答

0

要傳遞到一個空對象的引用:

void passTakenImage(Mat& img) { 

之前AuthThread將開始工作,該對象被刪除:

delete takenImage; 

所以,按值傳遞,而不是參考:

void passTakenImage(Mat img) { 

現在,只要AuthThread處於活動狀態,AuthThread就會在img的副本上工作。

+0

如果我沒有錯誤opencv Mat包含一個指向它的數據以及引用計數的指針,所以只要圖像被傳遞給線程,Mat本身就會被複制,因爲它被分配到類領域takenImage。然後舊Mat將被刪除,但由於現在有另一個Mat指向相同的數據,因此該數據不會被刪除。我嘗試使用智能指針明確地克隆Mat,並通過值傳遞,但其中沒有一個能夠工作。奇怪的是,segfault只發生在第二次線程被實例化的時候。因爲valgrind指向我libtbb.so ... – Tom

+0

...我試着用另一個版本替換它。我自己運行拱門,所以我用從我的Ubuntu VM複製的版本替換了它,然後猜測是什麼。它運行完美無瑕。現在我幾乎沒有足夠的經驗足以確定這是libtbb中的錯誤還是其他任何東西(這似乎不大可能,因爲我認爲它是由intel製作的),或者它可能是opencv處理線程的bug。也許你或其他人可以將我指向正確的方向?我很想深究這一點。 – Tom