能跑
CSDN法兰盘圆孔检测
#include "iostream"
#include "opencv2\opencv.hpp"
#include "function.h"
using namespace std;
using namespace cv;
int main()
{
//读取图像
Mat img = imread("E:\\img\\img1129-04.bmp", IMREAD_GRAYSCALE);
//canny运算
Mat cannyImg;
int low_Val = 100, high_Val = 2 * low_Val;
Canny(img, cannyImg, low_Val, high_Val, 3);
namedWindow("canny", WINDOW_NORMAL);
imshow("canny", cannyImg);
/*****************************闭运算******************************/
/****************************************************************/
Mat closeSmallImg;
Mat elementSmall = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
morphologyEx(cannyImg, closeSmallImg, MORPH_CLOSE, elementSmall);
//morphologyEx(cannyImg, closeSmallImg, MORPH_CLOSE, kernel);
namedWindow("close", WINDOW_NORMAL);
imshow("CLOSE", closeSmallImg);
/***********************轮廓跟踪寻找轮廓***************************/
/****************************************************************/
vector<Vec4i> hierarchy;
vector<vector<Point>> contours;
findContours(closeSmallImg, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));
Mat drawImg = Mat::zeros(cannyImg.size(), CV_8UC3);
RNG rng(12345);//用来产生随机数的伪随机序列
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); //Scalar()函数在OpenCV中是用来设置颜色的如Scalar(B,G,R)
drawContours(drawImg, contours, i, color, 2, 8, hierarchy, 0, Point());
}
//imshow("s3 Contours Image", drawImg);
/*************************预设置参数******************************/
/****************************************************************/
//轮廓长度与轮廓间距离设置
int minCDis = 50; //筛选轮廓时,允许的最小轮廓间距离
int minCircleLen = 65; //筛选轮廓时定位圆孔的最小轮廓长度
int maxCircleLen = 75; //筛选轮廓时定位圆孔的最大轮廓长度
//圆环半径设置
float RH_mm = 140; //检测圆盘圆孔的半径 单位:毫米
float R0_mm = 115; //检测圆盘的内径 单位:毫米
float R1_mm = 160; //检测圆环的外径 单位:毫米
//标记ROI预览显示ROI
Point estimateCenter = Point(750, 500); //估计的ROI区域的中心坐标
int estimateRadius = 500; //估计的ROI区域的半径
Mat subImg = Mat::zeros(img.size(), CV_8UC1); //初始化一个mat,其大小与srcImg一样,类型为8位单通道类型
img.copyTo(subImg); //把src里的图像复制到subimg中
cvtColor(subImg, subImg, COLOR_GRAY2RGB); //把subimg转变为彩色图像
circle(subImg, estimateCenter, estimateRadius, Scalar(0, 0, 255), 1, 8, 0); //在subimg上画圆
//thickness为1,lineType为8,shift为对应给定点的小数位数(0对结果不产生影响)
//imshow("s0 ROI Img", subImg);
/****************************************************************/
/**************************轮廓筛选******************************/
const Scalar RED = Scalar(0, 0, 255);
const Scalar GREEN = Scalar(0, 255, 0);
const Scalar BLUE = Scalar(255, 0, 0);
const Scalar YELLOW = Scalar(0, 255, 255);
//1.不在估计区域范围内的轮廓去掉,相同重心的轮廓只保留一个contours2
// Get the moments,得到轮廓矩
vector<Moments> mu(contours.size());
for (int j = 0; j < contours.size(); j++)
{
mu[j] = moments(contours[j], false);
}
// Get the mass centers,重心
vector<Point2f> mc(contours.size());
for (int j = 0; j < contours.size(); j++)
{
mc[j] = Point2f(mu[j].m10 / mu[j].m00, mu[j].m01 / mu[j].m00);
cout << "xpos = " << mc[j].x << " ypos = " << mc[j].y << std::endl;
}
//开始筛选
vector<vector<Point>> contours2;
vector<Point2f> mc_finded;
for (int i = 0; i < contours.size(); ++i)
{
Point r_pos = mc[i];
bool disFlag = true;
//先检查轮廓出现的位置,计算轮廓矩重心的距离与估计圆心的距离
float disR = calDistance2D(r_pos.x, r_pos.y, estimateCenter.x, estimateCenter.y);
if (disR > estimateRadius)
{ //在ROI区域外 则无需比较
disFlag = false;
}
else
{ //检查这个轮廓与已找到的轮廓中心是否很接近,如果是则排除 跳出循环
for (int j = 0; j < mc_finded.size(); ++j)
{
Point f_pos = mc_finded[j];
float dis = calDistance2D(r_pos.x, r_pos.y, f_pos.x, f_pos.y);
if (dis <= minCDis) //轮廓中心之间的距离
{
disFlag = false;
break;
}
}
}
if (disFlag)
{ //此轮廓的中心点位置与前都不同
mc_finded.push_back(mc[i]);
contours2.push_back(contours[i]);
}
}
drawImg = Mat::zeros(cannyImg.size(), CV_8UC3);
for (int i = 0; i < contours2.size(); i++)
{
Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawImg, contours2, i, color, 2);
}
imshow("s4 reSelected Contours2 Image", drawImg);
cout << " * Contour2 Size = " << contours2.size() << std::endl;
//2.检查轮廓的长度contours3
vector<float> contourLens;
for (int i = 0; i < contours2.size(); ++i)
{
float conLen = arcLength(contours2[i], true);
contourLens.push_back(conLen);
cout << " * Contours Length = " << conLen << std::endl;
}
vector<vector<Point>> contours3;
for (int i = 0; i < contourLens.size(); ++i)
{
if ((contourLens[i] > 260) &&
(contourLens[i] < 400)) //检查轮廓的周长参数
{
contours3.push_back(contours2[i]);
//std::printf("Contours Idx = %d Length = %f\n", i, contourLens[i], true);
}
}
drawImg = Mat::zeros(cannyImg.size(), CV_8UC3);
for (int i = 0; i < contours3.size(); i++)
{
Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawImg, contours3, i, color, 2);
}
imshow("s5 reSelected Contours3 Image", drawImg);
cout << " * Contour3 Size = " << contours3.size() << std::endl;
//3.检查外接矩形 如果外接矩形的长和宽相差超过阈值 则删除contours4
vector<vector<Point>> contours4;
{
for (int i = 0; i < contours3.size(); i++)
{
RotatedRect one_rRect = minAreaRect(contours3[i]);
float jRes = abs(one_rRect.size.width - one_rRect.size.height);
if (jRes < 10) //检查轮廓外接矩形的长和宽
{
contours4.push_back(contours3[i]);
}
}
drawImg = Mat::zeros(cannyImg.size(), CV_8UC3);
for (int i = 0; i < contours4.size(); i++)
{
Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255),
rng.uniform(0, 255));
drawContours(drawImg, contours4, i, color, 2);
}
imshow("s6 check RECT Contours4 Image", drawImg);
cout << " * Contour4 Size = " << contours4.size() << std::endl;
}
//4.检查轮廓上各点到矩形中心的距离 计算每个轮廓距离的方差 选择方差最小的N个
vector<Point2f> holeCenter;
vector<float> holeRadius;
int sCnt = 10;
if (contours4.size() >= sCnt)
{ //如果contours4的轮廓数大于N,则再从距离方差的角度筛选
//依次计算各轮廓上各点到轮廓中心距离序列的方差
vector<float> contours_variance(contours4.size());
for (int i = 0; i < contours4.size(); i++)
{
//计算轮廓的最小外接圆中心点
Point2f circle_center;
float circle_radius;
minEnclosingCircle(contours4[i], circle_center, circle_radius);
//计算最小外接圆中心点到轮廓上各点的距离
vector<float> disList(contours4[i].size());
for (int j = 0; j < contours4[i].size(); j++)
{
Point pos = contours4[i][j];
float dis = calDistance2D(pos.x, pos.y, circle_center.x, circle_center.y);
disList[j] = dis;
}
//计算距离序列的方差
float varianceDis = calVariance_1DLine(disList);
contours_variance[i] = varianceDis;
cout << contours_variance[i] << std::endl;
}
//选择方差最小的N个轮廓
vector<vector<Point>> contours4Clone(contours4);
contours4.clear();
for (int i = 0; i < sCnt; i++)
{
int minIdx = min_element(contours_variance.begin(), contours_variance.end()) - contours_variance.begin(); //在方差序列中找到最小值位置
std::vector<float>::iterator idxIt = contours_variance.begin() + minIdx; //方差序列中的迭代器
std::vector<vector<Point>>::iterator contoursIt = contours4Clone.begin() + minIdx; //轮廓序列中的迭代器
contours4.push_back(*contoursIt); //把对应的轮廓存储到contours4中
contours_variance.erase(idxIt); //删除
contours4Clone.erase(contoursIt); //删除
}
}
//contours4中的轮廓依次计算外接矩形 轮廓中心和半径并存储contours5
vector<Rect> boundRect;
vector<vector<Point>> contours5;
holeCenter.clear();
holeRadius.clear();
for (int i = 0; i < contours4.size(); i++)
{
Rect one_bRect;
one_bRect = boundingRect(Mat(contours4[i]));
Point2f circle_center;
float circle_radius;
minEnclosingCircle(contours4[i], circle_center, circle_radius);
boundRect.push_back(one_bRect); //vector<cv::Rect> boundRect
holeCenter.push_back(circle_center);
holeRadius.push_back(circle_radius);
contours5.push_back(contours4[i]);
}
// Draw polygonal contour + bonding rects + circles
drawImg = Mat::zeros(cannyImg.size(), CV_8UC3);
for (int i = 0; i < contours5.size(); i++) {
cv::Scalar color = GREEN;
drawContours(drawImg, contours5, i, color, 1, 8, vector<cv::Vec4i>(), 0, cv::Point());
rectangle(drawImg, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);
cv::circle(drawImg, holeCenter[i], 2, GREEN, 1, 8, 0);
cv::putText(drawImg, std::to_string(i), holeCenter[i], cv::FONT_HERSHEY_SCRIPT_SIMPLEX, 1, cv::Scalar(0, 0, 255), 1);
}
cv::imshow("s7 selected Contours5 Image", drawImg);
std::cout << " * Contour5 Size = " << contours5.size() << std::endl;
/*cout << "a: " << a << endl;
cout << "b: " << b << endl;*/
waitKey(0);
return 0;
}