/* * 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.shapeFeatures; import de.lmu.ifi.dbs.jfeaturelib.LibProperties; import de.lmu.ifi.dbs.jfeaturelib.features.AbstractFeatureDescriptor; import de.lmu.ifi.dbs.jfeaturelib.utils.Distance; import de.lmu.ifi.dbs.jfeaturelib.utils.DistanceL2; import ij.process.ImageProcessor; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; /** * Computes a distance shape feature as Array List containing the x and y * coordinates from the points generating the polygon. This method is described * in "Yang Mingqiang, Kpalma Kidiyo and Ronsin Joseph (2008). A Survey of Shape * Feature Extraction Techniques, Pattern Recognition Techniques, Technology and * Applications, Peng-Yeng Yin (Ed.), ISBN: 978-953-7619-24-4, InTech, Available * from: * http://www.intechopen.com/articles/show/title/a_survey_of_shape_feature_extraction_techniques". * * @author Johannes Stadler * @since 09/29/2012 */ public class PolygonEvolution extends AbstractFeatureDescriptor { private static Logger log = Logger.getLogger(PolygonEvolution.class.getName()); private static final Distance dist = new DistanceL2(); private List<Integer> feature = new ArrayList<>(); private int iterations = 50; private int backgroundColor = -1; int nothingFound = 0; /** * default constructor starting with 50 iterations */ public PolygonEvolution() { } public PolygonEvolution(int iterations) { this.iterations = iterations; } @Override public void setProperties(LibProperties properties) { iterations = properties.getInteger(LibProperties.POLYGON_EVOLUTION, 50); } private double getKValue(int x1, int y1, int x2, int y2, int x3, int y3) { double lengthS1 = dist.distance(x1, y1, x2, y2); double lengthS2 = dist.distance(x2, y2, x3, y3); double lengthS3 = dist.distance(x1, y1, x3, y3); double angle = Math.toDegrees(Math.acos((lengthS1 * lengthS1 + lengthS2 * lengthS2 - lengthS3 * lengthS3) / (-2.0 * lengthS1 * lengthS2))); double k = (angle * lengthS1 * lengthS2) / (lengthS1 + lengthS2); return k; } private List<Integer> removeDublicates(List<Integer> l) { for (int i = 0; i <= l.size() - 4; i += 2) { for (int j = i + 2; j <= l.size() - 2; j += 2) { if ((int) l.get(i) == (int) l.get(j) && (int) l.get(i + 1) == (int) l.get(j + 1)) { l.set(j, -1); l.set(j + 1, -1); } else { break; } } } for (int k = 0; k < l.size(); k++) { while (k < l.size() && (int) l.get(k) < 0) { l.remove(k); } } while ((int) l.get(0) == (int) l.get(l.size() - 2) && (int) l.get(1) == (int) l.get(l.size() - 1)) { l.set(l.size() - 2, -1); l.set(l.size() - 1, -1); } for (int k = 0; k < l.size(); k++) { while (k < l.size() && (int) l.get(k) < 0) { l.remove(k); } } return l; } @Override public void run(ImageProcessor ip) { startProgress(); ExtremalPoints ep = new ExtremalPoints(); ep.run(ip); List<Integer> points = ep.getFeature(); points.remove(16); List<Double> kValue = new ArrayList(); int counter = 0; int maxI; points = removeDublicates(points); while (counter < iterations) { kValue.clear(); for (int x = 0; x <= points.size() - 6; x = x + 2) { kValue.add(getKValue((int) points.get(x), (int) points.get(x + 1), (int) points.get(x + 2), (int) points.get(x + 3), (int) points.get(x + 4), (int) points.get(x + 5))); } kValue.add(getKValue((int) points.get(points.size() - 4), (int) points.get(points.size() - 4 + 1), (int) points.get(points.size() - 4 + 2), (int) points.get(points.size() - 4 + 3), (int) points.get(0), (int) points.get(1))); kValue.add(getKValue((int) points.get(points.size() - 2), (int) points.get(points.size() - 1), (int) points.get(0), (int) points.get(1), (int) points.get(2), (int) points.get(3))); double maxK = Double.MIN_VALUE; maxI = Integer.MIN_VALUE; for (int i = 0; i < kValue.size(); i++) { if ((double) kValue.get(i) > maxK) { maxK = (double) kValue.get(i); maxI = i; } } int x1, x2, x3, y1, y2, y3; if (maxI * 2 <= points.size() - 6) { x1 = (int) points.get(maxI * 2); y1 = (int) points.get(maxI * 2 + 1); x2 = (int) points.get(maxI * 2 + 2); y2 = (int) points.get(maxI * 2 + 3); x3 = (int) points.get(maxI * 2 + 4); y3 = (int) points.get(maxI * 2 + 5); } else if (maxI * 2 == points.size() - 4) { x1 = (int) points.get(maxI * 2); y1 = (int) points.get(maxI * 2 + 1); x2 = (int) points.get(maxI * 2 + 2); y2 = (int) points.get(maxI * 2 + 3); x3 = (int) points.get(0); y3 = (int) points.get(1); } else { x1 = (int) points.get(maxI * 2); y1 = (int) points.get(maxI * 2 + 1); x2 = (int) points.get(0); y2 = (int) points.get(1); x3 = (int) points.get(2); y3 = (int) points.get(3); } byte[] pixelsCopy = (byte[]) ip.getPixelsCopy(); byte[][] pixel = new byte[ip.getWidth()][ip.getHeight()]; for (int i = 0; i < pixelsCopy.length; i++) { pixel[i % ip.getWidth()][(i - (i % ip.getWidth())) / ip.getWidth()] = pixelsCopy[i]; } byte[][] pixel2 = pixel.clone(); List p1 = getBorderDist(pixel, x1, y1, x2, y2, x3, y3); List p2 = getBorderDist(pixel2, x3, y3, x2, y2, x1, y1); if (maxI * 2 <= points.size() - 6) { points.addAll(maxI * 2 + 2, p1); points.addAll(maxI * 2 + 6, p2); } else if (maxI * 2 == points.size() - 4) { log.debug("spezialfall1"); points.addAll(maxI * 2 + 2, p1); points.addAll(p2); } else { log.debug("spezialfall2"); points.addAll(p1); points.addAll(2, p2); } points = removeDublicates(points); log.debug(maxI); // for (int i = 0; i < points.size(); i++) { // System.out.print(points.get(i) + " "); // } counter++; } feature = points; createFeature(); endProgress(); } private List getBorderDist(byte[][] pixel, int x1, int y1, int x2, int y2, int x3, int y3) { double maxDist = Double.MIN_VALUE; int currentX = x1; int currentY = y1; int maxX = x1; int maxY = y1; List returnValues = new ArrayList(); int flag = 1; while (flag > 0) { if (currentX != 0 && currentY != 0 && pixel[currentX - 1][currentY - 1] != backgroundColor) { currentX = currentX - 1; currentY = currentY - 1; double distTmp = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (distTmp > maxDist) { maxDist = distTmp; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else if (currentY != 0 && pixel[currentX][currentY - 1] != backgroundColor) { currentY = currentY - 1; double distTmp = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (distTmp > maxDist) { maxDist = distTmp; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else if (currentX != pixel.length - 1 && currentY != 0 && pixel[currentX + 1][currentY - 1] != backgroundColor) { currentX = currentX + 1; currentY = currentY - 1; double dist = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (dist > maxDist) { maxDist = dist; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else if (currentX != pixel.length - 1 && pixel[currentX + 1][currentY] != backgroundColor) { currentX = currentX + 1; double dist = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (dist > maxDist) { maxDist = dist; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else if (currentX != 0 && pixel[currentX - 1][currentY] != backgroundColor) { currentX = currentX - 1; double dist = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (dist > maxDist) { maxDist = dist; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else if (currentX != 0 && currentY != pixel[0].length - 1 && pixel[currentX - 1][currentY + 1] != backgroundColor) { currentX = currentX - 1; currentY = currentY + 1; double dist = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (dist > maxDist) { maxDist = dist; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else if (currentX != pixel.length - 1 && currentY != pixel[0].length - 1 && pixel[currentX + 1][currentY + 1] != backgroundColor) { currentX = currentX + 1; currentY = currentY + 1; double dist = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (dist > maxDist) { maxDist = dist; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else if (currentY != pixel[0].length - 1 && pixel[currentX][currentY + 1] != backgroundColor) { currentY = currentY + 1; double dist = Math.abs((x2 - x1) * (y1 - currentY) - (x1 - currentX) * (y2 - y1)) / Math.sqrt((Math.pow((x2 - x1), 2)) + (Math.pow((y2 - y1), 2))); if (dist > maxDist) { maxDist = dist; maxX = currentX; maxY = currentY; } log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("current" + currentX + " " + currentY); pixel[currentX][currentY] = (byte) backgroundColor; } else { log.debug("nix gefunden " + nothingFound); nothingFound++; returnValues.add(0, x1); returnValues.add(1, y1); log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("gef2: " + x1 + " " + y1); return returnValues; } if (currentX == x2 && currentY == y2) { flag = 0; pixel[currentX][currentY] = 0; } else if (currentX == x3 && currentY == y3) { currentX = x1; currentY = y1; maxX = currentX; maxY = currentY; maxDist = Double.MIN_VALUE; pixel[currentX][currentY] = (byte) backgroundColor; } } returnValues.add(0, maxX); returnValues.add(1, maxY); log.debug("ges" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3); log.debug("gef" + maxX + " " + maxY); return returnValues; } @Override public String getDescription() { return "PolygonEvolution"; } private void createFeature() { double[] arr = new double[feature.size()]; for (int i = 0; i < feature.size(); i++) { arr[i] = feature.get(i); } addData(arr); } }