一,利用面积对轮廓进行筛选
注意这种面积筛选有一个弊端就是比如有两个轮廓,
A轮廓为500
B轮廓为300
当面积设置为 area<400时就可以筛选出面积小于300的所有轮廓
反之大于300的轮廓 如果有两个圆轮廓一大一小,可能就只能保留一个了
如果知道这两个形状的轮廓面积,或许可以利用 逻辑与 进行筛选。
效果图
//圆心
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
//载入图像
Mat src = imread("D:\\Besktop\\faimage\\Image\\21_25_27.bmp");
Mat src_clone = src.clone();
if (src.empty())
{
cout << "图片为空" << endl;
return 0;
}
imshow("src", src);
//转灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
//图像二值化
threshold(gray, gray, 0, 255, THRESH_BINARY | THRESH_OTSU);
imshow("threshold", gray);
//执行形态学开操作去除噪点
/*Mat kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(-1, -1));
morphologyEx(gray, gray, MORPH_OPEN, kernel, Point(-1, -1), 1);
imshow("morphologyEx", gray);*/
//边缘检测
Canny(gray, gray, 0, 255);
Mat graycanny;
resize(gray, graycanny, Size(600, 600));
imshow("canny", graycanny);
//轮廓发现
vector<vector<Point>> contours;
vector<Vec4i> her;
findContours(gray, contours, her, RETR_TREE, CHAIN_APPROX_SIMPLE);
//获取某一轮廓重心点
Moments M;
M = moments(contours[0]);
double cX = double(M.m10 / M.m00);
double cY = double(M.m01 / M.m00);
//Mat resultImage = Mat::zeros(gray.size(), CV_8UC3);
RNG rng(12345);
double area = 0.0;
Point pRadius;
for (int i = 0; i < contours.size(); i++) {
double area = contourArea(contours[i], false);
//根据面积及纵横比过滤轮廓
if (area > 200) {
Rect rect = boundingRect(contours[i]);
float scale = float(rect.width) / float(rect.height);
if (scale < 1 && scale>0) {
drawContours(gray, contours, i, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), -1);
int x = rect.width / 2;
int y = rect.height / 2;
//找出圆心并绘制
pRadius = Point(rect.x + x, rect.y + y);
circle(gray, pRadius, 15, Scalar(0, 0, 255), 15);
}
}
}
Mat graycanny1;
resize(gray, graycanny1, Size(600, 600));
imshow("resultImage", graycanny1);
//imshow("resultImage", resultImage);
//在原图上绘制圆心
circle(src_clone, pRadius, 15, Scalar(0, 0, 255), 15);
cout << "圆心坐标:" << cX << "" << cY << endl;
//cout <<"[x,y]" << pRadius << endl;
Mat graycanny2;
resize(src_clone, graycanny2, Size(600, 600));
imshow("src_clone", graycanny2);
//imshow("src_clone", src_clone);
waitKey(0);
return 0;
}
二,利用边缘检测和轮廓筛选找出轮廓
这一种则是利用轮廓的中心点对轮廓进行拟合,从而达到寻找轮廓
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("D:\\Besktop\\faimage\\测试原图\\22_21_27.bmp");
//String dest = "D:\\Besktop\\1\\";
/*string path = "D:\\Besktop\\faimage\\测试原图\\";
String dest = "D:\\Besktop\\1\\";
String savedfilename;
int len = path.length();
vector<cv::String> filenames;
cv::glob(path, filenames);
for (int i = 0; i < filenames.size(); i++)
Mat frame;
frame = imread(filenames[i], i);
savedfilename = dest + filenames[i].substr(len);*/
Mat gray_img, thresh_img;
//灰度
cvtColor(img, gray_img, COLOR_BGR2GRAY);
threshold(gray_img, thresh_img, 0, 255, THRESH_TRIANGLE);
//开运算
Mat ellipse = getStructuringElement(MORPH_ELLIPSE, Size(13, 13));
morphologyEx(thresh_img, thresh_img, MORPH_OPEN, ellipse, Point(-1, -1), 2);
//寻找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy1;
findContours(thresh_img, contours, hierarchy1, RETR_LIST, CHAIN_APPROX_NONE, Point());
//获取某一轮廓重心点
Moments M;
M = moments(contours[0]);
double cX = double(M.m10 / M.m00);
double cY = double(M.m01 / M.m00);
//绘制轮廓
drawContours(img, contours, 0, Scalar(0, 255, 0), 2, 8, hierarchy1);
//显示轮廓重心并提取坐标点
circle(img, Point2d(cX, cY), 6, Scalar(0, 255, 0), 2, 8);
namedWindow("Center Point", CV_WINDOW_NORMAL);
imshow("Center Point", img);
//imwrite("D:\\Besktop\\1\\22_21_27.bmp", img);
putText(img, "center", Point2d(cX - 20, cY - 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1, 8);
cout << "重心坐标:" << cX << " " << cY << endl << endl;
waitKey(0);
return 0;
}
三,这一种方法类似于第一种的面积筛选唯一不同的时候增加了几步形态学处理
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//第二步 通过面积过滤,通过纵横比的测量,圆形的纵横比应该在1:1左右,如果不是1:1,就把它过滤掉
Mat srcImg, binaryImg, dstImg;
void test()
{
srcImg = imread("D:\\Besktop\\faimage\\Image\\21_25_27.bmp", IMREAD_GRAYSCALE);
if (srcImg.empty())
{
cout << "could not load image...\n" << endl;
}
namedWindow("Original image", CV_WINDOW_NORMAL); //CV_WINDOW_NORMAL 使得鼠标可以控制显示窗口的大小
imshow("Original image", srcImg);
//二值化
threshold(srcImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_OTSU);
namedWindow("Binary Result", CV_WINDOW_NORMAL);
imshow("Binary Result", binaryImg);
//形态学操作,开操作,去掉小的对象,闭操作,连接里面的洞(开闭操作要先获得结构元素)
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); //Point(-1, -1)是中心点,这里是 2 x 2位置
morphologyEx(binaryImg, dstImg, MORPH_CLOSE, kernel, Point(-1, -1));
namedWindow("Close Result", CV_WINDOW_NORMAL);
imshow("Close Result", dstImg);
kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(dstImg, dstImg, MORPH_OPEN, kernel, Point(-1, -1));
namedWindow("Open Result", CV_WINDOW_NORMAL);
imshow("Open Result", dstImg);
//轮廓发现
vector<vector<Point>> contours; //存储轮廓
vector<Vec4i> hireachy;
findContours(dstImg, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
//通过面积过滤,通过纵横比的测量,圆形的纵横比应该在1:1左右,如果不是1:1,就把它过滤掉
Mat resultImg = Mat::zeros(srcImg.size(), CV_8SC3);
Point cc;
for (int i = 0; i < contours.size(); i++)
{
//面积过滤
double area = contourArea(contours[i]); //循环获取指定的面积
if (area < 100) //通过循环过滤掉小于100的面积
continue;
//横纵比过滤
Rect rect = boundingRect(contours[i]);
float ratio = float(rect.width) / float(rect.height);
if (ratio < 1.1 && ratio> 0.9) //把满足条件的保留画出来
{
drawContours(resultImg, contours, i, Scalar(0, 0, 255), -1, 8, Mat(), 0, Point()); //画出来 第五个参数改为 -1 ,使得整个圆形填充
cout << "circle area: " << area << endl; //面积和周长打印出来(像素度量)
cout << "circle length: " << arcLength(contours[i], true) << endl;
//找中心点
int x = rect.x + rect.width / 2;
int y = rect.y + rect.height / 2;
cc = Point(x, y);
circle(resultImg, cc, 2, Scalar(0, 0, 255), 2, 8, 0); //画出中心点
}
}
namedWindow("Final Result", CV_WINDOW_NORMAL);
imshow("Final Result", resultImg);
//在原图上定位显示中心点
Mat circleImg = srcImg.clone();
cvtColor(circleImg, circleImg, COLOR_GRAY2BGR);
circle(circleImg, cc, 20, Scalar(0, 0, 255), 2, 8, 0);
namedWindow("Center Point", CV_WINDOW_NORMAL);
imshow("Center Point", circleImg);
}
int main()
{
test();
waitKey(0);
return 0;
}
这三种方法都是opencv中的一些方法,有任何建议和问题均可评论区提问,说不定你的建议正是我疑惑的地方!!!
四,利用轮廓拟合的方法对图片中的某个轮廓进行拟合(这里主要是圆心)
此方法运行环境为opencv4.5.5
相关代码
#if 1
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
// 读取图片
Mat src = imread("图片路径");
// 转换为灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// 进行边缘检测
Mat edge;
Canny(gray, edge, 100, 200);
// 圆形拟合
vector<Vec3f> circles;
HoughCircles(edge, circles, HOUGH_GRADIENT, 1, edge.rows / 16, 100, 30, 10, 50);
// 绘制圆形和圆心
for (size_t i = 0; i < circles.size(); i++)
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
circle(src, center, radius, Scalar(0, 0, 255), 2);
circle(src, center, 3, Scalar(0, 255, 0), -1);
}
// 显示结果
namedWindow("src", WINDOW_NORMAL);
imshow("src", src);
waitKey(0);
return 0;
}
#endif // 1
五,这种是利用凸包 对圆形进行拟合并且将所有的轮廓都绘制外界圆
opencv4.5.5
代码
#if 1
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
// 读取图片
Mat img = imread("图片路径");
// 转换为灰度图
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
// 进行二值化处理
Mat thresh;
threshold(gray, thresh, 128, 255, THRESH_BINARY);
// 找到所有轮廓
vector<vector<Point>> contours;
findContours(thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 遍历轮廓,找到圆形轮廓
for (size_t i = 0; i < contours.size(); i++)
{
// 只考虑凸包
vector<Point> hull;
convexHull(contours[i], hull);
// 尝试将轮廓拟合为圆形
Point2f center;
float radius;
//bool isCircle = fitCircle(hull, center, radius);
minEnclosingCircle(hull, center, radius);
cout << center.x << "," << center.y << endl;
// 标出圆心
circle(img, center, 5, Scalar(0, 255, 0), -1);
// 绘制圆形
circle(img, center, radius, Scalar(0, 0, 255), 2);
}
// 显示结果
Mat ss;
resize(img, ss, Size(600, 600));
imshow("Contours", ss);
waitKey();
}
#endif // 0
有问题欢迎评论区讨论