2016-05-31 3 views
0

Я работаю над биометрическим проектом входа, в котором распознавание лиц является частью. Мы используем OpenCV 2.4.13. У нас есть приложение Qt GUI, который порождает поток и передает его изображение, чтобы проверить, как это:OpenCV/QThread segfault

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, должен позаботиться о том, чтобы очистить модель после того, как она выходит из сферы действия (явно освобождение ее не помогает). Все остальные вещи выделены в стеке, поэтому они не должны вызывать проблем с памятью, насколько я знаю.

Еще одна вещь, которую я пробовал, - это клонирование изображения Mat, на случай, если он случайно удалится как-то раньше, чем понадобится. Это тоже не помогло.

Чтобы добавить в удовольствие, этот точный код отлично работает на компьютерах ребята, с которыми я работаю. Любая помощь/советы были бы очень оценены.

+0

Это более общий, но рекомендуется всегда запрашивать пустой указатель перед разыменованием. I.e .: поместите 'if (model)' где-нибудь перед тем, как вы попытаетесь устранить проблему. Например: 'model-> pred (tookImage, label, distance);'. Это должно избегать segfault, который вы испытываете. Надеюсь, что это помогает при отладке. – tobilocker

+0

не читал ваш код, вы защитили свои критические разделы с помощью мьютексов или атомных операций? Может быть, какой-то поток читает переменную, в то время как другой поток пишет ее => большая проблема – Micka

+0

@ Мичка Я не писал этот код, но я проверю, так ли это. Кажется, что все переменные являются локальными для самого потока, за исключением, может быть, takeImage, но я думаю, что все они копируются в любом случае. Я думаю, что самым странным является тот факт, что этот код работает отлично все время на двух других машинах, которые его запускают. – Tom

ответ

0

Вы передаете ссылку на несуществующий объект:

void passTakenImage(Mat& img) { 

Перед AuthThread начнет работать, объект удаляется:

delete takenImage; 

Таким образом, пройти по значению, а не по ссылке:

void passTakenImage(Mat img) { 

Теперь AuthThread будет работать над копией img, которая жива, пока AuthThread жив ,

+0

Если я не ошибаюсь, opencv Mat содержит указатель на его данные вместе со счетчиком ссылок, поэтому, как только изображение будет передано в поток, сам Mat будет скопирован, поскольку он назначен к классу field takenImage. Старый Mat затем действительно удаляется, но поскольку теперь есть еще один Mat, указывающий на те же данные, эти данные не удаляются. Я попытался явно клонировать Mat, используя умный указатель и передавая по значению, ни одна из которых не работала. Странная вещь, segfault происходит только во второй раз, когда поток создается. Потому что valgrind указал мне на libtbb.so ... – Tom

+0

... Я попытался заменить его другой версией. Я запускаю дугу, поэтому я заменил ее версией, которую я скопировал с моей ubuntu VM, и угадайте, что. Он работает безупречно. Теперь я недостаточно опытен, чтобы точно знать, что это ошибка в libtbb или что-то еще (что кажется маловероятным, поскольку я думаю, что это сделано intel?), Или это может быть ошибкой в ​​том, как opencv обрабатывает свои потоки. Может быть, вы или кто-нибудь еще может указать мне в правильном направлении? Мне бы хотелось разобраться в этом. – Tom