【图像轮廓特征查找】图像处理(OpenCV) -part8

17 图像轮廓特征查找

图像轮廓特征查找其实就是他的外接轮廓。

应用:

图像分割

形状分析

物体检测与识别

根据轮廓点进行,所以要先找到轮廓。

先灰度化、二值化。目标物体白色,非目标物体黑色,选择合适的儿值化方式。

有了轮廓点就可以找到最上、最下、最左、最右的四个坐标,X_{min}、X_{max}、Y_{min}、Y_{max}。就可以绘制出矩形。

17.1 外接矩形

复制代码
boundingRect(轮廓点)

形状的外接矩形有两种,如下图,绿色的叫外接矩形,表示不考虑旋转并且能包含整个轮廓的矩形。其中,外接矩形可根据获得到的轮廓坐标中最上、最下、最左、最右的点的坐标来绘制外接矩形,也就是下图中的绿色矩形。蓝色的是最小外接矩形,会考虑面积。

cv.boundingRect 函数用于计算一组点(通常是轮廓)的最小外接矩形。这个矩形是一个 轴对齐的矩形,也就是说,它的边与图像坐标轴平行。

作用

快速获取一个能够包含目标轮廓的矩形边界。

简单直观地定位目标轮廓的大致区域。

返回参数

x:外接矩形左上角的 x 坐标。

y:外接矩形左上角的 y 坐标。

w:外接矩形的宽度。

h:外接矩形的高度。


python 复制代码
import cv2 as cv
import numpy as np
img = cv.imread('images/ikun4.jpg')
#灰度图
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#二值化处理,阈值法 因为目标是白色我们需要白色
ret, binary = cv.threshold(gray, 200, 255, cv.THRESH_BINARY_INV)
#查找轮廓
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
    #查找凸包
    hull = cv.convexHull(contours[i])
    #绘制凸包
    cv.polylines(img, [hull], True, (100, 100, 255), 3)
#画出外接矩形
for i in range(len(contours)):
    x, y, w, h = cv.boundingRect(contours[i])
    cv.rectangle(img, (x, y), (x + w, y + h), (255, 255, 0), 2)
cv.imshow('1', img)
# cv.imshow('2', binary)
cv.waitKey(0)
cv.destroyAllWindows()

获取方式

在代码中,通过循环遍历查找到的轮廓 contours,对每个轮廓调用 cv.boundingRect 函数来获取其外接矩形的位置和大小参数,包括左上角顶点坐标(x,y)、宽度 w 和高度 h。

绘制目的

使用 cv.rectangle 函数在原图上绘制出这些外接矩形,通过矩形的边界将目标轮廓大致框选出来,便于更直观地观察和分析目标在图像中的分布情况,以及与其他图像元素的相对位置关系。

17.2 最小外接矩形

定义与原理

最小外接矩形 是能够完全包含目标轮廓的最小面积的矩形,它允许矩形相对于图像坐标轴旋转,因此可以更好地贴合轮廓的形状,相比普通外接矩形(轴对齐的外接矩形),它可以更精确地描述轮廓的形状和方向。

其基本原理是通过计算轮廓点集的最小面积矩形,这个矩形的边不一定与图像坐标轴对齐。它需要找到一组四个点,使得这四个点构成的矩形能够包含所有轮廓点,并且面积是所有可能的包含该轮廓的矩形中最小的。

计算方法

从轮廓点集出发,利用几何算法来确定最小外接矩形的边界。

一种常见的方法是基于旋转卡壳(Rotating Calipers)算法,它通过旋转两条平行的切线来寻找能够包含整个点集的最小矩形。具体步骤为:首先找到点集的凸包;然后在凸包上选择两个点作为初始两个的顶点,这两点确定一条边;接着,寻找与该边相对的另一条边,使得这两条边之间的距离最大,从而形成一个初始的矩形;最后,逐步旋转这两条边,找到使矩形面积最小的那一对边,从而确定最小外接矩形。

在 OpenCV 中,可以通过 cv.minAreaRect 函数获取轮廓的最小外接矩形,该函数返回一个包含中心坐标、长宽以及旋转角度的元组,利用这些参数可以绘制出最小外接矩形。

需要使用到的API说明:

contours, hierarchy = cv2.findContours(image_np_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

contours为二值图像上查找所有的外部轮廓

rect = cv2.minAreaRect(cnt)

传入的cnt参数为contours中的轮廓,可以遍历contours中的所有轮廓,然后计算出每个轮廓的小面积外接矩形

rect 是计算轮廓最小面积外接矩形:rect 结构通常包含中心点坐标 (x, y)、宽度 width、高度 height 和旋转角度 angle

cv2.boxPoints(rect).astype(int)

cv2.boxPoints(rect)返回 是一个形状为 4行2列的数组,每一行代表一个点的坐标(x, y),顺序按照逆时针或顺时针方向排列

将最小外接矩形转换为边界框的四个角点,并转换为整数坐标

cv2.drawContours(image, contours, contourIdx, color, thickness)

image:原图像,一般为 numpy 数组,通常为灰度或彩色图像。

contours:一个包含多个轮廓的列表,可以用上一个api得到的 box

contourIdx :要绘制的轮廓索引。如果设置为 -1,则绘制所有轮廓。

color :轮廓的颜色,可以是 BGR 颜色格式的三元组,例如 (0, 0, 255) 表示红色。

thickness:轮廓线的粗细,如果是正数,则绘制实线;如果是 0,则绘制轮廓点;如果是负数,则填充轮廓内部区域。


python 复制代码
#最小外接矩形 旋转卡壳法
#读取图像
img = cv.imread('images/feiwu.png')
gary = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#二值化
ret, binary = cv.threshold(gary, 200, 255, cv.THRESH_BINARY_INV)
#查找轮廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
    #最小外接矩形 rect = (center(x,y), (width, height), angle)
    rect = cv.minAreaRect(contours[i])
    box = cv.boxPoints(rect).astype(np.int32)
    cv.drawContours(img, [box], -1, (0, 0, 255), 2,cv.LINE_AA)
#画出外接矩形
for i in range(len(contours)):
    x, y, w, h = cv.boundingRect(contours[i])
    cv.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 2)
