一.问题描述
对图像中对象进行提取,获取所需要的对象,去掉其它干扰和非目标对象。
二.参考的博客
https://blog.youkuaiyun.com/weixin_41695564/article/details/80099917
https://blog.youkuaiyun.com/a369189453/article/details/86602403
上面两个博客的思路差不多,都是通过二值分割+形态学处理+横纵比计算,鉴于他们的程序注释清晰,方便以后自己查阅。
三.实际的问题
一开始,学习对象提取的时候,跟着教程学习的,由于视频中处理的图像我没有找到资源,于是我就随便找了个图像,想试试通过视频中的方法能不能也达到对象提取的效果,结果却不尽人意,找出了两个圆,我也换了二值化的几个方法,还是不行。程序如下:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
Mat srcImage, dstImage;
int main(int argc, char** argv)
{
srcImage = imread("E:\\pictures\\44.jpg", IMREAD_GRAYSCALE);
if (!srcImage.data)
{
cout << "图片读取错误,请检测路径!" << endl;
return -1;
}
namedWindow("原图", WINDOW_AUTOSIZE);
imshow("原图", srcImage);
//二值化
Mat binaryImage;
threshold(srcImage, binaryImage, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
imshow("二值化后", binaryImage);
//形态学操作:闭运算
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(binaryImage, dstImage, MORPH_CLOSE, kernel, Point(-1, -1));
imshow("闭运算后", dstImage);
//开运算
morphologyEx(binaryImage, dstImage, MORPH_OPEN, kernel, Point(-1, -1));
imshow("开运算后", dstImage);
//寻找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hireachy;
findContours(dstImage, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
//轮廓的绘制
//同时通过对轮廓的面积、横纵比大小的过滤,留下我们想要的那个圆
Mat resultImage = Mat::zeros(srcImage.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
{
//在黑色背景图上画出圆,注意其中参数-1的意义
drawContours(resultImage, contours, t, Scalar(0, 0, 255), -1, 8, Mat(), 0, Point());
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(resultImage, circle_center, 2, Scalar(0, 255, 255), 2, 8, 0);
}
}
imshow("效果图", resultImage);
Mat circle_img = srcImage.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;
}
运行结果:
四.解决的办法
后来突然想到了霍夫圆变换:HoughCricles()函数。
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <cmath>
using namespace cv;
using namespace std;
const double PI = atan(1.) * 4.; //圆周率
int main(int argc, char** argv)
{
//【1】载入原始图、Mat变量定义
Mat srcImage = imread("E:\\pictures\\44.jpg");
Mat midImage, dstImage;//临时变量和目标图的定义
//【2】显示原始图
imshow("【原始图】", srcImage);
//【3】转为灰度图并进行图像平滑
cvtColor(srcImage, midImage, COLOR_BGR2GRAY);//转化边缘检测后的图为灰度图
GaussianBlur(midImage, midImage, Size(9, 9), 2, 2);
//【4】进行霍夫圆变换
vector<Vec3f> circles;
HoughCircles(midImage, circles, HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0);
//【5】依次在图中绘制出圆
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(srcImage, center, 3, Scalar(0, 255, 0), -1, 8, 0);
//绘制圆轮廓
circle(srcImage, center, radius, Scalar(155, 50, 255), 3, 8, 0);
cout << "圆心坐标:宽:" << center.x << " 高:" << center.y << endl;
cout << "圆的周长:" << 2 * PI * radius << endl;
cout << "圆的面积:" << PI * pow(radius, 2) << endl;
}
//【6】显示效果图
imshow("【效果图】", srcImage);
waitKey(0);
return 0;
}
运行结果:
终于解决了。。。