目录
1、背景介绍
2、代码实现
2.1 获取原图
2.1.1 区域图像imread
2.1.2 具体实现
2.2 获取图像大小
2.3 阈值分割
2.3.1 阈值分割threshold
2.3.2 具体实现
2.4 区域面积筛选
2.4.1 获取轮廓findContours
2.4.2 获取轮廓面积contourArea
2.4.3 填充区域fillPoly
2.4.4 具体实现
2.5 统计区域个数并获取质心坐标
2.5.1 获取图像中心矩moments
2.5.2 具体实现
3、测试界面
4、总结
1、背景介绍
本文实现了根据源图像的灰度值来分割成二值图像,通过面积筛选剔除掉面积小的区域,并统计区域的个数以及区域中心坐标。
IDE:Qt Creator 4.8.0
编译器:MSVC 2017 64bit
Opencv库:opencv4.5.1
2、代码实现
2.1 获取原图
2.1.1 区域图像imread
cv::Mat cv::imread(const String& filename, int flags = IMREAD_COLOR);
- 第一个参数为图像地址
- 第二个参数为读取类型
IMREAD_COLOR | 总是读取三通道图像 |
IMREAD_GRAYSCALE | 总是读取单通道图像 |
IMREAD_ANYCOLOR | 通道数由文件实际通道数(不超过3) |
IMREAD_ANYDEPTH | 允许加载超过8bit深度。 |
IMREAD_UNCHANGED | 等于将Cv::IMREAD_ANYCOLOR和CV::IMREAD_ANYDEPTH组合了起来。 |
2.1.2 具体实现
通过imread函数获取源图像,因为后续需要做阈值分割,需要用到灰度图像,所以imread的第二个参数取IMREAD_GRAYSCALE;
//获取图像
std::string strPicName = "./pic.png";
m_mSrcImage = cv::imread(strPicName, cv::IMREAD_GRAYSCALE);
cv::imshow("Src",m_mSrcImage);
2.2 获取图像大小
//获取图像大小
int iHeight = m_mSrcImage.rows;
int iWidth = m_mSrcImage.cols;
2.3 阈值分割
2.3.1 阈值分割threshold
double cv::threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type);
src:输入的灰度图像或彩色图像。
dst:输出的二值化图像。
thresh:阈值,用于将像素点的亮度值与该值进行比较,从而确定像素点的颜色。
maxval:最大值,当像素点的亮度值大于等于阈值时,将其设置为该值。
type:二值化类型,常用的有以下几种:
THRESH_BINARY | 大于等于阈值的像素点设置为最大值,小于阈值的像素点设置为0。 |
THRESH_BINARY_INV | 大于等于阈值的像素点设置为0,小于阈值的像素点设置为最大值。 |
THRESH_TRUNC | 大于等于阈值的像素点设置为阈值,小于阈值的像素点保持不变。 |
THRESH_TOZERO | 大于等于阈值的像素点保持不变,小于阈值的像素点设置为0。 |
THRESH_TOZERO_INV | 大于等于阈值的像素点设置为0,小于阈值的像素点保持不变。 |
如果想要实现获取某个灰度阈值区间的区域,则可以先使用THRESH_TOZERO,获取小于thresholdMax的区域,然后使用THRESH_BINARY,获取大于thresholdMin的区域。
2.3.2 具体实现
//阈值分割
double thresholdMin = 5;
double thresholdMax = 200;
double MaxVal = 255;
cv::Mat MatThreshold1;
cv::threshold(m_mSrcImage,MatThreshold1,thresholdMax,MaxVal,cv::THRESH_TOZERO_INV);
cv::Mat MatThreshold2;
cv::threshold(MatThreshold1,MatThreshold2,thresholdMin,MaxVal,cv::THRESH_BINARY);
cv::imshow("Threshold",MatThreshold2);
2.4 区域面积筛选
2.4.1 获取轮廓findContours
cv::void findContours(cv::InputOutputArray image,cv::OutputArrayOfArray contours,cv::OutputArray hierarchy,int mode, int method,cv::Point offset = cv::Point())
findContours输入一个图像矩阵,返回一个双重向量 vector<vector<Point>> contours 每一组Point都连续,构成一组向量集合,在图像上的显示即为一个轮廓(点集),由于一张图像往往包含很多对象,因此一个轮廓不足以描述图像中的所有对象,因此还需要一个容器去包含所有的轮廓,我们称这个包含所有轮廓的容器为轮廓集。所以我们有上述的双重向量的定义方式。 轮廓数量=contours的元素个数
这里参数介绍太多了,就不具体介绍了。
2.4.2 获取轮廓面积contourArea
double cv::contourArea( InputArray _contour, bool oriented )
- contour:轮廓的像素点
- oriented:区域面积是否具有方向的标志,true表示面积具有方向性,false表示不具有方向性,默认值为不具有方向性的false。
2.4.3 填充区域fillPoly
void cv::fillPoly( InputOutputArray img,InputArrayOfArrays pts,const Scalar & color,int lineType = LINE_8,int shift = 0,Point offset = Point()
)
2.4.4 具体实现
- 通过findContours函数获取轮廓数据;
- 获取每个轮廓数据的面积,筛选给定的面积区间并保存到新的轮廓数据;
- 通过轮廓数据进行填充生成新的图像。
//区域面积筛选
double dAreaMin = 7000;
double dAreaMax = 9500;
std::vector<std::vector<cv::Point >> Contours;
cv::findContours(MatThreshold2,Contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);
std::vector<std::vector<cv::Point >> SelectContours;
for(int i=0;i!=(int)Contours.size();i++)
{std::vector<cv::Point > Contour = Contours[i];double dArea = cv::contourArea(Contour,false);if(dArea>dAreaMin&&dArea<dAreaMax){SelectContours.push_back(Contour);}
}
cv::Mat SelectMat = cv::Mat::zeros(iHeight,iWidth,CV_8UC1);
cv::fillPoly(SelectMat,SelectContours,cv::Scalar(255,0,0));
cv::imshow("SelectMat",SelectMat);
2.5 统计区域个数并获取质心坐标
2.5.1 获取图像中心矩moments
cv::Moments cv::moments ( InputArray array,bool binaryImage = false)
- opencv中提供了moments()来计算图像中的中心矩(最高到三阶);
- x坐标通过cv::Moments的成员变量m10/m00获得;
- y坐标通过cv::Moments的成员变量m01/m00获得;
2.5.2 具体实现
- 获取新生成区域的轮廓,根据双重向量的size获取区域个数
- 通过moments()来获取质心坐标
//获取各个区域质心的坐标vector
std::vector<std::vector<cv::Point >> CenterContours;
cv::findContours(SelectMat,CenterContours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);
//统计区域个数
int iCount = CenterContours.size();
ui->sb_Count->setValue(iCount);
//获取质心坐标
std::vector<int> vCenterX;//质心X坐标
std::vector<int> vCenterY;//质心Y坐标
for(int i=0;i!=(int)CenterContours.size();i++)
{std::vector<cv::Point > CenterContour = CenterContours[i];cv::Moments M = cv::moments(CenterContour,false);int iCenterX = (M.m10/M.m00);int iCenterY = (M.m01/M.m00);vCenterX.push_back(iCenterX);vCenterY.push_back(iCenterY);
}
3、测试界面
4、总结
本文通过opencv的函数进行图像的基本处理,实现了图像阈值化、面积筛选、统计区域个数、统计区域质心等功能模块,成功实现了功能需求。