Linux41:OPENCV图形计算面积、弧长API讲解

1.OPENCV图形面积、弧长计算的API介绍

前两节课我们已经把图形轮廓的检测、画框等功能讲解了一遍。那今天这节课我们主要结合轮廓检测的API去计算图形的面积,这些面积可以是矩形、圆形等等。图形面积计算和弧长计算常用于车辆识别、桥梁识别等重要功能,常用的API如contourArea arcLength minAreaRect boundingRect rectangle line等等。

1.1 contourArea API 讲解

contourArea 主要的用途是计算轮廓的曲线面积,也就是去计算图像本身的面积,如上图。countArea就是计算白色区域的面积,计算的过程一般是用微积分等方式去计算。

CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false );

第一个参数:contour指的是每一个轮廓的数据,也称之为轮廓的点

第二个参数:oriented表示的是某一个方向上轮廓的面积值

返回值:计算后的轮廓面积

1.2. arcLength API 讲解

arcLength主要的用途是计算轮廓的周长,也就是图形形状本身的曲线弧度周长。如上图所述,arcLength计算的是每个点连接的长度,并计算出来。

CV_EXPORTS_W double arcLength( InputArray curve, bool closed );

第一个参数:curve轮廓曲线的2D像素点

第二个参数:closed轮廓或者曲线是否闭合标志,true表示闭合

返回值:计算后的轮廓周长

1.3. minAreaRect API 讲解

minAreaRect 主要的用途是计算最小的外接矩形,最小外接矩形指的是找到一个矩形能够完全包裹所有的给定点,并且这个矩形是最小的。如上图:从上图我们可以看到8这个形状,被minAreaRect的矩形包围了。这个矩形包含了整个形状的所有点,更重要的这个矩形具有旋转功能,这个8实际上有倾斜的角度,而这个最小矩形也能够完美包含进来。

CV_EXPORTS_W RotatedRect minAreaRect( InputArray points );

第一个参数:points 输入的二维点数,可以Mat类型也可以是std::vector的向量类型

返回值:RotatedRect 的矩形对象,它表示的是一个轮廓的最小外接矩形,我们来看看RotatedRect结构体成员变量

  • center:旋转矩形的质心
  • size:旋转矩形的宽度和高度
  • angle:顺时针的旋转角度。

RotatedRect 矩形四个点的确定

RotatedRect 中矩形四个点通常用Point2f来表示,其中p[0]点的确定是最关键的,p[0]的位置通常分为两种情况:

  1. 如果当前最小外接矩形没有与坐标轴平行,则Y坐标最大的为点p[0],如2,3,4三张图
  2. 如果当前最小矩形和坐标轴平行,则有两个Y坐标最大的点,如图1。

1.4. boundingRect API 讲解

boundingRect 主要的用途是计算图形轮廓垂直边界的最小矩形,这个矩形必须要和图像是上下边界平行的。我们看上图:我们还是看8这个形状依然还是之前的位置,然后boundingRect产生的矩形对整个8进行垂直边界包围。

CV_EXPORTS_W Rect boundingRect( InputArray array );

第一个参数: array输入的灰度图像或者2D点集,数据类型为vector或者Mat矩阵数据

返回值:Rect的矩形对象,它表示的是物体轮廓的最大外接矩形。我们来看看Rect主要的成员变量

  • x:矩形的x坐标轴
  • y:矩形的y坐标轴
  • width:矩形的宽度
  • height:矩形的高度

1.5. rectangle API 讲解

rectangle函数的作用是绘制矩形,它有两种表示形式

1.5.1. 以两个顶点的方式画矩形

void cv::rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar & color, int thickness = 1,int lineType = LINE_8, int shift = 0)

**第一个参数:**输入的矩阵图像数据

第二个参数: pt1是矩形的一个顶点,左上角的顶点

第三个参数: pt2矩形中与pt1相对的顶点,也就是两个点在对角线上,也就是右下角的顶点

**第四个参数:**Scalar颜色的标量

**第五个参数:**thickness线宽

**第六个参数:**lineType线的类型,默认是LINE_8就行,具体的类型如下图:

**第七个参数:**shift坐标的小数点位,默认为0就可以

1.5.2. Rect 的方式画矩形

void cv::rectangle(InputOutputArray img, Rect rec, const Scalar & color, int thickness = 1,

int lineType = LINE_8, int shift = 0)

**第一个参数:**输入的矩阵图像数据

第二个参数: Rect的结构体,我们来看看这个Rect的重要成员变量,BoundingRect函数的返回值是Rect结构体

  • x:矩形的x坐标轴
  • y:矩形的y坐标轴
  • width:矩形的宽度
  • height:矩形的高度

**第三个参数:**Scalar颜色的标量

**第四个参数:**thickness线宽,默认是1

