原文链接:http://blog.sina.com.cn/s/blog_154bd48ae0102weuk.html
边缘检测的一般步骤:
1.滤波
边缘检测的算法主要是基于图像的一阶和二阶导数。但是导数通常对噪声很敏感,所以首先要用滤波器降低噪声。常见的滤波方法主要是高斯滤波。
2.增强
增强边缘的基础是确定图像各点领域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸现出来,在具体计算的过程中,可以通过计算梯度幅值来确定。
3.检测
经过增强的图像,往往领域中有很多点的梯度值比较大,而在特定的场合中,这些点并不是边缘点,所以应采用某种方式进行取舍,我们通常采取阈值化的方法来检测。
Canny边缘检测的步骤:
1.消除噪声(高斯滤波)
2.计算梯度幅值与方向(sobel滤波器)
3.非极大值抑制(排除一些非边缘像素)
4.滞后阈值
Sobel算子是计算图像梯度的,所以在canny和laplacian中都调用过sobel算子。
Sobel算子是一个主要用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任意一点使用该算子,都将会产生对应的梯度矢量或者法向量。
拉普拉斯算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度的散度。
除了canny算子,其他都是有x和y两个方向的
Void canny(InputArray image,OutputArray edges,double threshold1,double threshold2,int apertureSize=3,bool L2gradient=false)
需要注意的是,这个函数阈值1和阈值2中较小的值用于边缘连接,而较大的值用来控制强边缘的初始段。
Void Sobel(InputArray image,OutputArray edges,int ddepth,int dx,int dy,int ksize=3,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)
Laplacian(InputArray image,OutputArray edges,int ddepth,int ksize=1,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)
Void Scharr(InputArray image,OutputArray edges,int ddepth,int dx,int dy,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)
Scharr与soble只差一个参数,内核数,Scharr只作用于大小为3的内核,该函数和sobel函数一样快,但结果却更加精确。
//#include"stdafx.h"; #include"opencv2/opencv.hpp" //空间变量 using namespace cv; using namespace std; void main() { //显示原图像 Mat image = imread("./image/test3.jpg"); namedWindow("原图"); imshow("原图", image); //canny边缘检测的简单用法 Mat result; Canny(image, result, 150, 70); namedWindow("canny边缘检测后的图像"); imshow("canny边缘检测后的图像", result); //高阶的canny用法,转成灰度图,降噪,用canny,最后将得到的边缘作为掩码,拷贝原图到效果图上,得到彩色边缘图 Mat grayimage, edge; cvtColor(image, grayimage, COLOR_BGR2GRAY); boxFilter(grayimage, edge, -1, Size(3, 3)); Canny(edge, edge, 150, 70); Mat dst; dst = Scalar::all(123); image.copyTo(dst, edge); namedWindow("canny高阶边缘检测后的图像"); imshow("canny高阶边缘检测后的图像", dst); //sobel算子边缘检测 Mat x_result, y_result; Sobel(image, x_result, 0, 1, 0); Sobel(image, y_result, 0, 0, 1); addWeighted(x_result, 0.5, y_result, 0.5, 0, result); imshow("sobel边缘检测后x轴的图像", x_result); imshow("sobel边缘检测后y轴的图像", y_result); imshow("sobel边缘检测后的图像", result); //laplacian边缘检测 Laplacian(image, result, 0); imshow("laplacian边缘检测后的图像", result); //scharr滤波器 boxFilter(image, image, -1, Size(3, 3)); Scharr(image, x_result, 0, 1, 0); Scharr(image, x_result, 0, 0, 1); addWeighted(x_result, 0.5, y_result, 0.5, 0, result); imshow("scharr边缘检测后x轴的图像", x_result); imshow("scharr边缘检测后y轴的图像", y_result); imshow("scharr边缘检测后的图像", result); waitKey(); }
执行结果:
CV_EXPORTS_W void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point());
void drawContours(InputOutputArray image, InputOutputArrays contours, int contourIdx, const Scalar& color, int thickness = 1, int lineType = 8, InputArray hierarchy = noArray(), int maxLevel = INT_MAX, Point offset = Point());
#include <iostream> #include <vector> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; int main() { Mat img = imread("./image/test.jpg"); imshow("原图", img); cvtColor(img, img, COLOR_BGR2GRAY);//转化为灰度图 //大津法进行二值化 threshold(img, img, 100, 255, THRESH_OTSU); imshow("二值化", img); //提取二值化图像中的轮廓数据 vector<vector<Point> > contour_vec; vector<Vec4i> hierarchy; //1. RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略; //2. RETR_LIST:检测所有的轮廓 findContours(img, contour_vec, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE); cout << "contours number: " << contour_vec.size() << endl; // 以前常用的for循环绘制轮廓 /*Mat blkImg(binImg.size(), CV_8UC1, Scalar(0)); for(int i = 0; i < contour_vec.size(); i++) { drawContours(blkImg, contour_vec, i, Scalar(255), -1); } */ //绘制单通道轮廓图像,背景为白色,轮廓线条用黑色 Mat blkImg(img.size(), CV_8UC1, Scalar(255)); drawContours(blkImg, contour_vec, -1, Scalar(0), 2); imshow("单通道轮廓", blkImg); //绘制彩色轮廓图像,背景颜色为蓝绿色,轮廓线条为红色 Mat colorImg(img.size(), CV_8UC3, Scalar(255, 255, 0)); drawContours(colorImg, contour_vec, -1, Scalar(0, 0, 255), 3); imshow("彩色轮廓", colorImg); waitKey(0); return 0; }
执行结果:
#include <iostream> #include <vector> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; int main() { Mat img = imread("./image/test17.jpg"); imshow("原图", img); //图像取反 Mat lookUpTable(1, 256, CV_8U); uchar* p = lookUpTable.data; for (int i = 0; i < 256; i++) p[i] = 255 - i; LUT(img, lookUpTable, img);//通过LUT函数实现图像取反 cvtColor(img, img, COLOR_BGR2GRAY);//转化为灰度图 //大津法进行二值化 threshold(img, img, 100, 255, THRESH_OTSU); imshow("二值化", img); //提取二值化图像中的轮廓数据 vector<vector<Point> > contour_vec; vector<Vec4i> hierarchy; findContours(img, contour_vec, hierarchy, RETR_LIST, CHAIN_APPROX_NONE); cout << "contours number: " << contour_vec.size() << endl; // 以前常用的for循环绘制轮廓 /*Mat blkImg(binImg.size(), CV_8UC1, Scalar(0)); for(int i = 0; i < contour_vec.size(); i++) { drawContours(blkImg, contour_vec, i, Scalar(255), -1); } */ //绘制单通道轮廓图像,背景为白色,轮廓线条用黑色 Mat blkImg(img.size(), CV_8UC1, Scalar(255)); drawContours(blkImg, contour_vec, -1, Scalar(0), 2); imshow("单通道轮廓", blkImg); //绘制彩色轮廓图像,背景颜色为蓝绿色,轮廓线条为红色 Mat colorImg(img.size(), CV_8UC3, Scalar(255, 255, 0)); drawContours(colorImg, contour_vec, -1, Scalar(0, 0, 255), 3); imshow("彩色轮廓", colorImg); waitKey(0); return 0; }
执行结果: