/*
* This file is part of the JFeatureLib project: https://github.com/locked-fg/JFeatureLib
* JFeatureLib is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* JFeatureLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JFeatureLib; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* You are kindly asked to refer to the papers of the according authors which
* should be mentioned in the Javadocs of the respective classes as well as the
* JFeatureLib project itself.
*
* Hints how to cite the projects can be found at
* https://github.com/locked-fg/JFeatureLib/wiki/Citation
*/
package de.lmu.ifi.dbs.jfeaturelib.pointDetector;
import de.lmu.ifi.dbs.jfeaturelib.ImagePoint;
import de.lmu.ifi.dbs.jfeaturelib.Progress;
import ij.process.ImageProcessor;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
*
* Moravec Corner Detector.
*
* This implementation is based on the Original Paper from Moravec, 1980. See link below. And also based on the Aricle
* from Donovan Parks and Jean-Philippe Gravel. Also see link below. This implementation does differ from the Wikipedia
* Article about the Moravec Detector!
*
* See http://www.ri.cmu.edu/pub_files/pub4/moravec_hans_1980_1/moravec_hans_1980_1.pdf and
* http://kiwi.cs.dal.ca/~dparks/CornerDetection/moravec.htm for more information
*
* @author Robert Zelhofer
*/
public class Moravec implements PointDetector {
//Returning List of Corners
private List<ImagePoint> corners;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
//Threshold value
private int threshold;
//windowsize value
private int size;
//static window sizes
//windows for the 8 Principle Directions around the current Pixel
private final int[] xPricipleDelta = new int[]{
-1, 0, 1,
-1, 1,
-1, 0, 1
};
private final int[] yPrincipleDelta = new int[]{
-1, -1, -1,
0, 0,
1, 1, 1
};
//Shifted Window for both the orignial Pixel and the shifted Pixels
private int[] xRedDelta;
private int[] yRedDelta;
/*
* This is how a window for windowsize = 3 looks like
private int[] xRedDelta = new int[] {
-1, 0, 1,
-1, 0, 1,
-1, 0, 1
};
private int[] yRedDelta = new int[] {
-1, -1, -1,
0, 0, 0,
1, 1, 1
};
*/
//Shifted Window for both the orignial Pixel and the shifted Pixels
//private ArrayList<ImagePoint> redDeltaList;
@Override
public List<ImagePoint> getPoints() {
return corners;
}
@Override
public EnumSet<Supports> supports() {
return EnumSet.of(
Supports.NoChanges,
Supports.DOES_8G);
}
@Override
public void run(ImageProcessor ip) {
pcs.firePropertyChange(Progress.getName(), null, Progress.START);
this.generateWindow(size);
int width = ip.getWidth();
int height = ip.getHeight();
//radius is used to prevent the Algorithm to search out of bounds
int radius = size / 2;
//Check each Pixel
for (int y = radius, maxY = height - radius; y < maxY; y++) {
for (int x = radius, maxX = width - radius; x < maxX; x++) {
int minSum = Integer.MAX_VALUE;
//check the shifting directions
for (int k = 0; k < 8; k++) {
// sy and sx is the Center Point of the Shifted Window
int sy = y + yPrincipleDelta[k];
int sx = x + xPricipleDelta[k];
// Shifted Window out of Bounds Check
if (sy < radius || sx < radius || sy >= maxY || sx >= maxX) {
continue;
}
int sum = 0;
// Sum up the difference of the window around the actual pixel and the shifted window
for (int i = 0; i < xRedDelta.length; i++) {
int redX = x + xRedDelta[i];
int redY = y + yRedDelta[i];
int redValue = ip.getPixel(redX, redY);
int blueX = sx + xRedDelta[i];
int blueY = sy + yRedDelta[i];
int blueValue = ip.getPixel(blueX, blueY);
int dif = redValue - blueValue;
sum += dif * dif;
}
if (sum < minSum) {
minSum = sum;
}
}
//System.out.print("\t" + minSum+ "\t|");
//Threshold check
if (minSum < threshold) {
minSum = 0;
} else {
corners.add(new ImagePoint(x, y));
}
}
//System.out.println();
}
pcs.firePropertyChange(Progress.getName(), null, Progress.END);
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
/**
* Creates Moravec Detector with Default values: Threshold = 500
*/
public Moravec() {
this(500, 3);
}
/**
* Creates Moravec Detector
*
* @param threshold Threshold Value, which is used for filtering uninteresting Points
*/
public Moravec(int threshold) {
this(threshold, 3);
}
/**
* Creates Moravec Detector
*
* @param threshold Threshold Value, which is used for filtering uninteresting Points
* @param size Window Size. Must be Odd and >= 3. Used for Initial Search for Corners and Search for Local Maximums.
*/
public Moravec(int threshold, int size) {
this.threshold = threshold;
if (size < 3) {
throw new IllegalArgumentException("Window Size is smaller than 3!");
}
if (size % 2 == 0) {
throw new IllegalArgumentException("Window Size is not odd!");
}
this.size = size;
corners = new ArrayList<>();
}
/**
*
* create the shifted Windows for calculation the Itensity Differences creates for window size = 7: -3 -3 -2 -3 -1
* -3 0 -3 1 -3 2 -3 3 -3 -3 -2 3 -2 -3 -1 3 -1 -3 0 0 0 3 0 -3 1 3 1 -3 2 3 2 -3 3 -2 3 -1 3 0 3 1 3 2 3 3 3
*
* @param size Window Size
* @return List of ImagePoints
*/
private void generateWindow(int size) {
int redDeltaLength = (size - 1) * 4;
xRedDelta = new int[redDeltaLength];
yRedDelta = new int[redDeltaLength];
//ArrayList<ImagePoint> tempList = new ArrayList<>();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
int radius = size / 2;
if (Math.abs(j - radius) == radius || Math.abs(i - radius) == radius) {
//System.out.print((j-radius) + " " + (i-radius) + "\t");
//tempList.add(new ImagePoint(j-radius, i-radius));
xRedDelta[i] = j - radius;
yRedDelta[i] = i - radius;
} else {
//System.out.print("\t");
}
}
//System.out.println();
//System.out.println();
}
//tempList.add(new ImagePoint(0, 0));
xRedDelta[redDeltaLength - 1] = 0;
yRedDelta[redDeltaLength - 1] = 0;
}
}