边缘检测的一般步骤
- 滤波
边缘检测的算法对噪声很敏感,所以采用滤波器来改善边缘检测器的性能。
- 增强
增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。
- 检测
经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。
Canny算子API函数
void Canny( InputArray image, OutputArray edges,double threshold1,
double threshold2,int apertureSize = 3, bool L2gradient = false );
//InputArray image:输入图像,需要为8bit单通道图像。
//OutputArray edges:输出图像,为相同大小8bit图像。
//threshold1:低阈值。
//threshold2:高阈值。
//apertureSize:sobel算子大小。
//L2gradient:是否用L2计算梯度的方法:sqrt{(dI/dx)^2 + (dI/dy)^2},否则用L1:|dI/dx|+|dI/dy|。
注意:对于Canny函数的使用,推荐的高低阈值比在2:1到3:1之间。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//全局变量
Mat dst, dst1, dst2;
Mat gray, edge;
//载入原图
Mat src = imread("12.bmp", 1);
void print()
{
cout << "键盘按键【1】:进行复杂canny算子" << endl;
cout << "键盘按键【2】:进行简单canny算子" << endl;
cout << "键盘按键【Esc】:退出程序" << endl;
cout << "————————————————————————————————————————" << endl;
}
int main()
{
print();
//改变console(控制台)字体颜色
system("color 0B");
//判断图片是否读入
if (!src.data)
{
printf("读取图片image0错误~! \n");
return false;
}
//循环轮询按键
while (1)
{
//先显示原图
namedWindow("原图");
imshow("原图", src);
//获取键盘按键
int c = waitKey(0);
//判断ESC是否按下,若按下便退出
if ((c & 255) == 27)
{
cout << "程序退出!\n";
break;
}
//根据按键的不同,进行各种操作
switch ((char)c)
{
case '1':
//高阶canny算子用法,转换为灰度图,降噪,使用canny算子,将最后
//得到的边缘作为掩膜,拷贝到原图上,得到彩色边缘图
cout << "您正在进行复杂canny算子操作" << endl;
cvtColor(src,gray,COLOR_BGR2GRAY);
blur(gray, edge, Size(3, 3));
Canny(edge, edge,150,100,3);
src.copyTo(dst, edge);
imshow("灰度图", gray);
imshow("边缘图", edge);
imshow("canny图1", dst);
break;
case '2':
cout << "您正在进行简单canny算子操作" << endl;
Canny(src, dst1, 150, 100, 3);
imshow("canny图2", dst1);
break;
}
}
return 0;
}
Sobel算子API函数
cv::Sobel(
InputArray Src, //输入图像
OutputArray dst, //输出图像,大小与输入图像一致
int depth, //输出图像深度
int dx, //x方向,几阶导数
int dy, //y方向,几阶导数
int ksize, //Sobel算子的kernel大小,必须是1,3,5,7
double scale=1,
double delta=0,
int borderType =BORDER_DEFAULT
)
最终的图像梯度:G=√Gx2+Gy2
为了加快计算速度,将图像梯度的计算方法简化为:G=|Gx|+|Gy|
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
cout << "请按下任意键" << endl;
Mat x,y,dst;
Mat abs_x,abs_y;
//载入原图
Mat src = imread("12.bmp", 1);
//改变console(控制台)字体颜色
system("color 0B");
//判断图片是否读入
if (!src.data)
{
printf("读取图片image0错误~! \n");
return false;
}
//循环轮询按键
while (1)
{
//先显示原图
namedWindow("原图");
imshow("原图", src);
//获取键盘按键
int c = waitKey(0);
//判断ESC是否按下,若按下便退出
if ((c & 255) == 27)
{
cout << "程序退出!\n";
break;
}
cvtColor(src, src, COLOR_BGR2GRAY);
//滤波
GaussianBlur(src, src, Size(3, 3), 0, 0);
//求x方向梯度
Sobel(src, x, CV_16S, 1, 0, 5, 1, 1);
//计算绝对值,并将结果转换为8位
convertScaleAbs(x, abs_x);
imshow("x方向sobel", abs_x);
//求y方向梯度
Sobel(src,y, CV_16S, 0,1, 5, 1, 1);
//计算绝对值,并将结果转换为8位
convertScaleAbs(y, abs_y);
imshow("y方向sobel", abs_y);
//合并梯度(近似)
addWeighted(abs_x, 0.5, abs_y, 0.5, 0, dst);
imshow("整体方向sobel", dst);
}
return 0;
}
Laplacian算子API函数
void Laplacian( InputArray src,
OutputArray dst,
int ddepth,
int ksize = 1,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT );
其中ddepth
一般设为-1表示输入输出图像类型一致。ksize
必须为奇数,默认为1,表示是四领域算子,大于1则是八领域算子。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
cout << "请按下任意键" << endl;
Mat dst,abs_dst;
//载入原图
Mat src = imread("12.bmp", 1);
//改变console(控制台)字体颜色
system("color 0B");
//判断图片是否读入
if (!src.data)
{
printf("读取图片image0错误~! \n");
return false;
}
//循环轮询按键
while (1)
{
//先显示原图
namedWindow("原图");
imshow("原图", src);
//获取键盘按键
int c = waitKey(0);
//判断ESC是否按下,若按下便退出
if ((c & 255) == 27)
{
cout << "程序退出!\n";
break;
}
cvtColor(src, src, COLOR_BGR2GRAY);
//滤波
GaussianBlur(src, src, Size(3, 3), 0, 0);
//求x方向梯度
Laplacian(src, dst, CV_16S, 3,1, 0);
//计算绝对值,并将结果转换为8位
convertScaleAbs(dst, abs_dst);
imshow("Laplacian", abs_dst);
}
return 0;
}
Scharr算子API函数
Scarry是sobel算子的特殊改进情况。当内核大小为3时,Sobel内核可能产生比较明显的误差,为了解决这一问题,Opencv提供了Scharr函数,但该函数仅作用于大小为3的内核,运行速度与Sobel函数一样,但结果却更加精确。
void Scharr(InputArray src, OutputArray dst,int ddepth ,int dx, int dy,double scale = 1,double delta = 0,int borderType=BORDER_DEFAULT)
InputArray src:输入图像。
OutputArray dst:输出图像。
int ddepth:输出图像深度。
int dx:x方向上的差分阶数。
int dy:y方向上的差分阶数。
double scale :计算导数值时可选的缩放因子,默认值1,表示默认情况下没用应用缩放。
double delta:表示在结果存入输出图像之前可选的delta值,默认值0。
int borderType:边界模式。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
cout << "请按下任意键" << endl;
Mat x, y, dst;
Mat abs_x, abs_y;
//载入原图
Mat src = imread("12.bmp", 1);
//改变console(控制台)字体颜色
system("color 0B");
//判断图片是否读入
if (!src.data)
{
printf("读取图片image0错误~! \n");
return false;
}
//循环轮询按键
while (1)
{
//先显示原图
namedWindow("原图");
imshow("原图", src);
//获取键盘按键
int c = waitKey(0);
//判断ESC是否按下,若按下便退出
if ((c & 255) == 27)
{
cout << "程序退出!\n";
break;
}
cvtColor(src, src, COLOR_BGR2GRAY);
//滤波
GaussianBlur(src, src, Size(3, 3), 0, 0);
//求x方向梯度
Scharr(src, x, CV_16S, 1, 0, 3, 1, 1);
//计算绝对值,并将结果转换为8位
convertScaleAbs(x, abs_x);
imshow("x方向Scharr", abs_x);
//求y方向梯度
Scharr(src, y, CV_16S, 0, 1, 3, 1, 1);
//计算绝对值,并将结果转换为8位
convertScaleAbs(y, abs_y);
imshow("y方向Scharr", abs_y);
//合并梯度(近似)
addWeighted(abs_x, 0.5, abs_y, 0.5, 0, dst);
imshow("整体方向Scharr", dst);
}
return 0;
}