目录
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的函数进行图像的基本处理,实现了图像阈值化、面积筛选、统计区域个数、统计区域质心等功能模块,成功实现了功能需求。