From ed4eb34e0d21b08eb948a440b9aed6ba7d207725 Mon Sep 17 00:00:00 2001 From: Pierrik Lassalas Date: Wed, 25 Oct 2017 09:35:01 +0200 Subject: [PATCH] gs-cv: refactored LineDetectors(1,2,3) and Line(s) to extract common code --- .../org/genericsystem/cv/LinesDetector.java | 6 +- .../org/genericsystem/cv/LinesDetector2.java | 64 +++--- .../org/genericsystem/cv/LinesDetector3.java | 189 ++++-------------- .../java/org/genericsystem/cv/utils/Line.java | 46 +++++ 4 files changed, 118 insertions(+), 187 deletions(-) diff --git a/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector.java b/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector.java index 1879fae6c..027d93b94 100644 --- a/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector.java +++ b/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector.java @@ -8,9 +8,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; -import javafx.scene.image.ImageView; -import javafx.scene.layout.GridPane; - import org.genericsystem.cv.utils.Line; import org.genericsystem.cv.utils.NativeLibraryLoader; import org.genericsystem.cv.utils.Ransac; @@ -26,6 +23,9 @@ import org.opencv.imgproc.Imgproc; import org.opencv.videoio.VideoCapture; +import javafx.scene.image.ImageView; +import javafx.scene.layout.GridPane; + public class LinesDetector extends AbstractApp { static { diff --git a/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector2.java b/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector2.java index c385c5637..dbb4c6174 100644 --- a/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector2.java +++ b/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector2.java @@ -8,9 +8,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; -import javafx.scene.image.ImageView; -import javafx.scene.layout.GridPane; - import org.genericsystem.cv.LinesDetector.Damper; import org.genericsystem.cv.utils.Line; import org.genericsystem.cv.utils.NativeLibraryLoader; @@ -27,6 +24,9 @@ import org.opencv.imgproc.Imgproc; import org.opencv.videoio.VideoCapture; +import javafx.scene.image.ImageView; +import javafx.scene.layout.GridPane; + public class LinesDetector2 extends AbstractApp { static { @@ -61,33 +61,33 @@ protected void fillGrid(GridPane mainGrid) { if (lines.size() > 10) { // lines.draw(frame, new Scalar(0, 0, 255)); - frameView.setImage(Tools.mat2jfxImage(frame)); - // Mat dePerspectived = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(255, 255, 255)); - Ransac ransac = lines.vanishingPointRansac(frame.width(), frame.height()); - Mat homography = (Mat) ransac.getBestModel().getParams()[0]; - lines = Lines.of(ransac.getBestDataSet().values()); - lines = Lines.of(lines.perspectivTransform(homography)); - - System.out.println("Ransac angle : " + ((double) ransac.getBestModel().getParams()[1]) / Math.PI * 180); - // System.out.println("vpx : " + ((double) ransac.getBestModel().getParams()[2])); - // System.out.println("vpy : " + ((double) ransac.getBestModel().getParams()[3])); - Mat mask = new Mat(frame.size(), CvType.CV_8UC1, new Scalar(255)); - Mat maskWarpped = new Mat(); - Imgproc.warpPerspective(mask, maskWarpped, homography, frame.size()); - Mat tmp = new Mat(); - Imgproc.warpPerspective(frame, tmp, homography, frame.size(), Imgproc.INTER_LINEAR, Core.BORDER_REPLICATE, Scalar.all(255)); - tmp.copyTo(dePerspectived, maskWarpped); - lines.draw(dePerspectived, new Scalar(0, 255, 0)); - deskiewedView.setImage(Tools.mat2jfxImage(dePerspectived)); - - } else - System.out.println("Not enough lines : " + lines.size()); - - } catch (Throwable e) { - e.printStackTrace(); - } - - }, 33, 33, TimeUnit.MILLISECONDS); + frameView.setImage(Tools.mat2jfxImage(frame)); + // Mat dePerspectived = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(255, 255, 255)); + Ransac ransac = lines.vanishingPointRansac(frame.width(), frame.height()); + Mat homography = (Mat) ransac.getBestModel().getParams()[0]; + lines = Lines.of(ransac.getBestDataSet().values()); + lines = Lines.of(lines.perspectivTransform(homography)); + + System.out.println("Ransac angle : " + ((double) ransac.getBestModel().getParams()[1]) / Math.PI * 180); + // System.out.println("vpx : " + ((double) ransac.getBestModel().getParams()[2])); + // System.out.println("vpy : " + ((double) ransac.getBestModel().getParams()[3])); + Mat mask = new Mat(frame.size(), CvType.CV_8UC1, new Scalar(255)); + Mat maskWarpped = new Mat(); + Imgproc.warpPerspective(mask, maskWarpped, homography, frame.size()); + Mat tmp = new Mat(); + Imgproc.warpPerspective(frame, tmp, homography, frame.size(), Imgproc.INTER_LINEAR, Core.BORDER_REPLICATE, Scalar.all(255)); + tmp.copyTo(dePerspectived, maskWarpped); + lines.draw(dePerspectived, new Scalar(0, 255, 0)); + deskiewedView.setImage(Tools.mat2jfxImage(dePerspectived)); + + } else + System.out.println("Not enough lines : " + lines.size()); + + } catch (Throwable e) { + e.printStackTrace(); + } + + }, 33, 33, TimeUnit.MILLISECONDS); } @@ -143,8 +143,8 @@ public Ransac vanishingPointRansac(int width, int height) { Core.transform(new MatOfPoint2f(new Point(rotTargets[0].x, bary.y), new Point(rotTargets[1].x, bary.y)), resultsInv, matrixInv); Point[] rotInvTargets = resultsInv.toArray(); - homography[0] = Imgproc.getPerspectiveTransform(new MatOfPoint2f(new Point(line.getX1(), line.getY1()), new Point(line.getX2(), line.getY2()), rotInvTargets[1], rotInvTargets[0]), new MatOfPoint2f(new Point[] { - new Point(rotTargets[0].x, newy1), new Point(rotTargets[1].x, newy1), new Point(rotTargets[1].x, bary.y), new Point(rotTargets[0].x, bary.y) })); + homography[0] = Imgproc.getPerspectiveTransform(new MatOfPoint2f(new Point(line.getX1(), line.getY1()), new Point(line.getX2(), line.getY2()), rotInvTargets[1], rotInvTargets[0]), + new MatOfPoint2f(new Point[] { new Point(rotTargets[0].x, newy1), new Point(rotTargets[1].x, newy1), new Point(rotTargets[1].x, bary.y), new Point(rotTargets[0].x, bary.y) })); } return new Model() { diff --git a/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector3.java b/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector3.java index c1474fdb3..8ea6b1c7b 100644 --- a/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector3.java +++ b/gs-cv/src/main/java/org/genericsystem/cv/LinesDetector3.java @@ -1,7 +1,5 @@ package org.genericsystem.cv; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -9,11 +7,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Function; -import java.util.stream.Collectors; - -import javafx.scene.image.ImageView; -import javafx.scene.layout.GridPane; +import org.genericsystem.cv.utils.Line; import org.genericsystem.cv.utils.NativeLibraryLoader; import org.genericsystem.cv.utils.Ransac; import org.genericsystem.cv.utils.Ransac.Model; @@ -26,9 +21,11 @@ import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; -import org.opencv.utils.Converters; import org.opencv.videoio.VideoCapture; +import javafx.scene.image.ImageView; +import javafx.scene.layout.GridPane; + public class LinesDetector3 extends AbstractApp { static { @@ -78,7 +75,8 @@ protected void fillGrid(GridPane mainGrid) { // vpyDamper.pushNewValue(vp.y); Point bary = new Point(frame.width() / 2, frame.height() / 2); Mat homography = findHomography(new Point(vp.x, vp.y), bary, frame.width(), frame.height()); - lines = new Lines(ransac.getBestDataSet().values()).perspectivTransform(homography); + lines = Lines.of(ransac.getBestDataSet().values()); + lines = Lines.of(lines.perspectivTransform(homography)); Mat mask = new Mat(frame.size(), CvType.CV_8UC1, new Scalar(255)); Mat maskWarpped = new Mat(); @@ -166,31 +164,30 @@ private Mat findHomography(Point vp, Point bary, double width, double height) { return Imgproc.getPerspectiveTransform(new MatOfPoint2f(rotate(bary, -alpha, A_, B_, C_, D_)), new MatOfPoint2f(A, B, C, D)); } - public static class Lines { + public static class Lines extends org.genericsystem.cv.utils.Lines { - private final List lines = new ArrayList<>(); - private final double mean; private static Mat K; public Lines(Mat src) { - double mean = 0; - for (int i = 0; i < src.rows(); i++) { - double[] val = src.get(i, 0); - Line line = new Line(val[0], val[1], val[2], val[3]); - lines.add(line); - mean += line.getAngle(); - } - this.mean = mean / src.rows(); + super(src); + } + + public Lines(Collection lines) { + super(lines); + } + + public static Lines of(Collection lines) { + return new Lines(lines); } private Mat getLineMat(Line line) { Mat a = new Mat(3, 1, CvType.CV_32F); Mat b = new Mat(3, 1, CvType.CV_32F); - a.put(0, 0, new float[] { Double.valueOf(line.x1).floatValue() }); - a.put(1, 0, new float[] { Double.valueOf(line.y1).floatValue() }); + a.put(0, 0, new float[] { Double.valueOf(line.getX1()).floatValue() }); + a.put(1, 0, new float[] { Double.valueOf(line.getY1()).floatValue() }); a.put(2, 0, new float[] { Double.valueOf(1d).floatValue() }); - b.put(0, 0, new float[] { Double.valueOf(line.x2).floatValue() }); - b.put(1, 0, new float[] { Double.valueOf(line.y2).floatValue() }); + b.put(0, 0, new float[] { Double.valueOf(line.getX2()).floatValue() }); + b.put(1, 0, new float[] { Double.valueOf(line.getY2()).floatValue() }); b.put(2, 0, new float[] { Double.valueOf(1d).floatValue() }); Mat an = new Mat(3, 1, CvType.CV_32F); Mat bn = new Mat(3, 1, CvType.CV_32F); @@ -198,11 +195,15 @@ private Mat getLineMat(Line line) { Core.gemm(K.inv(), b, 1, new Mat(), 0, bn); Mat li = an.cross(bn); Core.normalize(li, li); + a.release(); + b.release(); + an.release(); + bn.release(); return li; } + // @SuppressWarnings({ "rawtypes", "unchecked" }) public Ransac vanishingPointRansac(int width, int height) { - int minimal_sample_set_dimension = 2; double maxError = (float) 0.01623 * 2; if (K == null) { @@ -213,9 +214,11 @@ public Ransac vanishingPointRansac(int width, int height) { K.put(1, 2, new float[] { height / 2 }); K.put(2, 2, new float[] { 1 }); } + return new Ransac<>(getLines(), getModelProvider(minimal_sample_set_dimension, maxError), minimal_sample_set_dimension, 100, maxError, Double.valueOf(Math.floor(this.size() * 0.7)).intValue()); + } - Function, Model> modelProvider = datas -> { - + private Function, Model> getModelProvider(int minimal_sample_set_dimension, double maxError) { + return datas -> { Mat vp; if (datas.size() == minimal_sample_set_dimension) { @@ -223,10 +226,9 @@ public Ransac vanishingPointRansac(int width, int height) { vp = getLineMat(it.next()).cross(getLineMat(it.next())); Core.normalize(vp, vp); } else { - // Extract the line segments corresponding to the indexes contained in the set Mat li_set = new Mat(3, datas.size(), CvType.CV_32F); - Mat Tau = new Mat(datas.size(), datas.size(), CvType.CV_32F, new Scalar(0, 0, 0)); + Mat tau = new Mat(datas.size(), datas.size(), CvType.CV_32F, new Scalar(0, 0, 0)); int i = 0; for (Line line : datas) { @@ -234,18 +236,18 @@ public Ransac vanishingPointRansac(int width, int height) { li_set.put(0, i, li.get(0, 0)); li_set.put(1, i, li.get(1, 0)); li_set.put(2, i, li.get(2, 0)); - Tau.put(i, i, line.size()); + tau.put(i, i, line.size()); i++; } // Least squares solution - // Generate the matrix ATA (a partir de LSS_set=A) + // Generate the matrix ATA (from LSS_set=A) Mat L = li_set.t(); Mat ATA = new Mat(3, 3, CvType.CV_32F); Mat dst = new Mat(); - Core.gemm(L.t(), Tau.t(), 1, new Mat(), 0, dst); - Core.gemm(dst, Tau, 1, new Mat(), 0, dst); + Core.gemm(L.t(), tau.t(), 1, new Mat(), 0, dst); + Core.gemm(dst, tau, 1, new Mat(), 0, dst); Core.gemm(dst, L, 1, new Mat(), 0, ATA); // Obtain eigendecomposition @@ -278,12 +280,11 @@ public Ransac vanishingPointRansac(int width, int height) { } return new Model() { - @Override public double computeError(Line line) { Mat lineMat = getLineMat(line); double di = vp.dot(lineMat); - di /= Core.norm(vp) * Core.norm(lineMat); + di /= (Core.norm(vp) * Core.norm(lineMat)); return di * di; } @@ -296,7 +297,8 @@ public double computeGlobalError(List datas, Collection consensusDat error = maxError; globalError += error; } - return globalError = globalError / datas.size(); + globalError = globalError / datas.size(); + return globalError; } @Override @@ -306,123 +308,6 @@ public Object[] getParams() { }; }; - return new Ransac<>(lines, modelProvider, minimal_sample_set_dimension, 100, maxError, Double.valueOf(Math.floor(lines.size() * 0.7)).intValue()); - } - - public Lines rotate(Mat matrix) { - return new Lines(lines.stream().map(line -> line.transform(matrix)).collect(Collectors.toList())); - } - - public Lines perspectivTransform(Mat matrix) { - return new Lines(lines.stream().map(line -> line.perspectivTransform(matrix)).collect(Collectors.toList())); - } - - public void draw(Mat frame, Scalar color) { - lines.forEach(line -> line.draw(frame, color)); - } - - public Lines(Collection lines) { - double mean = 0; - for (Line line : lines) { - this.lines.add(line); - mean += line.getAngle(); - } - this.mean = mean / lines.size(); - - } - - public int size() { - return lines.size(); - } - - public double getMean() { - return mean; - } - - } - - public static class Line { - private final double x1, y1, x2, y2, angle; - - public Line(Point p1, Point p2) { - this(p1.x, p1.y, p2.x, p2.y); - } - - public Line(double x1, double y1, double x2, double y2) { - this.x1 = x1; - this.x2 = x2; - this.y1 = y1; - this.y2 = y2; - this.angle = Math.atan2(y2 - y1, x2 - x1); - } - - public double size() { - return Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)); - } - - public Line transform(Mat rotationMatrix) { - MatOfPoint2f results = new MatOfPoint2f(); - Core.transform(Converters.vector_Point2f_to_Mat(Arrays.asList(new Point(x1, y1), new Point(x2, y2))), results, rotationMatrix); - Point[] targets = results.toArray(); - return new Line(targets[0].x, targets[0].y, targets[1].x, targets[1].y); - } - - public Line perspectivTransform(Mat homography) { - MatOfPoint2f results = new MatOfPoint2f(); - Core.perspectiveTransform(Converters.vector_Point2f_to_Mat(Arrays.asList(new Point(x1, y1), new Point(x2, y2))), results, homography); - Point[] targets = results.toArray(); - return new Line(targets[0].x, targets[0].y, targets[1].x, targets[1].y); - } - - public void draw(Mat frame, Scalar color) { - Imgproc.line(frame, new Point(x1, y1), new Point(x2, y2), color, 1); - } - - @Override - public String toString() { - return "Line : " + angle; - } - - public double getAngle() { - return angle; - } - - public double geta() { - return (y2 - y1) / (x2 - x1); - } - - public double getOrthoa() { - return (x2 - x1) / (y1 - y2); - } - - public double getOrthob(Point p) { - return p.y - getOrthoa() * p.x; - } - - public double getb() { - return y1 - geta() * x1; - } - - public double distance(Point p) { - return Math.abs(geta() * p.x - p.y + getb()) / Math.sqrt(1 + Math.pow(geta(), 2)); - } - - public Point intersection(double a, double b) { - double x = (b - getb()) / (geta() - a); - double y = a * x + b; - return new Point(x, y); - } - - public Point intersection(Line line) { - double x = (line.getb() - getb()) / (geta() - line.geta()); - double y = geta() * x + getb(); - return new Point(x, y); - } - - public Point intersection(double verticalLinex) { - double x = verticalLinex; - double y = geta() * x + getb(); - return new Point(x, y); } } diff --git a/gs-cv/src/main/java/org/genericsystem/cv/utils/Line.java b/gs-cv/src/main/java/org/genericsystem/cv/utils/Line.java index 28eddef9b..a8c40b809 100644 --- a/gs-cv/src/main/java/org/genericsystem/cv/utils/Line.java +++ b/gs-cv/src/main/java/org/genericsystem/cv/utils/Line.java @@ -13,6 +13,10 @@ public class Line { protected final double x1, y1, x2, y2, angle; + public Line(Point p1, Point p2) { + this(p1.x, p1.y, p2.x, p2.y); + } + public Line(double x1, double y1, double x2, double y2) { this.x1 = x1; this.x2 = x2; @@ -51,6 +55,30 @@ public void draw(Mat frame, Scalar color, int thickness) { Imgproc.line(frame, new Point(x1, y1), new Point(x2, y2), color, thickness); } + public double size() { + return Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)); + } + + public double geta() { + return (y2 - y1) / (x2 - x1); + } + + public double getb() { + return y1 - geta() * x1; + } + + public Point intersection(Line line) { + double x = (line.getb() - getb()) / (geta() - line.geta()); + double y = geta() * x + getb(); + return new Point(x, y); + } + + public Point intersection(double verticalLinex) { + double x = verticalLinex; + double y = geta() * x + getb(); + return new Point(x, y); + } + @Override public String toString() { return "Line : " + angle; @@ -75,4 +103,22 @@ public double getX2() { public double getY2() { return y2; } + + // public double distance(Point p) { + // return Math.abs(geta() * p.x - p.y + getb()) / Math.sqrt(1 + Math.pow(geta(), 2)); + // } + + // public Point intersection(double a, double b) { + // double x = (b - getb()) / (geta() - a); + // double y = a * x + b; + // return new Point(x, y); + // } + + // public double getOrthoa() { + // return (x2 - x1) / (y1 - y2); + // } + // + // public double getOrthob(Point p) { + // return p.y - getOrthoa() * p.x; + // } }