概述
轮廓面积与轮廓周长是图像分析中的两项核心统计特征,它们为理解和量化图像中的形状提供了基础。
-
轮廓面积:这代表了轮廓所界定区域的像素数量,是衡量区域大小的直接指标。面积的计算结果以像素平方为单位,为我们提供了一个量化的尺度来比较不同物体的相对大小。
-
轮廓周长:它反映了轮廓边界的总长度,以像素为单位。周长是描述形状复杂度和边界长度的关键参数,对于评估物体的形状和轮廓的平滑度或不规则性至关重要。
通过对这些特征的细致分析,我们能够实现以下目标:
-
物体大小区分:通过比较物体的面积和周长,我们可以区分不同尺寸的物体,筛选出符合特定大小标准的物体。
-
形状识别:不同形状的物体具有独特的面积与周长比。例如,对于正方形,周长与面积的比值是常数,而圆形的周长与面积之间遵循特定的数学关系。
-
几何属性推断:利用面积和周长之间的固定数学关系,我们可以对物体的几何属性进行推断,比如判断物体是否接近于理论的几何形状。
-
异常检测与分析:如果物体的面积与周长比例显著偏离预期,这可能揭示了物体的异常或变形,为进一步的分析提供了线索。
-
模式识别与机器学习:在这些领域中,轮廓的面积和周长可以作为特征向量的一部分,辅助算法进行更准确的物体分类和识别。
OpenCV对轮廓点集计算面积的API函数如下:
double cv::contourArea(
InputArray contour,
bool oriented = false
)
计算轮廓的面积,其原理是基于格林公式。
参数contour表示输入的轮廓点集
参数oriented默认是false返回的面积是正数,如果方向参数为true表示会根据是顺时针或者逆时针方向返回正值或者负值面积。
OpenCV对轮廓点集计算弧长的API函数如下:
double cv::arcLength(
InputArray curve,
bool closed
)
参数curve表示输入的轮廓点集
参数closed默认表示是否闭合区域
C++实现
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("12.jpg");
if (src.empty())
{
cout << "could not load image.." << endl;
}
cv::namedWindow("src", 0);
imshow("src", src);
// 提取边缘
Mat binary;
Canny(src, binary, 80, 160);
cv::namedWindow("binary", 0);
cv::imshow("binary", 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));
// 面积与弧长过滤
double area = contourArea(contours[t]);
double curvelen = arcLength(contours[t], true);
if (area < 100 || curvelen < 100)
{
continue;
}
// 最小外接矩形
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);
}
Point2f cpt = rrt.center;
circle(src, cpt, 2, Scalar(255, 0, 0), 2);
}
cv::namedWindow("contours", 0);
cv::imshow("contours", src);
waitKey(0);
return 0;
}
Python 实现
import cv2 as cv
import numpy as np
def canny_demo(image):
t = 80
canny_output = cv.Canny(image, t, t * 2)
cv.imshow("canny_output", canny_output)
cv.imwrite("D:/canny_output.png", canny_output)
return canny_output
src = cv.imread("D:/images/zhifang_ball.png")
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);
area = cv.contourArea(contours[c])
arclen = cv.arcLength(contours[c], True)
if area < 100 or arclen < 100:
continue
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()