天津中医药大学-20级医学信息工程 教师:王翌 学生:邓集亲
声明:本文章中所涉及的代码并非我个人独立完成的成果。
在撰写的过程中,我广泛地吸取了前一辈人,尤其是学长学姐们的宝贵学习经验。通过深入研究他们的学习轨迹,以及查阅和分析了众多相关的理论文献与资料,并在王老师的悉心指导下,经过反复的实验、调试与优化,最终得以总结完成本文所展现的代码。
建议各位学弟学妹们,先根据王老师的授课内容,独立思考一下应该如何完成。如果实在是有理解上的困难,不知道从何下手,再来学习和参考本文。
学长我是用Python写的,如果你使用MATLAB,同样可以参考此代码进行翻译。我在代码中加入了许多注释和测试环节,以便于理解。由于学长能力有限,代码中或许存在一些疏漏或错误,请批判性地参考。
实验七:形态学变换
作业要求:
- 参考“形态学图像处理”课的内容,对输入图像进行开、闭、腐蚀、膨胀这四种形态学变换,并显示每种变换后的结果图像。
实验图片:
word_bw.bmp,再自选其它至少20张图片(包括灰度图片和彩色图片)。
图像开、闭、膨胀、腐蚀是形态学图像处理中常用的操作,它们可以用于改变图像的形状、去除噪声、分离物体等。
图像开操作是指先进行腐蚀操作,再进行膨胀操作。其原理是先对图像进行腐蚀,去除掉小区域的噪声或毛刺,然后再进行膨胀,使得物体边缘更加清晰和平滑。这样可以使得物体轮廓更加鲜明,并且去除了不必要的细节信息。
相反,图像闭操作则是指先进行膨胀操作,再进行腐蚀操作。其原理是先对图像进行膨胀,填充物体内部的孔洞和空隙,然后再进行腐蚀,使得物体边缘更加平滑和连续。这样可以使得物体轮廓更加鲜明,并且填补了不必要的空隙。
图像膨胀操作是指将结构元素在二值化图像上向外扩张一定距离。其原理是将结构元素与待处理图像进行卷积,如果结构元素中心与待处理图像的某个像素点重合,则将结构元素中的所有像素点都赋值给该像素点。这样可以使得物体变大,边缘更加平滑和连续。
图像腐蚀操作则是指将结构元素在二值化图像上向内收缩一定距离。其原理是将结构元素与待处理图像进行卷积,如果结构元素中心与待处理图像的某个像素点重合,则判断该像素点周围是否都为白色(或黑色),如果不是,则将该像素点置为黑色(或白色)。这样可以使得物体变小,去除掉小区域的噪声或毛刺。
Windows图片文件目录:
ImageSet文件目录下储存其他待处理图片
Python代码:
#导入库
import cv2 as cv
import numpy as np
from matplotlib import pyplot
#开
def open_operation(image, kernel):
erosion_img = img_erode(image, kernel)
dilation_img = img_dilate(erosion_img, kernel)
return dilation_img
#闭
def close_operation(image, kernel):
dilation_img = img_dilate(image, kernel)
erosion_img = img_erode(dilation_img, kernel)
return erosion_img
#腐蚀
def img_erode(src, kernel, borderType=cv.BORDER_CONSTANT):
dst = np.zeros_like(src) #创建并初始化数组
kernel_h, kernel_w = kernel.shape[:2]
kernel_half_h, kernel_half_w = kernel_h // 2, kernel_w // 2
padded_src = cv.copyMakeBorder(src, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
for i in range(kernel_half_h, padded_src.shape[0] - kernel_half_h):
for j in range(kernel_half_w, padded_src.shape[1] - kernel_half_w):
roi = padded_src[i-kernel_half_h:i+kernel_half_h+1, j-kernel_half_w:j+kernel_half_w+1]
dst[i-kernel_half_h, j-kernel_half_w] = cv.minMaxLoc(roi, mask=kernel)[0] #寻找最小值
padded_src = cv.copyMakeBorder(dst, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
return dst
#膨胀
def img_dilate(src, kernel, borderType=cv.BORDER_CONSTANT):
dst = np.zeros_like(src) #创建并初始化数组
kernel_h, kernel_w = kernel.shape[:2]
kernel_half_h, kernel_half_w = kernel_h // 2, kernel_w // 2
padded_src = cv.copyMakeBorder(src, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
for i in range(kernel_half_h, padded_src.shape[0] - kernel_half_h):
for j in range(kernel_half_w, padded_src.shape[1] - kernel_half_w):
roi = padded_src[i-kernel_half_h:i+kernel_half_h+1, j-kernel_half_w:j+kernel_half_w+1]
dst[i-kernel_half_h, j-kernel_half_w] = cv.minMaxLoc(roi, mask=kernel)[1] #寻找最大值
padded_src = cv.copyMakeBorder(dst, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
return dst
#读取图像
original = cv.imread(r'D:\deng\smalljob\ImageSet\word_bw.bmp')
original_gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY) #将图像从RGB颜色空间转换到灰度颜色空间
kernel=np.ones((3,3),np.uint8) #卷积核
img_1=img_erode(original_gray,kernel)
img_2=img_dilate(original_gray,kernel)
img_3=open_operation(original_gray,kernel)
img_4=close_operation(original_gray,kernel)
#展示结果
pyplot.subplot(231),pyplot.imshow(original_gray,cmap='gray')
pyplot.title('original')
pyplot.axis('off')
pyplot.subplot(232),pyplot.imshow(img_1,cmap='gray')
pyplot.title('erode')
pyplot.axis('off')
pyplot.subplot(233),pyplot.imshow(img_2,cmap='gray')
pyplot.title('dilate')
pyplot.axis('off')
pyplot.subplot(234),pyplot.imshow(img_3,cmap='gray')
pyplot.title('open')
pyplot.axis('off')
pyplot.subplot(235),pyplot.imshow(img_4,cmap='gray')
pyplot.title('close')
pyplot.axis('off')
pyplot.show()
运行结果:(处理白色部分,通过更改img_erode和img_dilate中寻找最大和最小值的cv.minMaxLoc函数实现,cv.minMaxLoc函数用法在此不做详细解释。膨胀操作在cv.minMaxLoc函数中我们处理的是最大值,0~255,即对应“白色”部分)
处理黑色部分
以上是处理灰度图,以下是处理彩色图像
import cv2 as cv
import numpy as np
from matplotlib import pyplot
#开
def open_operation(image, kernel):
erosion_img = img_erode(image, kernel)
dilation_img = img_dilate(erosion_img, kernel)
return dilation_img
#闭
def close_operation(image, kernel):
dilation_img = img_dilate(image, kernel)
erosion_img = img_erode(dilation_img, kernel)
return erosion_img
#腐蚀
def img_erode(src, kernel, borderType=cv.BORDER_CONSTANT):
dst = np.zeros_like(src) #创建并初始化数组
kernel_h, kernel_w = kernel.shape[:2]
kernel_half_h, kernel_half_w = kernel_h // 2, kernel_w // 2
padded_src = cv.copyMakeBorder(src, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
for i in range(kernel_half_h, padded_src.shape[0] - kernel_half_h):
for j in range(kernel_half_w, padded_src.shape[1] - kernel_half_w):
roi = padded_src[i-kernel_half_h:i+kernel_half_h+1, j-kernel_half_w:j+kernel_half_w+1]
dst[i-kernel_half_h, j-kernel_half_w] = cv.minMaxLoc(roi, mask=kernel)[0] #寻找最小值
padded_src = cv.copyMakeBorder(dst, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
return dst
#膨胀
def img_dilate(src, kernel, borderType=cv.BORDER_CONSTANT):
dst = np.zeros_like(src) #创建并初始化数组
kernel_h, kernel_w = kernel.shape[:2]
kernel_half_h, kernel_half_w = kernel_h // 2, kernel_w // 2
padded_src = cv.copyMakeBorder(src, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
for i in range(kernel_half_h, padded_src.shape[0] - kernel_half_h):
for j in range(kernel_half_w, padded_src.shape[1] - kernel_half_w):
roi = padded_src[i-kernel_half_h:i+kernel_half_h+1, j-kernel_half_w:j+kernel_half_w+1]
dst[i-kernel_half_h, j-kernel_half_w] = cv.minMaxLoc(roi, mask=kernel)[1] #寻找最大值
padded_src = cv.copyMakeBorder(dst, kernel_half_h, kernel_half_h, kernel_half_w, kernel_half_w, borderType)
return dst
#读取图像
original = cv.imread(r'D:\deng\smalljob\ImageSet\dio.jpg')
original_gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY) #将图像从RGB颜色空间转换到灰度颜色空间
origin = cv.cvtColor(original, cv.COLOR_BGR2RGB)
b,g,r=cv.split(origin)#分离颜色通道
kernel=np.ones((3,3),np.uint8) #卷积核
erode_b=img_erode(b,kernel)#腐蚀通道
erode_g=img_erode(g,kernel)
erode_r=img_erode(r,kernel)
result_erode=np.dstack((erode_b,erode_g,erode_r))
result_erode=np.abs(result_erode).astype(np.float32)/255.0
dilate_b=img_dilate(b,kernel)#膨胀通道
dilate_g=img_dilate(g,kernel)
dilate_r=img_dilate(r,kernel)
result_dilate=np.dstack((dilate_b,dilate_g,dilate_r))
result_dilate=np.abs(result_dilate).astype(np.float32)/255.0
open_b=open_operation(b,kernel)#开通道
open_g=open_operation(g,kernel)
open_r=open_operation(r,kernel)
result_open=np.dstack((open_b,open_g,open_r))
result_open=np.abs(result_open).astype(np.float32)/255.0
close_b=close_operation(b,kernel)#闭通道
close_g=close_operation(g,kernel)
close_r=close_operation(r,kernel)
result_close=np.dstack((close_b,close_g,close_r))
result_close=np.abs(result_close).astype(np.float32)/255.0
#展示结果
pyplot.subplot(231),pyplot.imshow(origin)
pyplot.title('original')
pyplot.axis('off')
pyplot.subplot(232),pyplot.imshow(result_erode)
pyplot.title('erode')
pyplot.axis('off')
pyplot.subplot(233),pyplot.imshow(result_dilate)
pyplot.title('dilate')
pyplot.axis('off')
pyplot.subplot(234),pyplot.imshow(result_open)
pyplot.title('open')
pyplot.axis('off')
pyplot.subplot(235),pyplot.imshow(result_close)
pyplot.title('close')
pyplot.axis('off')
pyplot.show()
运行结果:(仔细看会发现,腐蚀操作和闭操作最后得到的图片最外边缘有道黑框,其实这个跟你处理的图像像素值有关,腐蚀操作在cv.minMaxLoc函数中我们处理的是最小值,对应黑色部分,但由于图像外框边缘每个像素点的周围8个像素点会丢失部分信息,例如最左上角第一个像素点,只有[1][1]、[1][2]、[2][1]、[2][2]有信息,缺失的部分做零处理,于是就出现了黑框)