/*
*轮廓周围绘制矩形框和圆形框
基于轮廓绘制出来的矩形或者椭圆
先把图像转换为灰度图像,然后模糊,模糊的目的是为了后面进行一个二值化的时候可以降低噪声,让二值化更精准一些二值化处理通过
threshold阈值这个方法实现或者当你输入一幅灰度图像给Canny同样可以帮你进行一个最后得出一个二值化的边缘图像,基于二值图像我们
可以发现边缘findcontour,它其实这个轮廓有很多点,基于轮廓绘制多边形矩形和圆会有一点困难,所以我们要做一步处理,把一个多点的多
边形变成一个点数比较少的多边形,但它的形状基本保持不变,OPencv用到一个算法即RDP,已经有API
椒盐噪声比较多可能选择中值模糊,觉得不是椒盐噪声就是其他噪声比较多可以选择均值模糊效果比较好,,高斯模糊对椒盐噪声
和其他的噪声都有一定的抑制作用,如果两者兼有,你可以选择高斯模糊
* 轮廓周围绘制矩形 -API
approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
输入是多边形,就是有多少个点你就输入
输出是多边形,基于输入的多边形做了一个大致的形状,比如你那个多边形可能有100个点,这个新做出来的多边形只有50个点,就是一个精简
基于RDP算法实现,目的是减少多边形轮廓点数,这样为我们下面寻找四角坐标提供了一定的方便,在多的点寻找和在少的点寻找效率是不一样的
epsilon 就是数学当中经常用的符号,表示一个任意的整数或者任意的数的意思,这个数表示两点之间最小距离,当取舍这个点的时候,减少轮廓
点的时候小于最小距离就去掉,大于最小距离我们还要把它保留
closed 形成的多边形是不是一个闭合的曲线
cv::boundingRect(InputArray points) 基于我们寻找到的多边形轮廓就可以得到最小矩形
轮廓周围最小矩形获取它的左上脚点坐标和右下角点坐标,绘制一个矩形
cv::minAreaRect(InputArray points)得到一个旋转的矩形,返回旋转矩形,绘制旋转矩形
*轮廓周围绘制圆和椭圆-API
cv::minEnclosingCircle(InputArray points, //得到最小区域圆形
Point2f& center, // 圆心位置
float& radius// 圆的半径
)
cv::fitEllipse(InputArray points)得到最小椭圆
*步骤
1:首先将图像变为二值图像。
2:发现轮廓,找到图像轮廓。
3:通过相关API在轮廓点上找到最小包含矩形和圆,旋转矩形与椭圆。
4:绘制它们。
*/
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
Mat src, gray_src, drawImg;
int threshold_v = 150;
int threshold_max = 255;
RNG rng(12345);
void Contours_Callback(int, void*);
int main()
{
src = imread("D:/A_Graduation/Learning/opencv/learningOpencv/hand.png");
cvtColor(src, gray_src, CV_BGR2GRAY);
blur(gray_src, gray_src, Size(3, 3), Point(-1, -1)); //均值模糊
imshow("src", src);
createTrackbar("Threshold Value:", "output", &threshold_v, threshold_max, Contours_Callback);
Contours_Callback(0, 0);
waitKey(0);
return 0;
}
void Contours_Callback(int, void*)
{
Mat binary_output;
threshold(gray_src, binary_output, threshold_v, threshold_max, THRESH_BINARY); //灰度图像变成二值图像
//imshow("binary image", binary_output);
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
findContours(binary_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1)); //在二值图像上找轮廓
vector<vector<Point>> contours_ploy(contours.size()); //把每个轮廓的外接多边形都找出来,不初始化会报错
vector<Rect> ploy_rects(contours.size()); //把最小矩形找出来,多边形的外接矩形
vector<Point2f> ccs(contours.size()); //圆心
vector<float> radius(contours.size()); //圆的半径
vector<RotatedRect> minRects(contours.size());
vector<RotatedRect> myellipse(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{ //输入的是多边形 输出结果 最小距离 是封闭的多边形
approxPolyDP(Mat(contours[i]), contours_ploy[i], 3, true); //获取多边形
ploy_rects[i] = boundingRect(contours_ploy[i]); //获取最小矩形,点数就从多边形的点数开始,结果是第i个外接矩形,两个点就行了,左上角和有下角坐标
//输入多边形的点数 中心 半径
minEnclosingCircle(contours_ploy[i], ccs[i], radius[i]); //最小的圆
if (contours_ploy[i].size() > 5)
{
myellipse[i] = fitEllipse(contours_ploy[i]);
minRects[i] = minAreaRect(contours_ploy[i]);
}
}
// draw it 位置信息已经有了就绘制出来呗
drawImg = Mat::zeros(src.size(), src.type());
Point2f pts[4];
for (size_t t = 0; t < contours.size(); t++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
// rectangle(drawImg, ploy_rects[t], color, 2, 8);
// circle(drawImg, ccs[t], radius[t], color, 2, 8);
if (contours_ploy[t].size() > 5)
{
ellipse(drawImg, myellipse[t], color, 1, 8);
minRects[t].points(pts); //就把矩形的四个点给它取到里面去了
for (int r = 0; r < 4; r++)
{
line(drawImg, pts[r], pts[(r + 1) % 4], color, 1, 8);
}
}
}
imshow("output", drawImg);
return;
}