用Python实现形态学处理(膨胀、腐蚀算法)

用Python实现形态学处理(膨胀、腐蚀算法)

在不调用opencv库的情况下,利用卷积的思路实现膨胀腐蚀算法


前言

虽然大部分情况下,我们直接调用cv库就可以完成膨胀腐蚀的操作,但如果在“完成老师的任务”的情况下,自己想办法做一下膨胀腐蚀运算还是有必要的。
与常见的“扫描-判定”的算法不同,本文提出了一种利用卷积的思路,实现适应自定义非规则的结构元素及中心点进行膨胀腐蚀运算的方法。


提示:以下是本篇文章正文内容,下面案例可供参考

一、膨胀与腐蚀的简介

简单介绍二值图像、膨胀、腐蚀的概念

图像像素中只有0和1的图像称为二值图像,其中0区域显示为黑色,1区域显示为白色
图1.二值图像示例
膨胀与腐蚀,都是通过结构元素在二值图像上做遍历的运算。
接下来以实例说明该种运算——
现有一目标图像,其中像素值只有1和0,结构元素为3×3,原点在正中心,如图所示:未标记位置像素值均为0

腐蚀(erode)

腐蚀可以理解为一种“与”运算,在结构元素在目标图像上移动的过程中,若结构元素内的像素值全为1(即结构元素与目标区域完全“重合”)时,将结构元素的原点位置的像素值设置为1。
遍历完毕后,将其它位置的像素值均设为0。
腐蚀结果如下:
未标记位置像素值均为0

膨胀(dilate)

膨胀预算可以理解为一种“或”运算,在结构元素在目标图像上移动的过程中,若结构元素内存在像素值为1(即结构元素与目标区域存在“重合”部分)时,将结构元素的原点位置的像素设置为1。
遍历完毕后,将其它位置的像素值均设为0。
膨胀结果如下:未标记位置像素值均为0

二、针对结构元素不规则的情况

针对规则的结构元素,我们可以采用“搜索-验证”的逻辑进行处理,即找到目标图像中每次结构元素移动所对应的规则小矩形,对其所有像素值进行判定,对其中心原点进行标记。

但进一步的,假设,在某种情况下,我希望程序能够实现使用不规则的结构元素对目标图像进行膨胀与腐蚀操作,该如何实现?

这里我引入一个卷积(自相关)的思路:

现在,假设我们目标图像不变,结构元素与其原点均不规则,如图:未标记位置像素值均为0
它的腐蚀、膨胀结果将如下:(有兴趣可以自己手工做一些有所体会)未标记位置像素值均为0
假设结构元素中,属于其的部分为1,不属于结构元素的部分为0。
可以验证如下事实:
当结构元素与目标图像中与其对应的小像素矩阵的卷积和与其“面积”(非0像素点个数之和)相等时,说明发生了“重合”,中心点位置像素值设为1,不等时为0。
当结构元素与目标图像中与其对应的小像素矩阵的卷积和大于0时,说明有重合部分,中心点像素值设为1,卷积和等于0时设为0。

核心代码如下:

1.调用库函数

可以看到,引入的库文件中不包括cv库:

import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
import matplotlib as mpl

2.腐蚀算法

输入有三个变量,分别为待处理的二值图像bin_im,结构元素kernel,结构元素的原点位置(起始点为[0,0]):

def img_erode(bin_im, kernel, center_coo):
    kernel_w = kernel.shape[0]
    kernel_h = kernel.shape[1]
    if kernel[center_coo[0], center_coo[1]] == 0:
        raise ValueError("指定原点不在结构元素内!")
    erode_img = np.zeros(shape=bin_im.shape)
    for i in range(center_coo[0], bin_im.shape[0]-kernel_w+center_coo[0]+1):
        for j in range(center_coo[1], bin_im.shape[1]-kernel_h+center_coo[1]+1):
            a = bin_im[i-center_coo[0]:i-center_coo[0]+kernel_w,
                j-center_coo[1]:j-center_coo[1]+kernel_h]  # 找到每次迭代中对应的目标图像小矩阵
            if np.sum(a * kernel) == np.sum(kernel):  # 判定是否“完全重合”
                erode_img[i, j] = 1
    return erode_img

