图像处理之高斯一阶及二阶导数计算
图像的一阶与二阶导数计算在图像特征提取与边缘提取中十分重要。一阶与二阶导数的
作用,通常情况下:
一阶导数可以反应出图像灰度梯度的变化情况
二阶导数可以提取出图像的细节同时双响应图像梯度变化情况
常见的算子有Robot, Sobel算子,二阶常见多数为拉普拉斯算子,如图所示:
对于一个1D的有限集合数据f(x) = {1…N}, 假设dx的间隔为1则一阶导数计算公式如下:
Df(x) = f(x+1) – f(x-1) 二阶导数的计算公式为:df(x)= f(x+1) + f(x-1) – 2f(x);
稍微难一点的则是基于高斯的一阶导数与二阶导数求取,首先看一下高斯的1D与2D的
公式。一维高斯对应的X阶导数公式:
二维高斯对应的导数公式:
二:算法实现
1. 高斯采样,基于间隔1计算,计算mask窗口计算,这样就跟普通的卷积计算差不多
2. 设置sigma的值,本例默认为10,首先计算高斯窗口函数,默认为3 * 3
3. 根据2的结果,计算高斯导数窗口值
4. 卷积计算像素中心点值。
注意点:计算高斯函数一定要以零为中心点, 如果窗口函数大小为3,则表达为-1, 0, 1
三:程序实现关键点
1. 归一化处理,由于高斯计算出来的窗口值非常的小,必须实现归一化处理。
2. 亮度提升,对X,Y的梯度计算结果进行了亮度提升,目的是让大家看得更清楚。
3. 支持一阶与二阶单一方向X,Y偏导数计算
四:运行效果:
高斯一阶导数X方向效果
高斯一阶导数Y方向效果
五:算法全部源代码:
/* * @author: gloomyfish * @date: 2013-11-17 * * Title - Gaussian fist order derivative and second derivative filter */ package com.gloomyfish.image.harris.corner; import java.awt.image.BufferedImage; import com.gloomyfish.filter.study.AbstractBufferedImageOp; public class GaussianDerivativeFilter extends AbstractBufferedImageOp { public final static int X_DIRECTION = 0; public final static int Y_DIRECTION = 16; public final static int XY_DIRECTION = 2; public final static int XX_DIRECTION = 4; public final static int YY_DIRECTION = 8; // private attribute and settings private int DIRECTION_TYPE = 0; private int GAUSSIAN_WIN_SIZE = 1; // N*2 + 1 private double sigma = 10; // default public GaussianDerivativeFilter() { System.out.println("高斯一阶及多阶导数滤镜"); } public int getGaussianWinSize() { return GAUSSIAN_WIN_SIZE; } public void setGaussianWinSize(int gAUSSIAN_WIN_SIZE) { GAUSSIAN_WIN_SIZE = gAUSSIAN_WIN_SIZE; } public int getDirectionType() { return DIRECTION_TYPE; } public void setDirectionType(int dIRECTION_TYPE) { DIRECTION_TYPE = dIRECTION_TYPE; } @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; double[][] winDeviationData = getDirectionData(); 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 = -GAUSSIAN_WIN_SIZE; subrow <= GAUSSIAN_WIN_SIZE; subrow++) { for(int subcol = -GAUSSIAN_WIN_SIZE; subcol <= GAUSSIAN_WIN_SIZE; 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; xred += (winDeviationData[subrow + GAUSSIAN_WIN_SIZE][subcol + GAUSSIAN_WIN_SIZE] * tr); xgreen +=(winDeviationData[subrow + GAUSSIAN_WIN_SIZE][subcol + GAUSSIAN_WIN_SIZE] * tg); xblue +=(winDeviationData[subrow + GAUSSIAN_WIN_SIZE][subcol + GAUSSIAN_WIN_SIZE] * tb); } } outPixels[index] = (ta << 24) | (clamp((int)xred) << 16) | (clamp((int)xgreen) << 8) | clamp((int)xblue); // clean up values for next pixel newRow = newCol = 0; xred = xgreen = xblue = 0; // yred = ygreen = yblue = 0; } } setRGB( dest, 0, 0, width, height, outPixels ); return dest; } private double[][] getDirectionData() { double[][] winDeviationData = null; if(DIRECTION_TYPE == X_DIRECTION) { winDeviationData = this.getXDirectionDeviation(); } else if(DIRECTION_TYPE == Y_DIRECTION) { winDeviationData = this.getYDirectionDeviation(); } else if(DIRECTION_TYPE == XY_DIRECTION) { winDeviationData = this.getXYDirectionDeviation(); } else if(DIRECTION_TYPE == XX_DIRECTION) { winDeviationData = this.getXXDirectionDeviation(); } else if(DIRECTION_TYPE == YY_DIRECTION) { winDeviationData = this.getYYDirectionDeviation(); } return winDeviationData; } public int clamp(int value) { // trick, just improve the lightness otherwise image is too darker... if(DIRECTION_TYPE == X_DIRECTION || DIRECTION_TYPE == Y_DIRECTION) { value = value * 10 + 50; } return value < 0 ? 0 : (value > 255 ? 255 : value); } // centered on zero and with Gaussian standard deviation // parameter : sigma public double[][] get2DGaussianData() { int size = GAUSSIAN_WIN_SIZE * 2 + 1; double[][] winData = new double[size][size]; double sigma2 = this.sigma * sigma; for(int i=-GAUSSIAN_WIN_SIZE; i<=GAUSSIAN_WIN_SIZE; i++) { for(int j=-GAUSSIAN_WIN_SIZE; j<=GAUSSIAN_WIN_SIZE; j++) { double r = i*1 + j*j; double sum = -(r/(2*sigma2)); winData[i + GAUSSIAN_WIN_SIZE][j + GAUSSIAN_WIN_SIZE] = Math.exp(sum); } } return winData; } public double[][] getXDirectionDeviation() { int size = GAUSSIAN_WIN_SIZE * 2 + 1; double[][] data = get2DGaussianData(); double[][] xDeviation = new double[size][size]; double sigma2 = this.sigma * sigma; for(int x=-GAUSSIAN_WIN_SIZE; x<=GAUSSIAN_WIN_SIZE; x++) { double c = -(x/sigma2); for(int i=0; i<size; i++) { xDeviation[i][x + GAUSSIAN_WIN_SIZE] = c * data[i][x + GAUSSIAN_WIN_SIZE]; } } return xDeviation; } public double[][] getYDirectionDeviation() { int size = GAUSSIAN_WIN_SIZE * 2 + 1; double[][] data = get2DGaussianData(); double[][] yDeviation = new double[size][size]; double sigma2 = this.sigma * sigma; for(int y=-GAUSSIAN_WIN_SIZE; y<=GAUSSIAN_WIN_SIZE; y++) { double c = -(y/sigma2); for(int i=0; i<size; i++) { yDeviation[y + GAUSSIAN_WIN_SIZE][i] = c * data[y + GAUSSIAN_WIN_SIZE][i]; } } return yDeviation; } /*** * * @return */ public double[][] getXYDirectionDeviation() { int size = GAUSSIAN_WIN_SIZE * 2 + 1; double[][] data = get2DGaussianData(); double[][] xyDeviation = new double[size][size]; double sigma2 = sigma * sigma; double sigma4 = sigma2 * sigma2; // TODO:zhigang for(int x=-GAUSSIAN_WIN_SIZE; x<=GAUSSIAN_WIN_SIZE; x++) { for(int y=-GAUSSIAN_WIN_SIZE; y<=GAUSSIAN_WIN_SIZE; y++) { double c = -((x*y)/sigma4); xyDeviation[x + GAUSSIAN_WIN_SIZE][y + GAUSSIAN_WIN_SIZE] = c * data[x + GAUSSIAN_WIN_SIZE][y + GAUSSIAN_WIN_SIZE]; } } return normalizeData(xyDeviation); } private double[][] normalizeData(double[][] data) { // normalization the data double min = data[0][0]; for(int x=-GAUSSIAN_WIN_SIZE; x<=GAUSSIAN_WIN_SIZE; x++) { for(int y=-GAUSSIAN_WIN_SIZE; y<=GAUSSIAN_WIN_SIZE; y++) { if(min > data[x + GAUSSIAN_WIN_SIZE][y + GAUSSIAN_WIN_SIZE]) { min = data[x + GAUSSIAN_WIN_SIZE][y + GAUSSIAN_WIN_SIZE]; } } } for(int x=-GAUSSIAN_WIN_SIZE; x<=GAUSSIAN_WIN_SIZE; x++) { for(int y=-GAUSSIAN_WIN_SIZE; y<=GAUSSIAN_WIN_SIZE; y++) { data[x + GAUSSIAN_WIN_SIZE][y + GAUSSIAN_WIN_SIZE] = data[x + GAUSSIAN_WIN_SIZE][y + GAUSSIAN_WIN_SIZE] /min; } } return data; } public double[][] getXXDirectionDeviation() { int size = GAUSSIAN_WIN_SIZE * 2 + 1; double[][] data = get2DGaussianData(); double[][] xxDeviation = new double[size][size]; double sigma2 = this.sigma * sigma; double sigma4 = sigma2 * sigma2; for(int x=-GAUSSIAN_WIN_SIZE; x<=GAUSSIAN_WIN_SIZE; x++) { double c = -((x - sigma2)/sigma4); for(int i=0; i<size; i++) { xxDeviation[i][x + GAUSSIAN_WIN_SIZE] = c * data[i][x + GAUSSIAN_WIN_SIZE]; } } return xxDeviation; } public double[][] getYYDirectionDeviation() { int size = GAUSSIAN_WIN_SIZE * 2 + 1; double[][] data = get2DGaussianData(); double[][] yyDeviation = new double[size][size]; double sigma2 = this.sigma * sigma; double sigma4 = sigma2 * sigma2; for(int y=-GAUSSIAN_WIN_SIZE; y<=GAUSSIAN_WIN_SIZE; y++) { double c = -((y - sigma2)/sigma4); for(int i=0; i<size; i++) { yyDeviation[y + GAUSSIAN_WIN_SIZE][i] = c * data[y + GAUSSIAN_WIN_SIZE][i]; } } return yyDeviation; } }
国足都战胜亚洲强队印尼了,我还有什么理由不坚持写下去!
转载请务必注明!!!