openCV 分水岭

本文介绍了一个使用OpenCV实现的分水岭算法实战案例,通过鼠标绘制区域选择,自动进行图像分割,展示了从图像读取到最终效果展示的完整过程。代码中详细解释了如何利用分水岭算法进行图像处理,包括轮廓检测、颜色映射和图像混合等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "srcImg"
#define WINDOW_NAME2 "dstImg"

static void on_Mouse(int event, int x, int y, int flags, void*);
Mat srcImg, dstImg, tmpImg1, tmpImg2,maskImg;
Point prevPoint(-1, -1);


int main()
{
	/*载入原图,初始化掩模和灰度图*/
	srcImg = imread("test2.jpg");
	namedWindow(WINDOW_NAME1);
	namedWindow(WINDOW_NAME2);
	imshow(WINDOW_NAME1,srcImg);
	tmpImg1 = srcImg.clone();
	cvtColor(srcImg, maskImg, COLOR_BGR2GRAY);
	cvtColor(maskImg, tmpImg2, COLOR_GRAY2BGR);
	maskImg = Scalar::all(0);
	/*设置鼠标回调函数*/
	setMouseCallback(WINDOW_NAME1, on_Mouse, 0);
	/*询问按键进行处理*/
	while (1) {
		/*获取键值*/
		int c = waitKey(0);
		//c = (char)c;
		if ((char)c == 27) break;//esc退出
		if ((char)c == '2') {
			maskImg = Scalar::all(0);
			srcImg = tmpImg1.clone();
			imshow(WINDOW_NAME1, srcImg);//
		}
		if ((char)c == '1' || (char)c == ' ') {
			/*定义一些参数进行图像处理*/
			int i, j, compCount = 0;
			vector<vector<Point>> contoursExp;
			vector<Vec4i> hierarchyExp;
			/*寻找轮廓*/
			findContours(maskImg, contoursExp,hierarchyExp,RETR_CCOMP,CHAIN_APPROX_SIMPLE);
			if (contoursExp.empty()) continue;//如果轮廓为空,继续下一次循环运算
			Mat maskImg2(maskImg.size(), CV_32S);//复制掩模
			maskImg2 = Scalar::all(0);
			/*循环绘制轮廓*/
			for (int i = 0; i >= 0; i = hierarchyExp[i][0], compCount++) {
				drawContours(maskImg2, contoursExp, i, Scalar::all(compCount + 1), -1, 8, hierarchyExp, INT_MAX);
			}
			if (compCount == 0) continue;
			/*生成随机颜色*/
			vector<Vec3b> colorTab;
			for (i = 0; i < compCount; i++) {
				uchar b = theRNG().uniform(0, 255);
				uchar g = theRNG().uniform(0, 255);
				uchar r = theRNG().uniform(0, 255);
				colorTab.push_back(Vec3b(b,g,r));
			}
			/*计算处理时间*/
			double dTime = (double)getTickCount();
			watershed(srcImg, maskImg2);
			dTime = (double)getTickCount() - dTime;
			printf("\t处理时间=%gms\n", dTime*1000. / getTickFrequency());
			/*将分水岭图像遍历存入watershedImg*/
			Mat watershedImg(maskImg2.size(), CV_8UC3);
			for (i = 0; i < maskImg2.rows; i++) {
				for (j = 0; j < maskImg2.cols; j++) {
					int index = maskImg2.at<int>(i, j);
					if (index == -1) watershedImg.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
					else if (index <= 0 || index > compCount) watershedImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);//
					else watershedImg.at<Vec3b>(i, j) = colorTab[index - 1];
				}
			}
				/*混合灰度图和分水岭效果图并显示*/
			watershedImg = watershedImg * 0.5 + tmpImg2 * 0.5;
			imshow("waterTransform", watershedImg);
		}
	}

	//while (waitKey(1) != 'q') {};
	return 0;
}

