python 形态学

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

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

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

膨胀和腐蚀

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

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

import cv2
import numpy as np

img = 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 np

img = 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 np

img = 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 np

img = 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 np

img = 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 np

def 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 dst

img = 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 cv2


def thin(img):  # 细化算法

    # 8 个细化 kernel
    B1 = 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 - ret2

            ret3 = cv2.morphologyEx(ret2, cv2.MORPH_HITMISS, B3)
            ret3 = ret2 - ret3

            ret4 = cv2.morphologyEx(ret3, cv2.MORPH_HITMISS, B4)
            ret4 = ret3 - ret4

            ret5 = cv2.morphologyEx(ret4, cv2.MORPH_HITMISS, B5)
            ret5 = ret4 - ret5

            ret6 = cv2.morphologyEx(ret5, cv2.MORPH_HITMISS, B6)
            ret6 = ret5 - ret6

            ret7 = cv2.morphologyEx(ret6, cv2.MORPH_HITMISS, B7)
            ret7 = ret6 - ret7

            ret8 = cv2.morphologyEx(ret7, cv2.MORPH_HITMISS, B8)
            ret8 = ret7 - ret8

            img = ret8  # 八次迭代完成 保存结果

        if (img == tmp).all():  # 如果所有结构元遍历的结果不再发生变化,则操作完成
            dst = img  # 保留细化结果
            break

    return 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) * 255

img = 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 cv2


def skeleton_extraction(img):  # 骨架抽取算法
    """
1. 建立一个canvas,用来保存骨架的子集或者全集。并建立3*3 十字架的结构元

2. 做循环迭代,循环停止的条件是原图被腐蚀成空集。img.any() 可以理解为,img 这个二维矩阵里,所有元素为零的话,返回False

3. 对原图做开运算,然后得到原图和开图像的差。这里减号不会溢出,因为二值图像非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()

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一壶浊酒..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值