【问题标题】:debugging code using opencv fails with segmentation fault使用 opencv 调试代码因分段错误而失败
【发布时间】:2015-10-30 08:34:35
【问题描述】:

尝试调试一些利用 opencv 库的 c++11 代码,会在 gdb 中产生分段错误。

我正在使用 linux 中的 gdb 调试以下功能。

MatchedFeatures extract_best_features(vector<Mat> imgs)
{
  MatchedFeatures result;

  cv::Ptr<Feature2D> f2d = xfeatures2d::SIFT::create();
  //cv::Ptr<Feature2D> f2d = xfeatures2d::SURF::create();
  //cv::Ptr<Feature2D> f2d = ORB::create();

  for (auto it = 0; it < imgs.size(); ++it) {
    vector<KeyPoint> keyPoints;
    Mat descriptors;
    f2d->detect(imgs[it], keyPoints);
    f2d->compute(imgs[it], keyPoints, descriptors);
    result.imgFeatures.push_back(ImgFeatures{imgs[it], keyPoints, descriptors});
  }

  BFMatcher matcher;
  vector< DMatch > matches;
  matcher.match(result.imgFeatures[0].descriptors,
                result.imgFeatures[1].descriptors,
                matches);

  cout << matches.size() << "\n";

  //extract top 10
  sort(matches.begin(), matches.end(),
            [&](const DMatch x, const DMatch y) -> bool
            { return x.distance <= y.distance; });
  result.matches = vector<DMatch>(matches.begin(),matches.begin()+5); // <- debug fails here and matches is empty in debug mode
  return result;
}

当调试器到达我对matches 向量进行切片的点时,就会产生分段错误。检查matches 变量并查看堆栈跟踪发现matches 是空的。但是,仅在调试模式下为空。

代码在我正常运行时运行良好,只有在调试时失败

我注意到 BFMatcher 在执行匹配时产生了很多线程,所以我怀疑问题是由线程引起的。虽然我刚开始学习c++,所以我只能猜测是哪里出了问题。

有什么方法可以“等待”线程在 gdb 中正确完成吗? 或者是否有其他技巧可以让我在 C++ 中调试和检查此类代码,而不会出现分段错误和来自副作用生成过程的空结果,例如 BFMatcher 中的 match

编辑(解决了问题):

显然,在 cgdb 中运行程序时使用文件路径作为参数时应该使用完整路径(而不是使用主文件夹快捷方式 ~/ 的可执行文件的相对路径或路径)......我现在能够调试我的程序非常好..

【问题讨论】:

  • 如何确保matches 中有5 个项目?更好的测试是:size_t lastItem = std::min(matches.size(), 5);,然后是vector&lt;DMatch&gt;(matches.begin(), matches.begin() + lastItem)
  • 同样的事情:matcher.match(result.imgFeatures[0].descriptors, result.imgFeatures[1].descriptors, 您假设您在imageFeatures 中至少有 2 个项目。如果只有 1 个项目或为空怎么办?您正在访问越界的元素。添加第一条评论,再加上我给出的答案,你的代码有很多明显的问题,而线程没有问题。
  • matches.begin(),matches.begin()+5 在计算该行之前,您可以验证matches.size 是6 还是更大?也许在调试模式下,由于路径错误或某事,您没有成功读取图像,可能导致空或不同的匹配向量。
  • 啊好吧,你已经看到matches是空的。所以分析一下为什么在调试模式下if不同。由于错误的相对路径,可能候选人仍然是您确实阅读了不同的图像或根本没有图像。尝试显示中间结果等...

标签: c++ opencv c++11 gdb


【解决方案1】:

在调试器中运行时崩溃但其他情况下没有崩溃的代码可能表明您使用了未初始化的数据和缺少空指针检查。在调试器中运行时,调试器通常会清除程序使用的所有内存,包括局部变量。这意味着指针将是空指针,并且取消引用空指针通常会导致崩溃。

当不在调试器中运行时,局部变量没有被初始化,所以没有空指针访问。另一方面,由于本地数据(当不在调试器中运行时)未初始化,所以值是不确定的,使用未初始化的数据会导致未定义的行为。它可能似乎可以工作。


另一方面,如果您在 not 在调试器中运行时发生崩溃(或意外结果),但它在调试器中运行良好,则使用未初始化的本地数据仍然存在基本相同的问题,但你确实有空指针检查。

调试此类问题的最佳方法是使用内存调试器,例如Valgrind

【讨论】:

    【解决方案2】:

    一个问题可能与线程无关。是否使用线程存在问题。

    问题是这样的:

      //extract top 10
      sort(matches.begin(), matches.end(),
                [&](const DMatch x, const DMatch y) -> bool
                { return x.distance <= y.distance; });   // <-- Suspect
    

    您的排序标准似乎没有遵循strict weak ordering。每当您将&lt;= 视为“小于”标准时,这几乎总是一个错误。 std::sort 需要严格-弱顺序才能正常运行。

    将行改为:

     { return x.distance < y.distance; });  
    

    这是否完全解决了你的问题,我不能说。但这您必须解决的问题才能继续测试。


    由于您使用的是gcc,我只想提一下,Visual Studio 的调试运行时会检测到这样的问题。

    它检测它的方式是调用您的排序条件两次。第一次按顺序排列参数,第二次交换参数。运行时比较两个结果,如果它们不遵循严格-弱排序,则运行时断言。对于您的情况,如果 x.distance == y.distance,那么调试运行时将立即断言。

    【讨论】:

    • 我试图从代码中删除排序,但调试时结果相同(分段错误)。但是,非常感谢关于使用 VS 和弱排序的提示。如果我设法为 windows 安装和配置 opencv,我可能会在周末玩一下 VS。
    • 在您的原始帖子中查看我的评论。假设您知道大小,请不要使用幻数。始终通过使用std::min 之类的东西来进行防御性编程,以将数字限制到不会失败的大小。
    猜你喜欢
    • 2016-06-13
    • 2018-08-14
    • 2013-02-15
    • 1970-01-01
    • 2018-09-01
    • 2023-03-10
    • 2018-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多