形态学是生物学的一个分支,主要研究动植物的形态和结构。这里,我们应用形态学简化图像的数据,取出不重要的结构,仅仅保持图像的基本形状特性.

形态学的基础是集合论。将感兴趣的区域称为前景像素点,不感兴趣的部分称为背景像素点。通过定义的结构元SE(structural element) 对前景像素点操作,从而达到处理图像的效果

结构元类似之前的滤波器模板,不同的是结构元需要定义原点。原点对应像素点的输出,原点可以在SE的内部或者外部。当结构元对称且未指定原点的时候,原点位于对称中心处

膨胀和腐蚀

腐蚀的过程定义如下:结构元原点访问每个像素点,若结构元都被包含在前景像素点当中,则输出前景像素点。因此,腐蚀是一种缩小前景目标的操作.

膨胀的过程定义如下:结构元原点访问每个像素点,(膨胀的时候需要SE绕原点翻转,通常使用的SE都是对称的,所以可以不用管)若结构元与前景像素点有交集,则输出前景像素点。因此,膨胀是一种扩大前景目标的操作

import cv2
import numpy as npimg = cv2.imread('./img1.png', 0)
ret,img_binary=cv2.threshold(img,0,255,cv2.THRESH_BINARY-cv2.THRESH_OTSU)"""
cv2.MORPH_RECT 矩形    cv2.MORPH_CROSS十字架      cv2.MORPH_ELLIPSE椭圆
"""
SE=cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))dst = cv2.erode(img,SE)cv2.imshow('img', np.hstack((img, dst)))
cv2.imwrite('./image.png', np.hstack((img, dst)))
cv2.waitKey()
cv2.destroyAllWindows()

import cv2
import numpy as npimg = cv2.imread('./img1.png', 0)
ret,img_binary=cv2.threshold(img,0,255,cv2.THRESH_BINARY-cv2.THRESH_OTSU)"""
cv2.MORPH_RECT 矩形    cv2.MORPH_CROSS十字架      cv2.MORPH_ELLIPSE椭圆
"""
SE=cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))dst = cv2.dilate(img,SE)cv2.imshow('img', np.hstack((img, dst)))
cv2.imwrite('./image.png', np.hstack((img, dst)))
cv2.waitKey()
cv2.destroyAllWindows()

开运算和闭运算

膨胀和腐蚀操作都会有一个通病,就是会改变原目标的大小

开运算:先对目标腐蚀在膨胀

开运算能够平滑物体的轮廓、断开狭窄的狭颈、消除细长的突出物等等

闭运算:先膨胀在腐蚀

闭运算能够弥合狭窄的断裂和细长的沟壑、消除小孔、填补轮廓中的缝隙等等

看开、闭运算的作用,主要看第一次操作是膨胀还是腐蚀,因为第一次占的是主导作用,而第二次的膨胀腐蚀操作只是还原目标

import cv2
import numpy as npimg = cv2.imread('./img1.png', 0)
ret,img_binary=cv2.threshold(img,0,255,cv2.THRESH_BINARY-cv2.THRESH_OTSU)"""
cv2.MORPH_RECT 矩形    cv2.MORPH_CROSS十字架      cv2.MORPH_ELLIPSE椭圆
"""
SE=cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
"""
这里iteration 代表open运算的迭代次数。
这里是先进行五次腐蚀,然后再五次膨胀
"""
dst = cv2.morphologyEx(img,cv2.MORPH_OPEN,SE,iterations=5)cv2.imshow('img', np.hstack((img, dst)))
cv2.imwrite('./image.png', np.hstack((img, dst)))
cv2.waitKey()
cv2.destroyAllWindows()

