图像处理梯度算子2*2

2012-06-14 23:05:48 jia20003 阅读数 48113
  • 图像梯度

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    98人学习 贾志刚
    免费试看

图像处理之图像梯度效果


基本思想:

利用X方向与Y方向分别实现一阶微分,求取振幅,实现图像梯度效果。关于如何计算图像

一阶微分参见这里:http://blog.csdn.net/jia20003/article/details/7562092

使用的两种微分算子分别为Prewitt与Sobel,其中Soble在X, Y两个方向算子分别为:


Prewitt在X, Y方向上梯度算子分别为:


二:程序思路及实现

梯度滤镜提供了两个参数:

– 方向,用来要决定图像完成X方向梯度计算, Y方向梯度计算,或者是振幅计算

– 算子类型,用来决定是使用sobel算子或者是prewitt算子。

计算振幅的公式可以参见以前《图像处理之一阶微分应用》的文章


 三:运行效果

原图像如下:


基于Prewitt与sobel算子的XY方向振幅效果如下:



该滤镜的源代码如下:

package com.process.blur.study;

import java.awt.image.BufferedImage;
/**
 * 
 * @author gloomy-fish
 * @date 2012-06-11
 * 
 * prewitt operator 
 * X-direction
 * -1, 0, 1
 * -1, 0, 1
 * -1, 0, 1
 * 
 * Y-direction
 * -1, -1, -1
 *  0,  0,  0
 *  1,  1,  1
 *  
 * sobel operator
 * X-direction
 * -1, 0, 1
 * -2, 0, 2
 * -1, 0, 1
 * 
 * Y-direction
 * -1, -2, -1
 *  0,  0,  0
 *  1,  2,  1
 *
 */
public class GradientFilter extends AbstractBufferedImageOp {

	// prewitt operator
	public final static int[][] PREWITT_X = new int[][]{{-1, 0, 1}, {-1, 0, 1}, {-1, 0, 1}};
	public final static int[][] PREWITT_Y = new int[][]{{-1, -1, -1}, {0,  0,  0}, {1,  1,  1}};
	
