欢迎光临
我们一直在努力

OpenCV基础操作:轮廓检测详解

前言:轮廓检测是图像处理和计算机视觉中非常核心的技术,用于识别图像中物体的边界。通过轮廓,我们可以获得物体的形状、大小、位置等信息,进而实现目标识别、图像分割、形状分析等高级任务。OpenCV提供了强大的轮廓检测与处理函数,本文将结合代码实例,详细讲解轮廓检测的完整流程,并展示实际运行结果。

一、轮廓检测基础

1.1 什么是轮廓

轮廓可以简单理解为图像中连接所有连续点(沿着边界)的曲线,这些点具有相同的颜色或灰度。轮廓检测常用于物体识别、形状分析等场景。

1.2 轮廓检测的步骤

轮廓检测通常遵循以下步骤:

  • 读取图像并转换为灰度图。

  • 二值化处理:将灰度图转换为二值图像(像素值仅为0和255),因为轮廓检测算法通常在二值图像上工作。

  • 执行轮廓检测:使用cv2.findContours()查找轮廓。

  • 绘制或分析轮廓:根据需要绘制轮廓或提取轮廓特征。

  • 二、关键函数详解

    2.1 转换为灰度图与二值化

    import cv2

    # 读取图像
    phone = cv2.imread('phone.png')

    # 转换为灰度图
    phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
    cv2.imshow('Gray', phone_gray)
    cv2.waitKey(0)

    # 二值化处理(阈值120,最大值255,THRESH_BINARY类型)
    ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
    cv2.imshow('Binary', phone_binary)
    cv2.waitKey(0)

    运行结果说明:

    • 灰度图窗口显示原图的灰度版本。

    • 二值图窗口显示黑白图像,其中灰度值大于120的像素变为白色,其余为黑色。

    2.2 查找轮廓:cv2.findContours()

    函数语法(OpenCV 3/4版本):

    contours, hierarchy = cv2.findContours(image, mode, method)

    注意:在OpenCV 3和4中,该函数只返回两个值(contours, hierarchy),而OpenCV 2返回三个值(image, contours, hierarchy)。本教程基于OpenCV 3/4。

    参数详解:

    • image:输入的二值图像(通常为8位单通道,像素值0或255)。注意:该函数会直接修改输入图像,建议传入图像的副本。

    • mode:轮廓检索模式,决定轮廓的组织方式。

      模式描述
      cv2.RETR_EXTERNAL 只检测最外层轮廓,忽略内部轮廓。
      cv2.RETR_LIST 检测所有轮廓,但不建立等级关系,所有轮廓处于同一层级。
      cv2.RETR_CCOMP 检测所有轮廓,并建立两个层级的层次结构:顶层为外边界,次层为内孔边界。
      cv2.RETR_TREE 检测所有轮廓,并建立完整的等级树结构,包含父子关系。
    • method:轮廓近似方法,决定如何存储轮廓点。

      方法描述
      cv2.CHAIN_APPROX_NONE 存储所有轮廓点,相邻两点距离不超过1像素。
      cv2.CHAIN_APPROX_SIMPLE 压缩水平、垂直、对角方向,只保留端点。例如一个矩形只需4个点。

    返回值:

    • contours:一个Python列表,每个元素是一个轮廓(由NumPy数组存储的边界点坐标,形状为(N,1,2))。

    • hierarchy:轮廓的层次信息,形状为(1,N,4)的数组,每个轮廓对应四个整数:[Next, Previous, First_Child, Parent]。

      • Next:同一层级的下一条轮廓的索引,无则-1。

      • Previous:同一层级的上一条轮廓的索引,无则-1。

      • First_Child:第一个子轮廓的索引,无则-1。

      • Parent:父轮廓的索引,无则-1。

    代码示例:

    # 使用RETR_TREE检索所有轮廓,使用CHAIN_APPROX_NOME存储所有点
    contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    print(f"检测到 {len(contours)} 个轮廓")

    输出示例:

    检测到 9 个轮廓

    (具体数值取决于图像内容,此处为笔者上传图片的轮廓)

    2.3 绘制轮廓:cv2.drawContours()

    函数语法:

    cv2.drawContours(image, contours, contourIdx, color, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]])

    参数详解:

    • image:要在其上绘制轮廓的目标图像(可以是原图或副本)。

    • contours:轮廓列表(即findContours返回的contours)。

    • contourIdx:要绘制的轮廓索引。若为负数(如-1),则绘制所有轮廓。

    • color:轮廓颜色,BGR格式,如(0,255,0)为绿色。

    • thickness:线条粗细,若为负数则填充轮廓内部。

    • lineType:线条类型,如cv2.LINE_8(默认)。

    • hierarchy:层次结构(可选),用于绘制特定层级。

    • maxLevel:绘制轮廓的最大层级(配合hierarchy使用)。

    • offset:轮廓点的偏移量。

    代码示例:

    # 复制原图,避免修改原图
    image_copy = phone.copy()

    # 绘制所有轮廓,绿色,线宽3
    cv2.drawContours(image_copy, contours, -1, (0, 255, 0), 3)
    cv2.imshow('All Contours', image_copy)
    cv2.waitKey(0)

    运行结果说明:

    • 显示窗口“All Contours”,原图上用绿色线条绘制了所有检测到的轮廓。

    三、轮廓特征

    检测到轮廓后,我们通常需要提取其特征,如面积、周长等。

    3.1 轮廓面积:cv2.contourArea()

    函数语法:

    area = cv2.contourArea(contour[, oriented])

    • contour:单个轮廓(如contours[i])。

    • oriented:若为True,返回带符号的面积(可用于判断轮廓方向),默认False返回绝对值。

    示例:

    area_0 = cv2.contourArea(contours[0])
    area_1 = cv2.contourArea(contours[1])
    print(f"轮廓0面积: {area_0}")
    print(f"轮廓1面积: {area_1}")

    输出示例:

    轮廓0面积: 50716.5
    轮廓1面积: 255.5

    3.2 轮廓周长:cv2.arcLength() 

    函数语法:

    perimeter = cv2.arcLength(curve, closed)

    • curve:轮廓点集。

    • closed:布尔值,指示轮廓是否闭合(True表示闭合,计算闭合周长;False表示不闭合,计算曲线长度)。

    示例:

    length = cv2.arcLength(contours[0], True)
    print(f"轮廓0周长: {length}")

    输出示例:

    轮廓0周长: 952.437722325325

    3.3 根据面积筛选轮廓

    通过遍历轮廓,可以根据面积大小筛选出感兴趣的轮廓。

    示例:保留面积大于10000的轮廓并绘制

    large_contours = []
    for contour in contours:
    if cv2.contourArea(contour) > 10000:
    large_contours.append(contour)

    image_large = phone.copy()
    cv2.drawContours(image_large, large_contours, -1, (0, 255, 0), 3)
    cv2.imshow('Large Contours (>10000)', image_large)
    cv2.waitKey(0)

    运行结果说明:

    • 窗口“Large Contours (>10000)”仅显示面积大于10000的轮廓,过滤掉了小面积噪点或无关轮廓。

    四、几何形状拟合

    通过轮廓,可以拟合出各种几何形状,如最小外接圆、外接矩形等,从而获得物体的位置和尺寸。

    4.1 最小外接圆:cv2.minEnclosingCircle()

    函数语法:

    (x, y), radius = cv2.minEnclosingCircle(points)

    • points:轮廓点集。

    • 返回值:圆心坐标(x, y)和半径radius(均为浮点数)。

    示例:

    # 选择第6个轮廓(根据实际图像调整索引)
    cnt = contours[6]
    (x, y), r = cv2.minEnclosingCircle(cnt)
    center = (int(x), int(y))
    radius = int(r)

    # 在原图上绘制外接圆
    phone_circle = phone.copy()
    cv2.circle(phone_circle, center, radius, (0, 255, 0), 2)
    cv2.imshow('Min Enclosing Circle', phone_circle)
    cv2.waitKey(0)

    运行结果说明:

    • 窗口“Min Enclosing Circle”显示用绿色圆圈包围的轮廓。

    4.2 外接矩形:cv2.boundingRect()

    函数语法:

    x, y, w, h = cv2.boundingRect(array)

    • array:轮廓点集。

    • 返回值:矩形左上角坐标(x, y),宽度w,高度h。

    示例:

    x, y, w, h = cv2.boundingRect(cnt)
    phone_rect = phone.copy()
    cv2.rectangle(phone_rect, (x, y), (x + w, y + h), (0, 0, 255), 2)
    cv2.imshow('Bounding Rectangle', phone_rect)
    cv2.waitKey(0)

    运行结果说明:

    • 窗口“Bounding Rectangle”显示用红色矩形框出的轮廓。

    五、完整示例代码及运行结果

    以下将所有步骤整合,方便读者运行观察。代码中包含必要的注释和输出语句。

    import cv2

    # 1. 读取图像
    phone = cv2.imread('phone.png')
    if phone is None:
    print("无法读取图像,请检查路径")
    exit()

    # 2. 转换为灰度图并二值化
    gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
    ret, binary = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)

    # 3. 查找轮廓(使用TREE模式,存储所有点)
    contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    print(f"找到 {len(contours)} 个轮廓")

    # 4. 绘制所有轮廓
    all_contours_img = phone.copy()
    cv2.drawContours(all_contours_img, contours, -1, (0, 255, 0), 3)
    cv2.imshow('All Contours', all_contours_img)

    # 5. 计算并打印第一个轮廓的面积和周长
    if len(contours) > 0:
    area = cv2.contourArea(contours[0])
    perimeter = cv2.arcLength(contours[0], True)
    print(f"第一个轮廓面积: {area}, 周长: {perimeter}")

    # 6. 根据面积筛选(面积大于10000)
    large_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 10000]
    large_img = phone.copy()
    cv2.drawContours(large_img, large_contours, -1, (0, 255, 0), 3)
    cv2.imshow('Large Contours (>10000)', large_img)

    # 7. 对某个轮廓绘制最小外接圆和外接矩形
    if len(contours) > 6: # 确保索引存在
    cnt = contours[6]
    # 最小外接圆
    (x, y), r = cv2.minEnclosingCircle(cnt)
    center = (int(x), int(y))
    radius = int(r)
    circle_img = phone.copy()
    cv2.circle(circle_img, center, radius, (0, 255, 0), 2)
    cv2.imshow('Min Enclosing Circle', circle_img)

    # 外接矩形
    x, y, w, h = cv2.boundingRect(cnt)
    rect_img = phone.copy()
    cv2.rectangle(rect_img, (x, y), (x + w, y + h), (0, 0, 255), 2)
    cv2.imshow('Bounding Rectangle', rect_img)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

    运行结果示例(基于某张phone.png图像):

    检测到 9 个轮廓
    轮廓0面积: 50716.5
    轮廓1面积: 255.5
    轮廓0周长: 952.437722325325

    同时依次弹出以下窗口:

    • Gray:灰度图

    • Binary:二值图

    • All Contours:所有轮廓绘制结果(绿色线条)

    • Large Contours (>10000):筛选后的大面积轮廓

    • Min Enclosing Circle:第6个轮廓的最小外接圆

    • Bounding Rectangle:第6个轮廓的外接矩形

    读者可根据实际图像调整阈值和轮廓索引,观察不同效果。

    六、注意事项

  • 输入图像要求:cv2.findContours()的输入必须是二值图像(0或255),且通常为单通道。如果不是,需要提前二值化。

  • 图像副本:findContours会修改传入的图像,建议传入副本或确保原图不再需要。

  • OpenCV版本差异:

    • OpenCV 2:返回三个值(image, contours, hierarchy)

    • OpenCV 3/4:返回两个值(contours, hierarchy)
      根据使用的版本调整代码。

  • 轮廓近似:CHAIN_APPROX_SIMPLE能大幅减少轮廓点数,节省内存,但可能损失微小细节;CHAIN_APPROX_NONE保留所有点,更精确。

  • 层次结构理解:在复杂图像中,利用hierarchy可以分析轮廓的包含关系(如物体内部有空洞),实现更精细的筛选。

  • 七、总结

    本文详细介绍了OpenCV中轮廓检测的完整流程,包括:

    • 图像预处理(灰度化、二值化)

    • 使用findContours查找轮廓,并理解不同检索模式和近似方法的区别

    • 使用drawContours绘制轮廓

    • 提取轮廓特征(面积、周长)并据此筛选

    • 拟合几何形状(最小外接圆、外接矩形)

    通过实际代码运行和输出示例,读者可以直观理解各步骤的效果。轮廓检测是图像分析的基础,掌握这些操作后,可以进一步实现物体计数、形状分类、目标定位等高级功能。建议读者多尝试不同参数,观察结果变化,加深理解。

    赞(0)
    未经允许不得转载:171主机测评 » OpenCV基础操作:轮廓检测详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址