3.膨胀算法

输入有三个变量,分别为待处理的二值图像bin_im,结构元素kernel,结构元素的原点位置(起始点为[0,0])。

def img_dilate(bin_im, kernel, center_coo):
    kernel_w = kernel.shape[0]
    kernel_h = kernel.shape[1]
    if kernel[center_coo[0], center_coo[1]] == 0:
        raise ValueError("指定原点不在结构元素内!")
    dilate_img = np.zeros(shape=bin_im.shape)
    for i in range(center_coo[0], bin_im.shape[0] - kernel_w + center_coo[0] + 1):
        for j in range(center_coo[1], bin_im.shape[1] - kernel_h + center_coo[1] + 1):
            a = bin_im[i - center_coo[0]:i - center_coo[0] + kernel_w,
                j - center_coo[1]:j - center_coo[1] + kernel_h]
            dilate_img[i, j] = np.max(a * kernel)  # 若“有重合”,则点乘后最大值为0
    return dilate_img

4.其它相关图像处理函数

RGB图像转灰度图

def rgb2gray(rgb_img):
    gray = rgb_img[:, :, 0] * 0.299 + rgb_img[:, :, 1] * 0.587 + rgb_img[:, :, 2] * 0.114
    return gray

阈值法灰度图转二值图

def im_binary(gray_image, t=80):
    binary_image = np.zeros(shape=(gray_image.shape[0], gray_image.shape[1]), dtype=np.uint8)
    for i in range(gray_image.shape[0]):
        for j in range(gray_image.shape[1]):
            if gray_image[i][j] > t:
                binary_image[i][j] = 1
            else:
                binary_image[i][j] = 0
    return binary_image

开运算

先腐蚀再膨胀

def img_open(bin_im, erope_k, erope_c_c, dilate_k, dilate_c_c):
    open_img = img_erode(bin_im, erope_k, erope_c_c)
    open_img = img_dilate(open_img, dilate_k, dilate_c_c)
    return open_img

闭运算

先膨胀再腐蚀

def img_close(bin_im, erope_k, erope_c_c, dilate_k, dilate_c_c):
    close_img = img_dilate(bin_im, dilate_k, dilate_c_c)
    close_img = img_erode(close_img, erope_k, erope_c_c)
    return close_img

三、主函数调用

if __name__ == "__main__":
    img = np.array(Image.open(r".\logo.jpg"))  # 此处可以是指定路径的rgb图像
    gray_img = 255 - rgb2gray(img)
    print("原始二值图像")
    bin_img = im_binary(gray_img)
    kernel1 = np.ones(shape=(3, 3))  # 此处声明结构元素,可以自定义为非规则形状
    center1 = [1, 1]  # 此处声明结构元素原点,这里取其正中心像素点
    kernel2 = np.ones(shape=(5, 5))  # 同上
    center2 = [2, 2]  # 同上
    print("原始二值图像腐蚀")
    erode_im = img_erode(bin_img, kernel2, center2)
    print("原始二值图像膨胀")
    dilate_im = img_dilate(bin_img, kernel2, center2)
    print("原始二值图像开运算")
    open_im = img_open(bin_img, kernel1, center1, kernel1, center1)
    print("原始二值图像闭运算")
    close_im = img_close(bin_img, kernel1, center1, kernel1, center1)

    plt.subplot(241)
    plt.title("原始图像")
    plt.imshow(img)
    plt.subplot(242)
    plt.title("灰度图像")
    plt.imshow(gray_img, cmap="gray")
    plt.subplot(243)
    plt.title("二值图像")
    plt.title("二值图像")
    plt.imshow(bin_img, cmap="gray")
    plt.subplot(245)
    plt.title("腐蚀运算")
    plt.imshow(erode_im, cmap="gray")
    plt.subplot(246)
    plt.title("膨胀运算")
    plt.imshow(dilate_im, cmap="gray")
    plt.subplot(247)
    plt.title("开运算")
    plt.imshow(open_im, cmap="gray")
    plt.subplot(248)
    plt.title("闭运算")
    plt.imshow(close_im, cmap="gray")
    plt.show()

处理结果

在这里插入图片描述
希望能够对做相关课后作业的其它同学有帮助。

  • 20
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值