	// sobel operator
	public final static int[][] SOBEL_X = new int[][]{{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
	public final static int[][] SOBEL_Y = new int[][]{{-1, -2, -1}, {0,  0,  0}, {1,  2,  1}};
	
	// direction parameter
	public final static int X_DIRECTION = 0;
	public final static int Y_DIRECTION = 2;
	public final static int XY_DIRECTION = 4;
	private int direction;
	private boolean isSobel;
	
	public GradientFilter() {
		direction = XY_DIRECTION;
		isSobel = true;
	}
	
	public void setSoble(boolean sobel) {
		this.isSobel = sobel;
	}

	public int getDirection() {
		return direction;
	}

	public void setDirection(int direction) {
		this.direction = direction;
	}
	
	@Override
	public BufferedImage filter(BufferedImage src, BufferedImage dest) {
		int width = src.getWidth();
        int height = src.getHeight();

        if (dest == null )
        	dest = createCompatibleDestImage( src, null );

        int[] inPixels = new int[width*height];
        int[] outPixels = new int[width*height];
        getRGB( src, 0, 0, width, height, inPixels );
        int index = 0, index2 = 0;
        double xred = 0, xgreen = 0, xblue = 0;
        double yred = 0, ygreen = 0, yblue = 0;
        int newRow, newCol;
        for(int row=0; row<height; row++) {
        	int ta = 255, tr = 0, tg = 0, tb = 0;
        	for(int col=0; col<width; col++) {
        		index = row * width + col;
        		for(int subrow = -1; subrow <= 1; subrow++) {
        			for(int subcol = -1; subcol <= 1; subcol++) {
        				newRow = row + subrow;
        				newCol = col + subcol;
        				if(newRow < 0 || newRow >= height) {
        					newRow = row;
        				}
        				if(newCol < 0 || newCol >= width) {
        					newCol = col;
        				}
        				index2 = newRow * width + newCol;
                        tr = (inPixels[index2] >> 16) & 0xff;
                        tg = (inPixels[index2] >> 8) & 0xff;
                        tb = inPixels[index2] & 0xff;
                        
                        if(isSobel) {
                        	xred += (SOBEL_X[subrow + 1][subcol + 1] * tr);
                        	xgreen +=(SOBEL_X[subrow + 1][subcol + 1] * tg);
                        	xblue +=(SOBEL_X[subrow + 1][subcol + 1] * tb);
                        	
                        	yred += (SOBEL_Y[subrow + 1][subcol + 1] * tr);
                        	ygreen +=(SOBEL_Y[subrow + 1][subcol + 1] * tg);
                        	yblue +=(SOBEL_Y[subrow + 1][subcol + 1] * tb);
                        } else {
                        	xred += (PREWITT_X[subrow + 1][subcol + 1] * tr);
                        	xgreen +=(PREWITT_X[subrow + 1][subcol + 1] * tg);
                        	xblue +=(PREWITT_X[subrow + 1][subcol + 1] * tb);
                        	
                        	yred += (PREWITT_Y[subrow + 1][subcol + 1] * tr);
                        	ygreen +=(PREWITT_Y[subrow + 1][subcol + 1] * tg);
                        	yblue +=(PREWITT_Y[subrow + 1][subcol + 1] * tb);
                        }
        			}
        		}
        		
                double mred = Math.sqrt(xred * xred + yred * yred);
                double mgreen = Math.sqrt(xgreen * xgreen + ygreen * ygreen);
                double mblue = Math.sqrt(xblue * xblue + yblue * yblue);
                if(XY_DIRECTION == direction) 
                {
                	outPixels[index] = (ta << 24) | (clamp((int)mred) << 16) | (clamp((int)mgreen) << 8) | clamp((int)mblue);
                } 
                else if(X_DIRECTION == direction)
                {
                	outPixels[index] = (ta << 24) | (clamp((int)yred) << 16) | (clamp((int)ygreen) << 8) | clamp((int)yblue);
                } 
                else if(Y_DIRECTION == direction) 
                {
                	outPixels[index] = (ta << 24) | (clamp((int)xred) << 16) | (clamp((int)xgreen) << 8) | clamp((int)xblue);
                } 
                else 
                {
                	// as default, always XY gradient
                	outPixels[index] = (ta << 24) | (clamp((int)mred) << 16) | (clamp((int)mgreen) << 8) | clamp((int)mblue);
                }
                
                // cleanup for next loop
                newRow = newCol = 0;
                xred = xgreen = xblue = 0;
                yred = ygreen = yblue = 0;
                
        	}
        }
        setRGB(dest, 0, 0, width, height, outPixels );
        return dest;
	}
	
	public static int clamp(int value) {
		return value < 0 ? 0 : (value > 255 ? 255 : value);
	}

}

转载文章请务必注明出自本博客!

精彩内容查看 - 《数字图像处理-空间域卷积视频教程》

2019-04-03 11:29:55 baidu_38172402 阅读数 2136
  • 图像梯度

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    98人学习 贾志刚
    免费试看

前面我们介绍过图像的梯度,其定义是根据微积分的定义在二维离散函数中推导出来的。但是,我们需要理解,梯度只是一个工具,方法,核心目的是得到像素点与其相邻像素的灰度值变化情况,并通过这种变化来增强图像。这种原始定义的梯度只是这种灰度值变化情况的度量工具。

我们再回顾一下,假设某像素与其8领域用如下矩阵表示:
这里写图片描述

那么,根据图像梯度的定义:
gx = z8 - z5
gy = z6 - z5
上面提到,这种原始定义的梯度只是这种灰度值变化情况的度量工具,这种度量工具只有这一种吗?显然不是的!
z9-z5算不算灰度值的变化?z1-z5呢?z7-z5呢?z4-z5呢?z3-z5呢?

我们利用梯度的本质目的,是要找到某像素与其相邻像素的灰度差值,并放大这种差值,从而用于图像增强。而原始定义的梯度计算方法只是灰度差值计算方法中的一种,还可以有其它计算方法来度量这种变化。

为简化起见,我们把这些计算方法统称为梯度算子。根据不同的相邻像素差值计算得到的效果可能有所不同,但基本原理是一致的。

例如罗伯特(Roberts)交叉梯度算子,定义的是:
gx = z9-z5
gy = z8-z6
为什么名字中有“交叉”,看看下图就知道了。

这里写图片描述

我们可以看到,不管是原始梯度也好,Roberts算子也好,都只是利用了z5,z6,z8,z9的像素值,那么可不可以扩展到8领域呢,当然是可以的。

例如,你可以自己定义一个:
gx = (z7+z8+z9)-(z1+z2+z3)
gy = (z3+z6+z9)-(z1+z4+z7)

这里写图片描述

事实上,这个你自定义的算子和著名的Sobel算子非常接近了,只是Sobel算子增加了距离权重而已。Sobel算子的定义如下(与中心点Z5更近的点Z3,Z4,Z6,Z8的权重为2,其它对角线上的权重为1):
gx = (z7+2*z8+z9)-(z1+2*z2+z3)
gy = (z3+2*z6+z9)-(z1+2*z4+z7)

关于Sobel算子后面还会再重点介绍,下面用Roberts交叉算子来看看图像增强的效果。


import cv2
import numpy as np

moon = cv2.imread("moon.tif", 0)
row, column = moon.shape
moon_f = np.copy(moon)
moon_f = moon_f.astype("float")

Roberts = np.zeros((row, column))

for x in range(row - 1):
    for y in range(column - 1):
        gx = abs(moon_f[x + 1, y + 1] - moon_f[x, y])
        gy = abs(moon_f[x + 1, y] - moon_f[x, y + 1])
        Roberts[x, y] = gx + gy

sharp = moon_f + Roberts
sharp = np.where(sharp < 0, 0, np.where(sharp > 255, 255, sharp))
sharp = sharp.astype("uint8")

cv2.imshow("moon", moon)
cv2.imshow("Roberts_sharp", sharp)
cv2.waitKey()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

输出结果:

这里写图片描述

再看看用上面自定义算子的情况:
user_defined = np.zeros((row, column))

for x in range(1, row - 1):
    for y in range(1, column - 1):
        gx = abs((moon_f[x + 1, y - 1] + moon_f[x + 1, y] + moon_f[x + 1, y + 1]) - (
            moon_f[x - 1, y - 1] + moon_f[x - 1, y] + moon_f[x - 1, y + 1]))
        gy = abs((moon_f[x - 1, y + 1] + moon_f[x, y + 1] + moon_f[x + 1, y + 1]) - (
            moon_f[x - 1, y - 1] + moon_f[x, y - 1] + moon_f[x + 1, y - 1]))

        user_defined[x, y] = gx + gy

sharp = moon_f + user_defined
sharp = np.where(sharp < 0, 0, np.where(sharp > 255, 255, sharp))
sharp = sharp.astype("uint8")

cv2.imshow("moon", moon)
cv2.imshow("defined_sharp", sharp)
cv2.waitKey()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出结果:
这里写图片描述

转载自:https://blog.csdn.net/saltriver/article/details/78987170

2018-11-20 17:17:43 dyq1995 阅读数 5349
  • 图像梯度

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    98人学习 贾志刚
    免费试看

 下面简要介绍一下不同的梯度算子对于图像处理的区别:

其中Roberts算子,又称罗伯茨算子,是一种最简单的算子,是一种利用局部差分算子寻找边缘的算子。他采用对角线方向相邻两象素之差近似梯度幅值检测边缘。检测垂直边缘的效果好于斜向边缘,定位精度高,对噪声敏感,无法抑制噪声的影响。

而Prewitt算子是一种一阶微分算子边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。

1、首先打开MATLAB软件,在主界面的编辑器中写入下列代码:

I=imread('G:\MATLAB\bm.bmp');         %读取当前路径下的图片
subplot(3,2,1);
imshow(I);
title('原始图像');
axis([50,250,50,200]);
grid on;                  %显示网格线
axis on;                  %显示坐标系
I1=im2bw(I);
subplot(3,2,2);
imshow(I1);
title('二值图像');
axis([50,250,50,200]);
grid on;                  %显示网格线
axis on;                  %显示坐标系
I2=edge(I1,'roberts');
figure;
subplot(3,2,3);
imshow(I2);
title('roberts算子分割结果');
axis([50,250,50,200]);
grid on;                  %显示网格线
axis on;                  %显示坐标系
I3=edge(I1,'sobel');
subplot(3,2,4);
imshow(I3);
title('sobel算子分割结果');
axis([50,250,50,200]);
grid on;                  %显示网格线
axis on;                  %显示坐标系
I4=edge(I1,'Prewitt');
subplot(3,2,5);
imshow(I4);
title('Prewitt算子分割结果 ');
axis([50,250,50,200]);
grid on;                  %显示网格线
axis on;                  %显示坐标系

2、命名保存好之后,点击运行按钮,出现如下所示结果:

至此,关于roberts算子、和prewitt算子的图像处理效果就介绍完毕了,请大家继续关注!!

2019-04-06 19:11:31 Small_Munich 阅读数 493
  • 图像梯度

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    98人学习 贾志刚
    免费试看
图像梯度算子简介

  相信只要是懂些图像处理的知识,都知道图像梯度的含义。不知道是否考虑过为什么图像梯度如此广泛认知与使用?为什么不使用图像纹理、图像色彩、图像相位等等,在这里我并不是说上述除了图像梯度之外,其它的图像信息不重要,我只是想说图像梯度最为广泛的被研究与使用。下面我们来看一幅图像:

  从上图坐标为灰度图像,右边为Laplacian算子提取的图像梯度图;现在我们来看一下左边图片中的A、B、C、D、E、F区域,你可以通过这些子区域发现处于图像中的哪个位置吗?通过人眼观察可以轻易得出结果:E、F最容易分辨,C、D能够确定更小的范围区域,A、B最难分辨在哪个范围。OK,让我们来看右边的梯度算子,可以发现:E、F小区域在梯度图像上较为完整的保留下来,C、D保留边缘曲线,A、B已经完全发现不出什么明显的特征。

  通过上述一个简单的观察,你会发现:E、F是角点区域(梯度各个方向变化最为明显),C、D为边缘区域(梯度垂直于边缘方向最为明显)、A、B为平坦区域(梯度各个方向变化都不明显)。这不就是后面做特征点检测的信息丰富度准则,角点最为稳定、边缘检测等特征。图像梯度提取最为简单直接,能够有效的描述图像的原始状态,因此发展一系列的图像梯度算子:Roberts、Prewitt、Sobel、Laplacian、Scharr等。下面介绍一下比较经常使用的梯度提取算子卷积核函数:

Sobel算子:

Wsobelx=[w00w01w02w10w11w12w20w21w22]=[10+120+210+1]W_{sobelx}= \left[ \begin{matrix} w_{00} &amp; w_{01} &amp; w_{02} \\ w_{10} &amp; w_{11} &amp; w_{12} \\ w_{20} &amp; w_{21} &amp; w_{22} \end{matrix} \right] = \left[ \begin{matrix} -1 &amp; 0 &amp; +1 \\ -2 &amp; 0 &amp; +2 \\ -1 &amp; 0 &amp; +1 \end{matrix} \right]

Wsobely=[w00w01w02w10w11w12w20w21w22]=[121000+1+2+1]W_{sobely}= \left[ \begin{matrix} w_{00} &amp; w_{01} &amp; w_{02} \\ w_{10} &amp; w_{11} &amp; w_{12} \\ w_{20} &amp; w_{21} &amp; w_{22} \end{matrix} \right] = \left[ \begin{matrix} -1 &amp; -2 &amp; -1 \\ 0 &amp; 0 &amp; 0 \\ +1 &amp; +2 &amp; +1 \end{matrix} \right]

分别为Sobel算子针对XXYY方向提取的图像,然后对梯度图像:
(1)Gimage=Gx2+Gy2G_{image}=\sqrt{G^{2}_x+G^{2}_y}\tag{1}

  参数解释:GimageG_{image}为Sobel算子提取的梯度图像,GxG_xWsobelxW_{sobelx}卷积核与原始图像卷积提取X方向的结果,GyG_yWsobelyW_{sobely}卷积核与原始图像卷积提取YY方向的结果。

Scharr算子:

Wscharrx=[w00w01w02w10w11w12w20w21w22]=[30+3100+1030+3]W_{scharrx}= \left[ \begin{matrix} w_{00} &amp; w_{01} &amp; w_{02} \\ w_{10} &amp; w_{11} &amp; w_{12} \\ w_{20} &amp; w_{21} &amp; w_{22} \end{matrix} \right] = \left[ \begin{matrix} -3 &amp; 0 &amp; +3 \\ -10 &amp; 0 &amp; +10 \\ -3 &amp; 0 &amp; +3 \end{matrix} \right]

Wscharry=[w00w01w02w10w11w12w20w21w22]=[3103000+3+10+3]W_{scharry}= \left[ \begin{matrix} w_{00} &amp; w_{01} &amp; w_{02} \\ w_{10} &amp; w_{11} &amp; w_{12} \\ w_{20} &amp; w_{21} &amp; w_{22} \end{matrix} \right] = \left[ \begin{matrix} -3 &amp; -10 &amp; -3 \\ 0 &amp; 0 &amp; 0 \\ +3 &amp; +10 &amp; +3 \end{matrix} \right]

Scharr算子与Sobel算子原理一致,只是改进版,效果要比Sobel好些。

Laplacian算子:

  拉普拉斯算子是最广为人知的算子,完全的尺度不变性,依赖二阶导数能够提取稳定的特征点,著名的差分高斯近似加速等等。其提取图像特征的核函数为:

Wlaplacian=[w00w01w02w10w11w12w20w21w22]=[010141010]W_{laplacian}= \left[ \begin{matrix} w_{00} &amp; w_{01} &amp; w_{02} \\ w_{10} &amp; w_{11} &amp; w_{12} \\ w_{20} &amp; w_{21} &amp; w_{22} \end{matrix} \right] = \left[ \begin{matrix} 0 &amp; 1 &amp; 0 \\ 1 &amp; -4 &amp; 1 \\ 0 &amp; 1 &amp; 0 \end{matrix} \right]

  利用卷积核与图像进行卷积运算,以此来提取图像梯度。关于卷积操作具体解释可以参考图像滤波算法系列实战总结之一博文开头卷积示例图片,抱歉暂无找到引用何处,发现后会添加上。

OpenCV-Python代码实践
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('./data/feature_building.jpg', 0)

laplacian = cv2.Laplacian(img, cv2.CV_64F)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)

sobelxy = np.sqrt(sobelx*sobelx + sobely*sobely)

scharrx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=-1)
scharry = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=-1)

scharrxy = np.sqrt(scharrx*scharrx + scharry*scharry)


plt.subplot(2, 4, 1), plt.imshow(img, cmap='gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 4, 2), plt.imshow(laplacian, cmap='gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 4, 3), plt.imshow(sobelx, cmap='gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 4, 4), plt.imshow(sobely, cmap='gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 4, 5), plt.imshow(scharrx, cmap='gray')
plt.title('Scharr X'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 4, 6), plt.imshow(scharry, cmap='gray')
plt.title('Scharr Y'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 4, 7), plt.imshow(sobelxy, cmap='gray')
plt.title('Sobel XY'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 4, 8), plt.imshow(scharrxy, cmap='gray')
plt.title('Scharr XY'), plt.xticks([]), plt.yticks([])

plt.show()
实验结果
参考

https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_gradients/py_gradients.html

2017-09-01 15:51:53 firstlai 阅读数 10443
  • 图像梯度

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    98人学习 贾志刚
    免费试看


0 定义

本质上是用于卷积运算的模板,最终效果上是求得梯度。Roberts 算子,Sobel算子、Prewitt算子以及Laplace算子等。按功能分,上述算子都是求边缘检测的算子。如果按求导的阶数分类,Roboert、Sobel、Prewitt都是一阶算子,而Laplace属于二阶算子。

