前言
在二值形态学的学习过程中,已经学习了二值形态学算法的基本理论知识,本篇文章将会利用Python实现有关二值形态学的基本算法,包括膨胀腐蚀运算,图像的开闭运算等。为了验证形态学的算法的正确性,本篇文章将会利用OpenCV
算法库中的二值形态学算法和Python实现的二值形态学算法的结果进行对比学习。
在实现二值形态学之前,首先需要对原始图像进行预处理,这些预处理部分包括图像灰度和图像二值化过程,关于图像的二值化实现,本次实现采用的是OTSU算法,在图像算法处理中,已经对OTSU算法的实现,有过介绍,本篇文章,将会直接使用OpenCV
算法库提供的OTSU阈值方法实现灰度图像的二值化。
对上述所示的原始图像,实现灰度化与二值化的代码如下所示:
import cv2
import numpy as np
img = cv2.imread("./test.jpg")
#opencv中的图像灰度化方法
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 灰度化图像显示
cv2.imshow("test image",gray) #显示图片
cv2.waitKey(0)
//OTSU算法实现图像二值化,dst为二值化的图像,而retval表示利用OTSU算法得到的阈值
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
cv2.imshow("binary image",dst)
cv2.waitKey(0)
灰度化和二值化的图像效果如下所示:
图像膨胀的实现
图像膨胀的实现,可以看作利用结构化元素(类似于卷积运算中的kernel),对图像进行逐像素的或操作。膨胀算法的实现过程中,通过定义一个3×3的结构化元素,逐像素地对原始图像实现或运算,可以看成是在原始图像被结构化元素所覆盖的3×3区域内,寻找该区域内的最大像素值,并用3×3区域内所找到的最大的像素值代替二值化图像的原始像素值,其实现代码如下所示:
def dilated_img(src):
m,n=src.shape
max_=0
result_img = np.zeros(src.shape)
for r in range(1,m):
for c in range(1,n):
result_img[r,c] = np.max(src[r-1:r+2,c-1:c+2])
return result_img
作为对比,将自定义的膨胀算法与opencv提供的膨胀算法进行对比,其实现效果如下图所示:
# 自定义的膨胀算法
result = dilated_img(dst)
cv2.imshow("dilated img",result)
cv2.waitKey(0)
#opencv提供的图像膨胀方法
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # 3×3的矩形结构化元素
dilation = cv2.dilate(dst, kernel)
cv2.imshow("dilation img",dilation)
cv2.waitKey(0)
图像腐蚀的实现
二值图像的腐蚀算法实现和膨胀算法的实现基本一样,只不过腐蚀算法中,结构化元素所覆盖的二值化图像中的像素区域执行的与操作,在代码实现中,这一与操作,可以被“翻译”用被结构化元素所覆盖的原始图像中3×3的像素区域中的最小值替换原始图像中的原来的像素点。仍旧将结构化元素定义为3×3的矩形,则图像实现腐蚀的代码和腐蚀后的效果如下图所示:
def erode_img(src):
m,n=src.shape
max_=0
result_img = np.zeros(src.shape)
for r in range(1,m):
for c in range(1,n):
result_img[r,c] = np.min(src[r-1:r+2,c-1:c+2])
return result_img
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # 3×3的矩形结构化元素
erosion = cv2.erode(dst, kernel)
cv2.imshow("erosion img",erosion)
cv2.waitKey(0)
图像开运算和闭运算的实现
- 图像开运算的实现
图像的开运算,即使对原始的二值图像先进行一次腐蚀运算,在对腐蚀运算得到的二值图像进行一次膨胀运算的过程,其实现代码和效果如下所示:
#手动实现的开运算方式
def bin_open(src):
e_result = erode_img(src)
result = dilated_img(e_result)
return result
result = bin_open(dst)
cv2.imshow("open img",result)
cv2.waitKey(0)
# opencv实现的开运算
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
result = cv2.morphologyEx(dst, cv2.MORPH_OPEN, kernel)
- 二值闭运算的实现
二值闭运算的实现与开运算的实现非常相似,只不过,二值闭运算的实现方式是先对原始的二值图像进行一次膨胀运算,再对膨胀运算后的图像进行一次腐蚀运算,其实现代码和效果如下图所示:
# 自定义的闭运算
def bin_close(src):
e_result = dilated_img(src)
result = erode_img(e_result)
return result
result = bin_close(dst)
cv2.imshow("close img",result)
cv2.waitKey(0)
# opencv实现的闭运算
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
result = cv2.morphologyEx(dst, cv2.MORPH_CLOSE, kernel)
Distance Transform
对一幅图像执行Distance Transform转换可以认为是对一幅原始的二值图像不断进行腐蚀的过程,每次将腐蚀掉的像素置换为腐蚀的次数,从distance transform的过程可以看出,这是一个将二值图像像素为1的像素点进行逐渐替换的过程,执行distance transform过程中,图像的像素值逐渐增大。其详细过程已二值形态学算法中有过详细介绍,
在具体实现代码如下所示:
def distanceTransform(src):
src_r= src.copy()
c=2
src_r = src_r/255
while(src.any()==1): # 知道整个图片不存在为1的像素
src = cv2.erode(src,kernel) # 用3*3 的SE进行腐蚀运算
[x,y] = np.where(src_r==1)
for i in range(len(x)):
if(src[x[i],y[i]]==0):
src_r[x[i],y[i]]=c
c = c+1
src_r = (src_r/src_r.max())*255
return src_r
执行此代码之后,图像的效果如下所示:
图像Skeleton实现
为了得到一幅二值图像的Skeleton,整个图像的执行过程可解释为对原始二值图像先进行腐蚀,再对腐蚀后的图像实现开运算,再将腐蚀得到的图像和开运算得到的图像进行相减,最后将相减得到一系列图像进行累加的过程,其公式如下所示:
- 根据所选SE的大小,得到一系列Skeleton的子集
其中,表示一系列尺度不同的SE - 对得到的一系列SKeleton子集进行求并集:
def get_skeleton(src,kernel=kernel):
m,n = src.shape
img_result = np.zeros((m,n))
c = 0
while(src.any()==1):
# 先对原始的二值图像进行腐蚀操作
# 完成第一次腐蚀操作
src = cv2.erode(src,kernel)
temp_img = src.copy()
#再对图像进行开运算
temp_img = cv2.morphologyEx(temp_img,cv2.MORPH_OPEN,kernel)
# 腐蚀后的图像与开运算得到的图像相减
img = src - temp_img
img_result = img_result+img
c = c+1
return img_result,c
本次代码实现中,用的是3×3的结构化元素,其实现效果如下所示: