一.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()
{
//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;
}
原图 处理后图