本质上是用于卷积运算的模板,最终效果上是求得梯度。Roberts 算子,Sobel算子、Prewitt算子以及Laplace算子等。按功能分,上述算子都是求边缘检测的算子。如果按求导的阶数分类,Roboert、Sobel、Prewitt都是一阶算子,而Laplace属于二阶算子。
利用局部差分算子寻找边缘的算子,采用对角线相邻两像素之差(正常梯度是采用垂直和水平像素之差)作为梯度幅值检测边缘。该算子检测垂直边缘的效果好于斜向边缘,定位精度高。但是对噪声敏感,无法抑制噪声。另外该算子运算快,但是采用的偶数模板,所求点处的梯度幅度值,其实是图中交叉点处的值,从而导致偏移了半个像素。
推导过程。(因为平方和平方根需要大量的计算开销,所以使用绝对值来近似梯度幅值)
其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。
另外能够抑制噪声(一个方向求微分,一个方向求平均,而平均对噪声有抑制作用)。但抑制噪声的同时,由于求了平均,所以相当于对图像的低通滤波。正因此,对边缘的定位不如Roberts算子。
Sobel算子和Prewitt算子都是加权平均,但是Sobel算子认为,邻域的像素对当前像素产生的影响不是等价的,所以距离不同的像素具有不同的权值,对算子结果产生的影响也不同。一般来说,距离越远,产生的影响越小。同样,也分为垂直方向和水平方向两个。
由于Sobel算子是滤波算子的形式,用于提取边缘,可以利用快速卷积函数,简单有效,因此应用广泛。美中不足的是,Sobel算子并没有将图像的主体与背景严格地区分开来,换言之就是Sobel算子没有基于图像灰度进行处理,由于Sobel算子没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。 在观测一幅图像的时候,我们往往首先注意的是图像与背景不同的部分,正是这个部分将主体突出显示,基于该理论,我们可以给出阈值化轮廓提取算法,该算法已在数学上证明当像素点满足正态分布时所求解是最优的。
加权平均算子,权值反比于邻点与中心点的距离,当沿不同方向检测边缘时梯度幅度一致,就是通常所说的各向同性。
二阶微分算子,具有各向同向性,与坐标轴无关(所以无法检测方向)。但是对噪声敏感,所以图像一般需要经过平滑处理,因为平滑处理也是基于模板的,所以通常是使用Laplace算子和平滑算子结合起生成新的模板。
一维一阶差分公式和二阶差分公式分别为如下。
分别对Laplace算子x,y两个方向的二阶导数进行差分就得到了离散函数的Laplace算子。在一个二维函数f(x,y)中,x,y两个方向的二阶差分分别为。
所以Laplace算子的差分形式为。
写成模板的形式如下。
0 |
1 |
0 |
1 |
-4 |
1 |
0 |
1 |
0 |
注意该mask的特点,mask在上下左右四个90度的方向上结果相同,也就是说在90度方向上无方向性。为了让该mask在45度的方向上也具有该性质,对该filter mask进行扩展定义为,
1 |
1 |
1 |
1 |
-8 |
1 |
1 |
1 |
1 |
enum { ROBERT = 0, PREWWIT = 1, SOBEL = 2, LAPLACE = 3 }GradTemplate;
enum { X = 0, Y = 1 }Direction;
class Gradient { public: Gradient(int grad_template, int direction) : grad_template_(grad_template), direction_(direction) { if (grad_template_ == PREWWIT) { if (direction == X) { memcpy(calc_factor_, prewitt_x_, sizeof(int) * 9); } else { memcpy(calc_factor_, prewitt_y_, sizeof(int) * 9); } } else if (grad_template_ == SOBEL) { if (direction == X) { memcpy(calc_factor_, sobel_x_, sizeof(int) * 9); } else { memcpy(calc_factor_, sobel_y_, sizeof(int) * 9); } } else if (grad_template_ == LAPLACE) { if (direction == X) { memcpy(calc_factor_, laplace_x_, sizeof(int) * 9); } else { memcpy(calc_factor_, laplace_y_, sizeof(int) * 9); } } } ~Gradient() { } void find_edge(cv::Mat& src_img, cv::Mat& dst_img) { for (int i = 0; i < src_img.rows - 1; ++i) { for (int j = 0; j < src_img.cols - 1; ++j) { if (grad_template_ == ROBERT) { if (direction_ == X) { dst_img.at<unsigned char>(i, j) = cv::saturate_cast<unsigned char>(src_img.at<unsigned char>(i, j) - src_img.at<unsigned char>(i + 1, j + 1)); } else { dst_img.at<unsigned char>(i, j) = cv::saturate_cast<unsigned char>(src_img.at<unsigned char>(i, j+1) - src_img.at<unsigned char>(i + 1, j)); } } else { int index = 0; int value = 0; for (int m = -radius_; m <= radius_; ++m) { int r_offset = m + i; r_offset = (r_offset < 0) ? 0 : (r_offset >= src_img.rows ? src_img.rows : r_offset); for (int n = -radius_; n <= radius_; n++) { int c_offset = n + j; c_offset = (c_offset < 0) ? 0 : (c_offset >= src_img.cols ? src_img.cols : c_offset); value += static_cast<int>(src_img.at<unsigned char>(r_offset, c_offset)) * calc_factor_[index++]; } } dst_img.at<unsigned char>(i, j) = cv::saturate_cast<unsigned char>(value); } } } } const int radius_ = 1; // 3 * 3 private: int robert_x_[4] = { 1, 0, 0, -1}; int robert_y_[4] = { 0, 1, -1, 0}; int prewitt_x_[9] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 }; int prewitt_y_[9] = { -1, -1, -1, 0, 0, 0, 1, 1, 1 }; int sobel_x_[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1}; int sobel_y_[9] = {1, 2, 1, 0, 0, 0, -1, -2, -1}; int laplace_x_[9] = { 0, 1, 0, 1, -4, 1, 0, 1, 0 }; int laplace_y_[9] = { 1, 1, 1, 1, -8, 1, 1, 1, 1 }; int calc_factor_[9]; int direction_; int grad_template_; }; |
Robert 算子在X和Y方向,分别对无噪声和有噪声的处理后的效果。可以很明显看出噪声影响较大。
Prewwit算子效果如下。
Sobel算子如下。
拉普拉斯算子如下。
http://blog.csdn.net/xiaojiegege123456/article/details/7714863
http://www.cnblogs.com/german-iris/p/4840647.html