cv.imshow("1", img)
cv.waitKey(0)
cv.destroyAllWindows()

绘制外接矩形和最小外接矩形是有区别的

17.3 最小外接圆

寻找最小外接圆使用的算法是Welzl算法。Welzl算法基于一个定理:希尔伯特圆定理表明,对于平面上的任意三个不在同一直线上的点,存在一个唯一的圆同时通过这三个点,且该圆是最小面积的圆(即包含这三个点的圆中半径最小的圆,也称为最小覆盖圆)。

进一步推广到任意 n 个不在同一圆上的点,总存在一个唯一的最小覆盖圆包含这 n 个点。

若已经存在平面上互不共线(或共圆)的 n 个点,并确定了它们的最小覆盖圆,那么添加第 n+1 个点,并且要求这个点不在原来的最小覆盖圆内(即在圆外),为了使新的包含 n+1 个点的最小覆盖圆的半径增大,新加入的点必须位于由原 n 个点确定的最小覆盖圆的边界上(即圆周上)。这是因为,如果新点在原最小覆盖圆的内部,显然不会影响最小覆盖圆;如果新点在原最小覆盖圆之外但不在圆周上,那么通过新点和至少两个原有圆上的点可以构造出一个更大的圆,这个圆必然比原最小覆盖圆更大,因此不是包含所有 n+1 个点的最小覆盖圆。所以,按照这一逻辑,当第 n+1 个点在原 n 个点的最小覆盖圆外时,确实这个点会位于包含所有 n+1 个点的新最小覆盖圆的圆周上。

有了这个定理,就可以先取3个点建立一个圆(不共线的三个点即可确定一个圆,如果共线就取距离最远的两个点作为直径建立圆),然后遍历剩下的所有点,对于遍历到的点P来说:

如果该点在圆内,那么最小覆盖圆不变。

如果该点在圆外,根据上述定理,该点一定在想要求得的最小覆盖圆的圆周上,又因为三个点才能确定一个圆,所以需要枚举P点之前的点来找其余的两个点。当找到与P点组成的圆能够将所有点都包含在圆内或圆上,该圆就是这些点的最小外接圆。

在OpenCV中,可以直接使用cv2.minEnclosingCircle() 来获取最小外接圆,该函数只需要输入一个参数,就是要绘制最小外接圆的点集的坐标,然后会返回最小外接圆的圆心坐标与半径。通过该函数返回的内容信息即可绘制某点集的最小外接圆。如下图所示:

需要使用的API说明

cv2.minEnclosingCircle(points) -> (center, radius)

参数说明:

points:输入参数图片轮廓数据

返回值:

center:一个包含圆心坐标的二元组 (x, y)

radius:浮点数类型,表示计算得到的最小覆盖圆的半径。

cv2.circle(img, center, radius, color, thickness)

img:输入图像,通常是一个numpy数组,代表要绘制圆形的图像。

center:一个二元组 (x, y),表示圆心的坐标位置。

radius:整型或浮点型数值,表示圆的半径长度。

color:颜色标识,可以是BGR格式的三元组 (B, G, R),例如 (255, 0, 0) 表示红色。

thickness:整数,表示圆边框的宽度。如果设置为 -1,则会填充整个圆。

复制代码
python 复制代码
img = cv.imread('images/haimbb.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_BINARY_INV)
#查找轮廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
    #绘制外接圆
    (x, y), radius = cv.minEnclosingCircle(contours[i])
    center = (int(x), int(y))
    radius = int(radius)
    cv.circle(img, center, radius, (128, 128, 128), 2)
    #绘制外接矩形
    rect = cv.minAreaRect(contours[i])
    box = cv.boxPoints(rect)
cv.imshow("1", img)
cv.imshow("2", binary)
cv.waitKey(0)
cv.destroyAllWindows()

18 直方图均衡化

相关推荐
米小虾16 分钟前
Loop Engineering —— 循环的设计与自主执行
人工智能·agent
米小虾29 分钟前
Harness Engineering —— 系统的安全护栏
人工智能·agent
火山引擎开发者社区44 分钟前
积分当钱花,火山引擎开发者激励计划首月消费双倍回馈
人工智能
aqi001 小时前
15天学会AI应用开发(十)把文本嵌入模型换成国产模型
人工智能·python·ai编程
MobotStone2 小时前
为什么在AI时代,“好奇心”成了最值钱的能力?
人工智能
武子康3 小时前
调查研究-200 llama.cpp b9754:一次很小但很关键的 Agent 工具调用修复
人工智能·agent·llama
Ralph_Salar3 小时前
从0到1搭建AI智能支付风控助手Stage1-RAG知识库升级 — 元数据让检索更精准
人工智能
武子康3 小时前
调查研究-199 MCP Zero-Touch OAuth:为什么它是 MCP 进入企业生产的关键门槛?
人工智能·agent·mcp