经过两周opencv的学习,作者对opencv有了一些自己的理解和心得,总结出一套较为实用提取图像轮廓的方法。
操作步骤:
1、图像锐化。
图像锐化的目的是为了要增强图像中对比度,这样可以使提取轮廓的效果,让轮廓显现的更明显。
这里我们使用拉普拉斯算子遍历图像,预先定义矩阵:
Mat kernel = (Mat_(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);
运用filter2D()函数 将我们定义好的矩阵遍历图像。再进行图像的运算Mat resultImg = src - src_lapl。(具体参考下面给出的代码)
Top:当Mat类型的图像数据类型不同时,可能需要运用到convertTo()函数来转换图像的深度和通道数。
这里给出convertTo()函数的基础用法:
src.convertTo(dst, type, scale, shift)
dst:目的矩阵;
type:需要的输出矩阵类型,或者更明确的,是输出矩阵的深度,如果是负值(常用-1)则输出矩阵和输入矩阵类型相同;
scale:比例因子;
shift:将输入数组元素按比例缩放后添加的值;
dst(i)=src(i)xscale+(shift,shift,…)
2、Canny()函数进行边缘检测。
前面的学习笔记已经详细介绍了Canny()函数的用法,这里我们不作展开。
只是需要注意的是,Canny函数参数的调参,由于处理的图像不同,读者参考一下代码使用时注意要自己调整参数。
3、findContours()函数提取轮廓
使用findContours()函数之前,一般要先定义一个放有vector容器的vector容器,子容器为Point,再定义一个放置四个int类型的vector容器。
vector<vector > contours;
vector hierarchy;
再使用findContours()函数提取轮廓
void findContours(
InputOutputArray image,
OutputArrayOfArrays contours,
OutputArray hierarchy,
int mode,
int method,
Point offset = Point());
关于该函数的参数,详细介绍可参考以下链接:
4、drawContours()函数绘制轮廓
void drawContours(
InputOutputArray image,
InputArrayOfArrays contours,
int contourIdx,
const Scalar& color, int thickness=1,
int lineType=8,
InputArray hierarchy=noArray(),
int maxLevel=INT_MAX,
Point offset=Point() )
函数参数详解:
image表示目标图像;
contours表示输入的轮廓组,每一组轮廓由点vector构成;
contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓;
color为轮廓的颜色(Schlar类型);
thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部;
lineType为线型;
InputArray hierarchy表示轮廓结构信息;
代码演示:
#include <opencv2\opencv.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
int main()
{
Mat src, gray_src, dest;
src = imread("C:/Users/86159/Desktop/yasina.jpg");
Mat src_lapl;
Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); //定义拉普拉斯算子
filter2D(src, src_lapl, CV_32F, kernel, Point(-1, -1), 0, BORDER_DEFAULT);
src.convertTo(src, CV_32F);
Mat resultImg = src - src_lapl;
src.convertTo(src, CV_8UC3);
src_lapl.convertTo(src_lapl, CV_8UC3);
resultImg.convertTo(resultImg, CV_8UC3);
Canny(resultImg, dest, 630, 1200, 3);
Mat dst = Mat::zeros(src.size(), src.type());
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(dest, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0)); //提取轮廓
for (int i = 0; i < contours.size(); i++)
{
drawContours(dst, contours, i, Scalar(255,255,255), 1, 4, hierarchy, 0, Point()); //绘制轮廓
}
bitwise_not(dst, dst); //图像取反
namedWindow("原图", WINDOW_AUTOSIZE);
moveWindow("原图", 50, 0);
imshow("原图", src);
namedWindow("效果图", WINDOW_AUTOSIZE);
moveWindow("效果图", 693, 0);
imshow("效果图", dst);
waitKey(0);
return 0;
}
效果展示:
希望对读者有所帮助,喜欢的话可以关注一下我的公众号,我会把学习笔记发在上面,大家可以一起共同学习!