机器视觉学习-day14-绘制图像轮廓

1. 轮廓的概念

轮廓是目标物体或者区域在图像外部的边界线,通常由一系列像素点相连组成,这些像素点共同构成了一个封闭的形状,这样形状就是轮廓。

轮廓与边缘不同:
轮廓是连续的,边缘可以连续也可以离散

轮廓是完整的,边缘可以是完成的也可以不完整

轮廓主要分析物体的形态,比如计算物体的周长与面积;边缘作为图像特征使用,比如区分脸和手。

代码运行步骤:

图片输入→灰度化→二值化→寻找轮廓→绘制轮廓→图片输出

1.1 寻找轮廓

复制代码
    # 4. 寻找轮廓
    # 返回值1:所有轮廓的点坐标,是一个list列表
    # 返回值2:轮廓的层级关系
    contours, hierarchy = cv2.findContours(
        image = image_np_thresh,  # 二值化之后的图像
        mode = cv2.RETR_EXTERNAL,  # 默认的轮廓查找方式
        method = cv2.CHAIN_APPROX_SIMPLE  # 默认的轮廓近似办法
    )

●contours 轮廓s

tuple类型,所有轮廓的点(每个元素是Numpy数组),可以通过[n-1]取出第n个轮廓的数据

●hierarchy 轮廓关系

如果没有检测到轮廓,则返回的数组为空,通常不会出现。

对于第n个轮廓:

○hierarchy[n][0]表示当前轮廓的后一条轮廓

○hierarchy[n][1]表示当前轮廓的前一条轮廓

○hierarchy[n][2]表示当前轮廓的第一个子轮廓

○hierarchy[n][3]表示当前轮廓的父轮廓

如果出现-1表示没有。

此参数较少使用,通常用于处理复杂画面效果。

●image 输入图像

要求8位的二值化图像,前景色白色,背景色黑色

●mode 轮廓的查找方式

○RETR_EXTERNAL 查找最外层轮廓(不查找内层轮廓)

在hierarchy层级结构中,每一个轮廓只有前后轮廓的索引,没有父轮廓与子轮 廓的索引。

○RETR_LIST 查找所有轮廓(包括外层和内层轮廓)

在hierarchy层级结构中,每一个轮廓只有前后轮廓的索引,没有父轮廓与子轮 廓的索引。

○RETR_CCOMP 查找所有轮廓(包括外层和内层轮廓)

在hierarchy层级结构中,每一个轮廓只有前后轮廓的索引,有父轮廓与子轮 廓的索引,轮廓为两层结构。

○RETR_TREE (包括外层和内层轮廓)

在hierarchy层级结构中,每一个轮廓只有前后轮廓的索引,有父轮廓与子轮 廓的索引,轮廓为树状结构。

●method 轮廓的近似办法

○CHAIN_APPROX_NONE 存储所有轮廓点

○CHAIN_APPROX_SIMPLE 只存储有用的点

比如轮廓中的直线,则只存储起点和终点;比如轮廓是四边形,则只存储四个顶点。

○CHAIN_APPROX_TC89_L1 使用Teh_Chin链进行轮廓逼近

精度高(优化后的点更少),使用少。

1.2 绘制轮廓

使用绘制轮廓的方式把上一步的寻找的轮廓点连接在一起进行绘制,这一步绘制的是前景轮廓。

  • contourIdx 轮廓编号

-1表示绘制所有轮廓

  • color 轮廓颜色
  • thickness 绘制的线宽

1.3 轮廓检测算法 Suzuki

内容复杂,了解即可

2 代码实践

原图3.jpg

python 复制代码
import cv2

if __name__ == '__main__':
    # 1. 图片输入
    path = '3.jpg'
    image_np = cv2.imread(path)
    # 2. 灰度化
    image_np_gray = cv2.cvtColor(
        image_np,
        cv2.COLOR_BGR2GRAY
    )
    # 3. 二值化
    ret, image_np_thresh = cv2.threshold(
        image_np_gray,
        127,
        255,
        cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    print(ret)

    # 4. 寻找轮廓
    contours, hierarchy = cv2.findContours(
        image_np_thresh,  # 二值化之后的图像
        cv2.RETR_EXTERNAL,  # 查找方式
        cv2.CHAIN_APPROX_SIMPLE  # 近似办法
    )
    print(len(contours))
    for i in contours:
        print(i.shape)
    """
    cv2.findContours(): 查找图像中的轮廓

    参数详解:
    image_np_thresh: 二值化图像,必须是8位单通道图像
    cv2.RETR_EXTERNAL: 轮廓检索模式,只检测最外层轮廓
    cv2.CHAIN_APPROX_SIMPLE: 轮廓近似方法,压缩水平、垂直和对角线段,只保留它们的端点
    
    返回值:
    contours: 找到的轮廓列表,每个轮廓是一个点集
    hierarchy: 轮廓的层次信息(此代码中未使用)
    print(len(contours)): 打印找到的轮廓数量
    for i in contours: print(i.shape): 遍历所有轮廓并打印每个轮廓的形状(即包含多少个点)
    """

    # 5. 绘制轮廓
    image_np = cv2.drawContours(
        image_np,  # 在哪个图上绘制
        contours,  # 轮廓数据列表
        contourIdx=-1,  # 绘制轮廓的id,-1表示全绘制
        color=(0, 0, 255),  # 绘制的颜色
        thickness=2  # 线宽
    )
    """
    cv2.drawContours(): 在图像上绘制轮廓

    参数详解:
    image_np: 要绘制轮廓的目标图像(会在原图上直接修改)    
    contours: 要绘制的轮廓列表    
    contourIdx=-1: 指定要绘制哪个轮廓,-1表示绘制所有轮廓    
    color=(0, 0, 255): 轮廓颜色,BGR格式(这里是红色)    
    thickness=2: 轮廓线宽度
    """
    # 6. 图片输出
    cv2.imshow('image_np', image_np)
    cv2.waitKey(0)
    cv2.imwrite('image.png', image_np)

# 读取图像 → 2. 转换为灰度图 → 3. 二值化处理 → 4. 查找轮廓 → 5. 绘制轮廓 → 6. 显示结果

运行后图片:image.png

练习:绘制图像的所有轮廓,并使用不同的颜色标识。

图片:3.jpg

python 复制代码
"""绘制图像的所有轮廓,并使用不同的颜色标识。"""
import cv2
import numpy as np

if __name__ == '__main__':
    # 1.图片输入
    path = '3.jpg'  # 图像文件路径
    image_np = cv2.imread(path)  # 读取图像为NumPy数组(BGR格式)

    # # 2. 灰度化
    # image_np_gray = cv2.cvtColor(
    #     image_np,
    #     cv2.COLOR_BGR2GRAY
    # )
    # cv2.imshow('image_np_gray', image_np_gray)
    # # 3. 二值化
    # ret, image_np_thresh = cv2.threshold(
    #     image_np_gray,
    #     192,  # 191/192为青色和粉色的临界值
    #     255,
    #     cv2.THRESH_BINARY_INV
    # )

    # 使用灰度化、二值化后,颜色浅的 无法选中
    # 2.HSV空间转换
    hsv_image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2HSV)  # BGR转HSV

    # 3. 制作掩膜
    # 定义绿色范围/背景图掩膜
    green_low = np.array([35, 43, 46])  # 红色下限(Hmin, Smin, Vmin)
    green_high = np.array([77, 255, 255])  # 红色上限(Hmax, Smax, Vmax)

    mask1 = cv2.inRange(  # 创建掩膜
        hsv_image_np,  # 基于哪个图像, 输入HSV图像
        green_low,  # 颜色下限
        green_high  # 颜色上限
    )
    # 合并掩膜
    mask_image_np = cv2.bitwise_or(mask1, mask1)  # 红
    cv2.imshow('mask_image_np4', mask_image_np)

    # 4.反阈值二值化取到前景图片
    ret, image_np_thresh = cv2.threshold(
        mask_image_np,  # 输入的灰度图像
        127,  # 阈值(127),但使用OTSU方法时会自动计算最佳阈值
        255,  # 最大值(255),当像素值超过阈值时赋予的值
        cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU
    )
    cv2.imshow('image_np_thresh', image_np_thresh)

    # 5. 寻找轮廓
    contours, hierarchy = cv2.findContours(
        image_np_thresh,  # 二值化之后的图像
        cv2.RETR_LIST,  # 查找方式:查找所有轮廓(包括外层和内层轮廓)
        cv2.CHAIN_APPROX_SIMPLE  # 近似办法
    )
    print(len(contours))
    for i in contours:
        print(i.shape)

    # 定义不同颜色用于绘制不同轮廓
    colors = [
        (0, 0, 0),  # 黑色
        (255, 255, 0),  # 青色
        (255, 0, 0),  # 蓝色
        (255, 0, 255),  # 粉色
    ]

    # 遍历所有找到的轮廓
    for i, cnt in enumerate(contours):
        # enumerate()函数返回的是(index, value)元组对,所以不能直接将循环变量用于索引

        # 计算当前轮廓的面积,过滤掉太小的轮廓(可能是噪声)
        area = cv2.contourArea(cnt)
        if area < 197:  # 忽略面积小于100像素的轮廓
            continue
        # 选择颜色(循环使用预定义的颜色)
        # color = colors[i]  # 当i≥4时会报IndexError
        color = colors[i % len(colors)]  # 0%4=0...5%4=1(循环)

        # 6. 绘制轮廓
        image_np = cv2.drawContours(
            image_np,  # 在哪个图上绘制
            contours,  # 轮廓数据列表
            contourIdx=i,  # 绘制轮廓的id,-1表示全绘制,从0开始代表下标
            color=color,  # 使用选择的颜色
            thickness=2  # 线宽
        )

    # 6. 图片输出
    cv2.imshow('image_np', image_np)
    cv2.waitKey(0)
    cv2.imwrite('123.png', image_np)

运行结果:123.png

相关推荐
西岸行者3 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意3 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码3 天前
嵌入式学习路线
学习
毛小茛3 天前
计算机系统概论——校验码
学习
babe小鑫3 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms3 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下3 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。3 天前
2026.2.25监控学习
学习
im_AMBER3 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J3 天前
从“Hello World“ 开始 C++
c语言·c++·学习