import cv2
import numpy as npimg = cv2.imread('./img1.png', 0)
ret,img_binary=cv2.threshold(img,0,255,cv2.THRESH_BINARY-cv2.THRESH_OTSU)"""
cv2.MORPH_RECT 矩形    cv2.MORPH_CROSS十字架      cv2.MORPH_ELLIPSE椭圆
"""
SE=cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
"""
这里iteration 代表open运算的迭代次数。
这里是先进行五次腐蚀,然后再五次膨胀
"""
dst = cv2.morphologyEx(img,cv2.MORPH_CLOSE,SE,iterations=5)cv2.imshow('img', np.hstack((img, dst)))
cv2.imwrite('./image.png', np.hstack((img, dst)))
cv2.waitKey()
cv2.destroyAllWindows()

边界提取

前景像素点的边界可以通过如下方式得到:首先将原图A腐蚀得到腐蚀图B,然后将原图A减去腐蚀图B得到差集 。

opencv 库里面的形态学梯度是:原图的膨胀图 - 原图的腐蚀图

因为膨胀会使目标扩大,腐蚀会使目标减少,所以差值产生的边界宽度会比较大

import cv2
import numpy as npimg = cv2.imread('./img1.png', 0)
ret, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)  # 阈值处理
# 边界提取
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))  # kernel
img_erode = cv2.erode(img, kernel)  # 腐蚀
dst = img - img_erode  # 原图 - 腐蚀图
# 形态学梯度
"""
opencv 库里面的形态学梯度是:原图的膨胀图 - 原图的腐蚀图
因为膨胀会使目标扩大,腐蚀会使目标减少,所以差值产生的边界宽度会比较大
"""
dst_gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)  # 膨胀图 - 腐蚀图cv2.imshow('img', np.hstack((img, dst, dst_gradient))) # 原图、边界提取图、梯形态学梯度
cv2.imwrite('./image.png',np.hstack((img, dst, dst_gradient)))
cv2.waitKey()
cv2.destroyAllWindows()

孔洞填充

孔洞指的是被前景像素点或者说感兴趣的像素点包围起来的区域,这个区域是我们不感兴趣的背景区域。

# -*- coding: utf-8 -*-
import cv2
import numpy as npdef holefill(img):img_copy = img.copy()mask = np.zeros((img.shape[0] + 2, img.shape[1] + 2), dtype=np.uint8)"""floodFill 会对原图像进行操作,所以事先需要拷贝图像,将漫水的种子设为(0,0)也就是图像的左上角,填充的颜色为255。虽然通过计算找到孔洞的位置,然后直接填充就可以,但是这样比较麻烦,且孔洞较多的时候不好处理。这里我们将除了 前景像素点和孔洞 的位置都填充为前景像素点,然后通过求反就可以得到所有的孔洞的位置"""cv2.floodFill(img, mask, (0, 0), 255)img_inverse = cv2.bitwise_not(img)dst = cv2.bitwise_or(img_copy, img_inverse)return dstimg = cv2.imread('img1.png', 0)
thresh, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)img_copy = img.copy()
dst = holefill(img)
cv2.imshow('img', np.hstack((img_copy, dst)))
cv2.imwrite('./image.png',np.hstack((img, dst)))
cv2.waitKey()
cv2.destroyAllWindows()

细化

