OpenCV轮廓图的一些操作

1.按短边筛选

原始轮廓图:

python 复制代码
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.按长边筛选

python 复制代码
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.将粘连的轮廓分开

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

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

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

python 复制代码
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()

输出图如下:

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

代码:

python 复制代码
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()
复制代码
输出结果:

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

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

相关推荐
dNGUZ7UGj31 分钟前
10分钟完成第一个Python小游戏
python·django
没有梦想的咸鱼185-1037-166339 分钟前
AI-Python机器学习与深度学习技术:CNN/Transformer/扩散模型、SHAP可解释及Hermes智能体自动化
人工智能·python·深度学习·机器学习·chatgpt·cnn·transformer
霸道流氓气质1 小时前
SpringBoot中通用工具类库(Utils)封装与使用实践
spring boot·后端·python
动物园猫2 小时前
人体部位目标检测数据集:5类别、7,000张图像 | 目标检测
人工智能·目标检测·计算机视觉
Draina3 小时前
CBC填充预言攻击-CBC Padding Oracle Crypto Attack
python·安全·web安全·网络安全·密码学·安全性测试
Angel Q.3 小时前
特征提取 | DINO 到 DINOv3
图像处理·python
不如语冰4 小时前
AI大模型入门-pytorch-张量2 tensor创建
python
NPE~4 小时前
[AI]Agent开发——ADK框架使用
人工智能·python·ai·教程·adk·agent开发
矮个史蒂芬4 小时前
统计verilog .v文件中output 的bit数
python
gwf2164 小时前
磨损均衡算法(Wear Leveling)——SSD如何让每块闪存“公平退休“?
运维·数据库·人工智能·python·嵌入式硬件·算法·智能硬件