在新线程内调用OpenCV函数Canny()会导致分段错误

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在新线程内调用OpenCV函数Canny()会导致分段错误相关的知识,希望对你有一定的参考价值。

我正在开发一个服务器应用程序,它基于客户端请求使用OpenCV库执行某些图像处理操作。应用程序的性质决定了使用多个线程。最近我一直在处理一个非常顽固的错误,导致段错误。我能够对发生分段故障的代码部分进行调零。

这是最小的实现。

#include <iostream>
#include <thread>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

int main() {
  cv::Mat img_input = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);
  cv::Mat img_output = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);

  int i = 1;
  while (i < 100) {
    std::cout << "- - - - - - - - - - - - - - " << i++ << std::endl;
    std::thread([&]() {
        std::cout << "Thread started." << std::endl;
        cv::Canny(img_input, img_output, 10, 20);
        std::cout << "Thread finished." << std::endl;
    }).join();

    std::cout << "Thread joined." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(150));
  }

  return 0;
}

该程序失败了2个不同的输出。有了这个输出......

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Segmentation fault (core dumped)

..或与这一个。

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Thread finished.
Segmentation fault (core dumped)

让我分享我的其他调查结果。 segfault只发生在我安装了OpenCV版本3.3.0-dev的嵌入式Linux设备(Toradex Colibri iMX6 - 计算机模块)上。仅当我在新线程中使用Canny()函数时才会导致段错误。我也试过调用其他OpenCV函数,但是没有它们产生任何错误。

当我在我的PC上运行程序(Ubuntu 16.04,OpenCV版本3.3.0)时,不会发生段错误。

有任何想法吗?

******更新1 ******

我试图听取评论中的一些建议。但问题仍然存在。

我将Mat变量移动到每个线程的范围内,使它们成为线程的本地变量。我还添加了一些额外的睡眠时间来等待线程完成。

这是我的新实现。

#include <iostream>
#include <thread>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>


void run() {
  cv::Mat img_input = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);
  cv::Mat img_output = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);

  std::cout << "Thread started." << std::endl;
  cv::Canny(img_input, img_output, 10, 20);
  std::cout << "Thread finished." << std::endl;
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

int main() {
  int i = 1;
  while (i < 100) {
    std::cout << "- - - - - - - - - - - - - - " << i++ << std::endl;
    std::thread t1(run);

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    std::cout << "Waiting to join thread." << std::endl;
    t1.join();
    std::cout << "Thread joined." << std::endl;

    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  return 0;
}

输出再次在...之间变化

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Waiting to join thread.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Thread finished.
Segmentation fault (core dumped)

......而且......

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Waiting to join thread.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Segmentation fault (core dumped)

******更新2 ******

我找到了一个类似问题here的帖子。建议在代码中添加以下行解决问题。

cv::setNumThreads(0);

我在main()函数的开头添加了这一行,似乎它解决了分段错误的问题。这有助于解释该计划的情况吗?

目前这似乎是一个很好的快速解决方案,但在我接受它作为一个合适的解决方案之前,我想了解它的背景。谁能解释为什么不再发生段错误?

有更好的解决方案吗?

答案

事实证明,将线程数设置为0或1可以解决问题(不再发生段错误)。这是通过以下两行代码之一完成的。

cv::setNumThreads(0); // Setting the number of thread to 0.
cv::setNumThreads(1); // Setting the number of thread to 1.

我不太了解OpenCV库,无法理解为什么会出现段错误以及为什么这行会解决问题,但此时我对结果感到满意并将此问题标记为已回答。希望这对其他人有所帮助。

另一答案

这是一个评论,但我错过了相应的权利。对不起。

很可能错误不是由OpenCV的调用引起的,而是由使用中的线程设施引起的。一种可能性是在while循环中使用临时std :: thread对象。试着给它一个名字,例如

std::thread worker([]...);
worker.join()

由于上述变化没有帮助,问题也可能在较低的层面(例如,图书馆冲突)。

以上是关于在新线程内调用OpenCV函数Canny()会导致分段错误的主要内容,如果未能解决你的问题,请参考以下文章

HoughLines(opencv)之前的Canny有啥用?

OpenCV,pthread - 在新线程中显示图像

Canny边缘检测

在 Windows Server 2012 R2 中使用 OpenCV 的 Canny 函数时 C++ 崩溃

OpenCV:Canny 边缘检测器获取 minEnclosureCircle

Canny 边缘输出已损坏