OpenCV轮廓图的一些操作

1.按短边筛选

 原始轮廓图:

import cv2
import numpy as np

# 读取轮廓图
contour_image = cv2.imread('..\\IMGS\\pp_edge.png', cv2.IMREAD_GRAYSCALE)

# 使用cv2.findContours()函数获取所有轮廓
contours, _ = cv2.findContours(contour_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 设定的直径阈值
threshold_diameter = 25.0

# 遍历每个轮廓
new_image = np.zeros_like(contour_image)
for contour in contours:
    rect = cv2.minAreaRect(contour)  # 获取最小外接矩形
    diameter = rect[1][0] if rect[1][0] <= rect[1][1] else rect[1][1]    # 计算轮廓的最短尺寸,并获取直径

    # 如果轮廓的直径小于设定的阈值,则忽略该轮廓
    if diameter >= threshold_diameter:
        cv2.drawContours(new_image, [contour], -1, (255, 255, 255))
        print(diameter, end=', ')


cv2.imshow('Filtered Contours', new_image)   # 绘制保留的轮廓
cv2.waitKey(0)
cv2.destroyAllWindows()


 输出结果以及与原图对比:

                                     

2.按长边筛选

import cv2
import numpy as np

# 读取轮廓图
contour_image = cv2.imread('..\\IMGS\\pp_edge2.png', cv2.IMREAD_GRAYSCALE)

# 使用cv2.findContours()函数获取所有轮廓
contours, _ = cv2.findContours(contour_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 设定的直径阈值
threshold_diameter = 25.0

# 遍历每个轮廓
new_image = np.zeros_like(contour_image)
for contour in contours:
    diameter = cv2.minEnclosingCircle(contour)[1] * 2   # cv2.minEnclosingCircle():计算一个轮廓的最小包围圆,返回值是圆心坐标和半径

    # 如果轮廓的直径小于设定的阈值,则忽略该轮廓
    if diameter >= threshold_diameter:
        cv2.drawContours(new_image, [contour], -1, (255, 255, 255))
        print(diameter, end=', ')


cv2.imshow('Filtered Contours', new_image)   # 绘制保留的轮廓
cv2.waitKey(0)
cv2.destroyAllWindows()


结果图:

 3.将粘连的轮廓分开

有经过灰度、二值化处理后的图如下,需要将图中的所有轮廓读出,用以进行尺寸测量和数量统计等后续操作。

看得出,图中有两个轮廓 粘连到了一起,如果直接进行查找和统计就会出错。

例如用下面代码将轮廓找出并画出:

import cv2
import numpy as np

input_img = cv2.imread('..\\IMGS\\pp_binary.png', cv2.IMREAD_GRAYSCALE)     # 读取已转换过的轮廓图像
contours = cv2.findContours(input_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]   # 查找轮廓
tmp_img = np.zeros_like(input_img)  # 创建空白临时图像
cv2.drawContours(tmp_img, contours, -1, 255)  # 将轮廓绘制在临时图像上
cv2.imshow('OutPut', tmp_img)   # 显示轮廓图
cv2.waitKey(0)
cv2.destroyAllWindows()

输出图如下:

 看得出,轮廓是粘到一起的。为了将轮廓分开,初步的设想是,先用腐蚀的方法,如果存在轮廓粘连,那么腐蚀操作以后,图像就会被分成多个独立的部分,然后将每个独立的部分再进行膨胀操作恢复尺寸,并分别读取每个独立部分的轮廓,这样就实现了粘连轮廓的分离。

代码:

import cv2
import numpy as np

input_img = cv2.imread('..\\IMGS\\pp_binary.png', cv2.IMREAD_GRAYSCALE)     # 读取已转换过的轮廓图像
contours = cv2.findContours(input_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]   # 查找轮廓

out_contours = []    # 创建用以输出的轮廓线列表

for contour in contours:      # 针对每一个轮廓进行操作
    rect = cv2.minAreaRect(contour)   # 获取最小外接矩形
    diameter = rect[1][0] if rect[1][0] <= rect[1][1] else rect[1][1]   # 计算轮廓的最短尺寸,并获取直径

    num_kernel = int(diameter / 2.5)   # 定义核的大小
    if num_kernel % 2 == 0:
        num_kernel += 1
    kernel = np.ones((num_kernel, num_kernel), np.uint8)  # 定义核

    tmp_img = np.zeros_like(input_img)   # 创建空白临时图像
    cv2.drawContours(tmp_img, [contour], -1, (255, 255, 255), thickness=cv2.FILLED)    # 将当前的轮廓绘制在临时图像上

    erode_img = cv2.erode(tmp_img, kernel, iterations=1)   # 腐蚀操作,打破连接
    tmp_contours = cv2.findContours(erode_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]      # 读取腐蚀操作后的临时图像的轮廓
    if len(tmp_contours) > 1:    # 如果存在轮廓粘连,那么腐蚀操作以后,图像就会被分成多个独立的部分
        for tmp in tmp_contours:
            tmp_img = np.zeros_like(input_img)  # 创建空白临时图像
            cv2.drawContours(tmp_img, [tmp], -1, (255, 255, 255), thickness=cv2.FILLED)  # 将轮廓绘制在临时图像上
            dilate_img = cv2.dilate(tmp_img, kernel, iterations=1)  # 膨胀操作,恢复轮廓尺寸
            _ = cv2.findContours(dilate_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]  # 读取膨胀操作后的临时图像的轮廓
            out_contours.extend(_)     # 将膨胀操作后的轮廓加入输出用的轮廓列表
    else:
        out_contours.extend([contour])      # 不存在轮廓粘连,直接将当前轮廓加入输出用的轮廓列表

# 在原图上绘制轮廓
tmp_img = np.zeros_like(input_img)   # 创建空白临时图像,用以输出
cv2.drawContours(tmp_img, out_contours, -1, 255)   # 在临时图像上画出所有轮廓

# 显示结果
mask_img = cv2.cvtColor(tmp_img, cv2.COLOR_GRAY2BGR)    # 掩模图,用以将轮廓叠加在原图上
mask_img[tmp_img == 255] = [0, 255, 0]  # 按照临时图中的白色位置,将掩模图中的对应位置变为绿色
gray_img_3ch = cv2.cvtColor(input_img, cv2.COLOR_GRAY2BGR)   # 转换创建RGB图,用以显示
show_img = cv2.addWeighted(gray_img_3ch, 0.5, mask_img, 1, 0)  # 将掩模图与灰色3通道图叠加输出
cv2.imshow('OutPut', show_img)

cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果:

 粘连的轮廓已经被分开,成为两个相邻的独立轮廓。当然了,由于使用了先腐蚀再膨胀的操作,轮廓尺寸会有一些精度上的损失,但是比起将两个轮廓误统计为一个,误差还是小多了。

题外话,先腐蚀再膨胀看上去很熟悉是吧,不就是开操作吗?直接使用开操作行不行?我的实验结果是不行,会得到类似下面这样的结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

深蓝海拓

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

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

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

打赏作者

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

抵扣说明:

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

余额充值