**第五个参数:**lineType线的类型,默认是LINE_8就行,line的类型如下:

**第六个参数:**shift坐标点的小数点位数

1.6. line API 讲解

line函数的主要作用是通过两个点绘制直线

CV_EXPORTS_W void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,

int thickness = 1, int lineType = LINE_8, int shift = 0);

**第一个参数:**输入的矩阵图像数据

**第二个参数:**pt1是线的起始坐标,也就是图上x1坐标和y1坐标

**第三个参数:**pt2是线的终点坐标,也就是图上x2坐标和y2坐标

**第四个参数:**Scalar是颜色标量,绘制直线的颜色

**第五个参数:**thickness它是线的粗细程度,默认为1

**第六个参数:**lineType线的类型,默认是LINE_8就行,具体的类型

**第七个参数:**shift坐标点的小数点位数

1.7. threshold API 讲解

threshold主要用途是把图像进行二值化处理,二值化操作可以使图像中的数据量大大降低图像的复杂度,并且能够凸显出图像中的轮廓。

CV_EXPORTS_W double threshold( InputArray src, OutputArray dst, double thresh, double maxval, int type );

**第一个参数:**src源图像,可以是8位灰度图,也可以是32位的三通道图像

**第二个参数:**dst目标图像

第三个参数:thresh阈值

第四个参数: maxval 二值图像中灰度最大值,maxval只能在THRESH_BINARYTHRESH_BINARY_INV有用,但是其他选项也需要填这个值,不能空着。

第五个参数: type阈值操作类型,具体的阈值操作如下图:

类型值 (int) 类型名称 (OpenCV 常量) 核心逻辑描述
0 THRESH_BINARY 二值阈值:像素值大于阈值时取最大值,否则取0。
1 THRESH_BINARY_INV 反二值阈值:像素值大于阈值时取0,否则取最大值(与上一种相反)。
2 THRESH_TRUNC 截断阈值 :像素值大于阈值时取该阈值,否则保持不变(最大值参数maxval在此模式下被忽略)。
3 THRESH_TOZERO 阈值化为0:像素值大于阈值时保持不变,否则设为0。
4 THRESH_TOZERO_INV 反阈值化为0:像素值大于阈值时设为0,否则保持不变。

**THRESH_BINARY:**二值化阈值处理会将原始图像作为仅有的两个值图像,它针对的像素的处理方式是对于灰度值大于阈值thresh的像素点,将其灰度值设定为maxval最大值。而对于灰度值小于或等于阈值thresh的像素点,将其灰度值设定为0。

THRESH_BINARY_INV 反二值化阈值处理也会将原始图像作为仅有的两个值图像,但是它处理的方式和THRESH_BINARY不一样,

它的特点是:对于灰度值大于阈值的像素点,将其设置为0。而对于灰度值小于或者等于阈值的像素点,将这部分的部分设置为maxval最大像素点。

THRESH_TRUNC **:**截断阈值化处理会把图像中大于阈值的像素点设定为阈值,小于或者等于该阈值的像素点保持不变。比方说阈值设置成127,则说明对于像素超过127的像素点,而其像素值就被设置成127。而小于或者等于127的像素点,其数值保持不变。

THRESH_TOZERO_INV **:**超阈值处理会对图像中大于阈值的像素点处理为0,小于或者等于该阈值的像素点保持不变。比方说阈值的值设定为127,若当前像素点大于127则把像素点处理为0;若当前像素点小于或者等于阈值的像素点,那么该像素点保持不变

THRESH_TOZERO **:**低阈值处理会对图像中小于或者等于阈值的像素点处理为0,大于阈值的像素点则保持不变。比方说当前阈值设定为127,若当前像素点小于或者等于127则把像素点处理为0;若当前像素点大于127则保持像素点不变。

THRESH_OTSU OTSU方法会遍历所有可能的阈值,从而找到一个最佳的阈值。值得注意的是,在使用OTSU方法的时候需要把阈值设定为0。这个时候,threshold会自动寻找最优的值。

2.计算常见的图形形状的面积DEMO

本章节主要的讲解如何通过OPENCV计算图形的面积,常见的面积包括矩形,三角形,圆形等等。那本次的代码例程,我们会结合之前学习的OPENCV轮廓检测和上一节课的面积API,来计算一个矩形的各种面积(包括轮廓面积、最小外接矩形面积、垂直边界面积)。

2.1计算矩形面积的大体流程

计算矩形的面积,我们一般要分以下几个比较重要的步骤,分别是:读取图片、把图形进行灰度处理、对灰度图像进行二值处理、调用findContours 去查找二值图片形状的轮廓、循环轮廓数量并且调用contourArea 计算每个轮廓的曲线面积、然后再计算最小外接矩形面积(minAreaRect )、边界垂直矩形面积的计算(boundingRect)。如下图所示:

3.代码的实现DEMO

3.1. 调用 OPENCV 读取需要计算的图片

第一步,调用imread读取我们需要处理的图片,这里我们选择的图片是10这个图片。

3.2. 对图片进行灰度操作

读取完图片之后,使用cvtColor 把三通道的彩色图像转换成单通道(COLOR_RGB2GRAY)的灰度图。

3.3. 对灰度图进行二值操作

灰度完成后,我们需要通过OPENCV的API threshold 对图像进行二值操作,这样就可以得到更加精确的图像便于识别和计算。这里的阈值我们填写150 ,最大的阈值填写的是255 ,阈值处理类型填的是THRESH_BINARY_INV( 背景为黑色,数字为白色,这样更利于我们计算数字的面积**)**。当像素值超过150之后像素全部为0,否则像素值是maxVal也就是255.

3.4. 查找二值图像中的所有轮廓

调用findContours 去查找整个二值图像的轮廓,由于我们读取的图片没有嵌套的轮廓,所以我们选择RETR_EXTERNAL 的模式只查找外部轮廓,轮廓的近似方法是CV_CHAIN_APPROX_NONE来保存边界上所有连续的轮廓点。

3.5. 循环轮廓数量来计算图像轮廓的最小外接矩形的面积

这部分代码就是通过循环轮廓数量来计算最小外接矩形,需要调用minAreaRect 来查找查找出整个二值图像的最小矩形,并且用line 函数画矩形(如下图1),从图1可以看到四个顶点(顶点用Point2f表示)p[0]、p[1]、p[2]、p[3]都分别以p[0]->p[1]、p[1]->p[2]、p[2]->p[3]、p[3]->p[0]的顺序连接起来变成矩形 。最小矩形面积的计算 = 最小外接矩形的长度 * 最小外接矩形的高度,代码就是int minRectArea= rects.size.width * rects.size.height

(图1)

3.6. 循环轮廓数量来计算图像轮廓的最小垂直矩形面积

这部分代码就是通过循环轮廓数量来计算最小垂直矩形,需要调用boundingRect 来查找查找出整个二值图像的最小垂直矩形,并且用rectangle把矩形框出来。最小垂直矩形面积的计算 = 最小垂直矩形的长度 * 最小垂直矩形的高度,代码就是int boundingArea= rect.width * rect.height

3.7. 循环轮廓数量来计算图像轮廓的面积

这部分代码就是通过循环轮廓数量来计算轮廓的面积,通过contourArea来计算轮廓的面积。

输出的结果:

输出的结果有两个,第一个是10这个数字用最小外接矩形、最小垂直矩形并输出area.jpg,如左图。右图是输出边界垂直矩形面积(boundingArea)、最小外接矩形面积(minArea)、轮廓面积(contourArea)等信息。

代码

cs 复制代码
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv; //Must Need Write cv
using namespace std;

int main()
{

    Mat src = imread("rect.png");

    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);

    Mat binary;
    threshold(gray,binary,150,255,THRESH_BINARY_INV);

    vector<vector<Point>> contours;
    findContours(binary,contours,RETR_EXTERNAL,CHAIN_APPROX_NONE);

    Point2f point[4];
    for(int i = 0;i < contours.size();i++)
    {
        RotatedRect min_AreaRect = minAreaRect(contours[i]);
        min_AreaRect.points(point);
        line(src,point[0],point[1],Scalar(0,0,255),1);
        line(src,point[1],point[2],Scalar(0,0,255),1);
        line(src,point[2],point[3],Scalar(0,0,255),1);
        line(src,point[3],point[0],Scalar(0,0,255),1);
        int min_AreaRect_s = min_AreaRect.size.width * min_AreaRect.size.height;
        printf("contour[%d] min_AreaRect_s = %d\n",i,min_AreaRect_s);

        Rect bounding_Rect = boundingRect(contours[i]);
        rectangle(src,bounding_Rect,Scalar(255,0,255),1);
        int bounding_Rect_s = bounding_Rect.width * bounding_Rect.height;
        printf("counter[%d] bounding_Rect_s = %d\n",i,bounding_Rect_s);

        double contour_s = contourArea(contours[i]);
        printf("counter[%d] contour_s = %lf\n",i,contour_s);

        printf("---------------------------\n");

    }

    imwrite("src_rect.jpg",src);

    return 0;
}

注:在OpenCV中,面积的单位是像素的平方

如果要转换为实际的物理单位(如mm²、cm²等),需要知道图像的分辨率(每个像素对应的实际物理尺寸):

// 假设知道每个像素对应0.1mm

double pixel_to_mm = 0.1; // 每个像素0.1mm

double pixel_to_mm2 = pixel_to_mm * pixel_to_mm; // 每个像素²对应0.01mm²

