/*
* 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;
/**
* Trajkovic and Hedley Corner Detector.
*
* This Implementation does not yet use a Besenham Circle for the Points. This is a simpler Algorithm which only uses 4
* neighbours, like the Algorithm from Donovan Parks and Jean-Philippe Gravel.
*
* See http://kiwi.cs.dal.ca/~dparks/CornerDetection/trajkovic.htm and
* http://en.wikipedia.org/wiki/Corner_detection#The_Trajkovic_and_Hedley_corner_detector for further information. *
*
* @author Robert Zelhofer
*/
public class TrajkovicHedley4N implements PointDetector {
//Returning List of Corners
private List<ImagePoint> corners;
//Threshold values
private int threshold1;
private int threshold2;
//lower resolution Scale;
private int lowResScale;
//radius for the circle
private int distance;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
@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);
int lowResWidth = ip.getWidth() / lowResScale;
int lowResHeight = ip.getHeight() / lowResScale;
//Create lower Resolution image with custom Scale
ImageProcessor lowResIp = ip.resize(lowResWidth, lowResWidth);
ImageProcessor lowResIpResult = lowResIp.duplicate();
//First cSimple For low Res Image
for (int i = distance; i < lowResWidth - distance; i++) {
for (int j = distance; j < lowResHeight - distance; j++) {
int cSimple = cSimple(lowResIp, i, j);
if (cSimple >= threshold1) {
lowResIpResult.putPixel(i, j, 255);
} else {
lowResIpResult.putPixel(i, j, 0);
}
}
}
// Then cInterpixel for the actual Cornerness Measure
for (int i = distance; i < lowResWidth - distance; i++) {
for (int j = distance; j < lowResHeight - distance; j++) {
if (lowResIpResult.getPixel(i, j) == 255) {
int origX = i * lowResScale;
int origY = j * lowResScale;
int cSimpleOrig = cSimple(ip, origX, origY);
if (cSimpleOrig >= threshold2) {
if (cInterpixel(ip, origX, origY) >= threshold2) {
corners.add(new ImagePoint(origX, origY));
}
}
}
}
}
pcs.firePropertyChange(Progress.getName(), null, Progress.END);
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
/**
* Creates Trajkovic and Hedley Corner Detector with defauult values: distance = 1 threshold1 = 500 threshold2 = 500
* scale = 2
*/
public TrajkovicHedley4N() {
this(1, 500, 500, 2);
}
/**
* Creates Trajkovic and Hedley Corner Detector
*
* @param distance This value sets the distance from the nearby pixels.
* @param threshold1 This is the value for the first calcualtion of possible corners in the scaled lower resolution
* image.
* @param threshold2 This is the value for the second calculation of possible corners in the original image.
* @param scale Scale for the lower resolution image. The orignial images size is divided by this number;
*/
public TrajkovicHedley4N(int distance, int threshold1, int threshold2, int scale) {
corners = new ArrayList<>();
this.distance = distance;
this.threshold1 = threshold1;
this.threshold2 = threshold2;
this.lowResScale = scale;
}
/**
* Calculate the Simple Trajkovic and Hedley Cornerness Measure
*
* @param ip ImageProcessor
* @param x X value of the Pixel
* @param y Y value of the Pixel
* @return Simple Cornerness Measure
*/
private int cSimple(ImageProcessor ip, int x, int y) {
int iC = ip.getPixel(x, y);
int iA1 = ip.getPixel(x, y + distance);
int iA2 = ip.getPixel(x, y - distance);
int iB1 = ip.getPixel(x + distance, y);
int iB2 = ip.getPixel(x - distance, y);
int rA = (int) Math.pow((iA1 - iC), 2) + (int) Math.pow((iA2 - iC), 2);
int rB = (int) Math.pow((iB1 - iC), 2) + (int) Math.pow((iB2 - iC), 2);
return Math.min(rA, rB);
}
/**
* Calculate the Trajkovic and Hedley Cornerss Measure on the Original Image after the Simple Cornerness Measure
*
* @param ip Imageprocessor
* @param x X value of the Pixel
* @param y Y value of the Pixel
* @return Cornerness Measure
*/
private int cInterpixel(ImageProcessor ip, int x, int y) {
int iC = ip.getPixel(x, y);
int iA1 = ip.getPixel(x, y + distance);
int iA2 = ip.getPixel(x, y - distance);
int iB1 = ip.getPixel(x + distance, y);
int iB2 = ip.getPixel(x - distance, y);
int rA = (int) Math.pow((iA1 - iC), 2) + (int) Math.pow((iA2 - iC), 2);
int rB = (int) Math.pow((iB1 - iC), 2) + (int) Math.pow((iB2 - iC), 2);
int b1 = (iB1 - iA1) * (iA1 - iC) + (iB2 - iA2) * (iA2 - iC);
int b2 = (iB1 - iA2) * (iA2 - iC) + (iB2 - iA1) * (iA1 - iC);
int c = rA;
int b = Math.min(b1, b2);
int a = rB - rA - 2 * b;
int cInterpixel;
if (b < 0 && (a + b) > 0) {
cInterpixel = c - ((b * b) / a);
} else {
//else cSimple
cInterpixel = Math.min(rA, rB);
}
return cInterpixel;
}
}