【python】OpenCV—findContours(4.3)

文章目录

1、功能描述

找出图片中的轮廓,拟合轮廓外接椭圆和外接矩阵

2、代码实现

导入必要的库,固定好随机种子

python 复制代码
import cv2 as cv
import numpy as np
import argparse
import random as rng

rng.seed(12345)

读取输入图片,判定图片是否读入成功

python 复制代码
parser = argparse.ArgumentParser(
    description='Code for Creating Bounding rotated boxes and ellipses for contours tutorial.')
parser.add_argument('--input', help='Path to input image.', default='1.png')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))

if src is None:
    print('Could not open or find the image:', args.input)
    exit(0)

灰度化图片,并平滑

python 复制代码
# Convert image to gray and blur it
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
src_gray = cv.blur(src_gray, (3, 3))
source_window = 'Source'
cv.namedWindow(source_window)
cv.imshow(source_window, src)

创建滚动条,动态配置参数,随着滑动条的滑动实现不同的算法效果

python 复制代码
max_thresh = 255
thresh = 100  # initial threshold
cv.createTrackbar('Canny Thresh:', source_window, thresh, max_thresh, thresh_callback)
thresh_callback(thresh)
cv.waitKey()

算法核心函数 thresh_callback,下面具体看看细节

python 复制代码
def thresh_callback(val):
    global src_gray
    threshold = val
    src_gray = cv.GaussianBlur(src_gray, (3, 3), 0.1)
    canny_output = cv.Canny(src_gray, threshold, threshold * 2)

高斯模糊,canny 算子

python 复制代码
    contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

    # Find the rotated rectangles and ellipses for each contour
    minRect = [None] * len(contours)
    minEllipse = [None] * len(contours)
    for i, c in enumerate(contours):
        minRect[i] = cv.minAreaRect(c)
        if c.shape[0] > 5:
            minEllipse[i] = cv.fitEllipse(c)
    # Draw contours + rotated rects + ellipses

找出轮廓,遍历轮廓,最小外接矩形框直接调用 cv2.minAreaRect 即可

如果轮廓多于 5 个点,cv2.fiEllipse 找椭圆

python 复制代码
    # Draw contours + rotated rects + ellipses

    drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)

    for i, c in enumerate(contours):
        color = (rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256))
        # contour
        cv.drawContours(drawing, contours, i, color)
        # ellipse
        if c.shape[0] > 5:
            cv.ellipse(drawing, minEllipse[i], color, 2)
        # rotated rectangle
        box = cv.boxPoints(minEllipse[i])
        # box = cv.boxPoints(minRect[i])
        box = np.intp(box)  # np.intp: Integer used for indexing (same as C ssize_t; normally either int32 or int64)
        # box = np.int0(box)  # normally either int32 or int64)
        cv.drawContours(drawing, [box], 0, color)


    cv.imshow('Contours', drawing)
    cv.imshow("Canny", canny_output)

绘制轮廓,轮廓外接矩阵,轮廓拟合出来的椭圆,随机颜色

注意 box = cv.boxPoints(minEllipse[i]) 绘制的是椭圆的外接矩阵,而 box = cv.boxPoints(minRect[i]) 绘制轮廓的外接矩阵被注释掉了

3、完整代码

python 复制代码
import cv2 as cv
import numpy as np
import argparse
import random as rng

rng.seed(12345)


def thresh_callback(val):
    global src_gray
    threshold = val
    src_gray = cv.GaussianBlur(src_gray, (3, 3), 0.1)
    canny_output = cv.Canny(src_gray, threshold, threshold * 2)

    contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

    # Find the rotated rectangles and ellipses for each contour
    minRect = [None] * len(contours)
    minEllipse = [None] * len(contours)
    for i, c in enumerate(contours):
        minRect[i] = cv.minAreaRect(c)
        if c.shape[0] > 5:
            minEllipse[i] = cv.fitEllipse(c)

    # Draw contours + rotated rects + ellipses

    drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)

    for i, c in enumerate(contours):
        color = (rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256))
        # contour
        cv.drawContours(drawing, contours, i, color)
        # ellipse
        if c.shape[0] > 5:
            cv.ellipse(drawing, minEllipse[i], color, 2)
        # rotated rectangle
        box = cv.boxPoints(minEllipse[i])
        # box = cv.boxPoints(minRect[i])
        box = np.intp(box)  # np.intp: Integer used for indexing (same as C ssize_t; normally either int32 or int64)
        # box = np.int0(box)  # normally either int32 or int64)
        cv.drawContours(drawing, [box], 0, color)


    cv.imshow('Contours', drawing)
    cv.imshow("Canny", canny_output)


parser = argparse.ArgumentParser(
    description='Code for Creating Bounding rotated boxes and ellipses for contours tutorial.')
parser.add_argument('--input', help='Path to input image.', default='1.png')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))

if src is None:
    print('Could not open or find the image:', args.input)
    exit(0)

# Convert image to gray and blur it
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
src_gray = cv.blur(src_gray, (3, 3))
source_window = 'Source'
cv.namedWindow(source_window)
cv.imshow(source_window, src)
max_thresh = 255
thresh = 100  # initial threshold
cv.createTrackbar('Canny Thresh:', source_window, thresh, max_thresh, thresh_callback)
thresh_callback(thresh)
cv.waitKey()

4、结果展示

Canny Thresh:0

Canny Thresh:5

Canny Thresh:15

Canny Thresh:25

Canny Thresh:35

Canny Thresh:45

Canny Thresh:100

Canny Thresh:150

Canny Thresh:200

Canny Thresh:255

5、涉及到的库函数

5.1、cv2.Canny