import numpy as np
import cv2def thin(img):  # 细化算法# 8 个细化 kernelB1 = np.array([[-1, -1, -1], [0, 1, 0], [1, 1, 1]])B2 = np.array([[0, -1, -1], [1, 1, -1], [1, 1, 0]])B3 = np.array([[1, 0, -1], [1, 1, -1], [1, 0, -1]])B4 = np.array([[1, 1, 0], [1, 1, -1], [0, -1, -1]])B5 = np.array([[1, 1, 1], [0, 1, 0], [-1, -1, -1]])B6 = np.array([[0, 1, 1], [-1, 1, 1], [-1, -1, 0]])B7 = np.array([[-1, 0, 1], [-1, 1, 1], [-1, 0, 1]])B8 = np.array([[-1, -1, 0], [-1, 1, 1], [0, 1, 1]])while True:  # 循环迭代tmp = img  # 将上一步的操作暂存for i in range(8):  # 循环迭代八次ret1 = cv2.morphologyEx(img, cv2.MORPH_HITMISS, B1)  # B1 对图像做 击中-击不中变换ret1 = img - ret1  # 原图 减去 上一步击中-击不中的结果ret2 = cv2.morphologyEx(ret1, cv2.MORPH_HITMISS, B2)  # 将上步的结果作为新的输入ret2 = ret1 - ret2ret3 = cv2.morphologyEx(ret2, cv2.MORPH_HITMISS, B3)ret3 = ret2 - ret3ret4 = cv2.morphologyEx(ret3, cv2.MORPH_HITMISS, B4)ret4 = ret3 - ret4ret5 = cv2.morphologyEx(ret4, cv2.MORPH_HITMISS, B5)ret5 = ret4 - ret5ret6 = cv2.morphologyEx(ret5, cv2.MORPH_HITMISS, B6)ret6 = ret5 - ret6ret7 = cv2.morphologyEx(ret6, cv2.MORPH_HITMISS, B7)ret7 = ret6 - ret7ret8 = cv2.morphologyEx(ret7, cv2.MORPH_HITMISS, B8)ret8 = ret7 - ret8img = ret8  # 八次迭代完成 保存结果if (img == tmp).all():  # 如果所有结构元遍历的结果不再发生变化,则操作完成dst = img  # 保留细化结果breakreturn dst.astype(np.uint8)a = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],[0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
], dtype=np.uint8) * 255img = cv2.imread('./img1.png', 0)
_, img_bin = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY)  # 二值化处理ret = thin(img_bin.copy())  # 传入拷贝图像cv2.imshow('img', np.hstack((img_bin, ret)))
cv2.imwrite('./image.png',np.hstack((img_bin, ret)))
cv2.waitKey()
cv2.destroyAllWindows()

骨架

骨架的定义很简单:就是目标的前景像素点集合内部最大的内切圆盘,将所有的内切圆盘的圆心连起来就是骨架。

