package org.openpnp.vision; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import org.opencv.core.Point; public class Ransac { /* * http://users.utcluj.ro/~igiosan/Resources/PRS/L1/lab_01e.pdf * http://cs.gmu.edu/~kosecka/cs682/lect-fitting.pdf * http://introcs.cs.princeton.edu/java/36inheritance/LeastSquares.java.html */ public static Point[] ransac(List<Point> points, int maxIterations, double threshold) { Point bestA = null, bestB = null; int bestInliers = 0; for (int i = 0; i < maxIterations; i++) { // take a random sample of two points Collections.shuffle(points); Point a = points.get(0); Point b = points.get(1); // find the inliers int inliers = 0; for (Point p : points) { double distance = FluentCv.pointToLineDistance(a, b, p); if (distance <= threshold) { inliers++; } } if (inliers > bestInliers) { bestA = a; bestB = b; bestInliers = inliers; } } return new Point[] {bestA, bestB}; } // TODO: This currently seems to give much worse results than ransac. Figure out why. public static List<RansacLine> multiRansac(List<Point> points, int maxIterations, double threshold) { Random random = new Random(); Set<RansacLine> lines = new HashSet<>(); for (int i = 0; i < maxIterations; i++) { // take a random sample of two points Point a = points.get(random.nextInt(points.size())); Point b = points.get(random.nextInt(points.size())); RansacLine line = new RansacLine(a, b, 0); // if we have already processed this pair, skip it if (lines.contains(line)) { continue; } // add the result lines.add(line); // find the inliers for (Point p : points) { double distance = FluentCv.pointToLineDistance(a, b, p); if (distance <= threshold) { line.inliers++; } } } List<RansacLine> results = new ArrayList<>(lines); Collections.sort(results, new Comparator<RansacLine>() { @Override public int compare(RansacLine o1, RansacLine o2) { return o2.inliers - o1.inliers; } }); return results; } public static class RansacLine { public Point a; public Point b; public transient int inliers; public RansacLine(Point a, Point b, int inliers) { this.a = a; this.b = b; this.inliers = inliers; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((a == null) ? 0 : a.hashCode()); result = prime * result + ((b == null) ? 0 : b.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RansacLine other = (RansacLine) obj; if (a == null) { if (other.a != null) return false; } else if (!a.equals(other.a)) return false; if (b == null) { if (other.b != null) return false; } else if (!b.equals(other.b)) return false; return true; } @Override public String toString() { return "RansacLine [a=" + a + ", b=" + b + ", inliers=" + inliers + "]"; } } }