OpenCV图像分割与分水岭算法(C++)

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

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

CSDN图标

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

#include<iostream>
#include<opencv2\opencv.hpp>

using 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;
}

阈值后的图像如下所示:

CSDN图标

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

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

CSDN图标

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

Mat opening; Mat sure_bg;
Mat sure_fg; Mat unknow;
Mat dist_transform;
double maxValue;
// noise removal
Mat kernel = Mat::ones(3, 3, CV_8U);
morphologyEx(thresh, opening, MORPH_OPEN, kernel);

// sure background area
dilate(opening, sure_bg, kernel, Point(-1, -1), 3);

// Finding sure foreground area
distanceTransform(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 region
sure_fg.convertTo(sure_fg, CV_8U);
subtract(sure_bg, sure_fg, unknow);

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

CSDN图标
CSDN图标

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

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

// Marker labelling
Mat markers;
connectedComponents(sure_fg, markers);

// Add one to all labels so that sure background is not 0, but 1
markers = markers + 1;

// Now, mark the region of unknown with zero
markers.setTo(0, unknow);

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

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

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

CSDN图标
CSDN图标
  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: OpenCV是一个非常强大的图像处理库,它提供了很多图像分割算法。以下是一个简单的C++示例,使用OpenCV中的GrabCut算法对一张图片进行分割: ```c++ #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 读取图片 Mat image = imread("test.jpg"); // 定义矩形,选择初始前景区域 Rect rect(50, 50, 200, 150); // 定义输出掩码,初始化为0 Mat mask(image.size(), CV_8UC1, Scalar(0)); // 定义背景模型和前景模型 Mat bgModel, fgModel; // 使用GrabCut算法进行分割 grabCut(image, mask, rect, bgModel, fgModel, 5, GC_INIT_WITH_RECT); // 根据掩码提取前景区域 Mat foreground(image.size(), CV_8UC3, Scalar(255, 255, 255)); image.copyTo(foreground, mask); // 显示结果 imshow("Original Image", image); imshow("Foreground", foreground); waitKey(); return 0; } ``` 在这个示例中,我们首先读取了一张图片,然后选择了一个矩形作为初始前景区域。接着,我们定义了一个输出掩码,它的大小与图片一样,并且初始化为0。然后,我们定义了背景模型和前景模型,并使用GrabCut算法进行分割。最后,我们根据掩码提取前景区域,并将结果显示出来。 这只是一个非常简单的示例,OpenCV中还有很多其他的图像分割算法,包括基于颜色、纹理、形状等特征的算法。您可以根据自己的需求选择合适的算法。 ### 回答2: OpenCV是一个开源的计算机视觉库,它提供了许多用于图像处理和分析的功能。图像分割是其中一个重要的应用之一。 图像分割是指将一幅图像划分成多个具有特定语义的区域的过程。它可以帮助我们理解图像中的物体边界和结构,从而更好地提取图像中的特征和信息。 在OpenCV中,图像分割的主要方法有基于阈值的分割、区域生长、分水岭算法等。 基于阈值的分割是最简单和常用的方法之一。通过选定一个阈值,将图像像素分为两个类别,比如黑白图像的二值化处理。可以使用OpenCV中的cv2.threshold函数来实现。 区域生长算法则根据图像中的某个种子点,通过像素相似度将相邻的像素逐步归为同一类别。可以使用OpenCV中的cv2.floodFill函数来实现。 分水岭算法是一种基于图的分割方法,它通过将图像看作一个拓扑图来实现。可以通过计算图像的梯度和标记种子点,利用OpenCV中的cv2.watershed函数实现分割。 除了这些基本的图像分割方法,OpenCV还提供了一些高级的分割方法,比如GrabCut算法和MeanShift算法。这些算法可以根据图像的颜色和纹理等特征进行分割,适用于复杂的图像场景。 综上所述,OpenCV提供了丰富的图像分割算法和函数,可以方便地实现图像分割和处理。无论是简单的二值化还是复杂的图像分割OpenCV都可以为我们提供强大的工具和支持。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值