引言:
一个轮廓一般对应一系列的点,也就是图像中的一条曲线。其表示方法可能根据不同的情况而有所不同。在OpenCV中,可以用findContours()函数从二值图像中查找轮廓。
寻找轮廓:findContours()函数
关于第三、四个参数的理解可以参考博文:CSDN博客 https://blog.csdn.net/guduruyu/article/details/69220296
绘制轮廓:drawContours()函数
代码示例:
1.这是通过canny获得边缘二值图像,然后再进行轮廓查找
#include "pch.h"
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int thresholdval = 100;
int thresholdmax = 127;
int mode = 3;
void Demo_Contours(int pos, void* userdata);
Mat blursrc;
int main()
{
//待检测图像
Mat src = imread("F:\\visual studio\\Image\\contours.png");
if (src.empty())
{
cout << "Can't load the image" << endl;
return -1;
}
imshow("src", src);
//转化为灰度图
Mat graysrc;
cvtColor(src, graysrc, COLOR_BGR2GRAY);
//高斯模糊
GaussianBlur(graysrc, blursrc, Size(7, 7), 5, 5);
namedWindow("Contours", WINDOW_AUTOSIZE);
createTrackbar("Threshold", "Contours", &thresholdval, thresholdmax, Demo_Contours);
createTrackbar("mode", "Contours", &mode, 3, Demo_Contours);
Demo_Contours(0, 0);
waitKey(0);
}
void Demo_Contours(int pos, void* userdata)
{
//Canny边缘检测
Mat edge;
Canny(blursrc, edge, thresholdval, thresholdval * 2, 3,false);
imshow("edge", edge);
//查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(edge, contours, hierarchy, mode, CV_CHAIN_APPROX_SIMPLE, Point(0,0));
//绘制
RNG rng(getTickCount());
Mat ContoursImg = Mat::zeros(blursrc.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(ContoursImg, contours, i, color, 2, LINE_AA, hierarchy);
}
imshow("Contours", ContoursImg);
}
利用两副图像观察效果:
1.这个图是我自己画的,检测效果很好。
源图像和边缘二值图像:
第一种模式仅检测最外层轮廓,其余的模式好像检测效果差不多:
2.这个图像是我的手部图像, 不明白为什么它的边缘检测效果就很糟糕(希望有人可以给出解决办法,在下方评论),自然影响到了轮廓的查找,效果很糟糕。
这种轮廓查找的方式太受制于边缘的检测!
2.通过threshold()获得二值图像,然后再进行轮廓查找
#include "pch.h"
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int thresholdval = 127;
int thresholdmax = 255;
int mode = 3;
void Demo_Contours(int pos, void* userdata);
Mat blursrc;
int main()
{
//待检测图像
Mat src = imread("F:\\visual studio\\Image\\hand1.jpg");
if (src.empty())
{
cout << "Can't load the image" << endl;
return -1;
}
resize(src, src, Size(), 0.5, 0.5);
imshow("src", src);
//转化为灰度图
Mat graysrc;
cvtColor(src, graysrc, COLOR_BGR2GRAY);
//高斯模糊
GaussianBlur(graysrc, blursrc, Size(3, 3), 0, 0);
namedWindow("Contours", WINDOW_AUTOSIZE);
createTrackbar("Threshold", "Contours", &thresholdval, thresholdmax, Demo_Contours);
createTrackbar("mode", "Contours", &mode, 3, Demo_Contours);
Demo_Contours(0, 0);
waitKey(0);
}
void Demo_Contours(int pos, void* userdata)
{
//阈值化
Mat bin;
threshold(~blursrc, bin, thresholdval, thresholdmax, CV_THRESH_BINARY);
imshow("bin", bin);
//查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(bin, contours, hierarchy, mode, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//绘制
RNG rng(getTickCount());
Mat ContoursImg = Mat::zeros(blursrc.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(ContoursImg, contours, i, color, 2, LINE_AA, hierarchy);
}
imshow("Contours", ContoursImg);
}
效果如下:
因为手部颜色相近,通过选择合适的阈值,利用阈值化能够很好地与背景区分,再进行轮廓查找,能够找到一条完整的手部轮廓,而不是像上面的出现很多条轮廓,而且模式1和其余模式也可以看出区别,效果很好。
这也是博主的手,给大家看看效果,在这种条件下,明显比基于边缘检测的效果好。