OpenCV4.8 开发实战系列专栏之 49 二值图像分析 -轮廓外接矩形
欢迎大家学习OpenCV4.8 开发实战专栏,长期更新,不断分享源码。
专栏代码全部基于C++ 与Python双语演示。
送相关学习资料V : OpenCVXueTang_Asst
本文关键知识点: 二值图像分析 -轮廓外接矩形
对图像二值图像的每个轮廓,OpenCV都提供了API可以求取轮廓的外接矩形,其中求取轮廓外接矩形有两种方式一种是可以基于像素的最大外接轮廓短形,API解释如下:
Rect cv::boundingRect(
InputArray points
)
输入参数points可以一系列点的集合,对轮来说就是该轮的点集
返回结果是一个矩形,xy w,h
RotatedRect cv::minAreaRect(
InputArray points
)
输入参数points可以一系列点的集合,对轮来说就是该轮的点集
返回结果是一个旋转矩形,包含下面的信息:
- 矩形中心位置
- 矩形的宽高
- 旋转角度
在二值图像分析中,轮廓外接矩形是一种常用的几何特征提取方法,用于描述图像中目标物体的形状和位置信息。通过计算轮廓的外接矩形,可以获取目标物体的最小包围矩形,从而简化形状分析、目标定位和测量等任务。以下是关于轮廓外接矩形的详细分析:
1. 外接矩形的类型
在OpenCV等图像处理库中,轮廓外接矩形主要分为两种类型:
-
轴对齐外接矩形(AABB, Axis-Aligned Bounding Box)
通过cv2.boundingRect()
函数计算,返回一个与坐标轴平行的矩形。该矩形是最小的能够完全包围轮廓的矩形,但方向与坐标轴对齐,因此可能不是面积最小的包围矩形。- 输出参数:矩形的左上角坐标 ((x, y))、宽度 (w) 和高度 (h)。
- 适用场景:适用于目标物体方向已知或不需要考虑旋转的情况。
-
旋转外接矩形(OBB, Oriented Bounding Box)
通过cv2.minAreaRect()
函数计算,返回一个可以旋转的矩形,其面积是最小的。该矩形能够更好地适应倾斜或旋转的目标物体。- 输出参数:矩形的中心点坐标 ((x, y))、宽度 (w)、高度 (h) 和旋转角度 (\theta)(以度为单位,范围为 ([-90, 0)))。
- 适用场景:适用于目标物体方向未知或需要精确描述形状的情况。
2. 计算外接矩形的步骤
-
轮廓提取
首先,使用cv2.findContours()
函数从二值图像中提取轮廓。轮廓是图像中目标物体的边界,由一系列点组成。contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
-
计算外接矩形
- 轴对齐外接矩形:
for contour in contours:x, y, w, h = cv2.boundingRect(contour)cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
- 旋转外接矩形:
for contour in contours:rect = cv2.minAreaRect(contour)box = cv2.boxPoints(rect) # 获取矩形的四个顶点box = np.int0(box) # 转换为整数坐标cv2.drawContours(image, [box], 0, (0, 0, 255), 2)
- 轴对齐外接矩形:
-
绘制外接矩形
将计算得到的外接矩形绘制在原图像上,以便可视化结果。
3. 外接矩形的应用
-
目标定位
通过外接矩形可以快速定位目标物体的位置和大小,常用于目标检测、跟踪和识别任务。 -
形状分析
外接矩形的宽高比、面积等参数可以用于描述目标物体的形状特征,辅助分类和识别。 -
碰撞检测
在机器人导航、游戏开发等领域,外接矩形可用于快速判断物体是否发生碰撞。 -
图像压缩与编码
通过外接矩形可以减少不必要的像素处理,提高图像处理效率。
4. 注意事项
-
轮廓选择
在计算外接矩形之前,需要根据实际需求选择合适的轮廓。例如,可以使用轮廓面积、周长等特征过滤掉噪声或小物体。 -
旋转角度的处理
旋转外接矩形的角度 (\theta) 表示矩形长边与水平轴的夹角。需要注意的是,角度范围为 ([-90, 0)),且矩形的宽和高可能对应于长边和短边,具体取决于实现方式。 -
多轮廓处理
如果图像中存在多个目标物体,需要对每个轮廓分别计算外接矩形,并根据实际需求进行合并或筛选。
5. 示例代码
以下是一个完整的示例代码,展示如何从二值图像中提取轮廓并计算外接矩形:
import cv2
import numpy as np# 读取图像并转换为灰度图
image = cv2.imread('binary_image.png', cv2.IMREAD_GRAYSCALE)# 二值化处理
_, binary_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)# 提取轮廓
contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 创建彩色图像用于绘制结果
result = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)# 计算并绘制外接矩形
for contour in contours:# 轴对齐外接矩形x, y, w, h = cv2.boundingRect(contour)cv2.rectangle(result, (x, y), (x + w, y + h), (0, 255, 0), 2)# 旋转外接矩形rect = cv2.minAreaRect(contour)box = cv2.boxPoints(rect)box = np.int0(box)cv2.drawContours(result, [box], 0, (0, 0, 255), 2)# 显示结果
cv2.imshow('Bounding Rectangles', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
6. 总结
轮廓外接矩形是二值图像分析中一种简单而有效的几何特征提取方法。通过计算轴对齐外接矩形或旋转外接矩形,可以快速获取目标物体的位置、大小和方向信息,为后续的图像处理和分析任务提供重要依据。在实际应用中,需要根据具体需求选择合适的外接矩形类型,并结合其他图像处理技术实现更复杂的功能。
演示代码
#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main(int argc, const char *argv[])
{Mat src = imread("D:/images/stuff.jpg");if (src.empty()) {printf("could not load image...\n");return -1;}namedWindow("input", WINDOW_AUTOSIZE);imshow("input", src);// 去噪声与二值化Mat dst, gray, binary;Canny(src, binary, 80, 160, 3, false);imshow("binary", binary);imwrite("D:/binary.png", binary);Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));dilate(binary, binary, k);// 轮廓发现与绘制vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());for (size_t t = 0; t < contours.size(); t++) {// 最大外接轮廓Rect rect = boundingRect(contours[t]);rectangle(src, rect, Scalar(0, 0, 255), 1, 8, 0);// 最小外接轮廓RotatedRect rrt = minAreaRect(contours[t]);Point2f pts[4];rrt.points(pts);// 绘制旋转矩形与中心位置for (int i = 0; i < 4; i++) {line(src, pts[i % 4], pts[(i + 1) % 4], Scalar(0, 255, 0), 2, 8, 0);}Point2f cpt = rrt.center;circle(src, cpt, 2, Scalar(255, 0, 0), 2, 8, 0);}imshow("contours", src);waitKey(0);return 0;
}
python 代码演示
import cv2 as cv
import numpy as npdef canny_demo(image):t = 80canny_output = cv.Canny(image, t, t * 2)cv.imshow("canny_output", canny_output)cv.imwrite("D:/canny_output.png", canny_output)return canny_outputsrc = cv.imread("D:/images/stuff.jpg")
cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
cv.imshow("input", src)
binary = canny_demo(src)
k = np.ones((3, 3), dtype=np.uint8)
binary = cv.morphologyEx(binary, cv.MORPH_DILATE, k)# 轮廓发现
out, contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for c in range(len(contours)):# x, y, w, h = cv.boundingRect(contours[c]);# cv.drawContours(src, contours, c, (0, 0, 255), 2, 8)# cv.rectangle(src, (x, y), (x+w, y+h), (0, 0, 255), 1, 8, 0);rect = cv.minAreaRect(contours[c])cx, cy = rect[0]box = cv.boxPoints(rect)box = np.int0(box)cv.drawContours(src,[box],0,(0,0,255),2)cv.circle(src, (np.int32(cx), np.int32(cy)), 2, (255, 0, 0), 2, 8, 0)# 显示
cv.imshow("contours_analysis", src)
cv.imwrite("D:/contours_analysis.png", src)
cv.waitKey(0)
cv.destroyAllWindows()
结束语
学习贵在坚持,学习OpenCV贵在每一天的代码练习,原理跟基本的函数解释,相关知识,后续更新边学边理解,搞技术永远要坚持做长期主义者!我们一起努力!!!
送相关学习资料,V: OpenCVXueTang_Asst