实时图形识别的实现:模板匹配与几何特征方法的对比
在计算机视觉领域,图形识别是一个核心任务,它不仅应用广泛,还涉及到多种不同的实现方法。在本文中,我们将深入探讨两种实现实时图形识别的方法:一种基于模板匹配的方法和另一种基于几何特征的方法。通过分析这两种方法的实现细节和优缺点,我们将帮助读者理解它们各自的适用场景,并提供对实际应用中的选择参考。
第一种方法通过创建标准形状的模板(可以根据自己的需要创建更多的模版),并与捕获的轮廓进行匹配,来实现图形的识别。该方法依赖于预定义的形状模板,通过计算轮廓与模板的相似度来判断形状。这种方法具有较高的准确性,尤其适用于标准化形状的识别。然而,它也需要更多的计算资源,并且在处理不规则形状时可能会遇到挑战。
第二种方法则更加直接,它通过对轮廓进行几何特征分析来判断形状。这种方法不依赖于预定义的模板,而是通过轮廓的顶点数量和宽高比来进行判断。虽然这种方法实现起来相对简单,但它在形状识别的准确性和复杂性上可能有所妥协,特别是在面对不规则图形时。
接下来,我们将详细介绍这两种方法的实现细节,并比较它们在不同应用场景中的表现,帮助你选择最适合的图形识别技术。
目录
第一种实现方式(复杂)
代码简要
主要功能
主要组件
详细说明
运行效果展示
部分代码详细解释
(一)dtype=np.int32
详细解释:
示例
(二)triangle.reshape((a, b, c))
示例
作用
(三)cv2.findContours(a,b,c)
返回值
示例
(四)peri = cv2.arcLength(contour, True)
参数解释
返回值
(五)cv2.approxPolyDP(curve, epsilon, closed)
返回值
(六)cv2.matchShapes(contour1, contour2, method, parameter)
返回值
返回值
功能
整体代码
第二种实现方式(较为简单)
代码简要
主要功能
主要组件
代码流程
运行效果展示
对比总结
第一种实现方式(复杂)

代码简要
这段代码是一个使用 OpenCV 进行形状识别的 Python 程序,能够实时从摄像头捕捉图像并识别其中的几何形状。以下是对这段代码的详细说明:
主要功能
- 创建形状模板:定义标准的几何形状作为模板,以便与实时捕捉到的图像中的轮廓进行匹配。
- 形状检测:通过分析图像中的轮廓,判断其是否匹配已定义的标准形状。
- 显示结果:将识别出的形状类型和相似度显示在视频帧上。
主要组件
-
导入库:
import cv2 import numpy as npcv2是 OpenCV 的 Python 接口,用于计算机视觉任务。numpy是用于处理数组和矩阵的库。
-
创建形状模板:
def create_template_shapes(): """ 创建标准形状的模板,包括三角形、正方形、长方形和圆形。 返回一个包含所有标准形状轮廓的字典。 """ shapes = {} ... return shapescreate_template_shapes函数定义了四种标准形状:三角形、正方形、长方形和圆形。每种形状被存储为轮廓数据,并返回一个字典。
-
判断是否是三角形:
def is_triangle(contour): """ 判断给定的轮廓是否为三角形。 通过轮廓的顶点数来确定是否为三角形。 """ peri = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, 0.02 * peri, True) return len(approx) == 3is_triangle函数通过计算轮廓的周长并进行多边形逼近,检查顶点数量是否为 3,从而判断轮廓是否为三角形。
-
计算相似率并识别形状:
def detect_shape(contour, templates): """ 识别轮廓的形状,并计算与模板的相似度。 """ best_match = ("Unknown", float("inf")) ... return best_match[0], best_match[1]detect_shape函数计算轮廓与所有模板的相似度,并返回最佳匹配的形状类型及其相似度。
-
主函数:
def main(): """ 主函数:从摄像头捕捉视频流,进行形状识别并显示结果。 """ templates = create_template_shapes() cap = cv2.VideoCapture(0) ... cap.release() cv2.destroyAllWindows()main函数是程序的入口点,负责从摄像头捕捉视频流,处理每一帧图像,检测图像中的形状,并将识别结果显示在视频帧上。
详细说明
-
创建形状模板:
- 定义了标准形状(如三角形、正方形、长方形和圆形),并将这些形状转换为适合 OpenCV 匹配的轮廓格式。
-
判断三角形:
- 使用轮廓的顶点数来判断轮廓是否为三角形。如果轮廓的近似多边形有 3 个顶点,则认为它是三角形。
-
形状识别:
- 计算轮廓与模板形状的匹配度,识别出最相似的形状。如果识别出的是正方形或长方形,还会检查其宽高比来区分正方形和长方形。
-
处理和显示视频流:
- 从摄像头捕捉视频帧,对每一帧进行灰度转换、模糊处理和边缘检测。查找轮廓,识别形状,并在帧上绘制矩形框和识别结果。
-
退出程序:
- 用户可以按 'q' 键退出程序。释放摄像头资源,并关闭所有 OpenCV 窗口。
总体来说,这段代码实现了如何使用 OpenCV 进行实时形状识别,包括从摄像头捕捉图像、处理图像、识别形状和显示结果的全过程。
运行效果展示
长方形

正方形

三角形

圆

部分代码详细解释
(一)dtype=np.int32
dtype=np.int32
在 NumPy 中,dtype=np.int32 是一种数据类型指定,用于定义数组中元素的数据类型。具体来说:
dtype是 NumPy 中用于定义数组元素类型的参数。np.int32是 NumPy 提供的一个数据类型,用于表示 32 位有符号整数。
详细解释:
-
数据类型
np.int32:np.int32表示一个 32 位的有符号整数,能够存储的整数范围从 -2,147,483,648 到 2,147,483,647。int32是 C 语言中的int类型在 32 位系统上的等效类型。
-
为何使用
np.int32:- 在创建数组时,指定
dtype可以确保数组中的元素按照预期的格式存储。例如,如果你知道你的数据将是整数,并且范围在 32 位有符号整数的范围内,使用np.int32可以优化存储效率和计算性能。 - 这对于涉及到图像处理、科学计算等应用非常重要,因为它帮助确保数据的一致性和避免内存浪费。
- 在创建数组时,指定
示例
import numpy as np # 创建一个整数数组,元素类型为 int32
array = np.array([1, 2, 3, 4], dtype=np.int32)
print(array)
print(array.dtype) # 输出: int32
在我们的代码中,dtype=np.int32 用于定义形状坐标的类型,确保所有坐标点都是 32 位的整数。这是因为图像处理和计算中经常需要对像素位置和图形轮廓进行精确的整数运算。
(二)triangle.reshape((a, b, c))
shapes["Triangle"] = triangle.reshape((-1, 1, 2))
-
原始数组:
triangle = np.array([[0, 0], [100, 0], [50, 100]], dtype=np.int32)这是一个二维数组,表示三角形的三个顶点坐标。它的形状是
(3, 2),即有 3 个顶点,每个顶点有 2 个坐标(x 和 y)。 -
reshape方法:triangle.reshape((-1, 1, 2))-1:这个参数表示“自动推断”,NumPy 会根据其他维度的大小来计算这一维度的大小。在这里,它自动推断出第一个维度的大小。1:第二个维度的大小为 1。2:第三个维度的大小为 2(代表 x 和 y 坐标)。
-
重塑后的数组:
- 原数组的形状
(3, 2)被重塑为(-1, 1, 2),即(3, 1, 2)。 - 这意味着,数组现在有 3 个子数组,每个子数组包含 1 个点,每个点有 2 个坐标(x 和 y)。
- 原数组的形状
示例
假设原始数组是:
triangle = np.array([[0, 0], [100, 0], [50, 100]], dtype=np.int32)
调用 reshape 后:
reshaped_triangle = triangle.reshape((-1, 1, 2))
reshaped_triangle 将变成:
array([[[ 0, 0]], [[100, 0]], [[ 50, 100]]], dtype=int32)
作用
在 OpenCV 中,轮廓通常需要以3D的形状传递给函数。特别是,OpenCV 的一些函数(如 cv2.drawContours)期望轮廓数组的形状是 (n, 1, 2),其中 n 是轮廓中点的数量,1 表示每个点的单独数组,2 表示每个点的坐标(x 和 y)。
因此,通过 reshape 操作,可以将三角形顶点数组调整为 OpenCV 期望3D的形状格式。
(三)cv2.findContours(a,b,c)
contours, _ = cv2.findContours(circle, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
-
circle:- 这是输入图像,通常是二值化(黑白)图像。在我们的代码中,它是一个圆形的二值图像,其中圆形区域为白色,背景为黑色。
-
cv2.RETR_EXTERNAL:- 这是轮廓检索模式的参数。
cv2.RETR_EXTERNAL表示只检索最外层的轮廓(即外轮廓)。这意味着只有最外层的轮廓会被检测到,内部的轮廓会被忽略。
- 这是轮廓检索模式的参数。
-
cv2.CHAIN_APPROX_SIMPLE:- 这是轮廓逼近方法的参数。
cv2.CHAIN_APPROX_SIMPLE表示只保存轮廓的端点坐标,压缩轮廓的水平、垂直和对角线段,只保留轮廓的主要部分。简化轮廓可以减少内存占用并提高处理速度,也有一定模糊匹配的意思,使得你的圆不必那么的“圆”。
- 这是轮廓逼近方法的参数。
返回值
-
contours:- 这是一个列表,其中每个元素都是一个轮廓。每个轮廓是一个包含轮廓点的 NumPy 数组。对于二值图像中的每个轮廓,
contours列表中的元素将是该轮廓的点集合。
- 这是一个列表,其中每个元素都是一个轮廓。每个轮廓是一个包含轮廓点的 NumPy 数组。对于二值图像中的每个轮廓,
-
_:- 第二个返回值是层级信息(
hierarchy),它在cv2.RETR_EXTERNAL模式下不被使用,因此用_来忽略这个值。层级信息提供了轮廓之间的层级关系,但在你的代码中不需要。
- 第二个返回值是层级信息(
示例
假设你有一个简单的二值图像,其中包含一个圆形:
# 创建一个圆形的二值图像
circle = np.zeros((200, 200), dtype=np.uint8)
cv2.circle(circle, (100, 100), 100, 255, -1)
# 找到图像中的轮廓
contours, _ = cv2.findContours(circle, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 打印轮廓的信息
print(len(contours)) # 1,表示一个轮廓
在这个例子中,contours 将是一个包含一个轮廓的列表,因为图像中只有一个圆形。每个轮廓是一个 NumPy 数组,包含了圆形的所有轮廓点。
(四)peri = cv2.arcLength(contour, True)
这个函数通常用于计算轮廓的周长(即轮廓线的长度)。这个函数通常用于计算图像中物体边界的长度。
参数解释
-
contour:- 类型: NumPy 数组
- 描述: 轮廓点的数组。这个数组是通过
cv2.findContours函数检测到的轮廓之一。每个轮廓点是一个二维坐标(x, y),并且轮廓是按顺序连接的点的集合。
例如,一个简单的轮廓可能是:
contour = np.array([[[10, 10]], [[20, 10]], [[20, 20]], [[10, 20]]], dtype=np.int32) -
closed:-
类型: 布尔值 (
True或False) -
描述: 指示轮廓是否是封闭的。如果设置为
True,则计算周长时将考虑轮廓的起点和终点之间的连线。如果设置为False,则不会将轮廓的起点和终点连线计算在内,适用于开口轮廓。 -
True: 表示轮廓是封闭的,即轮廓的起点和终点连线也会被考虑在内。 -
False: 表示轮廓是开口的,即不考虑起点和终点的连线。
-
返回值
- 返回类型:
float - 描述: 返回轮廓的周长,以像素为单位。如果轮廓是封闭的,返回的值是轮廓的完整周长;如果是开口的轮廓,返回的值则是仅计算的部分周长。
(五)cv2.approxPolyDP(curve, epsilon, closed)
approx = cv2.approxPolyDP(contour, 0.02 * peri, True)
-
curve:- 类型: NumPy 数组
- 描述: 输入的轮廓点集(即需要进行多边形逼近的轮廓)。这个轮廓通常是通过
cv2.findContours检测到的,并且是一个由多个点组成的数组。
-
epsilon:-
类型:
float -
描述: 逼近精度的参数,表示多边形逼近的误差范围。它控制了逼近的精度,即多边形与原始轮廓的最大距离。如果
epsilon较小,逼近的多边形将更接近原始轮廓;如果epsilon较大,逼近的多边形将更简单(即轮廓点更少)。 -
在你的代码中,
epsilon是通过0.02 * peri计算得到的,其中peri是轮廓的周长。这意味着epsilon是轮廓周长的 2%,这个比例可以根据实际需要进行调整。例如,可以设置为轮廓周长的 1% 到 5% 之间,具体取决于轮廓的复杂性和需要的逼近精度。
-
-
closed:-
类型: 布尔值 (
True或False) -
描述: 指示逼近的多边形是否是封闭的。如果设置为
True,则逼近的多边形将闭合,即起点和终点会相连。如果设置为False,则不会闭合轮廓。 -
True: 表示多边形是封闭的。 -
False: 表示多边形是开口的(如线段)。
-
返回值
- 返回类型: NumPy 数组
- 描述: 返回一个近似的多边形点集。这些点表示对输入轮廓的逼近结果,点的数量通常比原始轮廓点的数量少。
(六)cv2.matchShapes(contour1, contour2, method, parameter)
match_value = cv2.matchShapes(contour, template, cv2.CONTOURS_MATCH_I1, 0.0)
-
contour1:- 类型: NumPy 数组(或轮廓点集)
- 描述: 需要与
contour2进行比较的轮廓。
-
contour2:- 类型: NumPy 数组(或轮廓点集)
- 描述: 用于比较的参考轮廓。
-
method:- 类型: 整数
- 描述: 匹配方法的类型。OpenCV 提供了几种不同的方法:
cv2.CONTOURS_MATCH_I1: 形状匹配方法 1(比较形状的 Hu 矩)cv2.CONTOURS_MATCH_I2: 形状匹配方法 2cv2.CONTOURS_MATCH_I3: 形状匹配方法 3
- 我们选择的方法决定了形状比较的方式。
cv2.CONTOURS_MATCH_I1是最常用的方法,它基于 Hu 矩来进行匹配。
-
parameter:- 类型: 浮点数
- 描述: 用于调整匹配方法的额外参数。对于大多数方法,通常设置为
0.0。具体的参数值取决于所选择的匹配方法。
返回值
- 返回类型:
float - 描述: 返回值是一个表示两个轮廓相似度的浮点数。数值越小,表示轮廓之间的形状越相似。每种方法的返回值范围和含义可能略有不同,但通常都遵循这种“值越小,匹配度越高”的规则。
(七)x, y, w, h = cv2.boundingRect(contour)
x, y, w, h = cv2.boundingRect(contour)
contour:- 类型: NumPy 数组(轮廓点集)
- 描述: 要计算外接矩形的轮廓。轮廓应该是一个包含一系列点的数组,通常是通过轮廓检测函数(如
cv2.findContours)获得的。
返回值
-
x:- 类型: 整数
- 描述: 外接矩形的左上角 x 坐标。
-
y:- 类型: 整数
- 描述: 外接矩形的左上角 y 坐标。
-
w:- 类型: 整数
- 描述: 外接矩形的宽度。
-
h:- 类型: 整数
- 描述: 外接矩形的高度。
功能
cv2.boundingRect 计算并返回一个矩形,它能完全包围传入的轮廓。这个矩形的边界被定义为其左上角的坐标 (x, y) 和其宽度 (w) 及高度 (h)。矩形的边缘与轮廓的所有点都相接或在其内部。
整体代码
import cv2
import numpy as np# 预定义形状生成函数
def create_template_shapes():"""创建标准形状的模板,包括三角形、正方形、长方形和圆形。返回一个包含所有标准形状轮廓的字典。"""shapes = {}# 创建标准三角形# np.int32 表示一个 32 位的有符号整数,能够存储的整数范围从 -2,147,483,648 到 2,147,483,647。triangle = np.array([[0, 0], [100, 0], [50, 100]], dtype=np.int32)shapes["Triangle"] = triangle.reshape((-1, 1, 2))# 创建标准正方形square = np.array([[0, 0], [100, 0], [100, 100], [0, 100]], dtype=np.int32)shapes["Square"] = square.reshape((-1, 1, 2))# 创建标准长方形rectangle = np.array([[0, 0], [150, 0], [150, 100], [0, 100]], dtype=np.int32)shapes["Rectangle"] = rectangle.reshape((-1, 1, 2))# 创建标准圆形circle = np.zeros((200, 200), dtype=np.uint8)cv2.circle(circle, (100, 100), 100, 255, -1)contours, _ = cv2.findContours(circle, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 奖轮廓数据存储到字典中shapes["Circle"] = contours[0]return shapes# 判断是否是三角形
def is_triangle(contour):"""判断给定的轮廓是否为三角形。通过轮廓的顶点数来确定是否为三角形。:param contour: 输入轮廓:return: 如果是三角形返回 True,否则返回 False"""# 计算轮廓的周长# true表示的封闭图形peri = cv2.arcLength(contour, True)# 逼近多边形,得到近似的多边形轮廓approx = cv2.approxPolyDP(contour, 0.02 * peri, True)# 判断顶点数量是否为 3return len(approx) == 3# 计算相似率并识别形状
def detect_shape(contour, templates):"""识别轮廓的形状,并计算与模板的相似度。:param contour: 输入轮廓:param templates: 形状模板字典:return: 识别出的形状类型和相似度"""best_match = ("Unknown", float("inf"))# 遍历所有模板形状for shape_name, template in templates.items():# 计算轮廓与模板的匹配值# CONTOURS_MATCH_I1表示使用Hu矩来进行匹配match_value = cv2.matchShapes(contour, template, cv2.CONTOURS_MATCH_I1, 0.0)if match_value < best_match[1]:# 是目前最好的匹配值,那么存入这对kvbest_match = (shape_name, match_value)# TODO 光是上面的计算方式测试之后发现对长方形和正方形识别不佳准备修改# 额外检查四边形的宽高比if best_match[0] in ["Square", "Rectangle"]:# 得到这矩形的数据分别是:左上角x坐标,左上角y坐标,矩形宽度,矩形高度x, y, w, h = cv2.boundingRect(contour)# 计算宽高比判断是否为正方形aspect_ratio = w / float(h)# 这里采用0.9~1.1的阈值判断为正方形if 0.9 <= aspect_ratio <= 1.1:return "Square", best_match[1]else:return "Rectangle", best_match[1]return best_match[0], best_match[1]def main():"""主函数:从摄像头捕捉视频流,进行形状识别并显示结果。"""# 创建形状模板templates = create_template_shapes()# 打开摄像头cap = cv2.VideoCapture(0)while True:# 捕捉视频帧ret, frame = cap.read()# 将帧转换为灰度图gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 应用高斯模糊以减少噪声blurred = cv2.GaussianBlur(gray, (5, 5), 0)# 使用 Canny 边缘检测edged = cv2.Canny(blurred, 50, 150)# 查找轮廓contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)if contours:# 找到最大的轮廓(即离摄像头最近的)largest_contour = max(contours, key=cv2.contourArea)# 判断是否为三角形if is_triangle(largest_contour):shape_type = "Triangle"# 这里的相似度是写死的,因为并没有通过相似度判断三角形similarity = 0.0else:# 进行轮廓逼近peri = cv2.arcLength(largest_contour, True)# 对轮廓进行简化,将轮廓的点数减少到一个更简单的多边形,同时控制近似的精度。approx = cv2.approxPolyDP(largest_contour, 0.02 * peri, True) # 识别形状shape_type, similarity = detect_shape(approx, templates)# 计算外接矩形并绘制x, y, w, h = cv2.boundingRect(largest_contour)cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)# 标注图形类型和相似度label = f"{shape_type} ({similarity:.4f})"cv2.putText(frame, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)# 显示处理后的帧cv2.imshow("Frame", frame)# 按 'q' 键退出if cv2.waitKey(1) & 0xFF == ord("q"):break# 释放摄像头资源cap.release()# 关闭所有 OpenCV 窗口cv2.destroyAllWindows()if __name__ == "__main__":main()
第二种实现方式(较为简单)

代码简要
这次的实现思路在第一种之中有体现,是我在被识别三角形困扰到之后想到的简要解决方式,即使用顶点数量来判断是什么类型的图形。以下是对这段代码的整体说明:
主要功能
- 从摄像头捕捉视频流:使用 OpenCV 捕捉实时视频帧。
- 处理图像:将视频帧转换为灰度图像、应用模糊和边缘检测。
- 识别图形:通过分析轮廓来识别图形(如三角形、正方形、长方形和圆)。
- 显示结果:在图像上绘制识别到的图形的外接矩形,并标注图形类型。
主要组件
-
导入库:
import cv2 import numpy as npcv2是 OpenCV 的 Python 接口,用于计算机视觉任务。numpy是用于处理数组和矩阵的库。
-
形状检测函数:
def detect_shape(contour):# 使用多边形逼近来处理轮廓peri = cv2.arcLength(contour, True)approx = cv2.approxPolyDP(contour, 0.04 * peri, True)# 根据顶点数量来判断形状if len(approx) == 3:# 三角形return "Triangle"elif len(approx) == 4:# 使用最小矩形来区分正方形和长方形(x, y, w, h) = cv2.boundingRect(approx)aspect_ratio = w / float(h)if aspect_ratio >= 0.90 and aspect_ratio <= 1.1:# 正方形return "Square"else:# 长方形return "Rectangle"elif len(approx) > 4:# 圆return "Circle"return "Unknown"detect_shape函数用于识别轮廓的形状:- 通过
cv2.approxPolyDP逼近轮廓得到多边形近似。 - 根据顶点数量判断形状:
- 3 个顶点是三角形。
- 4 个顶点用宽高比区分正方形和长方形。
- 多于 4 个顶点认为是圆形。
- 通过
-
主函数:
def main():cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)blurred = cv2.GaussianBlur(gray, (5, 5), 0)edged = cv2.Canny(blurred, 50, 150)contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)if contours:# 找到最大的轮廓(即离摄像头最近的)largest_contour = max(contours, key=cv2.contourArea)shape_type = detect_shape(largest_contour)if shape_type:# 计算外接矩形并绘制x, y, w, h = cv2.boundingRect(largest_contour)cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)# 标注图形类型cv2.putText(frame, shape_type, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)cv2.imshow("Frame", frame)if cv2.waitKey(1) & 0xFF == ord("q"):breakcap.release()cv2.destroyAllWindows()main函数是程序的入口:- 打开摄像头捕捉视频帧。
- 对每一帧进行灰度转换、模糊处理和边缘检测。
- 查找轮廓并识别最大轮廓的形状。
- 在视频帧上绘制该轮廓的外接矩形,并标注识别到的形状类型。
- 按 'q' 键退出程序,释放摄像头资源并关闭所有 OpenCV 窗口。
代码流程
- 捕捉视频帧:从摄像头读取实时视频帧。
- 图像预处理:
- 转换为灰度图像。
- 应用高斯模糊减少噪声。
- 使用 Canny 边缘检测来提取边缘。
- 轮廓检测:查找图像中的轮廓。
- 形状识别:
- 找到最大的轮廓(认为是离摄像头最近的对象)。
- 通过
detect_shape函数判断轮廓的形状。
- 结果显示:
- 绘制外接矩形框。
- 在图像上标注识别的形状类型。
- 退出程序:按 'q' 键退出,释放资源并关闭窗口。
这个代码示例实现了通过另一种思路(判断图像的顶点数量)来实现这个功能。
整体代码
import cv2
import numpy as npdef detect_shape(contour):# 使用多边形逼近来处理轮廓peri = cv2.arcLength(contour, True)approx = cv2.approxPolyDP(contour, 0.04 * peri, True)# 根据顶点数量来判断形状if len(approx) == 3:# 三角形return "Triangle"elif len(approx) == 4:# 使用最小矩形来区分正方形和长方形(x, y, w, h) = cv2.boundingRect(approx)aspect_ratio = w / float(h)if aspect_ratio >= 0.90 and aspect_ratio <= 1.1:# 正方形return "Square"else:# 长方形return "Rectangle"elif len(approx) > 4:# 圆return "Circle"return "Unknown"def main():cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)blurred = cv2.GaussianBlur(gray, (5, 5), 0)edged = cv2.Canny(blurred, 50, 150)contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)if contours:# 找到最大的轮廓(即离摄像头最近的)largest_contour = max(contours, key=cv2.contourArea)shape_type = detect_shape(largest_contour)if shape_type:# 计算外接矩形并绘制x, y, w, h = cv2.boundingRect(largest_contour)cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)# 标注图形类型cv2.putText(frame, shape_type, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)cv2.imshow("Frame", frame)if cv2.waitKey(1) & 0xFF == ord("q"):breakcap.release()cv2.destroyAllWindows()if __name__ == "__main__":main()
运行效果展示




对比总结
-
复杂度:
- 第一段代码:复杂,通过模板匹配实现形状识别,能够识别多种标准形状,并计算相似度。适用于需要比较标准形状的应用。
- 第二段代码:简单,通过几何特征(顶点数和宽高比)直接识别形状。适用于对实时性要求高但形状种类较少的应用。
-
准确性:
- 第一段代码:可以提供更高的识别准确性,特别是对于标准形状,因为它利用了模板匹配。
- 第二段代码:准确性依赖于轮廓逼近和简单的几何特征,可能在某些情况下不如第一段代码精确,尤其是在形状不规则或复杂时。
-
实现难度:
- 第一段代码:实现较复杂,需要生成标准形状模板并进行模板匹配。
- 第二段代码:实现较简单,直接通过几何特征判断形状,无需预定义模板。
-
计算开销:
- 第一段代码:可能有较高的计算开销,特别是在模板匹配阶段。
- 第二段代码:计算开销较低,直接通过轮廓特征进行形状识别。
根据实际需求,可以选择适合的实现方式。例如,对于标准形状识别任务,第一段代码可能更合适;而对于实时性要求高的简单形状识别任务,第二段代码可能更优。