1 推导

1.1 Robert算子

利用局部差分算子寻找边缘的算子,采用对角线相邻两像素之差(正常梯度是采用垂直和水平像素之差)作为梯度幅值检测边缘。该算子检测垂直边缘的效果好于斜向边缘,定位精度高。但是对噪声敏感,无法抑制噪声。另外该算子运算快,但是采用的偶数模板,所求点处的梯度幅度值,其实是图中交叉点处的值,从而导致偏移了半个像素。



       推导过程。(因为平方和平方根需要大量的计算开销,所以使用绝对值来近似梯度幅值


1.2 Prewitt算子

       其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。

另外能够抑制噪声(一个方向求微分,一个方向求平均,而平均对噪声有抑制作用)。但抑制噪声的同时,由于求了平均,所以相当于对图像的低通滤波。正因此,对边缘的定位不如Roberts算子。

1.3 Sobel算子

Sobel算子和Prewitt算子都是加权平均,但是Sobel算子认为,邻域的像素对当前像素产生的影响不是等价的,所以距离不同的像素具有不同的权值,对算子结果产生的影响也不同。一般来说,距离越远,产生的影响越小。同样,也分为垂直方向和水平方向两个。


由于Sobel算子是滤波算子的形式,用于提取边缘,可以利用快速卷积函数,简单有效,因此应用广泛。美中不足的是,Sobel算子并没有将图像的主体与背景严格地区分开来,换言之就是Sobel算子没有基于图像灰度进行处理,由于Sobel算子没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。  在观测一幅图像的时候,我们往往首先注意的是图像与背景不同的部分,正是这个部分将主体突出显示,基于该理论,我们可以给出阈值化轮廓提取算法,该算法已在数学上证明当像素点满足正态分布时所求解是最优的。

1.4 Isotropic Sobel算子

       加权平均算子,权值反比于邻点与中心点的距离,当沿不同方向检测边缘时梯度幅度一致,就是通常所说的各向同性。


1.5 Laplace算子

       二阶微分算子,具有各向同向性,与坐标轴无关(所以无法检测方向)。但是对噪声敏感,所以图像一般需要经过平滑处理,因为平滑处理也是基于模板的,所以通常是使用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

2 代码

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算子如下。


       拉普拉斯算子如下。


3 参考

http://blog.csdn.net/xiaojiegege123456/article/details/7714863

http://www.cnblogs.com/german-iris/p/4840647.html