千家信息网

C++中如何实现OpenCV图像分割与分水岭算法

发表于:2025-01-22 作者:千家信息网编辑
千家信息网最后更新 2025年01月22日,本篇内容主要讲解"C++中如何实现OpenCV图像分割与分水岭算法",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"C++中如何实现OpenCV图像分割与分水
千家信息网最后更新 2025年01月22日C++中如何实现OpenCV图像分割与分水岭算法

本篇内容主要讲解"C++中如何实现OpenCV图像分割与分水岭算法",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"C++中如何实现OpenCV图像分割与分水岭算法"吧!

分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征。

API介绍

void watershed( InputArray image, InputOutputArray markers );

参数说明:

  • image: 必须是一个8bit 3通道彩色图像矩阵序列

  • markers: 在执行分水岭函数watershed之前,必须对第二个参数markers进行处理,它应该包含不同区域的轮廓,每个轮廓有一个自己唯一的编号,轮廓的定位可以通过Opencv中findContours方法实现,这个是执行分水岭之前的要求。算法会根据markers传入的轮廓作为种子(也就是所谓的注水点),对图像上其他的像素点根据分水岭算法规则进行判断,并对每个像素点的区域归属进行划定,直到处理完图像上所有像素点。而区域与区域之间的分界处的值被置为"-1",以做区分。

我们将一个如何使用距离变换和分水岭分割相互接触的物体的例子。

考虑一下下面的硬币图像,这些硬币相互接触。即使你去阈值化它,它也会互相碰触。

我们从找到硬币的大概估计值开始。为此,我们可以利用大津的二值化。

#include#includeusing namespace std;using namespace cv;int main() {        Mat gray, thresh;        Mat img = imread("coins.jpg");        cvtColor(img, gray, COLOR_BGR2GRAY);        threshold(gray, thresh, 0, 255, THRESH_BINARY_INV+CV_THRESH_OTSU);        imshow("Otst阈值图像", thresh);        waitKey(0);        return 0;}

阈值后的图像如下所示:

现在需要去除图像中任何微小的白色噪声。为此,我们可以使用形态开操作。为了去除物体上的任何小洞,我们可以使用形态闭操作。所以,现在我们可以确定的是,靠近物体中心的区域是前景,远离物体的区域是背景。只有我们不确定的区域是硬币的边界区域。

所以我们需要提取我们确定是硬币的区域。侵蚀去除边界像素。所以不管剩下多少,我们都能确定是硬币。如果物体不互相接触,那就可以了。但是由于它们彼此接触,另一个好的选择是找到距离变换并应用适当的阈值。接下来我们需要找到我们确信不是硬币的区域。为此,我们扩展了结果。膨胀将物体边界增加到背景。通过这种方式,我们可以确保结果中的任何背景区域都是真正的背景,因为边界区域。

剩下的区域是我们不知道的,无论是硬币还是背景。分水岭算法应该能找到它。这些区域通常围绕着硬币的边界,也就是前景和背景相遇的地方(甚至是两个不同的硬币相遇的地方)。我们称之为边界。用sure_fg 面积减去sure_bg面积可得。

Mat opening; Mat sure_bg;Mat sure_fg; Mat unknow;Mat dist_transform;double maxValue;// noise removalMat kernel = Mat::ones(3, 3, CV_8U);morphologyEx(thresh, opening, MORPH_OPEN, kernel);// sure background areadilate(opening, sure_bg, kernel, Point(-1, -1), 3);// Finding sure foreground areadistanceTransform(opening, dist_transform, DIST_L2, 5);minMaxLoc(dist_transform, 0, &maxValue, 0, 0);threshold(dist_transform, sure_fg, 0.7*maxValue, 255, 0);// Finding unknown regionsure_fg.convertTo(sure_fg, CV_8U);subtract(sure_bg, sure_fg, unknow);

看到结果。在阈值图像中,我们得到了一些区域的硬币,我们确定这些硬币是独立的。(在某些情况下,你可能只对前景分割感兴趣,而对相互接触的对象的分割不感兴趣。在这种情况下,你不需要使用距离变换,只要侵蚀就足够了。侵蚀只是提取前景区域的另一种方法,仅此而已。)

现在我们可以确定哪些是硬币区域,哪些是背景等等。因此我们创建了marker(它是一个与原始图像大小相同的数组,但是使用int32数据类型),并在其中标记区域。我们确定的区域(无论是前景还是背景)被标记为任何正整数,但是不同的整数,而我们不确定的区域则被保留为0。为此,我们使用了connectedComponents()。它用0标记图像的背景,然后用从1开始的整数标记其他对象。

但是我们知道,如果将background标记为0,watershed将认为它是未知区域。所以我们要用不同的整数来标记它。相反,我们将标记未知区域,由unknown定义,为0。

// Marker labellingMat markers;connectedComponents(sure_fg, markers);// Add one to all labels so that sure background is not 0, but 1markers = markers + 1;// Now, mark the region of unknown with zeromarkers.setTo(0, unknow);

现在我们的标记图像准备好了。到了最后一步,应用分水岭。然后修改标记图像。边界区域将标记为-1。

Mat marker;Mat mask;watershed(img, markers);compare(markers, -1, mask, CMP_EQ);img.setTo(Scalar(0, 0, 255), mask);

参见下面的结果。对于一些硬币,它们接触的区域被正确分割,而对于另一些硬币,它们没有被分割。

到此,相信大家对"C++中如何实现OpenCV图像分割与分水岭算法"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

区域 图像 硬币 分水岭 分水 标记 背景 算法 边界 像素 物体 接触 前景 轮廓 阈值 不同 为此 整数 C++ 兴趣 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 江西立体化软件开发要多少钱 常州企业管理软件开发团队 网吧机房的服务器有点烫手 游戏软件开发客户 虹口区品牌软件开发业务流程 政治学数据库 计算机网络技术基础就业 数据库生日 用友数据库显示可疑 预付账款软件开发费的分录 sql创建学生成绩数据库表 广州软件开发驻场代理公司 小麦族基因组数据库 北京软件开发价格参考价格 公安机关网络安全宣传月活动 组播业务软件开发怎么样 常州方便软件开发销售价格 网络技术基础单元目标 金蝶管理中心连接不到服务器 如何删除和元软件开发 护苗 网络安全课课件 怎么让数据库时间显示到分 电子科技大学网络安全导师哪个好 鹤壁软件开发销售公司 怎么租服务器上外网 服务器的mac地址什么意思 海口市第七届网络安全宣传 绝地求生2未来之役有几个服务器 河南易正软件开发有限公司 数据库实验7 触发器答案
0