void on_Mouse(int event, int x, int y, int flags, void*) {
	/*处理鼠标不在窗口中的情况*/
	if (x<0 || x>srcImg.cols || y<0 || y>srcImg.rows) return;
	/*处理鼠标左键相关消息*/
	if (event == EVENT_LBUTTONUP || !(flags&EVENT_FLAG_LBUTTON)) prevPoint = Point(-1, -1);
	else if (event == EVENT_LBUTTONDOWN) prevPoint = Point(x, y);
	/*鼠标左键按下并移动,绘制白色线条*/
	else if (event == EVENT_MOUSEMOVE && (flags&EVENT_FLAG_LBUTTON)) {
		Point pt(x, y);
		if (prevPoint.x < 0) prevPoint = pt;//?
		line(maskImg, prevPoint, pt, Scalar::all(255), 5, 8, 0);
		line(srcImg, prevPoint, pt, Scalar::all(255), 5, 8, 0);
		prevPoint = pt;
		imshow(WINDOW_NAME2, srcImg);
	}
}
### OpenCV分水岭算法的实现与应用 #### 1. 分水岭算法简介 分水岭算法是一种经典的图像分割方法,其核心思想来源于地形地貌中的水流汇聚现象。该算法通过模拟水流从高处流向低洼区域的过程来定义不同的分割区域[^1]。 #### 2. OpenCV分水岭算法实现原理 在 OpenCV 中,分水岭算法主要依赖于 `cv2.watershed()` 函数完成最终的分割任务。其实现过程可以分为以下几个部分: - **图像预处理** 首先对输入图像进行灰度化和降噪处理,通常会使用高斯模糊或其他滤波器去除噪声影响[^3]。 - **二值化操作** 使用阈值函数 `cv2.threshold()` 将图像转化为黑白两色,从而区分前景和背景区域。 - **形态学操作** 利用形态学膨胀 (`cv2.dilate`) 和腐蚀 (`cv2.erode`) 对二值化的图像进一步优化,减少不必要的细节干扰并增强目标物体轮廓[^4]。 - **距离变换** 应用 `cv2.distanceTransform()` 计算每个像素到最近零值点的距离,并生成热力图形式的结果作为后续标记的基础[^2]。 - **标记不同区域** 结合连通域分析工具如 `cv2.connectedComponentsWithStats()` 或手动指定种子点位置创建初始标签矩阵,其中包含三个类别:已知背景 (-1),待定未知区 (0) 及多个独立对象编号 (>0)。 - **执行分水岭运算** 输入上述准备好的标注图片以及原始彩色版本至 `cv2.watershed()` 方法得到精确划分后的边界线信息。 #### 3. Python 示例代码 以下是利用 OpenCV 进行简单分水岭图像分割的一个完整例子: ```python import numpy as np import cv2 from matplotlib import pyplot as plt # 加载原图 img = cv2.imread('coins.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 形态学梯度(边缘保留) grad = cv2.morphologyEx(gray, cv2.MORPH_GRADIENT, kernel=np.ones((3, 3), dtype='uint8')) # Otsu's thresholding after Gaussian filtering blur = cv2.GaussianBlur(grad, (5, 5), 0) _, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 创建结构元素用于开闭运算 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) # 开运算去掉一些小斑点 opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) # 距离变换获得更清晰的目标物内部区域 dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0) # 找到不确定区域 sure_bg = cv2.dilate(opening, kernel, iterations=3) unknown = cv2.subtract(sure_bg.astype(np.uint8), sure_fg.astype(np.uint8)) # 标记 ret, markers = cv2.connectedComponents(sure_fg.astype(np.int32)) markers += 1 markers[unknown == 255] = 0 # 分水岭算法 markers = cv2.watershed(img, markers.copy()) img[markers == -1] = [255, 0, 0] plt.figure(figsize=(10, 6)) titles = ['Original Image', 'Thresholded Image', 'Distance Transform', 'Foreground Markers', 'Background Subtraction', 'Final Result'] images = [img[:, :, ::-1], thresh, dist_transform, markers, unknown, img[:, :, ::-1]] for i in range(len(titles)): plt.subplot(2, 3, i+1), plt.imshow(images[i]) plt.title(titles[i]), plt.xticks([]), plt.yticks([]) plt.show() ``` 此脚本展示了如何加载一张硬币堆叠的照片并通过一系列步骤成功分离各个单独个体. ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值