这个系列的目的是通过对OpenCV示例,进一步了解OpenCV函数的使用,不涉及具体原理。
目录
简介
Example运行截图
Example分析
Example代码
简介
本文记录了对OpenCV示例edge.cpp
的分析。
首先这个示例,主要讲述了使用
Canny函数获取图像边缘。
Canny算法原理
Canny 边缘检测算法
是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的
最优算法
, 最优边缘检测的三个主要评价标准是:
- 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
- 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
- 最小响应: 图像中的边缘只能标识一次。
Canny步骤
-
消除噪声。 使用高斯平滑滤波器卷积降噪。 下面显示了一个size = 5 的高斯内核示例:
-
计算梯度幅值和方向。 此处,按照Sobel滤波器的步骤:
-
运用一对卷积阵列 (分别作用于 x 和 y方向):
-
使用下列公式计算梯度幅值和方向:
梯度方向近似到四个可能角度之一(一般 0, 45, 90, 135)
-
-
非极大值 抑制。 这一步排除非边缘像素, 仅仅保留了一些细线条(候选边缘)。
-
滞后阈值: 最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):
- 如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素。
- 如果某一像素位置的幅值小于 低 阈值, 该像素被排除。
- 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留。
Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。
以上内容摘至参考资料5.《图像边缘检测--OpenCV之cvCanny函数》
Canny函数在OpenCV中的原型
Canny
函数原型:
void Canny( InputArray image, OutputArray edges,
double threshold1, double threshold2,
int apertureSize = 3, bool L2gradient = false );
参数说明:
image: 输入图像,需要8位图像。
edges: 输出边缘图像。
threshold1: 第一个滞后阈值。
threshold2: 第二个滞后阈值。
apertureSize: Sobel算子的孔径大学。
L2gradient : 计算图像梯度值的标识,一般使用默认值false。
PS: 参数threshold1和threshold2中使用小的一个进行边缘连接,大的用来控制强边缘的初始段,一般比例为2:1至3:1之间。
|
Example截图
原图
|
处理结果
|
参数
|
| | threshold1 = 1 threshold2 = 3 apertureSize = 3 L2gradient =false |
Example分析
示例主要步骤如下:
1.申明需要使用的变量:
int edgeThresh = 1;
Mat image, gray, edge, cedge;
PS:Canny函数由高低两个阈值,用户在本示例只设置低阈值edgeThresh,令高阈值为低阈值的3倍。
2.从命令行参数中加载图像:
CommandLineParser parser(argc, argv, keys);
string filename = parser.get<string>(0);
image = imread(filename, 1);
if(image.empty())
{
printf("Cannot read image file: %s\n", filename.c_str());
help();
return -1;
}
3.创建与原图相同尺寸样式的图像,用于绘制边缘检测结果:
cedge.create(image.size(), image.type());
4.灰度化
cvtColor(image, gray, COLOR_BGR2GRAY);
5创建预览窗口
namedWindow("Edge map", 1);
6.创建滑动条,用于用户交互,设置阈值
createTrackbar("Canny threshold", "Edge map", &edgeThresh, 100, onTrackbar);
PS:认为低阈值上限为100;
PS1:
Trackbar变动改变edgeThresh,调用回调函数onTrackbar。
7.分析onTrackbar函数
7.1均值滤波,平滑图像
blur(gray, edge, Size(3,3));
7.2使用Canny寻找边缘
Canny(edge, edge, edgeThresh, edgeThresh*3, 3);
7.3将cedge 填充为黑色(0)
cedge = Scalar::all(0);
7.4将Canny函数结果(边缘检测结果)复制到cedge(背景为黑色)
image.copyTo(cedge, edge);
7.5显示图像
imshow("Edge map", cedge);
Example代码
#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <stdio.h>
using namespace cv;
using namespace std;
int edgeThresh = 1;
Mat image, gray, edge, cedge;
// define a trackbar callback
static void onTrackbar(int, void*)
{
blur(gray, edge, Size(3,3));
// Run the edge detector on grayscale
Canny(edge, edge, edgeThresh, edgeThresh*3, 3);
cedge = Scalar::all(0);
image.copyTo(cedge, edge);
imshow("Edge map", cedge);
}
static void help()
{
printf("\nThis sample demonstrates Canny edge detection\n"
"Call:\n"
" /.edge [image_name -- Default is ../data/fruits.jpg]\n\n");
}
const char* keys =
{
"{@image |../data/fruits.jpg|input image name}"
};
int main( int argc, const char** argv )
{
help();
CommandLineParser parser(argc, argv, keys);
string filename = parser.get<string>(0);
image = imread(filename, 1);
if(image.empty())
{
printf("Cannot read image file: %s\n", filename.c_str());
help();
return -1;
}
cedge.create(image.size(), image.type());
cvtColor(image, gray, COLOR_BGR2GRAY);
// Create a window
namedWindow("Edge map", 1);
// create a toolbar
createTrackbar("Canny threshold", "Edge map", &edgeThresh, 100, onTrackbar);
// Show the image
onTrackbar(0, 0);
// Wait for a key stroke; the same function arranges events processing
waitKey(0);
return 0;
}
参考资料:
1.《
【opencv 一日一练】 api 之 blur
》