使用递归算法在 OpenCV 中进行连通分量标记

2023-12-22

我正在尝试使用递归算法在 OpenCV 中实现连接组件标记。我不确定我实施了什么错误。算法是这样的

B 是二值图像,LB 是标记二值图像

procedure connected_component(B,LB)
{
    LB:=negate(B);
    label:=0;
    findComponents(LB,label);
    display(LB);
}

procedure findComponents(LB,label)
{
    for L:=0 to maxRow
        for P:= 0 to maxCol
            if LB[L,P] == -1 then
              {
               label:=label+1;
               search(LB,label,L,P);
              }
}

procedure search(LB,label,L,P)
{
    LB[L,P]:=label;;
    Nset:= neighbours(L,P);
      for each(L',P') in Nset
      {
        if(LB[L',P'] == -1) then
        search(LB,label,L',P');
      }
}

我在OpenCV中编写的代码如下

#include<iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;

void findComponents(Mat res, int label);
void search(Mat res, int label, int row, int col);

int main()
{
    Mat src = imread("D:/My Library/test/peppers.bmp",0);
    src.convertTo(src,CV_8S);
    Mat th = src.clone();
    threshold(src,th,128,255,CV_8S);
    Mat res = th.clone();

    for(int i=0;i<res.rows;i++)
        for(int j=0;j<res.cols;j++)
            res.at<signed char>(i,j) = 0 - th.at<signed char>(i,j);

    int label = 0;
    findComponents(res,label);


    waitKey(0);
    return 0;
}

void findComponents(Mat res, int label)
{
    for (int i = 1; i < res.rows - 1; i++)
    {
        for (int j = 1; j < res.cols - 1; j++)
        {
          if (res.at<signed char>(i, j) == -255)
          {
            label++;
            search(res, label, i, j);
          }
        }
    }
    imshow("CC Image", res);

}

void search(Mat res, int label, int row, int col)
{
    res.at<signed char>(row, col) = label;
    if (res.at<signed char>(row, col + 1) == -255) search(res, label, row, col + 1);
    if (res.at<signed char>(row + 1, col + 1) == -255) search(res, label, row+1, col + 1);
    if (res.at<signed char>(row + 1, col) == -255) search(res, label, row + 1, col);
    if (res.at<signed char>(row + 1, col - 1) == -255) search(res, label, row + 1, col - 1);
    else return;

}

该代码不起作用。我在实现算法时犯了什么错误?我是 OpenCV 新手。


您的代码中存在一些问题。最重要的是你不应该使用CV_8S矩阵。为什么?

  • 它们的值限制在 [-128, 127] 范围内
  • 检查值是否等于-255将无法正常工作
  • 每个图像最多有 127 个连接组件
  • threshold不会按预期工作
  • 也许其他人...

我重新实现了您的代码来纠正这些问题:

  • 你应该使用CV_32S为你的标签。
  • 你应该考虑边界
  • 您可以使用Mat_<Tp>为了方便访问,而不是.at<Tp>

下面是代码。我用了应用自定义颜色映射 https://stackoverflow.com/a/35995427/5008845更好地可视化结果。

#include <opencv2/opencv.hpp>
#include <algorithm>
#include <vector>
#include <stack>
using namespace cv;

void search(Mat1i& LB, int label, int r, int c)
{
    LB(r, c) = label;

    // 4 connected
    if ((r - 1 > 0) && LB(r - 1, c) == -1)       { search(LB, label, r - 1, c    ); }
    if ((r + 1 < LB.rows) && LB(r + 1, c) == -1) { search(LB, label, r + 1, c    ); }
    if ((c - 1 > 0) && LB(r, c - 1) == -1)       { search(LB, label, r    , c - 1); }
    if ((c + 1 < LB.cols) && LB(r, c + 1) == -1) { search(LB, label, r    , c + 1); }

    // 8 connected
    if ((r - 1 > 0) && (c - 1 > 0) && LB(r - 1, c - 1) == -1)             { search(LB, label, r - 1, c - 1); }
    if ((r - 1 > 0) && (c + 1 < LB.cols) && LB(r - 1, c + 1) == -1)       { search(LB, label, r - 1, c + 1); }
    if ((r + 1 < LB.rows) && (c - 1 > 0) && LB(r + 1, c - 1) == -1)       { search(LB, label, r + 1, c - 1); }
    if ((r + 1 < LB.rows) && (c + 1 < LB.cols) && LB(r + 1, c + 1) == -1) { search(LB, label, r + 1, c + 1); }

}

