package com.alibaba.simpleimage.analyze.harris;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.alibaba.simpleimage.analyze.harissurf.IntegralImage;
/**
* Harris角点
* @author hui.xueh
*/
public class HarrisFast {
public List<Corner> corners = new ArrayList<Corner>();
private int[][] image;
private int width, height;
private float[][] Lx2, Ly2, Lxy;
public HarrisFast(int[][] image, int width, int height,
IntegralImage mIntegralImage) {
this.image = image;
this.width = width;
this.height = height;
}
/**
* 高斯平滑
*
* @param x
* @param y
* @param sigma
* @return
*/
private double gaussian(double x, double y, double sigma) {
double sigma2 = sigma * sigma;
double t = (x * x + y * y) / (2 * sigma2);
double u = 1.0 / (2 * Math.PI * sigma2);
double e = u * Math.exp(-t);
return e;
}
/**
* Sobel边缘提取算子
*
* @param x
* @param y
* @return
*/
private float[] sobel(int x, int y) {
int v00 = 0, v01 = 0, v02 = 0, v10 = 0, v12 = 0, v20 = 0, v21 = 0, v22 = 0;
int x0 = x - 1, x1 = x, x2 = x + 1;
int y0 = y - 1, y1 = y, y2 = y + 1;
if (x0 < 0)
x0 = 0;
if (y0 < 0)
y0 = 0;
if (x2 >= width)
x2 = width - 1;
if (y2 >= height)
y2 = height - 1;
v00 = image[x0][y0];
v10 = image[x1][y0];
v20 = image[x2][y0];
v01 = image[x0][y1];
v21 = image[x2][y1];
v02 = image[x0][y2];
v12 = image[x1][y2];
v22 = image[x2][y2];
float sx = ((v20 + 2 * v21 + v22) - (v00 + 2 * v01 + v02)) / (1200f);
float sy = ((v02 + 2 * v12 + v22) - (v00 + 2 * v10 + v20)) / (1200f);
return new float[] { sx, sy };
}
/**
* 拉普拉斯高斯差分,Laplace of Gaussian,涉及到卷积操作,计算开销很大
*
* @param sigma
*/
private void computeDerivatives(double sigma) {
this.Lx2 = new float[width][height];
this.Ly2 = new float[width][height];
this.Lxy = new float[width][height];
float[][][] grad = new float[width][height][];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
grad[x][y] = sobel(x, y);
int radius = (int) (2 * sigma);
int window = 1 + 2 * radius;
float[][] gaussian = new float[window][window];
for (int j = -radius; j <= radius; j++)
for (int i = -radius; i <= radius; i++)
gaussian[i + radius][j + radius] = (float) gaussian(i, j, sigma);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
int xk = x + dx;
int yk = y + dy;
if (xk < 0 || xk >= width)
continue;
if (yk < 0 || yk >= height)
continue;
double gw = gaussian[dx + radius][dy + radius];
this.Lx2[x][y] += gw * grad[xk][yk][0]
* grad[xk][yk][0];
this.Ly2[x][y] += gw * grad[xk][yk][1]
* grad[xk][yk][1];
this.Lxy[x][y] += gw * grad[xk][yk][0]
* grad[xk][yk][1];
}
}
}
}
}
/**
* 角点判断的依据
*
* @param x
* @param y
* @param k
* ,一般设为0.06
* @return
*/
private float harrisMeasure(int x, int y, float k) {
float m00 = this.Lx2[x][y];
float m01 = this.Lxy[x][y];
float m10 = this.Lxy[x][y];
float m11 = this.Ly2[x][y];
return m00 * m11 - m01 * m10 - k * (m00 + m11) * (m00 + m11);
}
/**
* 是否为8邻域中的极大值
*
* @param hmap
* @param x
* @param y
* @return
*/
private boolean isSpatialMaxima(float[][] hmap, int x, int y) {
int n = 8;
int[] dx = new int[] { -1, 0, 1, 1, 1, 0, -1, -1 };
int[] dy = new int[] { -1, -1, -1, 0, 1, 1, 1, 0 };
double w = hmap[x][y];
for (int i = 0; i < n; i++) {
double wk = hmap[x + dx[i]][y + dy[i]];
if (wk >= w)
return false;
}
return true;
}
private static final double scala = 255/Math.log(256);
private float[][] computeHarrisMap(double k) {
float[][] harrismap = new float[width][height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
double h = harrisMeasure(x, y, (float) k);
if (h <= 0)
continue;
// log scale
h = Math.log(1 + h) * scala;
// store
harrismap[x][y] = (float) h;
}
}
return harrismap;
}
/**
* 角点的生成和过滤
*
* @param sigma
* @param k
* @param minDistance
* ,该邻域内只取算子最大的特征点
*/
public void filter(double sigma, double k, int minDistance) {
computeDerivatives(sigma);
// fastComputeDerivatives();
float[][] harrismap = computeHarrisMap(k);
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
float h = harrismap[x][y];
if (h <= 1E-3)
continue;
if (!isSpatialMaxima(harrismap, x, y))
continue;
corners.add(new Corner(x, y, h));
}
}
//System.out.println(corners.size() + " potential corners found.");
// remove corners to close to each other (keep the highest measure)
Iterator<Corner> iter = corners.iterator();
while (iter.hasNext()) {
Corner p = iter.next();
for (Corner n : corners) {
if (n == p)
continue;
int dist = (int) Math.sqrt((p.x - n.x) * (p.x - n.x)
+ (p.y - n.y) * (p.y - n.y));
if (dist > minDistance)
continue;
if (n.h < p.h)
continue;
iter.remove();
break;
}
}
// output
/*
* int[][] output = new int[width][height]; for (int y = 0; y < height;
* y++) for (int x = 0; x < width; x++) output[x][y] = (int)
* (image[x][y] * 0.75); // original image // (darker)
*
* // for each corner for (Corner p : corners) { // add the cross sign
* over the image for (int dt = -3; dt <= 3; dt++) { if (p.x + dt >= 0
* && p.x + dt < width) output[p.x + dt][p.y] = 255; if (p.y + dt >= 0
* && p.y + dt < height) output[p.x][p.y + dt] = 255; }
* System.out.println("corner found at: " + p.x + "," + p.y + " (" + p.h
* + ")"); } System.out.println(corners.size() + " corners found.");
*
* return output;
*/
}
}