cv2.Canny 是 OpenCV 库中用于边缘检测的一个函数,它实现了 Canny 边缘检测算法。Canny 边缘检测是一种非常流行的边缘检测算法,由 John F. Canny 在 1986 年提出。这个算法旨在寻找图像中的最优边缘,它通过应用多尺度高斯滤波来减少噪声,然后计算图像梯度,接着通过非极大值抑制和滞后阈值化来检测边缘。

python 复制代码
edges = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
  • image:输入的灰度图像。在进行 Canny 边缘检测之前,通常需要先将图像转换为灰度图像。
  • threshold1:第一个阈值,用于滞后阈值化中的低阈值。
  • threshold2:第二个阈值,用于滞后阈值化中的高阈值。只有那些梯度值高于 threshold2 的像素才会被当作边缘,而梯度值位于 threshold1 和 threshold2 之间的像素只有在它们连接到高阈值边缘时才会被接受。
  • edges:输出参数,用于存储检测到的边缘。如果不指定,则会自动创建一个同大小的输出图像。
  • apertureSize:Sobel 算子的大小,默认为 3。这个参数影响梯度计算的精度,但增加大小也会增加计算时间。
  • L2gradient:一个布尔值,指示是否使用更精确的 L2 范数来计算图像梯度幅值。默认值为 False,即使用 L1 范数(即简单地将梯度在 x 和 y 方向的分量相加)。

返回值:

  • edges:一个二值图像,其中检测到的边缘像素被设置为白色(255),其他像素被设置为黑色(0)。

Canny 边缘检测的优点在于它能够有效地抑制噪声,并且检测到的边缘通常是连续的。然而,它的效果也依赖于所选的阈值,因此在实际应用中,可能需要根据具体情况调整 threshold1 和 threshold2 的值。

5.2 cv2.boxPoints

cv2.boxPoints 用于计算给定矩形旋转后的顶点坐标。这个函数在需要处理旋转矩形时非常有用,比如在图像中绘制旋转的边界框时

python 复制代码
points = cv2.boxPoints(box[, rotMat])

参数解释:

  • box:表示矩形的参数。这可以是一个包含四个元素的元组或列表 (center_x, center_y, width, height),其中 (center_x, center_y) 是矩形中心的坐标,width 和 height 分别是矩形的宽度和高度(注意:这里的宽度和高度是按照矩形的原始大小,不考虑旋转)。在某些版本的 OpenCV 中,box 也可能是一个 cv2.RotatedRect 对象。
  • rotMat:可选参数,表示旋转矩阵。这是一个 2x3 的浮点数数组,用于指定对矩形进行额外旋转的角度。如果未提供此参数,则矩形不会被额外旋转,只返回根据 box 参数计算出的四个顶点坐标。

返回值:

  • points:一个包含四个点的 NumPy 数组,每个点都是一个包含两个元素的元组或列表 (x, y),表示旋转后矩形的顶点坐标。

需要注意的是,如果 box 是一个 cv2.RotatedRect 对象,那么它本身就包含了旋转信息(即旋转角度和中心点),此时 rotMat 参数将被忽略,因为 cv2.RotatedRect 已经定义了矩形的旋转状态。

示例代码:

python 复制代码
import cv2  
import numpy as np  
  
# 定义一个中心点、宽度、高度的矩形  
center = (100, 100)  
width = 200  
height = 100  
angle = 45  # 旋转角度(以度为单位)  
  
# 创建一个旋转矩形对象  
rect = cv2.RotatedRect(center, (width, height), angle)  
  
# 获取旋转矩形的顶点  
points = cv2.boxPoints(rect)  
points = np.intp(points)  # 将坐标转换为整数类型,以便在图像上绘制  
  
# 创建一个黑色图像  
image = np.zeros((256, 256, 3), dtype=np.uint8)  
  
# 在图像上绘制旋转矩形的边  
for i in range(4):  
    cv2.line(image, tuple(points[i]), tuple(points[(i+1)%4]), (255, 0, 0), 2)  
# 显示图像  
cv2.imshow('Rotated Rectangle', image)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

在这个示例中,我们首先定义了一个矩形的中心点、宽度、高度和旋转角度,然后创建了一个 cv2.RotatedRect 对象来表示这个旋转矩形。接着,我们使用 cv2.boxPoints 函数来获取旋转矩形的顶点坐标,并在一个黑色图像上绘制了这个矩形的边。最后,我们显示了包含旋转矩形的图像。

6、参考

相关推荐
alphaTao1 小时前
LeetCode 每日一题 2024/10/28-2024/11/3
python·算法·leetcode
Liquor14191 小时前
JavaScript知识点梳理及案例实践
开发语言·前端·javascript·python·css3·html5·js
张小生1801 小时前
《YOLO 目标检测》—— YOLO v4 详细介绍
人工智能·python·yolo
无敌最俊朗@2 小时前
unity3d————球形插值知识点
开发语言·学习·unity·c#·游戏引擎
凡人的AI工具箱3 小时前
15分钟学 Go 第 37 天:综合复习与小项目
开发语言·后端·算法·golang
hummhumm4 小时前
Oracle 第22章:数据仓库与OLAP
java·javascript·后端·python·sql·mysql·database
归仁6 小时前
wvp 推拉转级联时频繁出现流无法观看的解决办法
linux·服务器·python·音视频
Trouvaille ~6 小时前
【C++篇】在秩序与混沌的交响乐中: STL之map容器的哲学探寻
开发语言·数据结构·c++·算法·迭代器模式·stl·map
开心工作室_kaic6 小时前
ssm042在线云音乐系统的设计与实现+jsp(论文+源码)_kaic
java·开发语言·php
334554327 小时前
element表格双击修改时间
开发语言·前端·javascript