int findComponents(Mat1i& LB)
{
    int label = 0;

    for (int r = 0; r < LB.rows; ++r) {
        for (int c = 0; c < LB.cols; ++c) {
            if (LB(r, c) == -1) {
                ++label;
                search(LB, label, r, c);
            }
        }
    }
    return label;
}

int connected_components(const Mat1b& B, Mat1i& LB)
{
    // Foreground is > 0
    // Background is 0

    LB = Mat1i(B.rows, B.cols, 0);
    LB.setTo(-1, B > 0);

    // Foreground labels are initialized to -1
    // Background labels are initialized to 0

    return findComponents(LB);
}

void applyCustomColormap(const Mat1i& src, Mat3b& dst);

int main()
{
    // Load grayscale image
    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    // Binarize the image
    Mat1b bin;
    threshold(img, bin, 127, 255, THRESH_BINARY);

    // Find labels
    Mat1i labels;   
    int n_labels = connected_components(bin, labels);

    // Show results
    Mat3b out;
    applyCustomColormap(labels, out);

    imshow("Labels", out);
    waitKey();

    return 0;
}


void applyCustomColormap(const Mat1i& src, Mat3b& dst)
{
    // Create JET colormap

    double m;
    minMaxLoc(src, nullptr, &m);
    m++;

    int n = ceil(m / 4);
    Mat1d u(n * 3 - 1, 1, double(1.0));

    for (int i = 1; i <= n; ++i) {
        u(i - 1) = double(i) / n;
        u((n * 3 - 1) - i) = double(i) / n;
    }

    std::vector<double> g(n * 3 - 1, 1);
    std::vector<double> r(n * 3 - 1, 1);
    std::vector<double> b(n * 3 - 1, 1);
    for (int i = 0; i < g.size(); ++i)
    {
        g[i] = ceil(double(n) / 2) - (int(m) % 4 == 1 ? 1 : 0) + i + 1;
        r[i] = g[i] + n;
        b[i] = g[i] - n;
    }

    g.erase(std::remove_if(g.begin(), g.end(), [m](double v){ return v > m; }), g.end());
    r.erase(std::remove_if(r.begin(), r.end(), [m](double v){ return v > m; }), r.end());
    b.erase(std::remove_if(b.begin(), b.end(), [](double v){ return v < 1.0; }), b.end());

    Mat1d cmap(m, 3, double(0.0));
    for (int i = 0; i < r.size(); ++i) { cmap(int(r[i]) - 1, 0) = u(i); }
    for (int i = 0; i < g.size(); ++i) { cmap(int(g[i]) - 1, 1) = u(i); }
    for (int i = 0; i < b.size(); ++i) { cmap(int(b[i]) - 1, 2) = u(u.rows - b.size() + i); }

    Mat3d cmap3 = cmap.reshape(3);

    Mat3b colormap;
    cmap3.convertTo(colormap, CV_8U, 255.0);

    // Apply color mapping
    dst = Mat3b(src.rows, src.cols, Vec3b(0, 0, 0));
    for (int r = 0; r < src.rows; ++r)
    {
        for (int c = 0; c < src.cols; ++c)
        {
            dst(r, c) = colormap(src(r, c));
        }
    }
}

请注意,递归实现对于标记来说不是一个好主意:

  • 速度很慢
  • 如果你递归得太深,它可能会失败,即你的组件非常大

我建议使用另一种算法。这是(几乎)您的算法的实现迭代的形式。我强烈推荐这个而不是你的。可以简单地修改它以将每个连接组件的点输出为vector<vector<Point>>, 就像cv::findContours会做:

int connected_components2(const Mat1b& img, Mat1i& labels)
{
    Mat1b src = img > 0;
    labels = Mat1i(img.rows, img.cols, 0);

    int label = 0;
    int w = src.cols;
    int h = src.rows;
    int i;

    cv::Point point;
    for (int y = 0; y<h; y++)
    {
        for (int x = 0; x<w; x++)
        {
            if ((src(y, x)) > 0)   // Seed found
            {
                std::stack<int, std::vector<int>> stack2;
                i = x + y*w;
                stack2.push(i);

                // Current component
                std::vector<cv::Point> comp;

                while (!stack2.empty())
                {
                    i = stack2.top();
                    stack2.pop();

                    int x2 = i%w;
                    int y2 = i / w;

                    src(y2, x2) = 0;

                    point.x = x2;
                    point.y = y2;
                    comp.push_back(point);

                    // 4 connected
                    if (x2 > 0 && (src(y2, x2 - 1) != 0))
                    {
                        stack2.push(i - 1);
                        src(y2, x2 - 1) = 0;
                    }
                    if (y2 > 0 && (src(y2 - 1, x2) != 0))
                    {
                        stack2.push(i - w);
                        src(y2 - 1, x2) = 0;
                    }
                    if (y2 < h - 1 && (src(y2 + 1, x2) != 0))
                    {
                        stack2.push(i + w);
                        src(y2 + 1, x2) = 0;
                    }
                    if (x2 < w - 1 && (src(y2, x2 + 1) != 0))
                    {
                        stack2.push(i + 1);
                        src(y2, x2 + 1) = 0;
                    }

                    // 8 connected
                    if (x2 > 0 && y2 > 0 && (src(y2 - 1, x2 - 1) != 0))
                    {
                        stack2.push(i - w - 1);
                        src(y2 - 1, x2 - 1) = 0;
                    }
                    if (x2 > 0 && y2 < h - 1 && (src(y2 + 1, x2 - 1) != 0))
                    {
                        stack2.push(i + w - 1);
                        src(y2 + 1, x2 - 1) = 0;
                    }
                    if (x2 < w - 1 && y2>0 && (src(y2 - 1, x2 + 1) != 0))
                    {
                        stack2.push(i - w + 1);
                        src(y2 - 1, x2 + 1) = 0;
                    }
                    if (x2 < w - 1 && y2 < h - 1 && (src(y2 + 1, x2 + 1) != 0))
                    {
                        stack2.push(i + w + 1);
                        src(y2 + 1, x2 + 1) = 0;
                    }
                }

                ++label;
                for (int k = 0; k <comp.size(); ++k)
                {
                    labels(comp[k]) = label;
                }
            }
        }
    }

    return label;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用递归算法在 OpenCV 中进行连通分量标记 的相关文章

  • 在 C++ 中使用 matlab 结构(matlab 函数调用的返回值)(由 matlab 编译器生成的库)

    你好 我有一个相当简单的 matlab 函数 例如 function MYSTRUCT myfunc MYSTRUCT prop1 test MYSTRUCT prop2 foo MYSTRUCT prop3 42 end 我用 matla
  • 未提供参数时如何指定 C# System.Commandline 行为?

    在我的控制台应用程序中 当未提供控制台参数时 将执行我指定列表 在本例中为参数 3 的任何处理程序 调用该处理程序时 布尔参数设置为 false 但对我来说 根本不调用它更有意义 如何防止这种情况发生并显示帮助文本 using System
  • 如何让 Swagger 插件在自托管服务堆栈中工作

    我已经用 github 上提供的示例重新提出了这个问题 并为任何想要自己运行代码的人提供了一个下拉框下载链接 Swagger 无法在自托管 ServiceStack 服务上工作 https stackoverflow com questio
  • 确保 StreamReader 不会挂起等待数据

    下面的代码读取从 tcp 客户端流读取的所有内容 并且在下一次迭代中它将仅位于 Read 上 我假设正在等待数据 我如何确保它不会在没有任何内容可供读取时返回 我是否必须设置低超时 并在失败时响应异常 或者有更好的办法吗 TcpClient
  • 复制 std::function 的成本有多高?

    While std function是可移动的 但在某些情况下不可能或不方便 复制它会受到重大处罚吗 它是否可能取决于捕获变量的大小 如果它是使用 lambda 表达式创建的 它依赖于实现吗 std function通常被实现为值语义 小缓
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 如何使用 LINQ2SQL 连接两个不同上下文的表?

    我的应用程序中有 2 个数据上下文 不同的数据库 并且需要能够通过上下文 B 中的表的右连接来查询上下文 A 中的表 我该如何在 LINQ2SQL 中执行此操作 Why 我们正在使用 SaaS 产品来跟踪我们的时间 项目等 并希望向该产品发
  • 由 IHttpClientFactory 注入时模拟 HttpClient 处理程序

    我创建了一个自定义库 它会自动为依赖于特定服务的 Polly 策略设置HttpClient 这是使用以下方法完成的IServiceCollection扩展方法和类型化客户端方法 一个简化的例子 public static IHttpClie
  • qdbusxml2cpp 未知类型

    在使用 qdbusxml2cpp 程序将以下 xml 转换为 Qt 类时 我收到此错误 qdbusxml2cpp c ObjectManager a ObjectManager ObjectManager cpp xml object ma
  • 如何检测表单的任何控件的变化?

    如何检测 C 中表单的任何控件的更改 由于我在一个表单上有许多控件 并且如果表单中的任何控件值发生更改 我需要禁用按钮 我正在寻找一些内置函数 事件处理程序 属性 并且不想为此创建自定义函数 不 我不知道任何时候都会触发任何事件any控制表
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • C#:帮助理解 UML 类图中的 <>

    我目前正在做一个项目 我们必须从 UML 图编写代码 我了解 UML 类图的剖析 但我无法理解什么 lt
  • Azure 辅助角色“请求输入之一超出范围”的内部异常。

    我在辅助角色中调用 CloudTableClient CreateTableIfNotExist 方法 但收到一个异常 其中包含 请求输入之一超出范围 的内部异常 我做了一些研究 发现这是由于将表命名为非法表名引起的 但是 我尝试为我的表命
  • C# HashSet 只读解决方法

    这是示例代码 static class Store private static List
  • 如何设置 log4net 每天将我的文件记录到不同的文件夹中?

    我想将每天的所有日志保存在名为 YYYYMMdd 的文件夹中 log4net 应该根据系统日期时间处理创建新文件夹 我如何设置它 我想将一天中的所有日志保存到 n 个 1MB 的文件中 我不想重写旧文件 但想真正拥有一天中的所有日志 我该如
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • 使用 %d 打印 unsigned long long

    为什么我打印以下内容时得到 1 unsigned long long int largestIntegerInC 18446744073709551615LL printf largestIntegerInC d n largestInte
  • 使用 C# 读取 Soap 消息

  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看

随机推荐

  • 使用 SetFile 后 Java GetFile 返回错误的文件名

    我有一些Java代码 public static String getSaveFilePath String title2 FileDialog fd new FileDialog new Frame Save As 1 fd setFil
  • 关于Winsock Kernel Buffer和Nagle算法的疑问

    在读的时候this http support microsoft com kb 214397文章 我有一个疑问 据我了解 在传输小数据时 默认情况下会启用 Nagle 算法 该算法会合并小数据包 这会导致在传输之前缓存一些数据 我相信 Wi
  • Ruby-on-Rails:多个 has_many :through 可能吗?

    是否可以有多个has many throughRails 中相互传递的关系 我收到了这样做的建议 作为我发布的另一个问题的解决方案 但一直无法让它发挥作用 朋友是一个循环关联通过连接表 目标是创建一个has many through for
  • Xcode 5 - 无法构建模块

    我已经在我的项目 启用模块 C 和 ObjectiveC 中打开了标志 并且收到了 1000 多个错误 无法构建模块 安全 无法构建模块 基础 等等 对于每个包含的框架 还有其他人遇到过这个问题吗 我对 Social h 也有同样的问题 通
  • Android:strings.xml 中的 html

    我想显示例如这个html代码 p b Hello World b p p This is a test of the URL a href http www example com Example a p p b This text is
  • Javascript / React 中的动态配置变量

    我正在编写一个客户端 服务器应用程序 其前端 UI 基于 React 作为后端 Unix 开发人员 Web 技术不是我的强项 所以这对我来说是全新的 我需要能够配置 UI 以指向服务器的 URL 并设置其他首选项 典型的反应方法似乎是使用
  • 让 IE 缓存资源但始终重新验证

    缓存控制标头 no cache must revalidate private 允许浏览器缓存资源 但强制使用条件请求重新验证 这在 FF Safari 和 Chrome 中按预期工作 但是 IE7 8 不发送条件请求 即请求头中缺少 If
  • jQuery 插件:将 mCustomScrollbar 应用于 SCEditor

    我该如何申请m自定义滚动条 http manos malihu gr jquery custom content scroller to SCEditor http www sceditor com This http jsfiddle n
  • 模板模板成员继承带有“using”

    以下类实现 CRTP 我想要上课Derived使用提供的构造函数Base 所以我写using 但是 我收到错误消息 只能从直接基类继承构造函数 等价于成员变量x template
  • DateTime.ToBinary() 和 DateTime.ToFileTime() 有何不同?

    谁能帮忙解释一下两者之间的区别日期时间 ToBinary http msdn microsoft com en us library system datetime tobinary aspx and 日期时间 ToFileTime htt
  • WordPress 网站上 ModSecurity 的 Apache LocationMatch 通配符

    我在运行 WordPress 网站的 Ubuntu 14 04 Apache 2 4 7 上安装了 mod security 我有一些需要忽略的规则 但我在实现一些通配符规则时遇到了麻烦 这样我就不必指定每个页面 我所拥有的 在我的 sit
  • 如何将数据重定向到“getpass”(如密码输入)?

    我正在编写一个 python 脚本来运行一些命令 其中一些命令要求用户输入密码 我确实尝试在其标准输入中输入数据 但它不起作用 这里有两个简单的 python 程序代表问题 input py import getpass print raw
  • 锁定以将数据加载到缓存

    我在 Web 应用程序中有一个辅助类 它所做的事情之一就是将常见的 不变的数据对象呈现为静态属性 我正在加载这些对象 如下所示 public static class MyWebHelper region Constants Fields
  • 在有空间的路径中调用可执行文件时出现问题

    我刚刚在新的 Microsoft Windows 7 计算机上安装了 R 并且在使用命令行时出现以下错误 C Program Files R R 3 3 2 bin Rscript exe version C Program is not
  • javamail问题:如何附加文件而不创建文件

    我正在使用 javamail API 创建电子邮件并向其附加文件 有没有一种方法可以使用 javamail api 发送带有附件的电子邮件 无需在文件系统上物理创建文件 我只想从应用程序中选取一些数据 并将其作为文件附加到我的电子邮件中 我
  • Swift 中能否区分 Bool 和 Int?

    我有一个AnyObject类型可以是String Int or Bool类型 我需要区分它们 这段代码试图这样做 但它考虑了Bool to be Int import Cocoa var value AnyObject func check
  • 如何删除被覆盖模块上的路由?

    I added zfcUser通过 Composer 将模块添加到我的项目并在模块中覆盖它ZfcUserOverride 我想要尾部斜杠工作 所以我在覆盖的模块中添加了路线 zfcUserOverride file module confi
  • 了解多媒体计时器的奇怪行为

    我在我的应用程序 C NET 中使用多媒体计时器来提高计时器的准确性并实现 1 毫秒的计时器频率 到目前为止 我的应用程序一直运行良好 直到最近它开始表现奇怪 我试图了解我的应用程序出了什么问题 以下是采取的步骤 定时器频率设置为 1 ms
  • Clearcase 快照劫持文件:如何签出/签入更改的文件

    Clearcase 太糟糕了 看来我不能快速保存对项目的修改 我所说的快速是指不到 1 秒的时间 我找到的解决方案是使用组合clearcase git 我使用快照视图是因为我可以轻松劫持我的文件 而不必每次想要进行重构时都签出项目中的所有文
  • 使用递归算法在 OpenCV 中进行连通分量标记

    我正在尝试使用递归算法在 OpenCV 中实现连接组件标记 我不确定我实施了什么错误 算法是这样的 B 是二值图像 LB 是标记二值图像 procedure connected component B LB LB negate B labe