diff --git a/gs-cv/src/main/java/org/genericsystem/cv/application/DirectionalEnhancer.java b/gs-cv/src/main/java/org/genericsystem/cv/application/DirectionalEnhancer.java new file mode 100644 index 000000000..45a555fb3 --- /dev/null +++ b/gs-cv/src/main/java/org/genericsystem/cv/application/DirectionalEnhancer.java @@ -0,0 +1,41 @@ +package org.genericsystem.cv.application; + +import org.genericsystem.cv.Lines; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DirectionalEnhancer { + + public static Lines getLines(Mat frame) { + Mat mat = prepare(frame); + Mat houghLines = new Mat(); + Imgproc.HoughLinesP(mat, houghLines, 1, Math.PI / 180, 10, 100, 10); + mat.release(); + Lines lines = new Lines(houghLines); + houghLines.release(); + return lines; + } + + public static Mat prepare(Mat frame) { + Mat mat = new Mat(); + Imgproc.cvtColor(frame, mat, Imgproc.COLOR_BGR2GRAY); + Imgproc.GaussianBlur(mat, mat, new Size(13, 13), 0); + Imgproc.adaptiveThreshold(mat, mat, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 51, 2); + Imgproc.morphologyEx(mat, mat, Imgproc.MORPH_CLOSE, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(9, 9))); + Imgproc.morphologyEx(mat, mat, Imgproc.MORPH_OPEN, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5, 5))); + List contours = new ArrayList<>(); + Imgproc.findContours(mat, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); + for (MatOfPoint contour : contours) + Imgproc.drawContours(mat, Arrays.asList(contour), 0, new Scalar(255, 0, 0), -1); + Imgproc.morphologyEx(mat, mat, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(4, 4))); + return mat; + } + +} diff --git a/gs-cv/src/main/java/org/genericsystem/cv/application/DirectionalEnhancerDemo.java b/gs-cv/src/main/java/org/genericsystem/cv/application/DirectionalEnhancerDemo.java new file mode 100644 index 000000000..de30dea6a --- /dev/null +++ b/gs-cv/src/main/java/org/genericsystem/cv/application/DirectionalEnhancerDemo.java @@ -0,0 +1,194 @@ +package org.genericsystem.cv.application; + +import org.genericsystem.cv.AbstractApp; +import org.genericsystem.cv.Img; +import org.genericsystem.cv.Lines; +import org.genericsystem.cv.utils.NativeLibraryLoader; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javafx.application.Platform; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.GridPane; + +public class DirectionalEnhancerDemo extends AbstractApp { + + public static void main(String[] args) { + launch(args); + } + + static { + NativeLibraryLoader.load(); + } + + private final double f = 6.053 / 0.009; + + private GSCapture gsCapture = new GSVideoCapture(0, f, GSVideoCapture.HD, GSVideoCapture.VGA); + private SuperFrameImg superFrame = gsCapture.read(); + private ScheduledExecutorService timer = new BoundedScheduledThreadPoolExecutor(); + private Config config = new Config(); + private final ImageView[][] imageViews = new ImageView[][] { new ImageView[3], new ImageView[3], new ImageView[3], new ImageView[3] }; + + private void startTimer() { + timer.scheduleAtFixedRate(() -> { + try { + Image[] images = doWork(); + if (images != null) + Platform.runLater(() -> { + Iterator it = Arrays.asList(images).iterator(); + for (int row = 0; row < imageViews.length; row++) + for (int col = 0; col < imageViews[row].length; col++) + if (it.hasNext()) + imageViews[row][col].setImage(it.next()); + }); + } catch (Throwable e) { + e.printStackTrace(); + } + }, 1000, 30, TimeUnit.MILLISECONDS); + } + + @Override + protected void fillGrid(GridPane mainGrid) { + double displaySizeReduction = 1.5; + for (int col = 0; col < imageViews.length; col++) + for (int row = 0; row < imageViews[col].length; row++) { + ImageView imageView = new ImageView(); + imageViews[col][row] = imageView; + mainGrid.add(imageViews[col][row], col, row); + imageView.setFitWidth(superFrame.width() / displaySizeReduction); + imageView.setFitHeight(superFrame.height() / displaySizeReduction); + } + startTimer(); + } + + private Image[] doWork() { + System.out.println("do work"); + if (!config.stabilizedMode) { + superFrame = gsCapture.read(); + } + Image[] images = new Image[12]; + + long ref = System.currentTimeMillis(); + + // Mat mat = DirectionalEnhancer.prepare(superFrame.getFrame().getSrc()); + + Mat mat = new Mat(); + Imgproc.cvtColor(superFrame.getFrame().getSrc(), mat, Imgproc.COLOR_BGR2GRAY); + Imgproc.GaussianBlur(mat, mat, new Size(13, 13), 0); + images[0] = new Img(mat, false).toJfxImage(); + Imgproc.adaptiveThreshold(mat, mat, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 7, 2); + images[1] = new Img(mat, false).toJfxImage(); + Imgproc.morphologyEx(mat, mat, Imgproc.MORPH_CLOSE, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(9, 9))); + // images[2] = new Img(mat, false).toJfxImage(); + // Imgproc.morphologyEx(mat, mat, Imgproc.MORPH_OPEN, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5, 5))); + + images[2] = new Img(mat, false).toJfxImage(); + + List contours = new ArrayList<>(); + Imgproc.findContours(mat, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); + for (MatOfPoint contour : contours) + Imgproc.drawContours(mat, Arrays.asList(contour), 0, new Scalar(255, 0, 0), -1); + images[3] = new Img(mat, false).toJfxImage(); + Mat gradient = new Mat(); + Imgproc.morphologyEx(mat, gradient, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(4, 4))); + + images[4] = new Img(gradient, false).toJfxImage(); + Mat smallLines = new Mat(); + Mat bigLines = new Mat(); + Imgproc.HoughLinesP(gradient, bigLines, 1, Math.PI / 180, 10, 30, 10); + Imgproc.HoughLinesP(gradient, smallLines, 1, Math.PI / 180, 10, 30, 10); + Lines slines = new Lines(smallLines); + Lines blines = new Lines(bigLines); + + Mat result = Mat.zeros(superFrame.getFrame().getSrc().size(), superFrame.getFrame().getSrc().type()); + Lines horizontalLines = new Lines(blines.getLines().stream().filter(l -> Math.abs(l.y2 - l.y1) < Math.abs(l.x2 - l.x1)).collect(Collectors.toList())); + horizontalLines.draw(result, new Scalar(0, 255, 0), 2); + + Lines verticalLines = new Lines(slines.getLines().stream().filter(l -> Math.abs(l.y2 - l.y1) > Math.abs(l.x2 - l.x1)).collect(Collectors.toList())); + verticalLines.draw(result, new Scalar(0, 0, 255), 2); + + images[5] = new Img(result, false).toJfxImage(); + + horizontalLines.draw(mat, new Scalar(255), 2); + verticalLines.draw(mat, new Scalar(255), 2); + ref = trace("Draw lines", ref); + + Imgproc.findContours(mat, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); + for (MatOfPoint contour : contours) + Imgproc.drawContours(mat, Arrays.asList(contour), 0, new Scalar(255, 0, 0), -1); + + images[6] = new Img(mat, false).toJfxImage(); + + Imgproc.morphologyEx(mat, gradient, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(3, 3))); + + images[7] = new Img(gradient, false).toJfxImage(); + + Imgproc.HoughLinesP(gradient, bigLines, 1, Math.PI / 180, 100, 300, 50); + Imgproc.HoughLinesP(gradient, smallLines, 1, Math.PI / 180, 25, 40, 13); + slines = new Lines(smallLines); + blines = new Lines(bigLines); + + result = Mat.zeros(superFrame.getFrame().getSrc().size(), superFrame.getFrame().getSrc().type()); + horizontalLines = new Lines(blines.getLines().stream().filter(l -> Math.abs(l.y2 - l.y1) < Math.abs(l.x2 - l.x1)).collect(Collectors.toList())); + horizontalLines.draw(result, new Scalar(0, 255, 0), 2); + + verticalLines = new Lines(slines.getLines().stream().filter(l -> Math.abs(l.y2 - l.y1) > Math.abs(l.x2 - l.x1)).collect(Collectors.toList())); + verticalLines.draw(result, new Scalar(0, 0, 255), 2); + + images[8] = new Img(result, false).toJfxImage(); + + mat.release(); + result.release(); + return images; + } + + private long trace(String message, long ref) { + long last = System.currentTimeMillis(); + System.out.println(message + " : " + (last - ref)); + return last; + } + + @Override + protected void onS() { + config.stabilizedMode = !config.stabilizedMode; + } + + @Override + protected void onSpace() { + if (config.isOn) { + timer.shutdown(); + // gsCapture.release(); + } else { + timer = new BoundedScheduledThreadPoolExecutor(); + // gsCapture = new GSVideoCapture(0, f, GSVideoCapture.HD, GSVideoCapture.VGA); + startTimer(); + } + config.isOn = !config.isOn; + } + + @Override + protected void onT() { + config.textsEnabledMode = !config.textsEnabledMode; + } + + @Override + public void stop() throws Exception { + super.stop(); + timer.shutdown(); + timer.awaitTermination(5000, TimeUnit.MILLISECONDS); + gsCapture.release(); + } + +} diff --git a/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransform.java b/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransform.java index 6e5bd6bb7..d5df976ad 100644 --- a/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransform.java +++ b/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransform.java @@ -12,9 +12,9 @@ import org.opencv.core.Point; import org.opencv.core.Range; import org.opencv.core.Rect; -import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; +import org.opencv.ximgproc.Ximgproc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,7 +61,7 @@ public static Mat projectionMap(Mat radon, int minAngle) { return projectionMap; } - public static int[] bestTraject(Mat projectionMap, double anglePenality, double pow) { + public static TrajectStep[] bestTraject(Mat projectionMap, double anglePenality, double pow) { double[][] score = new double[projectionMap.rows()][projectionMap.cols()]; int[][] thetaPrev = new int[projectionMap.rows()][projectionMap.cols()]; for (int theta = 0; theta < projectionMap.cols(); theta++) @@ -93,8 +93,9 @@ public static int[] bestTraject(Mat projectionMap, double anglePenality, double // System.out.println(Arrays.toString(score[projectionMap.rows() - 1])); // System.out.println(Arrays.deepToString(thetaPrev)); double maxScore = Double.NEGATIVE_INFINITY; + int prevTheta = -1; - int[] thetas = new int[projectionMap.rows()]; + for (int theta = 0; theta < projectionMap.cols(); theta++) { double lastScore = score[projectionMap.rows() - 1][theta]; // System.out.println(lastScore); @@ -104,10 +105,10 @@ public static int[] bestTraject(Mat projectionMap, double anglePenality, double } } assert prevTheta != -1; - // System.out.println(maxScore + " for theta : " + prevTheta); + TrajectStep[] thetas = new TrajectStep[projectionMap.rows()]; for (int k = projectionMap.rows() - 1; k >= 0; k--) { - thetas[k] = prevTheta; + thetas[k] = new TrajectStep(k, prevTheta, projectionMap.get(k, prevTheta)[0]); // System.out.println(prevTheta); prevTheta = thetaPrev[k][prevTheta]; } @@ -126,7 +127,7 @@ public static Mat extractStrip(Mat src, int startX, int width) { return new Mat(src, new Range(0, src.rows()), new Range(startX, startX + width)); } - public static List estimateBaselines(Mat image, double anglePenalty, int minMaxAngle, double magnitudePow, int yStep) { + public static List estimateBaselines(Mat image, double anglePenalty, int minAngle, int maxAngle, double magnitudePow, int yStep) { Mat preprocessed = new Img(image, false).adaptativeGaussianInvThreshold(5, 3).getSrc(); List hLines = new ArrayList<>(); // Number of overlapping vertical strips. @@ -137,7 +138,7 @@ public static List estimateBaselines(Mat image, double // Image width = [n(1 - r) + r] w double w = (image.width() / (n * (1 - r) + r)); double step = (int) ((1 - r) * w); - int[][] angles = new int[n][]; + TrajectStep[][] angles = new TrajectStep[n][]; // 0, center of each vertical strip, image.width() - 1 double[] xs = new double[n + 2]; @@ -155,7 +156,7 @@ public static List estimateBaselines(Mat image, double List values = new ArrayList<>(); for (int k = 0; k < image.height(); k++) - values.add(new double[] { k, angles[i][k] }); + values.add(new double[] { k, angles[i][k].theta, angles[i][k].magnitude }); approxParams[i] = LevenbergImpl.fromBiFunction(f, values, new double[] { 0, 0, 0 }).getParams(); xs[i + 1] = x + .5 * w; x += step; @@ -195,12 +196,13 @@ public static List estimateBaselines(Mat image, double return hLines; } - public static Function approxTraject(int[] traj) { + public static Function approxTraject(TrajectStep[] traj) { List values = new ArrayList<>(); for (int k = 0; k < traj.length; k++) - values.add(new double[] { k, traj[k] }); + values.add(new double[] { k, traj[k].theta, traj[k].magnitude }); BiFunction f = (x, params) -> params[0] + params[1] * x + params[2] * x * x; - double[] params = LevenbergImpl.fromBiFunction(f, values, new double[] { 0, 0, 0 }).getParams(); + BiFunction error = (xy, params) -> (f.apply(xy[0], params) - xy[1]); + double[] params = new LevenbergImpl<>(error, values, new double[] { 0, 0, 0 }).getParams(); return x -> f.apply(x, params); } @@ -222,7 +224,33 @@ public static List toVerticalOrientedPoints(Function toHorizontalFHTOrientedPoints(Function f, int vStrip, int stripWidth, int height, int step, int minAngle) { + List orientedPoints = new ArrayList<>(); + for (int k = 0; k <= height; k += step) { + double angle = ((f.apply((double) k) / (2 * stripWidth - 1) * 90) - minAngle) / 180 * Math.PI; + orientedPoints.add(new OrientedPoint(new Point((vStrip + 1) * stripWidth / 2, k), angle, 1)); + } + return orientedPoints; + } + + public static List toVerticalFHTOrientedPoints(Function f, int hStrip, int stripHeight, int width, int step, int minAngle) { + List orientedPoints = new ArrayList<>(); + for (int k = 0; k <= width; k += step) { + double angle = (90 + minAngle - (f.apply((double) k) / (2 * stripHeight - 1) * 90)) / 180 * Math.PI; + orientedPoints.add(new OrientedPoint(new Point(k, (hStrip + 1) * stripHeight / 2), angle, 1)); + } + return orientedPoints; + } + private static boolean inImage(Point p, Mat img) { return p.x >= 0 && p.y >= 0 && p.x < img.width() && p.y < img.height(); } + + public static Mat fastHoughTransform(Mat vStrip, int stripSize) { + Mat houghTransform = new Mat(); + Ximgproc.FastHoughTransform(vStrip, houghTransform, CvType.CV_64FC1, Ximgproc.ARO_45_135, Ximgproc.FHT_ADD, Ximgproc.HDO_DESKEW); + Core.transpose(houghTransform, houghTransform); + Core.normalize(houghTransform, houghTransform, 0, 255, Core.NORM_MINMAX); + return new Mat(houghTransform, new Range(stripSize / 2, houghTransform.height() - stripSize / 2), new Range(0, houghTransform.width())); + } } diff --git a/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransformDemo.java b/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransformDemo.java index 1b033948b..6150984bd 100644 --- a/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransformDemo.java +++ b/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransformDemo.java @@ -1,24 +1,26 @@ package org.genericsystem.cv.application; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; - import org.genericsystem.cv.AbstractApp; import org.genericsystem.cv.Img; +import org.genericsystem.cv.Lines; import org.genericsystem.cv.application.GeneralInterpolator.OrientedPoint; import org.genericsystem.cv.utils.NativeLibraryLoader; import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; import org.opencv.core.Point; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + import javafx.application.Platform; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -79,104 +81,179 @@ private Image[] doWork() { if (!config.stabilizedMode) { superFrame = gsCapture.read(); } - Image[] images = new Image[8]; + Image[] images = new Image[9]; long ref = System.currentTimeMillis(); Img binarized = superFrame.getFrame().adaptativeGaussianInvThreshold(7, 5); + + Mat frameClone = superFrame.getFrame().getSrc().clone(); + + Imgproc.cvtColor(frameClone, frameClone, Imgproc.COLOR_BGR2GRAY); + Imgproc.GaussianBlur(frameClone, frameClone, new Size(13, 13), 0); + Imgproc.adaptiveThreshold(frameClone, frameClone, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 51, 2); + Imgproc.morphologyEx(frameClone, frameClone, Imgproc.MORPH_CLOSE, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(9, 9))); + Imgproc.morphologyEx(frameClone, frameClone, Imgproc.MORPH_OPEN, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5, 5))); + + List contours = new ArrayList<>(); + Imgproc.findContours(frameClone, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); + for (MatOfPoint contour : contours) + Imgproc.drawContours(frameClone, Arrays.asList(contour), 0, new Scalar(255, 0, 0), -1); + + Imgproc.morphologyEx(frameClone, frameClone, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(4, 4))); + Mat houghLines = new Mat(); + Imgproc.HoughLinesP(frameClone, houghLines, 1, Math.PI / 180, 10, 100, 10); + + Lines horizontalLines = new Lines(new Lines(houghLines).getLines().stream().filter(l -> Math.abs(l.y2 - l.y1) < Math.abs(l.x2 - l.x1)).collect(Collectors.toList())); + horizontalLines.draw(binarized.getSrc(), new Scalar(255), 1); + + Lines verticalLines = new Lines(new Lines(houghLines).getLines().stream().filter(l -> Math.abs(l.y2 - l.y1) > Math.abs(l.x2 - l.x1)).collect(Collectors.toList())); + verticalLines.draw(binarized.getSrc(), new Scalar(255), 1); + images[0] = binarized.toJfxImage(); Img transposedBinarized = binarized.transpose(); ref = trace("Binarization", ref); - int stripWidth = 40; + int stripWidth = 70; List vStrips = RadonTransform.extractStrips(binarized.getSrc(), stripWidth); - int stripHeight = 40; - List htrips = RadonTransform.extractStrips(transposedBinarized.getSrc(), stripHeight); + int stripHeight = 70; + List hStrips = RadonTransform.extractStrips(transposedBinarized.getSrc(), stripHeight); ref = trace("Extract strips", ref); - List vRadons = vStrips.stream().map(strip -> RadonTransform.transform(strip, 45)).collect(Collectors.toList()); - List hRadons = htrips.stream().map(strip -> RadonTransform.transform(strip, 45)).collect(Collectors.toList()); + int minAngle = 45; + int maxAngle = 45; + List vRadons = vStrips.stream().map(strip -> RadonTransform.transform(strip, minAngle, maxAngle)).collect(Collectors.toList()); + List hRadons = hStrips.stream().map(strip -> RadonTransform.transform(strip, minAngle, maxAngle)).collect(Collectors.toList()); ref = trace("Compute radons", ref); - List vProjectionMaps = vRadons.stream().map(radon -> RadonTransform.projectionMap(radon)).collect(Collectors.toList()); - List hProjectionMaps = hRadons.stream().map(radon -> RadonTransform.projectionMap(radon)).collect(Collectors.toList()); + List vProjectionMaps = vRadons.stream().map(radon -> RadonTransform.projectionMap(radon, minAngle)).collect(Collectors.toList()); + List hProjectionMaps = hRadons.stream().map(radon -> RadonTransform.projectionMap(radon, minAngle)).collect(Collectors.toList()); ref = trace("Compute projections", ref); vProjectionMaps.stream().forEach(projectionMap -> Imgproc.morphologyEx(projectionMap, projectionMap, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(1, 2)))); hProjectionMaps.stream().forEach(projectionMap -> Imgproc.morphologyEx(projectionMap, projectionMap, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(1, 2)))); + List vHoughs = vStrips.stream().map(strip -> RadonTransform.fastHoughTransform(strip, stripWidth)).collect(Collectors.toList()); + List hHoughs = hStrips.stream().map(strip -> RadonTransform.fastHoughTransform(strip, stripHeight)).collect(Collectors.toList()); + ref = trace("Compute gradients", ref); - List vTrajs = vProjectionMaps.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -1000, 2)).collect(Collectors.toList()); - List hTrajs = hProjectionMaps.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -1000, 2)).collect(Collectors.toList()); + List vTrajs = vProjectionMaps.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -10000, 3)).collect(Collectors.toList()); + List hTrajs = hProjectionMaps.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -10000, 3)).collect(Collectors.toList()); + List vHoughTrajs = vHoughs.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -10000 / (2 * stripWidth - 1) * 90 * 2, 3)).collect(Collectors.toList()); + List hHoughTrajs = hHoughs.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -10000 / (2 * stripHeight - 1) * 90 * 2, 3)).collect(Collectors.toList()); + ref = trace("Compute trajects", ref); List> approxVFunctions = vTrajs.stream().map(traj -> RadonTransform.approxTraject(traj)).collect(Collectors.toList()); List> approxHFunctions = hTrajs.stream().map(traj -> RadonTransform.approxTraject(traj)).collect(Collectors.toList()); + List> approxVFHTFunctions = vHoughTrajs.stream().map(traj -> RadonTransform.approxTraject(traj)).collect(Collectors.toList()); + List> approxHFHTFunctions = hHoughTrajs.stream().map(traj -> RadonTransform.approxTraject(traj)).collect(Collectors.toList()); - ref = trace("Compute approx", ref); + ref = trace("Compute approxs", ref); - int hStep = 30; + int hStep = stripHeight / 2; int vStrip = 0; List horizontals = new ArrayList<>(); - for (Function f : approxVFunctions) { - horizontals.addAll(RadonTransform.toHorizontalOrientedPoints(f, vStrip, stripWidth, binarized.height(), hStep)); - vStrip++; - } - int vStep = 30; + for (Function f : approxVFunctions) + horizontals.addAll(RadonTransform.toHorizontalOrientedPoints(f, vStrip++, stripWidth, binarized.height(), hStep, minAngle)); + int vStep = stripWidth / 2; int hStrip = 0; List verticals = new ArrayList<>(); - for (Function f : approxHFunctions) { - verticals.addAll(RadonTransform.toVerticalOrientedPoints(f, hStrip, stripHeight, binarized.width(), vStep)); - hStrip++; - } + for (Function f : approxHFunctions) + verticals.addAll(RadonTransform.toVerticalOrientedPoints(f, hStrip++, stripHeight, binarized.width(), vStep, minAngle)); + + GeneralInterpolator interpolator = new GeneralInterpolator(horizontals, verticals, 6, 20); + + vStrip = 0; + List fhtHorizontals = new ArrayList<>(); + for (Function f : approxVFHTFunctions) + fhtHorizontals.addAll(RadonTransform.toHorizontalFHTOrientedPoints(f, vStrip++, stripWidth, binarized.height(), hStep, minAngle)); + hStrip = 0; + List fhtVerticals = new ArrayList<>(); + for (Function f : approxHFHTFunctions) + fhtVerticals.addAll(RadonTransform.toVerticalFHTOrientedPoints(f, hStrip++, stripHeight, binarized.width(), vStep, minAngle)); - GeneralInterpolator interpolator = new GeneralInterpolator(horizontals, verticals, 4, 50); + GeneralInterpolator interpolatorFHT = new GeneralInterpolator(fhtHorizontals, fhtVerticals, 6, 20); ref = trace("Prepare interpolator", ref); - Img frameDisplay = superFrame.getDisplay(); + Img frameDisplay = new Img(superFrame.getFrame().getSrc().clone(), false); vStrip = 0; for (Function f : approxVFunctions) { - for (int k = hStep; k < binarized.height(); k += hStep) { - double angle = (f.apply((double) k) - 45) / 180 * Math.PI; - Imgproc.line(frameDisplay.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 4, k - Math.sin(angle) * stripWidth / 4), - new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 4, k + Math.sin(angle) * stripWidth / 4), new Scalar(0, 255, 0), 1); + for (int k = hStep; k + hStep <= binarized.height(); k += hStep) { + double angle = (f.apply((double) k) - minAngle) / 180 * Math.PI; + Imgproc.line(frameDisplay.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 6, k - Math.sin(angle) * stripWidth / 6), + new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 6, k + Math.sin(angle) * stripWidth / 6), new Scalar(0, 255, 0), 2); angle = interpolator.interpolateHorizontals((vStrip + 1) * stripWidth / 2, k); - Imgproc.line(frameDisplay.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 4, k - Math.sin(angle) * stripWidth / 4), - new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 4, k + Math.sin(angle) * stripWidth / 4), new Scalar(255, 0, 0), 1); + Imgproc.line(frameDisplay.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 6, k - Math.sin(angle) * stripWidth / 6), + new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 6, k + Math.sin(angle) * stripWidth / 6), new Scalar(255, 0, 0), 2); } vStrip++; } hStrip = 0; for (Function f : approxHFunctions) { - for (int k = vStep; k < binarized.width(); k += vStep) { - double angle = (90 + 45 - f.apply((double) k)) / 180 * Math.PI; - Imgproc.line(frameDisplay.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 4, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 4), - new Point(k + Math.cos(angle) * stripHeight / 4, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 4), new Scalar(0, 0, 255), 1); + for (int k = vStep; k + vStep <= binarized.width(); k += vStep) { + double angle = (90 + minAngle - f.apply((double) k)) / 180 * Math.PI; + Imgproc.line(frameDisplay.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 6), + new Point(k + Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 6), new Scalar(0, 0, 255), 2); angle = interpolator.interpolateVerticals(k, (hStrip + 1) * stripHeight / 2); - Imgproc.line(frameDisplay.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 4, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 4), - new Point(k + Math.cos(angle) * stripHeight / 4, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 4), new Scalar(255, 0, 0), 1); + Imgproc.line(frameDisplay.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 6), + new Point(k + Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 6), new Scalar(255, 0, 0), 2); } hStrip++; } images[1] = frameDisplay.toJfxImage(); + Img frameDisplayFHT = new Img(superFrame.getFrame().getSrc().clone(), false); + vStrip = 0; + for (Function f : approxVFHTFunctions) { + for (int k = hStep; k + hStep <= binarized.height(); k += hStep) { + double angle = (f.apply((double) k) / (2 * stripWidth - 1) * 90 - minAngle) / 180 * Math.PI; + Imgproc.line(frameDisplayFHT.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 6, k - Math.sin(angle) * stripWidth / 6), + new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 6, k + Math.sin(angle) * stripWidth / 6), new Scalar(0, 255, 0), 2); + angle = interpolatorFHT.interpolateHorizontals((vStrip + 1) * stripWidth / 2, k); + Imgproc.line(frameDisplayFHT.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 6, k - Math.sin(angle) * stripWidth / 6), + new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 6, k + Math.sin(angle) * stripWidth / 6), new Scalar(255, 0, 0), 2); + } + vStrip++; + } + hStrip = 0; + for (Function f : approxHFHTFunctions) { + for (int k = vStep; k + vStep <= binarized.width(); k += vStep) { + double angle = (90 + minAngle - (f.apply((double) k) / (2 * stripHeight - 1) * 90)) / 180 * Math.PI; + Imgproc.line(frameDisplayFHT.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 6), + new Point(k + Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 6), new Scalar(0, 0, 255), 2); + angle = interpolatorFHT.interpolateVerticals(k, (hStrip + 1) * stripHeight / 2); + Imgproc.line(frameDisplayFHT.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 6), + new Point(k + Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 6), new Scalar(255, 0, 0), 2); + + } + hStrip++; + } + images[2] = frameDisplayFHT.toJfxImage(); + ref = trace("Display lines", ref); MeshGrid meshGrid = new MeshGrid(new Size(16, 9), interpolator, 20, 20, superFrame.getFrame().getSrc()); meshGrid.build(); + MeshGrid meshGridFHT = new MeshGrid(new Size(16, 9), interpolatorFHT, 20, 20, superFrame.getFrame().getSrc()); + meshGridFHT.build(); ref = trace("Build mesh", ref); - images[3] = new Img(meshGrid.drawOnCopy(new Scalar(0, 255, 0)), false).toJfxImage(); + + images[4] = new Img(meshGrid.drawOnCopy(new Scalar(0, 255, 0)), false).toJfxImage(); + images[5] = new Img(meshGridFHT.drawOnCopy(new Scalar(0, 255, 0)), false).toJfxImage(); ref = trace("Draw mesh", ref); - Img dewarp = new Img(meshGrid.dewarp()).adaptativeGaussianInvThreshold(7, 3); - images[4] = dewarp.toJfxImage(); + Img dewarp = new Img(meshGrid.dewarp()); + Img dewarpFHT = new Img(meshGridFHT.dewarp()); + images[7] = dewarp.toJfxImage(); + images[8] = dewarpFHT.toJfxImage(); ref = trace("Dewarp", ref); // images[7] = new Img(RadonTransform.estimateBaselines(superFrame.getFrame().getSrc(), 0), false).toJfxImage(); @@ -200,10 +277,10 @@ protected void onS() { protected void onSpace() { if (config.isOn) { timer.shutdown(); - gsCapture.release(); + // gsCapture.release(); } else { timer = new BoundedScheduledThreadPoolExecutor(); - gsCapture = new GSVideoCapture(0, f, GSVideoCapture.HD, GSVideoCapture.VGA); + // gsCapture = new GSVideoCapture(0, f, GSVideoCapture.HD, GSVideoCapture.VGA); startTimer(); } config.isOn = !config.isOn; diff --git a/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransformDemo2.java b/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransformDemo2.java new file mode 100644 index 000000000..e8732a62f --- /dev/null +++ b/gs-cv/src/main/java/org/genericsystem/cv/application/RadonTransformDemo2.java @@ -0,0 +1,265 @@ +package org.genericsystem.cv.application; + +import org.genericsystem.cv.AbstractApp; +import org.genericsystem.cv.Img; +import org.genericsystem.cv.utils.NativeLibraryLoader; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Range; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +import javafx.application.Platform; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.GridPane; + +public class RadonTransformDemo2 extends AbstractApp { + + public static void main(String[] args) { + launch(args); + } + + static { + NativeLibraryLoader.load(); + } + + private final double f = 6.053 / 0.009; + + private GSCapture gsCapture = new GSVideoCapture(0, f, GSVideoCapture.HD, GSVideoCapture.VGA); + private SuperFrameImg superFrame = gsCapture.read(); + private ScheduledExecutorService timer = new BoundedScheduledThreadPoolExecutor(); + private Config config = new Config(); + private final ImageView[][] imageViews = new ImageView[][] { new ImageView[3], new ImageView[3], new ImageView[3], new ImageView[3] }; + + private void startTimer() { + timer.scheduleAtFixedRate(() -> { + try { + Image[] images = doWork(); + if (images != null) + Platform.runLater(() -> { + Iterator it = Arrays.asList(images).iterator(); + for (int row = 0; row < imageViews.length; row++) + for (int col = 0; col < imageViews[row].length; col++) + if (it.hasNext()) + imageViews[row][col].setImage(it.next()); + }); + } catch (Throwable e) { + e.printStackTrace(); + } + }, 1000, 30, TimeUnit.MILLISECONDS); + } + + @Override + protected void fillGrid(GridPane mainGrid) { + double displaySizeReduction = 1.5; + for (int col = 0; col < imageViews.length; col++) + for (int row = 0; row < imageViews[col].length; row++) { + ImageView imageView = new ImageView(); + imageViews[col][row] = imageView; + mainGrid.add(imageViews[col][row], col, row); + imageView.setFitWidth(superFrame.width() / displaySizeReduction); + imageView.setFitHeight(superFrame.height() / displaySizeReduction); + } + startTimer(); + } + + private Image[] doWork() { + System.out.println("do work"); + if (!config.stabilizedMode) { + superFrame = gsCapture.read(); + } + Image[] images = new Image[9]; + + long ref = System.currentTimeMillis(); + + Img binarized = superFrame.getFrame().adaptativeGaussianInvThreshold(7, 5); + + images[0] = binarized.toJfxImage(); + + ref = trace("Binarization", ref); + + int stripWidth = 46; + Mat vStrip = RadonTransform.extractStrip(binarized.getSrc(), binarized.width() / 2 - stripWidth / 2, stripWidth); + + Mat vStripDisplay = Mat.zeros(binarized.size(), binarized.type()); + Mat roi = new Mat(vStripDisplay, new Range(0, binarized.height()), new Range(binarized.width() / 2 - stripWidth / 2, binarized.width() / 2 + stripWidth / 2)); + vStrip.copyTo(roi); + images[1] = new Img(vStripDisplay, false).toJfxImage(); + ref = trace("Extract strip", ref); + + Mat hough = RadonTransform.fastHoughTransform(vStrip, stripWidth); + images[3] = new Img(hough, false).toJfxImage(); + ref = trace("FastHoughTransform", ref); + System.out.println(hough); + Imgproc.morphologyEx(hough, hough, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(1, 2))); + // Core.normalize(houghTransform, houghTransform, 0, 255, Core.NORM_MINMAX); + images[4] = new Img(hough, false).toJfxImage(); + + TrajectStep[] houghVtraj = RadonTransform.bestTraject(hough, -10000, 3); + Mat vHoughColor = Mat.zeros(hough.size(), CvType.CV_8UC3); + hough.release(); + for (int y = 0; y < vHoughColor.height(); y++) + vHoughColor.put(y, houghVtraj[y].theta, 0, 0, 255); + ref = trace("Best traject hough", ref); + Function approxHoughVFunction = RadonTransform.approxTraject(houghVtraj); + for (int y = 0; y < vHoughColor.height(); y++) { + int x = (int) Math.round(approxHoughVFunction.apply((double) y)); + assert x >= 0 && x < vHoughColor.width() : x + " " + vHoughColor.width(); + vHoughColor.put(y, x, 0, 255, 0); + } + ref = trace("Display approx radon", ref); + images[5] = new Img(vHoughColor, false).toJfxImage(); + + Mat vTransform = RadonTransform.transform(vStrip, 45, 45); + Mat vProjection = RadonTransform.projectionMap(vTransform, 45); + images[6] = new Img(vProjection, false).toJfxImage(); + System.out.println(vProjection); + ref = trace("Radon + Projection", ref); + + Imgproc.morphologyEx(vProjection, vProjection, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(1, 2))); + // Core.normalize(vProjection, vProjection, 0, 255, Core.NORM_MINMAX); + images[7] = new Img(vProjection, false).toJfxImage(); + + TrajectStep[] vtraj = RadonTransform.bestTraject(vProjection, -10000, 3); + Mat vProjectionColor = Mat.zeros(vProjection.size(), CvType.CV_8UC3); + for (int y = 0; y < vProjectionColor.height(); y++) + vProjectionColor.put(y, vtraj[y].theta, 0, 0, 255); + ref = trace("Best traject radon", ref); + Function approxRadonVFunction = RadonTransform.approxTraject(vtraj); + for (int y = 0; y < vProjectionColor.height(); y++) { + int x = (int) Math.round(approxRadonVFunction.apply((double) y)); + assert x >= 0 && x < vProjectionColor.width() : x + " " + vProjectionColor.width(); + vProjectionColor.put(y, x, 255, 0, 0); + x = (int) Math.round(approxHoughVFunction.apply((double) y) / (2 * stripWidth - 1) * 90); + assert x >= 0 && x < vProjectionColor.width() : x + " " + vProjectionColor.width(); + vProjectionColor.put(y, x, 0, 255, 0); + } + ref = trace("Display approx radon", ref); + images[8] = new Img(vProjectionColor, false).toJfxImage(); + + // ref = trace("Compute radons", ref); + // + // List vProjectionMaps = vRadons.stream().map(radon -> RadonTransform.projectionMap(radon, minAngle)).collect(Collectors.toList()); + // List hProjectionMaps = hRadons.stream().map(radon -> RadonTransform.projectionMap(radon, minAngle)).collect(Collectors.toList()); + // + // ref = trace("Compute projections", ref); + // + // vProjectionMaps.stream().forEach(projectionMap -> Imgproc.morphologyEx(projectionMap, projectionMap, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(1, 2)))); + // hProjectionMaps.stream().forEach(projectionMap -> Imgproc.morphologyEx(projectionMap, projectionMap, Imgproc.MORPH_GRADIENT, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(1, 2)))); + // + // ref = trace("Compute gradients", ref); + // + // List vTrajs = vProjectionMaps.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -10000, 3)).collect(Collectors.toList()); + // List hTrajs = hProjectionMaps.stream().map(projectionMap -> RadonTransform.bestTraject(projectionMap, -10000, 3)).collect(Collectors.toList()); + // ref = trace("Compute trajects", ref); + // List> approxVFunctions = vTrajs.stream().map(traj -> RadonTransform.approxTraject(traj)).collect(Collectors.toList()); + // List> approxHFunctions = hTrajs.stream().map(traj -> RadonTransform.approxTraject(traj)).collect(Collectors.toList()); + // + // ref = trace("Compute approxs", ref); + // + // int hStep = stripHeight / 2; + // int vStrip = 0; + // List horizontals = new ArrayList<>(); + // for (Function f : approxVFunctions) + // horizontals.addAll(RadonTransform.toHorizontalOrientedPoints(f, vStrip++, stripWidth, binarized.height(), hStep, minAngle)); + // int vStep = stripWidth / 2; + // int hStrip = 0; + // List verticals = new ArrayList<>(); + // for (Function f : approxHFunctions) + // verticals.addAll(RadonTransform.toVerticalOrientedPoints(f, hStrip++, stripHeight, binarized.width(), vStep, minAngle)); + // + // GeneralInterpolator interpolator = new GeneralInterpolator(horizontals, verticals, 6, 20); + // + // ref = trace("Prepare interpolator", ref); + // + // Img frameDisplay = superFrame.getDisplay(); + // vStrip = 0; + // for (Function f : approxVFunctions) { + // for (int k = hStep; k + hStep <= binarized.height(); k += hStep) { + // double angle = (f.apply((double) k) - minAngle) / 180 * Math.PI; + // Imgproc.line(frameDisplay.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 6, k - Math.sin(angle) * stripWidth / 6), + // new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 6, k + Math.sin(angle) * stripWidth / 6), new Scalar(0, 255, 0), 2); + // angle = interpolator.interpolateHorizontals((vStrip + 1) * stripWidth / 2, k); + // Imgproc.line(frameDisplay.getSrc(), new Point((vStrip + 1) * stripWidth / 2 - Math.cos(angle) * stripWidth / 6, k - Math.sin(angle) * stripWidth / 6), + // new Point((vStrip + 1) * stripWidth / 2 + Math.cos(angle) * stripWidth / 6, k + Math.sin(angle) * stripWidth / 6), new Scalar(255, 0, 0), 2); + // } + // vStrip++; + // } + // hStrip = 0; + // for (Function f : approxHFunctions) { + // for (int k = vStep; k + vStep <= binarized.width(); k += vStep) { + // double angle = (90 + minAngle - f.apply((double) k)) / 180 * Math.PI; + // Imgproc.line(frameDisplay.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 6), + // new Point(k + Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 6), new Scalar(0, 0, 255), 2); + // angle = interpolator.interpolateVerticals(k, (hStrip + 1) * stripHeight / 2); + // Imgproc.line(frameDisplay.getSrc(), new Point(k - Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 - Math.sin(angle) * stripHeight / 6), + // new Point(k + Math.cos(angle) * stripHeight / 6, (hStrip + 1) * stripHeight / 2 + Math.sin(angle) * stripHeight / 6), new Scalar(255, 0, 0), 2); + // + // } + // hStrip++; + // } + // images[1] = frameDisplay.toJfxImage(); + // + // ref = trace("Display lines", ref); + // + // MeshGrid meshGrid = new MeshGrid(new Size(16, 9), interpolator, 20, 20, superFrame.getFrame().getSrc()); + // meshGrid.build(); + // ref = trace("Build mesh", ref); + // images[3] = new Img(meshGrid.drawOnCopy(new Scalar(0, 255, 0)), false).toJfxImage(); + // ref = trace("Draw mesh", ref); + // + // Img dewarp = new Img(meshGrid.dewarp()).adaptativeGaussianInvThreshold(7, 3); + // images[4] = dewarp.toJfxImage(); + // ref = trace("Dewarp", ref); + // + // // images[7] = new Img(RadonTransform.estimateBaselines(superFrame.getFrame().getSrc(), 0), false).toJfxImage(); + + return images; + + } + + private long trace(String message, long ref) { + long last = System.currentTimeMillis(); + System.out.println(message + " : " + (last - ref)); + return last; + } + + @Override + protected void onS() { + config.stabilizedMode = !config.stabilizedMode; + } + + @Override + protected void onSpace() { + if (config.isOn) { + timer.shutdown(); + // gsCapture.release(); + } else { + timer = new BoundedScheduledThreadPoolExecutor(); + // gsCapture = new GSVideoCapture(0, f, GSVideoCapture.HD, GSVideoCapture.VGA); + startTimer(); + } + config.isOn = !config.isOn; + } + + @Override + protected void onT() { + config.textsEnabledMode = !config.textsEnabledMode; + } + + @Override + public void stop() throws Exception { + super.stop(); + timer.shutdown(); + timer.awaitTermination(5000, TimeUnit.MILLISECONDS); + gsCapture.release(); + } + +} diff --git a/gs-cv/src/main/java/org/genericsystem/cv/application/TrajectStep.java b/gs-cv/src/main/java/org/genericsystem/cv/application/TrajectStep.java new file mode 100644 index 000000000..7cc02ff98 --- /dev/null +++ b/gs-cv/src/main/java/org/genericsystem/cv/application/TrajectStep.java @@ -0,0 +1,14 @@ +package org.genericsystem.cv.application; + +class TrajectStep { + public final int k; + public final int theta; + public final double magnitude; + + public TrajectStep(int k, int theta, double magnitude) { + this.k = k; + this.theta = theta; + this.magnitude = magnitude; + } + +} \ No newline at end of file