图像处理------高斯一阶及二阶导数计算

图像处理——高斯一阶及二阶导数计算

一:图像处理之高斯一阶及二阶导数计算

图像的一阶与二阶导数计算在图像特征提取与边缘提取中十分重要。一阶与二阶导数的

作用,通常情况下:

  • 一阶导数可以反应出图像灰度梯度的变化情况

  • 二阶导数可以提取出图像的细节同时双响应图像梯度变化情况

常见的算子有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阶导数公式:
    一维高斯模型对应x阶导数
  • 二维高斯对应的导数公式:
    一维高斯模型对应x阶导数

    二:算法实现

  1. 高斯采样,基于间隔1计算,计算mask窗口计算,这样就跟普通的卷积计算差不多

  2. 设置sigma的值,本例默认为10,首先计算高斯窗口函数,默认为3 * 3

  3. 根据2的结果,计算高斯导数窗口值

  4. 卷积计算像素中心点值。

注意点:计算高斯函数一定要以零为中心点, 如果窗口函数大小为3,则表达为-1, 0, 1

三:程序实现关键点

  1. 归一化处理,由于高斯计算出来的窗口值非常的小,必须实现归一化处理。

  2. 亮度提升,对X,Y的梯度计算结果进行了亮度提升,目的是让大家看得更清楚。

  3. 支持一阶与二阶单一方向X,Y偏导数计算

四:运行效果:

高斯一阶导数X方向效果
高斯一阶导数X方向效果
高斯一阶导数Y方向效果
高斯一阶导数Y方向效果

五:算法全部源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/* 
* @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;
}

}