实验前言与目的
这些算法都属于【基于形态学的图像分割】这一范畴,我们最终做的一切,都是为了图像分割(比如分成黑白两块,相对提取物体主题,为物体描轮廓…)
编写程序完成以下算法,并进行比较,得出结论。
- 区域生长算法
- 区域分裂合并算法
- 分水岭分割算法
1、区域生长算法
参考:https://blog.csdn.net/weixin_43487953/article/details/97395528
注意
- 区域生长是一种图像分割方法。
- 区域生长从某个像素出发,按照一定的准则,逐步加入邻近像素,当满足一定的条件时,区域生长终止,进而实现目标的提取。
- 区域生长的好坏决定于:1.初始点(种子点)的选取。2.生长准则。3.终止条件。
- 个人感悟:这个算法平均感觉都挺慢的,我运行一个狗子的图,即使换了不同的种子,平均也在10S~20S之间。
PPT思路
本程序思路:
- 先生成同样大小空白矩阵
- 设置一个种子点,通过计算判断周围是否有符合条件新种子,编写在空白矩阵中
- 通过循环可以获得含有种子标志的新矩阵,将矩阵与原图相乘可以得到利用区域生长分割出的图像。
备注:由于代码不长,所以思路主要用备注写代码里了、
输入图像经过区域生成算法之后的效果:
代码
################################################
#################区域生成算法###################
################################################
def 区域生成法(img):
img_array = np.array(img)#图片转为数组方便操作
[m,n]=img_array.shape#返回图片的长和宽
a = np.zeros((m,n)) #建立等大小空矩阵
a[70,70]=1 #设立种子点
k = 40 #设立生长阈值
isMyArea=1
#开始循环遍历周围像素,种子长大。
while isMyArea==1:
isMyArea=0
lim = (np.cumsum(img_array*a)[-1])/(np.cumsum(a)[-1])
for i in range(2,m):
for j in range(2,n):
if a[i,j]==1:
for x in range(-1,2):
for y in range(-1,2):
if a[i+x,j+y]==0:
if (abs(img_array[i+x,j+y]-lim)<=k) :
isMyArea = 1
a[i+x,j+y]=1
print("我是区域,我正在生长...")
data = img_array*a #矩阵相乘获取生长图像的矩阵
new_img = Image.fromarray(data) #data矩阵转化为二维图片
return new_img
2、区域分裂合并算法
参考:
https://www.jianshu.com/p/fa573431ef3d
- 个人感觉,这个分割算法有点【微积分】那种感觉了。
- 不用像区域生长一样还要选个“种子点”,算法比较“稳”?
算法的思想总的来说,就是:
- 先把图像分成4块
- 若这其中的一块符合分裂条件,那么这一块又分裂成4块
- 分裂到一定数量时,以每块为中心,检查相邻的各块,满足一定条件,就合并。
- 如此循环往复进行分裂和合并的操作。
- 最后合并小区,即把一些小块图像的合并到旁边的大块里。
注意
- 本次切割图像不使用专门的库,而是直接通过控制img[x,y,weight,height]来最原图片的一个个小区域直接操作(具体操作是二值化)。
- 从最终成像来看,区域分裂与合并算法的确具有一定的连续性,不会出现黑图中夹杂着一丝白丝儿的那种噪声。
效果图
代码
import numpy as np
import cv2
import matplotlib.pyplot as plt # plt 用于显示图片
#判断方框是否需要再次拆分为四个
def judge(w0, h0, w, h):
a = img[h0: h0 + h, w0: w0 + w]
ave = np.mean(a)
std = np.std(a, ddof=1)
count = 0
total = 0
for i in range(w0, w0 + w):
for j in range(h0, h0 + h):
#注意!我输入的图片数灰度图,所以直接用的img[j,i],RGB图像的话每个img像素是一个三维向量,不能直接与avg进行比较大小。
if abs(img[j, i] - ave) < 1 * std:
count += 1
total += 1
if (count / total) < 0.95:#合适的点还是比较少,接着拆
return True
else:
return False
##将图像将根据阈值二值化处理,在此默认125
def draw(w0, h0, w, h):
for i in range(w0, w0 + w):
for j in range(h0, h0 + h):
if img[j, i] > 125:
img[j, i] = 255
else:
img[j, i] = 0
def function(w0, h0, w, h):
if judge(w0, h0, w, h) and (min(w, h) > 5):
function(w0, h0, int(w / 2), int(h / 2))
function(w0 + int(w / 2), h0, int(w / 2), int(h / 2))
function(w0, h0 + int(h / 2), int(w / 2), int(h / 2))
function(w0 + int(w / 2), h0 + int(h / 2), int(w / 2), int(h / 2))
else:
draw(w0, h0, w, h)
##############################################
######################main_##################
###############################################
img = cv2.imread('pic/leave.jpg', 0)
img_input = cv2.imread('pic/leave.jpg', 0)#备份
height, width = img.shape
function(0, 0, width, height)
cv2.imshow('input',img_input)
cv2.imshow('output',img)
cv2.waitKey()
cv2.destroyAllWindows()
3、分水岭分割算法
参考opencv4 python 中文文档
思路:
- 分水岭算法实际上是根据图像的灰度值和形态学知识(膨胀啊,腐蚀啊),用一种标记来确定对象区域。
- 用另一种标记确定非对象的区域。
- 最后用 0 标记我们不确定的区域。
- 然后应用分水岭算法。然后我们的标记将使用我们给出的标签进行更新,对象的边界值将为 -1 。
实现:由于这部分代码有点长,所以将分步展开讲
- 先做图像的预处理,包括二值化和去噪(不去噪会导致图像的过度分割)
2. 在图像划分出对象区域和非对象区域,并且通过二者相减,获得不确定区域:
3. 为区域做标记:
4. 执行分水岭算法,将分水岭画成明显的线打到图上:
效果图
代码
################################################
##################分水岭########################
def 分水岭算法(img):
#转化成灰度图,方便处理
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#二值化
ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
#去除噪音(要不然最终成像会导致过度分割)
kernel=np.ones((3,3),np.uint8)
opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)
#确定非对象区域
sure_bg=cv2.dilate(opening,kernel,iterations=3)#进行膨胀操作
#确定对象区域
dist_transform=cv2.distanceTransform(opening,1,5)
ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
#寻找未知的区域
sure_fg=np.uint8(sure_fg)
unknown=cv2.subtract(sure_bg,sure_fg)#非对象区域减去对象区域就是不确定区域
# 为对象区域类别标记
ret, markers = cv2.connectedComponents(sure_fg)
# 为所有的标记加1,保证非对象是0而不是1
markers = markers+1
# 现在让所有的未知区域为0
markers[unknown==255] = 0
#执行分水岭算法
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
####################################################
########################打印########################
#解决中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.subplot(231), plt.imshow(gray, 'gray'), plt.title('输入图片'),plt.axis('off')
plt.subplot(232), plt.imshow(opening,'gray'), plt.title('二值化去噪之后'),plt.axis('off')
plt.subplot(233), plt.imshow(sure_bg,'gray'), plt.title('确定非对象区域'),plt.axis('off')
plt.subplot(234), plt.imshow(dist_transform,'gray'), plt.title('确定对象区域'),plt.axis('off')
plt.subplot(235), plt.imshow(unknown,'gray'), plt.title('未知区域'),plt.axis('off')
plt.subplot(236), plt.imshow(img,'gray'), plt.title('分水岭算法'),plt.axis('off')
plt.show()
项目代码
import cv2
from PIL import Image
import matplotlib.pyplot as plt # plt 用于显示图片
import numpy as np
################################################
#################区域生成算法###################
def 区域生成法(img):
img_array = np.array(img)#图片转为数组方便操作
[m,n]=img_array.shape#返回图片的长和宽
a = np.zeros((m,n)) #建立等大小空矩阵
a[100,100]=1 #设立种子点
k = 30 #设立生长阈值,灰度小于此值,则纳入区域。
isMyArea=1
while isMyArea==1:
isMyArea=0
lim = (np.cumsum(img_array*a)[-1])/(np.cumsum(a)[-1])
for i in range(2,m):
for j in range(2,n):
if a[i,j]==1:
for x in range(-1,2):
for y in range(-1,2):
if a[i+x,j+y]==0:
if (abs(img_array[i+x,j+y]-lim)<=k) :
isMyArea = 1
a[i+x,j+y]=1
print("我是区域,我正在生长...")
data = img_array*a #矩阵相乘获取生长图像的矩阵
new_img = Image.fromarray(data) #data矩阵转化为二维图片
return new_img
################################################
################区域分裂与合并###################
##################################################
import numpy as np
import cv2
import matplotlib.pyplot as plt # plt 用于显示图片
#判断方框是否需要再次拆分为四个
def judge(w0, h0, w, h):
a = img[h0: h0 + h, w0: w0 + w]
ave = np.mean(a)
std = np.std(a, ddof=1)
count = 0
total = 0
for i in range(w0, w0 + w):
for j in range(h0, h0 + h):
if abs(img[j, i] - ave) < 1 * std:
count += 1
total += 1
if (count / total) < 0.95:
return True
else:
return False
##将图像将根据阈值二值化处理,在此默认125
def draw(w0, h0, w, h):
for i in range(w0, w0 + w):
for j in range(h0, h0 + h):
if img[j, i] > 125:
img[j, i] = 255
else:
img[j, i] = 0
def function(w0, h0, w, h):
if judge(w0, h0, w, h) and (min(w, h) > 5):
function(w0, h0, int(w / 2), int(h / 2))
function(w0 + int(w / 2), h0, int(w / 2), int(h / 2))
function(w0, h0 + int(h / 2), int(w / 2), int(h / 2))
function(w0 + int(w / 2), h0 + int(h / 2), int(w / 2), int(h / 2))
else:
draw(w0, h0, w, h)
################################################
##################分水岭########################
def 分水岭算法(img):
#转化成灰度图,方便处理
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#二值化
ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
#去除噪音(要不然最终成像会导致过度分割)
kernel=np.ones((3,3),np.uint8)
opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)
#确定非对象区域
sure_bg=cv2.dilate(opening,kernel,iterations=3)#进行膨胀操作
#确定对象区域
dist_transform=cv2.distanceTransform(opening,1,5)
ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
#寻找未知的区域
sure_fg=np.uint8(sure_fg)
unknown=cv2.subtract(sure_bg,sure_fg)#非对象区域减去对象区域就是不确定区域
# 为对象区域类别标记
ret, markers = cv2.connectedComponents(sure_fg)
# 为所有的标记加1,保证非对象是0而不是1
markers = markers+1
# 现在让所有的未知区域为0
markers[unknown==255] = 0
#执行分水岭算法
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
####################################################
########################打印########################
#解决中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.subplot(231), plt.imshow(gray, 'gray'), plt.title('输入图片'),plt.axis('off')
plt.subplot(232), plt.imshow(opening,'gray'), plt.title('二值化去噪之后'),plt.axis('off')
plt.subplot(233), plt.imshow(sure_bg,'gray'), plt.title('确定非对象区域'),plt.axis('off')
plt.subplot(234), plt.imshow(dist_transform,'gray'), plt.title('确定对象区域'),plt.axis('off')
plt.subplot(235), plt.imshow(unknown,'gray'), plt.title('未知区域'),plt.axis('off')
plt.subplot(236), plt.imshow(img,'gray'), plt.title('分水岭算法'),plt.axis('off')
plt.show()
###################################################
####################主函数#########################
import cv2
from PIL import Image
import matplotlib.pyplot as plt # plt 用于显示图片
import numpy as np
################################################
#################区域生成算法###################
def 区域生成法(img):
img_array = np.array(img)#图片转为数组方便操作
[m,n]=img_array.shape#返回图片的长和宽
a = np.zeros((m,n)) #建立等大小空矩阵
a[100,100]=1 #设立种子点
k = 30 #设立生长阈值,灰度小于此值,则纳入区域。
isMyArea=1
while isMyArea==1:
isMyArea=0
lim = (np.cumsum(img_array*a)[-1])/(np.cumsum(a)[-1])
for i in range(2,m):
for j in range(2,n):
if a[i,j]==1:
for x in range(-1,2):
for y in range(-1,2):
if a[i+x,j+y]==0:
if (abs(img_array[i+x,j+y]-lim)<=k) :
isMyArea = 1
a[i+x,j+y]=1
print("我是区域,我正在生长...")
data = img_array*a #矩阵相乘获取生长图像的矩阵
new_img = Image.fromarray(data) #data矩阵转化为二维图片
return new_img
################################################
################区域分裂与合并###################
##################################################
import numpy as np
import cv2
import matplotlib.pyplot as plt # plt 用于显示图片
#判断方框是否需要再次拆分为四个
def judge(w0, h0, w, h):
a = img[h0: h0 + h, w0: w0 + w]
ave = np.mean(a)
std = np.std(a, ddof=1)
count = 0
total = 0
for i in range(w0, w0 + w):
for j in range(h0, h0 + h):
if abs(img[j, i] - ave) < 1 * std:
count += 1
total += 1
if (count / total) < 0.95:
return True
else:
return False
##将图像将根据阈值二值化处理,在此默认125
def draw(w0, h0, w, h):
for i in range(w0, w0 + w):
for j in range(h0, h0 + h):
if img[j, i] > 125:
img[j, i] = 255
else:
img[j, i] = 0
def function(w0, h0, w, h):
if judge(w0, h0, w, h) and (min(w, h) > 5):
function(w0, h0, int(w / 2), int(h / 2))
function(w0 + int(w / 2), h0, int(w / 2), int(h / 2))
function(w0, h0 + int(h / 2), int(w / 2), int(h / 2))
function(w0 + int(w / 2), h0 + int(h / 2), int(w / 2), int(h / 2))
else:
draw(w0, h0, w, h)
################################################
##################分水岭########################
def 分水岭算法(img):
#转化成灰度图,方便处理
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#二值化
ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
#去除噪音(要不然最终成像会导致过度分割)
kernel=np.ones((3,3),np.uint8)
opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)
#确定非对象区域
sure_bg=cv2.dilate(opening,kernel,iterations=3)#进行膨胀操作
#确定对象区域
dist_transform=cv2.distanceTransform(opening,1,5)
ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
#寻找未知的区域
sure_fg=np.uint8(sure_fg)
unknown=cv2.subtract(sure_bg,sure_fg)#非对象区域减去对象区域就是不确定区域
# 为对象区域类别标记
ret, markers = cv2.connectedComponents(sure_fg)
# 为所有的标记加1,保证非对象是0而不是1
markers = markers+1
# 现在让所有的未知区域为0
markers[unknown==255] = 0
#执行分水岭算法
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
####################################################
########################打印########################
#解决中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.subplot(231), plt.imshow(gray, 'gray'), plt.title('输入图片'),plt.axis('off')
plt.subplot(232), plt.imshow(opening,'gray'), plt.title('二值化去噪之后'),plt.axis('off')
plt.subplot(233), plt.imshow(sure_bg,'gray'), plt.title('确定非对象区域'),plt.axis('off')
plt.subplot(234), plt.imshow(dist_transform,'gray'), plt.title('确定对象区域'),plt.axis('off')
plt.subplot(235), plt.imshow(unknown,'gray'), plt.title('未知区域'),plt.axis('off')
plt.subplot(236), plt.imshow(img,'gray'), plt.title('分水岭算法'),plt.axis('off')
plt.show()
###################################################
####################主函数#########################
#区域生成法
#img = cv2.imread("pic/dog1.jpg",0)#读图
#result1 = 区域生成法(img)
#img2 = cv2.imread("pic/dog1.jpg",0)#读图
#区域分裂合并
img = cv2.imread('pic/leave.jpg', 0)
img_input = cv2.imread('pic/leave.jpg', 0)#备份
height, width = img.shape
function(0, 0, width, height)
cv2.imshow('input',img_input)
cv2.imshow('output',img)
cv2.waitKey()
cv2.destroyAllWindows()
#分水岭算法
#img3 = cv2.imread("pic/coin.jpg")#读图
#result3 = 分水岭算法(img3)
####################################################
########################打印########################
#解决中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
#plt.subplot(221), plt.imshow(img, 'gray'), plt.title('输入图片'),plt.axis('off')
#plt.subplot(222), plt.imshow(pic1,'gray'), plt.title('2'),plt.axis('off')
#plt.subplot(222), plt.imshow(pic2,'gray'), plt.title('3'),plt.axis('off')
#plt.subplot(222), plt.imshow(pic3,'gray'), plt.title('4'),plt.axis('off')
#plt.show()
致谢
最后,感谢舍友再区域分裂与合并算法中的智力支持,合影留念。
转载:https://blog.csdn.net/qq_30154571/article/details/109557559
查看评论