OPENCV图形计算面积、弧长API讲解(2)
一.threshold的API讲解
作用:把图像进行二值化处理
在一个彩色图像中有许多像素值,例如设置阈值为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_BINARY和THRESH_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()
{//step1Mat img =imread("QT.png");//读取QT这张图片//step2Mat gray;cvtColor(img,gray,COLOR_RGB2GRAY);//转成灰度图//step3Mat 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获取的外接矩形的四个顶点传到ptsminRect.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;
}
原图 处理后图