一、分水岭算法介绍
通过pycharm安装时空门
问题: 讲一下分水岭算法的原理、实现步骤、以及应用。
回答:
分水岭算法是一种基于图像变换与分割的图像分析算法,主要用于图像分割。该算法可以解决很多图像处理领域的问题,例如医学图像分析、面部识别、数字水印等。下面将详细介绍分水岭算法的基本原理和实现步骤。
一、基本原理
分水岭算法基于一种数学概念——图像灰度级视为水面高度,图像亮度高的点相当于高水位,图像亮度低的点相当于低水位,图像分割时会将种子点视为河流的发源地,河流顺着最容易流动的方向汇聚,最后形成的汇聚点之间就是图像需要分割的部分。这个过程可以把整幅图叫做“下雨”,黑色区域就是汇水区,白色区域就是雪山区,而水位就往着低的地方流。当两个黑色的汇水区相遇的时候,它们合并成一个。二、实现步骤
分水岭算法的基本实现流程如下:
1.对图像进行预处理。首先对原始图像进行滤波和二值化,得到一个二值图像。接着,对二值图像执行开运算和闭运算操作,填补空洞,平滑过渡,消除噪声等。
2.寻找图像的种子点。根据应用需求,设置初始种子点,对于每一个种子点,都会生成一个分水岭。
3.计算距离变换图像。距离变换图像是对二值图像中每一个前景点离最近的背景点的距离进行计算,这个距离就是该点的灰度值。
4.计算梯度图像。梯度图像是对距离变换图像应用Sobel、Prewitt算子等获得的图像,反映了图像的边缘信息,即像素值在垂直和水平方向上的变化率。
5.将距离变换图像和梯度图像相乘,得到一个新的图像。
6.对于新的图像进行阈值处理,以唯一地定义图像的每一个像素所属的区域。这里的阈值可以根据图像的需求进行调整。
7.分水岭变换操作。对于阈值处理后的图像,通过分水岭变换得到最终的分割结果。
8.对分割结果进行后处理。对于分割结果进行细化、合并以及消除孤立点等后处理操作,以获得更好的效果。三、应用
1.基于分水岭算法的道路提取
这种应用将图像处理引进到公路建设领域,以实现对交通道路的智能自动化监测。通过分水岭算法对图像进行分割,然后根据道路特征进行筛选,最后提取出公路。这种方法在实践中表现出了很好的效果,能够自动识别多种复杂的路面拓扑结构。
2.基于分水岭算法的肝脏图像分割
该应用将分水岭算法应用于医学图像处理中,通过对肝脏 CT 图像的分割,实现对病变区域的定位和诊断。该方法的优点在于具有高计算效率和良好的分割准确性,可以很好地支持真实世界的医学模型。
3.基于分水岭算法的红外图像分割
该应用将分水岭算法应用于红外图像分割中,主要用于航天器和无人机红外图像的定位和监控。该方法依据热源强度不同,将图像分成不同的区域,从而实现对目标的定位和跟踪。该方法具有高鲁棒性和准确性,可以有效地处理复杂的红外图像。
总之,分水岭算法是一种强大的图像分割技术,在不同领域中得到广泛的应用。它能够帮助我们解决很多图像处理问题,提高生产效率和效果。随着计算机技术的不断发展,相信分水岭算法在未来也会得到更广泛的应用与研究。
二、练习图片(图片来源网上,仅作学术交流分享,侵联删)
三、代码
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
import cv2
def stackImages(scale,imgArray):
rows = len(imgArray)
cols = len(imgArray[0])
# & 输出一个 rows * cols 的矩阵(imgArray)
# print(rows,cols)
# & 判断imgArray[0] 是不是一个list
rowsAvailable = isinstance(imgArray[0], list)
# & imgArray[][] 是什么意思呢?
# & imgArray[0][0]就是指[0,0]的那个图片(我们把图片集分为二维矩阵,第一行、第一列的那个就是第一个图片)
# & 而shape[1]就是width,shape[0]是height,shape[2]是
width = imgArray[0][0].shape[1]
height = imgArray[0][0].shape[0]
# & 例如,我们可以展示一下是什么含义
# cv2.imshow("img", imgArray[0][1])
if rowsAvailable:
for x in range (0, rows):
for y in range(0, cols):
# & 判断图像与后面那个图像的形状是否一致,若一致则进行等比例放缩;否则,先resize为一致,后进行放缩
if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
else:
imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
# & 如果是灰度图,则变成RGB图像(为了弄成一样的图像)
if len(imgArray[x][y].shape) == 2: imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
# & 设置零矩阵
imageBlank = np.zeros((height, width, 3), np.uint8)
hor = [imageBlank]*rows
hor_con = [imageBlank]*rows
for x in range(0, rows):
hor[x] = np.hstack(imgArray[x])
ver = np.vstack(hor)
# & 如果不是一组照片,则仅仅进行放缩 or 灰度转化为RGB
else:
for x in range(0, rows):
if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
else:
imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
hor= np.hstack(imgArray)
ver = hor
return ver
def watershed_demo(img):
# 图像预处理
# 二值化前先进性灰度化、其实也可以通过其他通道,这里用灰度图就足够了
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 固定阈值二值化,将大于thresh得像素点设置为maxval,
ret,binary = cv.threshold(gray,thresh=110,maxval=255,type = cv.THRESH_BINARY)
# 形态学开操作,先腐蚀后膨胀,去掉一些小噪声
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE,(11,11))
open = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel,iterations=1)
# 对二值化图像进行膨胀、用来后面于masker相减
kernel_dilate = cv.getStructuringElement(cv.MORPH_ELLIPSE,(40,40))
dilate = cv.dilate(open,kernel,iterations=1)
# distance transform
dist = cv.distanceTransform(open,cv.DIST_L2,3) # 距离变换,可以认为中间部分距离越大、边缘越小
dist_output = cv.normalize(dist,0,1.0,cv.NORM_MINMAX)*50 # 进行归一化
ret,surface = cv.threshold(dist,dist.max()*0.6,255,cv.THRESH_BINARY) # 对归化的图片再进行二值化,对中间部分进行截断
surface_fg = np.uint8(surface) # 将图片的格式转为8位
unknown = cv.subtract(dilate,surface_fg) # 用膨胀之后的图片于距离变换后二值化图片进行减操作
ret,markers = cv.connectedComponents(surface_fg) # 连通域操作
# watershed transform
markers = markers +1 #
markers[unknown ==255] =0
markers = cv.watershed(img,markers = markers)
# 去掉边缘,因为经过分水岭操作之后,多出了一个边界
markers[0] = 1
markers[-1] = 1
markers[0:img.shape[0],0] = 1
markers[0:img.shape[0],-1] = 1
# 进行连通域操作
open[markers ==-1] = 0 # 利用分水岭出来的线条进行分割
k3 = np.ones((4,4),np.uint8) # 进行稍微的腐蚀
open = cv.erode(open,k3)
# num_labels:连通域数量、labels:大小和原图一样大,每一个连通域会及进行标记,stats:x,y,wh,s,centroid:中心
num_labels, labels, stats, centers = cv2.connectedComponentsWithStats(open, connectivity=8)
# 利用连通域进行不同轮廓画出不同颜色
output = np.zeros((img.shape[0], img.shape[1], 3), np.uint8)
for i in range(1, num_labels):
mask = labels == i
output[:, :, 0][mask] = np.random.randint(0, 255)
output[:, :, 1][mask] = np.random.randint(0, 255)
output[:, :, 2][mask] = np.random.randint(0, 255)
# 因为dist没有转换为8位,保存图片打开之后是黑色的话就需要进行格式转换。
dist_output= cv2.normalize(dist_output, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
dist= cv2.normalize(dist, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
#为了图片更加好看,对所有图片转化为3通道
gray = cv.cvtColor(gray,cv.COLOR_GRAY2BGR)
binary = cv.cvtColor(binary,cv.COLOR_GRAY2BGR)
open = cv.cvtColor(open,cv.COLOR_GRAY2BGR)
dilate = cv.cvtColor(dilate,cv.COLOR_GRAY2BGR)
dist = cv.cvtColor(dist,cv.COLOR_GRAY2BGR)
dist_output = cv.cvtColor(dist_output,cv.COLOR_GRAY2BGR)
surface = cv.cvtColor(surface,cv.COLOR_GRAY2BGR)
surface_fg = cv.cvtColor(surface_fg,cv.COLOR_GRAY2BGR)
unknown = cv.cvtColor(unknown,cv.COLOR_GRAY2BGR)
gray[markers ==-1] =[0,0,255]
binary[markers == -1] = [0, 0, 255]
open[markers == -1] = [0, 0, 255]
dilate[markers == -1] = [0, 0, 255]
dist[markers ==-1] =[0,0,255]
dist_output[markers ==-1] =[0,0,255]
# 下面语句就没有影响,因为surface的时候进行了8位的转换
surface[markers == -1] = [0,0,255]
surface_fg[markers == -1] = [255, 0, 255]
unknown[markers == -1] = [255, 0,255]
img[markers == -1] = [255, 0, 255]
imgStack = stackImages(1, ([gray,binary,open], [dilate,dist,dist_output], [surface,surface_fg,unknown]))
result = cv2.addWeighted(img, 0.8, output, 0.5, 0) # 图像权重叠加
for i in range(1, len(centers)):
cv2.drawMarker(result, (int(centers[i][0]), int(centers[i][1])), (0, 0, 255), 1, 20, 2)
cv.namedWindow("stack",0)
cv.imshow("stack",imgStack)
cv.namedWindow("out",0)
cv.imshow("out",result)
cv.imwrite("save_03.png",imgStack)
cv.imwrite("binary.png",open)
cv.imwrite("save_04.png", result)
img = cv.imread("1.png")
watershed_demo(img)
cv.namedWindow("img",0)
cv.imshow("img",img)
cv.waitKey(0)
cv.destroyAllWindows()
四、效果
五、Opencv专栏
1.理论系列:
第一章:pycharm、anaconda、opencv、pytorch、tensorflow、paddlex等环境配置大全总结【图像处理py版本】
第二章:OpenCv算法的基本介绍与应用
第三章:OpenCv图片、视频读写操作与基本应用
第四章:OpenCv阈值分割/二值化(单通道、多通道图片)总结
2.项目系列:
文章来源:https://www.toymoban.com/news/detail-777168.html
项目一:四六级改卷系统
项目二:实战篇:粘连物体分割——利用几何分割实现瓶盖分割检测
项目三:实战篇:粘连物体分割——利用几何分割实现硬币分割检测
项目四:实战篇:粘连物体分割——利用几何分割实现细胞分割检测
==》 项目五:实战篇:粘连物体分割——利用分水岭算法实现糖豆分割检测文章来源地址https://www.toymoban.com/news/detail-777168.html
到了这里,关于【实战篇:粘连物体分割——利用分水岭算法实现糖豆分割检测】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!