OPENCV图形计算面积、弧长API讲解(2)

发布于:2025-06-09 ⋅ 阅读:(17) ⋅ 点赞:(0)

一.thresholdAPI讲解

作用:把图像进行二值化处理

在一个彩色图像中有许多像素值,例如设置阈值为100,大于100的像素变成100,小于的变成0或者其他值。其就是将多个像素点变成两个。

二值化操作作用:可以使图像中的数据量大大降低图像的复杂度,并且能够凸显出图像中的轮廓。

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阈值操作类型,具体的阈值操作如下图:

1.THRESH_BINARY

作用:二值化阈值处理会将原始图像作为仅有的两个值图像

它针对的像素的处理方式是对于灰度值大于阈值thresh的像素点,将其灰度值设定为maxval最大值。而对于灰度值小于或等于阈值thresh的像素点,将其灰度值设定为0。

2.THRESH_BINARY_INV

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

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

3.THRESH_TRUNC

作用:截断阈值化处理会把图像中大于阈值的像素点设定为阈值,小于或者等于该阈值的像素点保持不变。

比方说阈值设置成127,则说明对于像素超过127的像素点,而其像素值就被设置成127。而小于或者等于127的像素点,其数值保持不变。

4.THRESH_TOZERO

作用:低阈值处理会对图像中小于或者等于阈值的像素点处理为0,大于阈值的像素点则保持不变。

比方说当前阈值设定为127,若当前像素点小于或者等于127则把像素点处理为0;若当前像素点大于127则保持像素点不变。

 5.THRESH_TOZERO_INV

作用:超阈值处理会对图像中大于阈值的像素点处理为0,小于或者等于该阈值的像素点保持不变。

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

6.THRESH_OTSU

作用:OTSU方法会遍历所有可能的阈值,从而找到一个最佳的阈值。

值得注意的是,在使用OTSU方法的时候需要把阈值设定为0。这个时候,threshold会自动寻找最优的值。

二.这两章的相关API综合应用案例

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

int main() {
    // 1. 读取图像并预处理
    Mat img = imread("objects.jpg");
    if (img.empty()) {
        cerr << "Error: Image not found!" << endl;
        return -1;
    }
    
    Mat gray, binary;
    cvtColor(img, gray, COLOR_BGR2GRAY);
    threshold(gray, binary, 128, 255, THRESH_BINARY);
    
    // 2. 查找轮廓
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    
    // 3. 处理每个轮廓
    for (size_t i = 0; i < contours.size(); i++) {
        const auto& cnt = contours[i];
        
        // 计算轮廓面积
        double area = contourArea(cnt);
        // 计算轮廓周长
        double perimeter = arcLength(cnt, true);
        
        // 紧凑度计算: $C = 4\pi \times \frac{A}{P^2}$
        double compactness = (area > 0) ? (4 * CV_PI * area) / (perimeter * perimeter) : 0;
        
        cout << "轮廓 " << i << " - 面积: " << area 
             << ", 周长: " << perimeter 
             << ", 紧凑度: " << compactness << endl;
        
        // 绘制水平外接矩形
        Rect bRect = boundingRect(cnt);
        rectangle(img, bRect, Scalar(0, 255, 0), 2);  // 绿色矩形
        
        // 绘制旋转矩形
        RotatedRect rRect = minAreaRect(cnt);
        Point2f vertices[4];
        rRect.points(vertices);
        for (int j = 0; j < 4; j++) {
            line(img, vertices[j], vertices[(j + 1) % 4], Scalar(0, 0, 255), 2);  // 红色线段
        }
        
        // 绘制面积和周长文本
        putText(img, format("A:%.1f", area), Point(bRect.x, bRect.y - 10),
                FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 0), 1);
        putText(img, format("P:%.1f", perimeter), Point(bRect.x, bRect.y - 30),
                FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 1);
    }
    
    // 4. 显示结果
    imshow("轮廓分析结果", img);
    waitKey(0);
    return 0;
}

三.利用OPENCV的API计算轮廓面积

本次的代码例程,我们会结合之前学习的OPENCV轮廓检测和上一节课的面积API,来计算一个矩形的各种面积(包括轮廓面积、最小外接矩形面积、垂直边界面积)。

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

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

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
  //step1
  Mat img =imread("QT.png");//读取QT这张图片
  //step2
  Mat gray;
  cvtColor(img,gray,COLOR_RGB2GRAY);//转成灰度图
  //step3
  Mat bin_img;
  //参数:原图,目标图,阈值,二直图最大灰度值,阈值操作类
  //THRESH_BINARY_INV:对于灰度值大于阈值的像素点,将其设置为0。
  //而对于灰度值小于或者等于阈值的像素点,将这部分的部分设置为maxval最大像素点
  threshold(gray,bin_img,150,255,THRESH_BINARY_INV);

  //step4  finContours寻找轮廓并获取轮廓数
  vector<vector<Point>> contours;
  findContours(bin_img,contours,RETR_EXTERNAL,CHAIN_APPROX_NONE);//查询轮廓
  //画框
  Point2f pts[4];
  for(int i=0;i<contours.size();i++)
  {
    RotatedRect minRect=minAreaRect(contours[i]); //通过minAreaRect找到最小外接矩形 
    //将minAreaect获取的外接矩形的四个顶点传到pts
    minRect.points(pts);
    //用line与四个顶点画出外接矩形
    line(img,pts[0],pts[1],Scalar(0),3);//用line连接p[0]->p[1]
    line(img,pts[1],pts[2],Scalar(0),3);//用line连接p[1]->p[2]
    line(img,pts[2],pts[3],Scalar(0),3);//用line连接p[2]->p[3]
    line(img,pts[3],pts[0],Scalar(0),3);//用line连接p[3]->p[0]

    //计算外接矩形的面积
    int minArea=minRect.size.width*minRect.size.height;
    printf("minArea = %d\n", minArea);
    //垂直矩形面积计算
    Rect bArea = boundingRect(contours[i]);//调用boundingRect查找边界矩形
    //rectangle矩形画框
    rectangle(img, bArea, Scalar(255,255,0));
    int boundingArea = bArea.width * bArea.height;//计算边界矩形面积
    printf("boundingArea = %d\n", boundingArea);
    //图像本身面积计算
    double cArea = contourArea(contours[i]);//计算轮廓面积
    printf("contourArea = %lf\n", cArea);
    

  }
    imwrite("area.jpg", img);
return 0;
}

                        原图                                                       处理后图