import numpy as np
import cv2def skeleton_extraction(img):  # 骨架抽取算法"""
1. 建立一个canvas,用来保存骨架的子集或者全集。并建立3*3 十字架的结构元2. 做循环迭代,循环停止的条件是原图被腐蚀成空集。img.any() 可以理解为,img 这个二维矩阵里,所有元素为零的话,返回False3. 对原图做开运算,然后得到原图和开图像的差。这里减号不会溢出,因为二值图像非0即255,而开图像一定是原图像的子集,所以不会溢出4. 将结果并在canvas里面,这里canvas 和自己并的话就是可以当骨架子集,又可以当骨架全集5. 腐蚀原图"""canvas = np.zeros(img.shape, dtype=np.uint8)  # 将结果保留在这kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))  # 3*3 方形结构元while img.any():  # 循环迭代img_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)  # 做开运算img_diff = img - img_open  # 原图 - 开运算图canvas = cv2.bitwise_or(canvas, img_diff)  # 将结果并在一起img = cv2.erode(img, kernel)  # 腐蚀dst = canvas  # dst 返回的图片return dst.astype(np.uint8)img = cv2.imread('./img1.png', 0)
_, img_bin = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)  # 二值化处理
ret = skeleton_extraction(img_bin.copy())  # 传入拷贝图像cv2.imshow('img', np.hstack((img_bin, ret)))
cv2.imwrite('./image.png',np.hstack((img_bin, ret)))
cv2.waitKey()
cv2.destroyAllWindows()

python 形态学相关推荐

  1. 发票表格检测——传统图像方法

    前言 偶尔整理笔记,发现两三年前写的纯用opencv实现发票图片中的表格检测的笔记.虽然现在已经是深度学习网络的天下,但是回头看看过去的探索,感觉还是有点意思的,主要用来给我自己做个笔记备份,同时分享 ...

  2. 使用Python,OpenCV进行形态学操作

    使用Python,OpenCV进行形态学操作) 1. 效果图 2. 原理 3. 源码 3.1 [制作logo源码](https://blog.csdn.net/qq_40985985/article/ ...

  3. Python图像处理:形态学操作

    来源:DeepHub IMBA本文约1400字,建议阅读5分钟 形态学的操作主要是去除影响图像形状和信息的噪声.形态学运算在图像分割中非常有用,可以得到无噪声的二值图像. 形态学方法 当图像经过预处理 ...

  4. OpenCV Python教程(3)(4)(5): 直方图的计算与显示 形态学处理 初级滤波内

    OpenCV Python教程(3.直方图的计算与显示) 本篇文章介绍如何用OpenCV Python来计算直方图,并简略介绍用NumPy和Matplotlib计算和绘制直方图 直方图的背景知识.用途 ...

  5. opencv进阶学习笔记13:图像形态学操作大全(膨胀,腐蚀,开闭,黑帽,顶帽,梯度)python版

    基础版学习笔记: python3+opencv学习笔记汇总目录(适合基础入门学习) 进阶版笔记目录链接: python+opencv进阶版学习笔记目录(适合有一定基础) 基础版形态学: opencv学 ...

  6. [Python图像处理] 十.形态学之图像顶帽运算和黑帽运算

    该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...

  7. [Python图像处理] 九.形态学之图像开运算、闭运算、梯度运算

    该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...

  8. Python+Opencv简易车牌识别(二):形态学运算,HSV颜色空间筛选与图像分割

    注:这是依然一个简单的车牌识别demo 1.前言 在上一篇Python+Opencv简易车牌识别(一):基于HSV颜色空间的图像分割中,我们讲了如何仅基于颜色来进行简单粗暴的车牌分割.今天我们考虑对图 ...

  9. Python+OpenCV:形态学变换

    Python+OpenCV:形态学变换 理论 形态学变换是基于图像形状的一些简单操作. 它通常在二值图像上执行.它需要两个输入,一个是我们的原始图像,另一个是结构元素(structuring elem ...

最新文章

  1. python---简单数据库
  2. mysql5.7.22密码设置_mysql5.7.22版本修改root密码
  3. 建立实体-关系模型3
  4. python系统-python实现用户登录系统
  5. nssl1336-膜拜神牛【LIS】
  6. SQL Server之索引
  7. 夏日清凉小风扇网站源码 抖音引流神器
  8. java 蓝桥杯算法训练 4-1打印下述图形(题解)
  9. 网管,真的是我该选择的路吗?
  10. c++游戏编程100事列_C/C++编程笔记:C语言开发经典游戏项目《五子棋》,内含源码...
  11. 解析TCP/UDP协议的通讯软件
  12. Unix.Trojan.Agent-37008木马查杀
  13. ScrollView 吸顶效果
  14. Excel甘特图 Gantt Chart
  15. arcgis显示后台错误_ArcGIS后台服务器抛出异常的解决方法
  16. maven 跳过单元测试打包
  17. 每日一题----空瓶子喝可乐问题
  18. icomoon 下载及使用
  19. htmlcss全屏视频背景
  20. 【R语言文本挖掘】:分析单词和文档频率——TF-IDF

热门文章

  1. 从今日头条抄袭到京东水逆,为何互联网公司人设会接连崩塌?
  2. Spring-04-Spring的入门配置
  3. MFC 类层次结构图
  4. 102-并发编程详解(中篇)
  5. LAMP - 学习/实践
  6. JavaEE1(4/23)
  7. 关于Windows7 64位系统下regsvr32执行报错的解决方案
  8. 【图的存储】邻接多重表
  9. 嵌入式技术(单片机原理)基本概念梳理(保研/考研面试)
  10. DBLINK使用的思考