double area_pixel = contourArea(contours[i]); // 像素²

double area_mm2 = area_pixel * pixel_to_mm2; // mm²

printf("面积: %.2f 像素², 实际: %.2f mm²\n", area_pixel, area_mm2);

代码详解

1. 图像预处理

cvtColor(src, dst: gray, code: COLOR_RGB2GRAY);

  • 将彩色图像 src 转换为灰度图 gray,因为轮廓检测通常在单通道图像上进行。

threshold(src: gray, dst: bin_img, thresh: 150, maxval: 255, type: THRESH_BINARY_INV);

  • 对灰度图进行二值化,阈值为 150,最大值 255。
  • 使用 THRESH_BINARY_INV(反向二值化),即像素值 >150 的变为 0,≤150 的变为 255(白底黑字或黑底白字取决于原图)。

2. 轮廓检测

vector<vector<Point>> contours;

findContours(bin_img, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

  • findContours 从二值图像中提取所有轮廓。
  • RETR_EXTERNAL:只检测最外层轮廓,忽略内部孔洞。
  • CHAIN_APPROX_NONE:存储轮廓的所有点,不做压缩。

3. 遍历每个轮廓

Point2f pts[4];

for (int i = 0; i < contours.size(); i++)

{

RotatedRect minRect = minAreaRect(contours[i]);

minRect.points(pts);

  • minAreaRect:计算包围当前轮廓的最小外接旋转矩形
  • points(pts):将矩形的四个角点坐标存入 pts 数组。

Point2f pts[4];

  • 创建一个包含4个Point2f(浮点型坐标点)的数组
  • 这个数组用来存储旋转矩形的4个角点坐标
  • Point2f表示每个点的x和y坐标都是浮点数(精确到小数)

minRect.points(pts);

points() 方法的作用:

  • RotatedRect的4个角点坐标提取出来,存入pts数组
  • 为什么需要这个方法?因为RotatedRect内部存储的是中心点+尺寸+角度,要得到四个角点需要计算
  • points()会按照特定顺序返回四个点

4. 绘制最小外接矩形

line(img: src, pt1: pts[0], pt2: pts[1], color: Scalar(v0: 0), thickness: 3);

line(img: src, pt1: pts[1], pt2: pts[2], color: Scalar(v0: 0), thickness: 3);

line(img: src, pt1: pts[2], pt2: pts[3], color: Scalar(v0: 0), thickness: 3);

line(img: src, pt1: pts[3], pt2: pts[0], color: Scalar(v0: 0), thickness: 3);

  • 用四条线绘制出最小外接矩形(黑色,线宽 3)。
  • Scalar(0) 表示黑色(BGR 中为 (0,0,0))。

5. 计算并打印最小外接矩形面积

int minArea = minRect.size.width * minRect.size.height;

printf(format: "minArea = %d\n", minArea);

  • 面积 = 宽度 × 高度(注意这里是未旋转的矩形面积,不是旋转后的实际像素面积)。

6. 计算并绘制垂直矩形包围框

Rect bArea = boundingRect(array: contours[i]);

int boundingArea = bArea.width * bArea.height;

rectangle(&img: src, rec: bArea, color: Scalar(v0: 255, v1: 255, v2: 0));

printf(format: "boundingArea = %d\n", boundingArea);

  • boundingRect:计算轮廓的轴对齐最小外接矩形(不旋转)。
  • 用黄色(BGR: 0,255,255?实际这里是 Scalar(255,255,0) 是青色)绘制该矩形。
  • 打印其面积。

7. 计算并打印轮廓真实面积

double cArea = contourArea(contour: contours[i]);

printf(format: "contourArea = %lf\n", cArea);

  • contourArea:计算轮廓本身的实际像素面积(不是矩形面积)。
相关推荐
香草泡芙2 小时前
解锁AI Agent潜能:基于Langchain组件库的落地指南(1)
人工智能
chushiyunen2 小时前
pycharm打包whl
人工智能·pytorch·python
墨染天姬2 小时前
【AI】PyTorch 框架
人工智能·pytorch·python
jeffsonfu2 小时前
学习率调度的艺术:从Warmup到余弦退火,掌握深度学习的训练节奏
人工智能·深度学习·神经网络
大强同学2 小时前
Obsidian 视觉化技能包
人工智能·ai编程
chaors2 小时前
Langchain入门到精通0x08:摘要链(load_summarize_chain)
人工智能·langchain·ai编程
CCC:CarCrazeCurator2 小时前
AI 提示词工程深度探究:基于 Claude 的技术原理、实战技巧与发展趋势
人工智能
只说证事2 小时前
中专电商专业,哪些证书性价比高?
人工智能·数据挖掘
愣锤2 小时前
详细易懂的OpenClaw安装指南
人工智能·openai·agent