【Python图像处理】13 形态学图像处理:腐蚀膨胀与开闭运算
摘要本文深入讲解形态学图像处理的原理与实现方法详细介绍腐蚀、膨胀、开运算、闭运算、形态学梯度、顶帽变换、黑帽变换等核心操作。文章通过大量综合性代码示例演示各种形态学操作的应用场景并介绍如何使用GPT-5.4辅助编写形态学处理代码。由于国内无法访问OpenAI官网因此使用国内镜像站可以注册使用GPT-5.4最新模型。注册入口AIGCBAR镜像站。如果涉及到调用API则使用API独立站。请广大读者遵守法律法规切勿翻墙访问境外网站使用国内合法镜像站即可满足学习需求。13.1 形态学基础13.1.1 数学形态学概述数学形态学是一种基于集合论的图像分析方法通过结构元素与图像的相互作用来提取图像的形态特征。形态学操作最初用于二值图像处理后来扩展到灰度图像。基本思想使用结构元素SE探测图像根据SE与图像局部区域的匹配程度决定输出。13.1.2 结构元素类型OpenCV常量特点矩形MORPH_RECT通用处理椭圆MORPH_ELLIPSE圆形目标十字形MORPH_CROSS细长目标13.2 基本形态学操作13.2.1 腐蚀与膨胀腐蚀(Erosion): 使目标缩小消除小的孤立区域E X ⊖ S { x ∣ S x ⊆ X } E X \ominus S \{x | S_x \subseteq X\}EX⊖S{x∣Sx⊆X}膨胀(Dilation): 使目标扩大填补小的空洞D X ⊕ S { x ∣ S x ∩ X ≠ ∅ } D X \oplus S \{x | S_x \cap X \neq \emptyset\}DX⊕S{x∣Sx∩X∅}13.2.2 开运算与闭运算开运算: 先腐蚀后膨胀消除小物体、平滑边界O P E N ( X ) ( X ⊖ S ) ⊕ S OPEN(X) (X \ominus S) \oplus SOPEN(X)(X⊖S)⊕S闭运算: 先膨胀后腐蚀填补小空洞、连接邻近物体C L O S E ( X ) ( X ⊕ S ) ⊖ S CLOSE(X) (X \oplus S) \ominus SCLOSE(X)(X⊕S)⊖S13.2.3 其他操作形态学梯度:G R A D D − E GRAD D - EGRADD−E顶帽变换:T O P X − O P E N ( X ) TOP X - OPEN(X)TOPX−OPEN(X)提取小物体黑帽变换:B L A C K C L O S E ( X ) − X BLACK CLOSE(X) - XBLACKCLOSE(X)−X提取小空洞13.3 形态学应用13.3.1 边缘提取利用腐蚀和膨胀的差异提取边界。13.3.2 骨架提取通过迭代腐蚀保留中心骨架。13.4 综合形态学系统实现 形态学图像处理详解 演示所有形态学操作 兼容Python 3.13 importcv2importnumpyasnpfromtypingimportTuple,Optional,Dictfromnumpy.typingimportNDArrayfrommatplotlibimportpyplotaspltimportmatplotlib matplotlib.rcParams[font.sans-serif][SimHei,Microsoft YaHei]matplotlib.rcParams[axes.unicode_minus]FalseclassMorphologyProcessor:综合形态学处理类def__init__(self,image:NDArray):iflen(image.shape)3:self.graycv2.cvtColor(image,cv2.COLOR_BGR2GRAY)else:self.grayimage.astype(np.uint8)self.height,self.widthself.gray.shape self.binaryNonedefbinarize(self,method:strotsu,threshold:int127):图像二值化ifmethodotsu:_,self.binarycv2.threshold(self.gray,0,255,cv2.THRESH_BINARYcv2.THRESH_OTSU)elifmethodfixed:_,self.binarycv2.threshold(self.gray,threshold,255,cv2.THRESH_BINARY)elifmethodadaptive:self.binarycv2.adaptiveThreshold(self.gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)returnself.binarydefcreate_kernel(self,shape:strrect,ksize:Tuple[int,int](3,3)):创建结构元素ifshaperect:returncv2.getStructuringElement(cv2.MORPH_RECT,ksize)elifshapeellipse:returncv2.getStructuringElement(cv2.MORPH_ELLIPSE,ksize)elifshapecross:returncv2.getStructuringElement(cv2.MORPH_CROSS,ksize)deferode(self,kernelNone,iterations:int1)-NDArray:腐蚀: X ⊖ SifkernelisNone:kernelself.create_kernel(rect,(5,5))returncv2.erode(self.binary,kernel,iterationsiterations)defdilate(self,kernelNone,iterations:int1)-NDArray:膨胀: X ⊕ SifkernelisNone:kernelself.create_kernel(rect,(5,5))returncv2.dilate(self.binary,kernel,iterationsiterations)defopen(self,kernelNone,iterations:int1)-NDArray:开运算: (X ⊖ S) ⊕ SifkernelisNone:kernelself.create_kernel(rect,(5,5))returncv2.morphologyEx(self.binary,cv2.MORPH_OPEN,kernel,iterationsiterations)defclose(self,kernelNone,iterations:int1)-NDArray:闭运算: (X ⊕ S) ⊖ SifkernelisNone:kernelself.create_kernel(rect,(5,5))returncv2.morphologyEx(self.binary,cv2.MORPH_CLOSE,kernel,iterationsiterations)defgradient(self,kernelNone)-NDArray:形态学梯度: D - EifkernelisNone:kernelself.create_kernel(rect,(5,5))returncv2.morphologyEx(self.binary,cv2.MORPH_GRADIENT,kernel)deftophat(self,kernelNone)-NDArray:顶帽: X - OPEN(X)ifkernelisNone:kernelself.create_kernel(rect,(5,5))returncv2.morphologyEx(self.binary,cv2.MORPH_TOPHAT,kernel)defblackhat(self,kernelNone)-NDArray:黑帽: CLOSE(X) - XifkernelisNone:kernelself.create_kernel(rect,(5,5))returncv2.morphologyEx(self.binary,cv2.MORPH_BLACKHAT,kernel)defskeletonize(self,kernel_size:int3)-NDArray:骨架提取skeletonnp.zeros_like(self.binary)kernelself.create_kernel(cross,(kernel_size,kernel_size))doneFalsewhilenotdone:erodedcv2.erode(self.binary,kernel)tempcv2.dilate(eroded,kernel)tempcv2.subtract(self.binary,temp)skeletoncv2.bitwise_or(skeleton,temp)self.binaryeroded.copy()ifcv2.countNonZero(self.binary)0:doneTruereturnskeletondefremove_small_objects(self,min_size:int50)-NDArray:去除小对象num_labels,labels,stats,_cv2.connectedComponentsWithStats(self.binary,connectivity8)resultnp.zeros_like(self.binary)foriinrange(1,num_labels):ifstats[i,cv2.CC_STAT_AREA]min_size:result[labelsi]255returnresultdeffill_holes(self)-NDArray:填充孔洞im_floodfillself.binary.copy()h,wself.binary.shape[:2]masknp.zeros((h2,w2),np.uint8)cv2.floodFill(im_floodfill,mask,(0,0),255)returncv2.bitwise_not(im_floodfill)defboundary_extraction(self,kernel_size:int3)-NDArray:边界提取: X - (X ⊖ S)kernelself.create_kernel(rect,(kernel_size,kernel_size))erodedcv2.erode(self.binary,kernel)returncv2.subtract(self.binary,eroded)defcreate_test_image()-NDArray:创建测试图像imgnp.zeros((400,600),dtypenp.uint8)cv2.rectangle(img,(100,100),(250,250),255,-1)cv2.circle(img,(400,200),80,255,-1)cv2.line(img,(50,350),(550,350),255,10)# 添加噪声noise(np.random.random((400,600))0.95).astype(np.uint8)*255imgcv2.add(img,noise)returnimgdefdemonstrate_morphology():演示形态学操作print(*60)print(形态学图像处理演示)print(*60)imgcreate_test_image()processorMorphologyProcessor(img)processor.binarize(otsu)figplt.figure(figsize(24,20))# 原始ax1fig.add_subplot(3,4,1)ax1.imshow(img,cmapgray)ax1.set_title(原始图像,fontsize12,fontweightbold)ax1.axis(off)# 二值化ax2fig.add_subplot(3,4,2)ax2.imshow(processor.binary,cmapgray)ax2.set_title(二值化,fontsize12,fontweightbold)ax2.axis(off)# 腐蚀ax3fig.add_subplot(3,4,3)ax3.imshow(processor.erode(processor.create_kernel(rect,(5,5)),1),cmapgray)ax3.set_title(腐蚀 (5x5),fontsize12,fontweightbold)ax3.axis(off)# 膨胀ax4fig.add_subplot(3,4,4)ax4.imshow(processor.dilate(processor.create_kernel(rect,(5,5)),1),cmapgray)ax4.set_title(膨胀 (5x5),fontsize12,fontweightbold)ax4.axis(off)# 开运算ax5fig.add_subplot(3,4,5)ax5.imshow(processor.open(processor.create_kernel(rect,(5,5)),1),cmapgray)ax5.set_title(开运算,fontsize12,fontweightbold)ax5.axis(off)# 闭运算ax6fig.add_subplot(3,4,6)ax6.imshow(processor.close(processor.create_kernel(rect,(5,5)),1),cmapgray)ax6.set_title(闭运算,fontsize12,fontweightbold)ax6.axis(off)# 形态学梯度ax7fig.add_subplot(3,4,7)ax7.imshow(processor.gradient(processor.create_kernel(rect,(5,5))),cmapgray)ax7.set_title(形态学梯度,fontsize12,fontweightbold)ax7.axis(off)# 顶帽ax8fig.add_subplot(3,4,8)ax8.imshow(processor.tophat(processor.create_kernel(rect,(5,5))),cmapgray)ax8.set_title(顶帽变换,fontsize12,fontweightbold)ax8.axis(off)# 黑帽ax9fig.add_subplot(3,4,9)ax9.imshow(processor.blackhat(processor.create_kernel(rect,(5,5))),cmapgray)ax9.set_title(黑帽变换,fontsize12,fontweightbold)ax9.axis(off)# 边界提取ax10fig.add_subplot(3,4,10)ax10.imshow(processor.boundary_extraction(3),cmapgray)ax10.set_title(边界提取,fontsize12,fontweightbold)ax10.axis(off)# 去除小对象ax11fig.add_subplot(3,4,11)ax11.imshow(processor.remove_small_objects(200),cmapgray)ax11.set_title(去除小对象,fontsize12,fontweightbold)ax11.axis(off)# 填充孔洞processor.binaryprocessor.close(processor.create_kernel(rect,(5,5)))ax12fig.add_subplot(3,4,12)ax12.imshow(processor.fill_holes(),cmapgray)ax12.set_title(填充孔洞,fontsize12,fontweightbold)ax12.axis(off)plt.tight_layout()plt.savefig(morphology_demo.png,dpi150,bbox_inchestight,facecolorwhite)plt.close()print(形态学演示图已保存至: morphology_demo.png)if__name____main__:demonstrate_morphology()print(\n形态学处理演示完成)13.5 本章小结本章介绍了形态学图像处理的基本概念和常用操作。GPT-5.4辅助编程提示词我需要实现形态学图像处理系统请帮我编写Python代码实现腐蚀、膨胀、开闭运算等。