现在我们想从一个杂乱的背景中提取出某个规则图像的轮廓,比方说圆。我们如何才能在一张图像中找的圆的轮廓,同时找到它的圆心坐标以及它的面积和周长呢?我的思路是阈值分割+形态学处理+高宽比过滤。大家也可以尝试下霍夫圆检测的思路。
接下我们编写代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
Mat src_img, binary_img, dst_img;
int main()
{
src_img = imread("2018.4.26.png", IMREAD_GRAYSCALE);
//灰度读取,在读入图像的同时进行色彩转换,这可以提高运行速度并减少内存的使用
if (src_img.empty())
{
printf("could not load the image...\n");
return -1;
}
namedWindow("原图", CV_WINDOW_AUTOSIZE);
imshow("原图", src_img);
// 图像二值化
threshold(src_img, binary_img, 0, 255, THRESH_BINARY | THRESH_OTSU);
imshow("二值图像", binary_img);
// 形态学操作
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1)); // 构建形态学操作的结构元
morphologyEx(binary_img, dst_img, MORPH_CLOSE, kernel, Point(-1, -1)); //闭操作
imshow("闭操作", dst_img);
kernel = getStructuringElement(MORPH_RECT, Size(5,5), Point(-1, -1)); // 构建形态学操作的结构元
morphologyEx(dst_img, dst_img, MORPH_OPEN, kernel, Point(-1, -1)); //开操作
imshow("开操作", dst_img);
// 寻找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hireachy;
findContours(dst_img, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
Mat result_img = Mat::zeros(src_img.size(), CV_8UC3); // 创建与原图同大小的黑色背景
Point circle_center; //定义圆心坐标
for (auto t = 0; t < contours.size(); ++t)
{
// 面积过滤
double area = contourArea(contours[t]); //计算点集所围区域的面积
if (area < 100) //晒选出轮廓面积大于100的轮廓
continue;
// 横纵比过滤
Rect rect = boundingRect(contours[t]); // 求点集的最小直立外包矩形
float ratio = float(rect.width) / float(rect.height); //求出宽高比
if (ratio < 1.1 && ratio > 0.9) //因为圆的外接直立矩形肯定近似于一个正方形,因此宽高比接近1.0
{
drawContours(result_img, contours, t, Scalar(0, 0, 255), -1, 8, Mat(), 0, Point()); 在黑色背景图上画出圆,注意其中参数-1的意义
printf("圆的面积: %f\n", area);
double arc_length = arcLength(contours[t], true); //计算点集所围区域的周长
printf("圆的周长 : %f\n", arc_length);
int x = rect.x + rect.width / 2;
int y = rect.y + rect.height / 2;
circle_center = Point(x, y); //得到圆心坐标
cout << "圆心坐标:" << "宽"<<circle_center.x<<" "<<"高"<< circle_center.y << endl;
circle(result_img, circle_center, 2, Scalar(0, 255, 255), 2, 8, 0);
}
}
imshow("结果", result_img);
Mat circle_img = src_img.clone();
cvtColor(circle_img, circle_img, COLOR_GRAY2BGR); //灰度图转化为彩色图
circle(circle_img, circle_center, 2, Scalar(0, 0, 255), 2, 8, 0); //在原图上画出圆心
imshow("最终结果", circle_img);
waitKey(0);
return 0;
}
运行程序,如下:
显然结果很准确!