add FrameProviders, meddle with 2.x code

This commit is contained in:
Banks Troutman
2020-03-28 18:35:34 -04:00
parent c8dab42bac
commit 465bf0181e
25 changed files with 409 additions and 209 deletions

View File

@@ -7,6 +7,7 @@ import com.chameleonvision._2.vision.pipeline.CVPipeline;
import com.chameleonvision._2.vision.pipeline.CVPipelineResult;
import com.chameleonvision._2.vision.pipeline.pipes.*;
import com.chameleonvision.common.util.MemoryManager;
import com.chameleonvision.common.vision.opencv.Contour;
import edu.wpi.first.wpilibj.geometry.Pose2d;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Point;
@@ -124,7 +125,7 @@ public class StandardCVPipeline extends CVPipeline<StandardCVPipeline.StandardCV
speckleRejectPipe.setConfig(settings.speckle.doubleValue());
groupContoursPipe.setConfig(settings.targetGroup, settings.targetIntersection);
sortContoursPipe.setConfig(settings.sortMode, camProps, 5);
collect2dTargetsPipe = new Collect2dTargetsPipe(settings.calibrationMode, settings.targetRegion, settings.targetOrientation, settings.point, settings.dualTargetCalibrationM, settings.dualTargetCalibrationB, camProps);
collect2dTargetsPipe.setConfig(settings.calibrationMode, settings.targetRegion, settings.targetOrientation, settings.point, settings.dualTargetCalibrationM, settings.dualTargetCalibrationB, camProps);
draw2dContoursPipe.setConfig(settings.multiple, camProps);
draw2dCrosshairPipe.setConfig(draw2dCrosshairPipeSettings, settings.calibrationMode, settings.point, settings.dualTargetCalibrationM, settings.dualTargetCalibrationB);
outputMatPipe.setConfig(settings.isBinary);
@@ -161,9 +162,10 @@ public class StandardCVPipeline extends CVPipeline<StandardCVPipeline.StandardCV
Pair<List<MatOfPoint>, Long> findContoursResult = findContoursPipe.run(hsvResult.getLeft());
totalPipelineTimeNanos += findContoursResult.getRight();
Pair<List<MatOfPoint>, Long> filterContoursResult = filterContoursPipe.run(findContoursResult.getLeft());
Pair<List<Contour>, Long> filterContoursResult = filterContoursPipe.run(findContoursResult.getLeft());
totalPipelineTimeNanos += filterContoursResult.getRight();
// ignore !
Pair<List<MatOfPoint>, Long> speckleRejectResult = speckleRejectPipe.run(filterContoursResult.getLeft());
totalPipelineTimeNanos += speckleRejectResult.getRight();

View File

@@ -4,23 +4,25 @@ import com.chameleonvision._2.vision.camera.CaptureStaticProperties;
import com.chameleonvision._2.vision.pipeline.Pipe;
import com.chameleonvision.common.util.math.MathUtils;
import com.chameleonvision.common.util.numbers.DoubleCouple;
import com.chameleonvision.common.vision.opencv.Contour;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Rect;
import org.opencv.core.RotatedRect;
import org.opencv.imgproc.Imgproc;
import java.util.ArrayList;
import java.util.List;
public class FilterContoursPipe implements Pipe<List<MatOfPoint>, List<MatOfPoint>> {
public class FilterContoursPipe implements Pipe<List<MatOfPoint>, List<Contour>> {
private DoubleCouple area;
private DoubleCouple ratio;
private DoubleCouple extent;
private CaptureStaticProperties camProps;
private List<MatOfPoint> filteredContours = new ArrayList<>();
private List<Contour> filteredContours = new ArrayList<>();
public FilterContoursPipe(DoubleCouple area, DoubleCouple ratio, DoubleCouple extent, CaptureStaticProperties camProps) {
this.area = area;
@@ -36,34 +38,47 @@ public class FilterContoursPipe implements Pipe<List<MatOfPoint>, List<MatOfPoin
this.camProps = camProps;
}
private void filterContour(MatOfPoint contourPoints) {
Contour realContour = new Contour(contourPoints);
// Area Filtering
double contourArea = realContour.getArea();
double areaRatio = (contourArea / camProps.imageArea) * 100;
double minArea = (MathUtils.sigmoid(area.getFirst()));
double maxArea = (MathUtils.sigmoid(area.getSecond()));
if (areaRatio < minArea || areaRatio > maxArea) {
return;
}
// TargetFillPercentage filtering
RotatedRect minAreaRect = realContour.getMinAreaRect();
double minExtent = (extent.getFirst() * minAreaRect.size.area()) / 100;
double maxExtent = (extent.getSecond() * minAreaRect.size.area()) / 100;
if (contourArea <= minExtent || contourArea >= maxExtent) {
return;
}
// AspectRatio filtering
Rect boundingRect = realContour.getBoundingRect();
double aspectRatio = ((double)boundingRect.width / boundingRect.height);
if (aspectRatio < ratio.getFirst() || aspectRatio > ratio.getSecond()) {
return;
}
filteredContours.add(realContour);
}
@Override
public Pair<List<MatOfPoint>, Long> run(List<MatOfPoint> input) {
public Pair<List<Contour>, Long> run(List<MatOfPoint> input) {
long processStartNanos = System.nanoTime();
filteredContours.clear();
if (input.size() > 0) {
for (MatOfPoint Contour : input) {
for (MatOfPoint contour : input) {
try {
double contourArea = Imgproc.contourArea(Contour);
double AreaRatio = (contourArea / camProps.imageArea) * 100;
double minArea = (MathUtils.sigmoid(area.getFirst()));
double maxArea = (MathUtils.sigmoid(area.getFirst()));
if (AreaRatio < minArea || AreaRatio > maxArea) {
continue;
}
var rect = Imgproc.minAreaRect(new MatOfPoint2f(Contour.toArray()));
double minExtent = (extent.getFirst() * rect.size.area()) / 100;
double maxExtent = (extent.getSecond() * rect.size.area()) / 100;
if (contourArea <= minExtent || contourArea >= maxExtent) {
continue;
}
Rect bb = Imgproc.boundingRect(Contour);
double aspectRatio = ((double)bb.width / bb.height);
if (aspectRatio < ratio.getFirst() || aspectRatio > ratio.getSecond()) {
continue;
}
filteredContours.add(Contour);
filterContour(contour);
} catch (Exception e) {
System.err.println("Error while filtering contours");
e.printStackTrace();

View File

@@ -1,6 +1,6 @@
package com.chameleonvision.common.util.numbers;
public class NumberCouple<T extends Number> {
public abstract class NumberCouple<T extends Number> {
private T first;
private T second;
@@ -30,4 +30,26 @@ public class NumberCouple<T extends Number> {
this.first = first;
this.second = second;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NumberCouple)) {
return false;
}
var couple = (NumberCouple) obj;
if (!couple.first.equals(first)) {
return false;
}
if (!couple.second.equals(second)) {
return false;
}
return true;
}
public boolean isEmpty() {
return first.intValue() == 0 && second.intValue() == 0;
}
}

View File

@@ -1,6 +0,0 @@
package com.chameleonvision.common.vision.base.camera;
import com.chameleonvision.common.vision.base.frame.provider.USBFrameProvider;
public class USBCamera extends USBFrameProvider {
}

View File

@@ -1,10 +0,0 @@
package com.chameleonvision.common.vision.base.frame;
import org.opencv.core.Mat;
import org.opencv.core.Size;
public class Frame {
public long timestamp;
public Mat image;
public Size imageSize;
}

View File

@@ -1,31 +0,0 @@
package com.chameleonvision.common.vision.base.pipeline.pipe;
import com.chameleonvision.common.vision.base.pipeline.CVPipe;
import com.chameleonvision.common.vision.base.pipeline.pipe.params.ResizeImageParams;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
/**
* Pipe that resizes an image to a given resolution
*/
public class ResizeImagePipe extends CVPipe<Mat, Mat, ResizeImageParams> {
public ResizeImagePipe() {
setParams(ResizeImageParams.DEFAULT);
}
public ResizeImagePipe(ResizeImageParams params) {
setParams(params);
}
/**
* Process this pipe
* @param in {@link Mat} to be resized
* @return Resized {@link Mat}
*/
@Override
protected Mat process(Mat in) {
Imgproc.resize(in, in, params.getSize());
return in;
}
}

View File

@@ -1,31 +0,0 @@
package com.chameleonvision.common.vision.base.pipeline.pipe;
import com.chameleonvision.common.vision.base.pipeline.CVPipe;
import com.chameleonvision.common.vision.base.pipeline.pipe.params.RotateImageParams;
import org.opencv.core.Core;
import org.opencv.core.Mat;
/**
* Pipe that rotates an image to a given orientation
*/
public class RotateImagePipe extends CVPipe<Mat, Mat, RotateImageParams> {
public RotateImagePipe() {
setParams(RotateImageParams.DEFAULT);
}
public RotateImagePipe(RotateImageParams params) {
setParams(params);
}
/**
* Process this pipe
* @param in {@link Mat} to be rotated
* @return Rotated {@link Mat}
*/
@Override
protected Mat process(Mat in) {
Core.rotate(in, in, params.rotation.value);
return in;
}
}

View File

@@ -1,26 +0,0 @@
package com.chameleonvision.common.vision.base.pipeline.pipe.params;
import org.opencv.core.Size;
public class ResizeImageParams {
public static ResizeImageParams DEFAULT = new ResizeImageParams(320, 240);
private Size size;
public int width;
public int height;
public ResizeImageParams() {
this(DEFAULT.width, DEFAULT.height);
}
public ResizeImageParams(int width, int height) {
this.width = width;
this.height = height;
size = new Size(new double[]{width, height});
}
public Size getSize() {
return size;
}
}

View File

@@ -1,33 +0,0 @@
package com.chameleonvision.common.vision.base.pipeline.pipe.params;
public class RotateImageParams {
public static RotateImageParams DEFAULT = new RotateImageParams(ImageRotation.DEG_0);
public ImageRotation rotation;
public RotateImageParams() {
rotation = DEFAULT.rotation;
}
public RotateImageParams(ImageRotation rotation) {
this.rotation = rotation;
}
public enum ImageRotation {
DEG_0(-1),
DEG_90(0),
DEG_180(1),
DEG_270(2);
public final int value;
ImageRotation(int value) {
this.value = value;
}
public boolean isRotated() {
return this.value==DEG_90.value || this.value==DEG_270.value;
}
}
}

View File

@@ -0,0 +1,6 @@
package com.chameleonvision.common.vision.camera;
import com.chameleonvision.common.vision.frame.provider.USBFrameProvider;
public class USBCamera extends USBFrameProvider {
}

View File

@@ -0,0 +1,19 @@
package com.chameleonvision.common.vision.frame;
import org.opencv.core.Mat;
import org.opencv.core.Size;
public class Frame {
public long timestampNanos;
public Mat image;
public Frame(Mat image) {
this.image = image;
timestampNanos = System.nanoTime();
}
public Frame(Mat image, long timestampNanos) {
this.image = image;
this.timestampNanos = timestampNanos;
}
}

View File

@@ -1,4 +1,4 @@
package com.chameleonvision.common.vision.base.frame;
package com.chameleonvision.common.vision.frame;
public interface FrameConsumer {
void consume(Frame frame);

View File

@@ -1,4 +1,4 @@
package com.chameleonvision.common.vision.base.frame;
package com.chameleonvision.common.vision.frame;
public interface FrameProvider {
Frame getFrame();

View File

@@ -1,7 +1,7 @@
package com.chameleonvision.common.vision.base.frame.consumer;
package com.chameleonvision.common.vision.frame.consumer;
import com.chameleonvision.common.vision.base.frame.Frame;
import com.chameleonvision.common.vision.base.frame.FrameConsumer;
import com.chameleonvision.common.vision.frame.Frame;
import com.chameleonvision.common.vision.frame.FrameConsumer;
import org.apache.commons.lang3.NotImplementedException;
public class MJPGFrameConsumer implements FrameConsumer {

View File

@@ -1,7 +1,7 @@
package com.chameleonvision.common.vision.base.frame.provider;
package com.chameleonvision.common.vision.frame.provider;
import com.chameleonvision.common.vision.base.frame.Frame;
import com.chameleonvision.common.vision.base.frame.FrameProvider;
import com.chameleonvision.common.vision.frame.Frame;
import com.chameleonvision.common.vision.frame.FrameProvider;
import org.apache.commons.lang3.NotImplementedException;
public class FileFrameProvider implements FrameProvider {

View File

@@ -1,7 +1,7 @@
package com.chameleonvision.common.vision.base.frame.provider;
package com.chameleonvision.common.vision.frame.provider;
import com.chameleonvision.common.vision.base.frame.Frame;
import com.chameleonvision.common.vision.base.frame.FrameProvider;
import com.chameleonvision.common.vision.frame.Frame;
import com.chameleonvision.common.vision.frame.FrameProvider;
import org.apache.commons.lang3.NotImplementedException;
public class NetworkFrameProvider implements FrameProvider {

View File

@@ -1,7 +1,7 @@
package com.chameleonvision.common.vision.base.frame.provider;
package com.chameleonvision.common.vision.frame.provider;
import com.chameleonvision.common.vision.base.frame.Frame;
import com.chameleonvision.common.vision.base.frame.FrameProvider;
import com.chameleonvision.common.vision.frame.Frame;
import com.chameleonvision.common.vision.frame.FrameProvider;
import org.apache.commons.lang3.NotImplementedException;
public class USBFrameProvider implements FrameProvider {

View File

@@ -0,0 +1,44 @@
package com.chameleonvision.common.vision.opencv;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
public class Contour {
private final MatOfPoint points;
private Double area = Double.NaN;
private RotatedRect minAreaRect = null;
private Rect boundingRect = null;
public Contour(MatOfPoint points) {
this.points = points;
}
public double getArea() {
if (Double.isNaN(area)) {
area = Imgproc.contourArea(points);
}
return area;
}
public RotatedRect getMinAreaRect() {
if (minAreaRect == null) {
MatOfPoint2f temp = new MatOfPoint2f(points.toArray());
minAreaRect = Imgproc.minAreaRect(temp);
temp.release();
}
return minAreaRect;
}
public Rect getBoundingRect() {
if (boundingRect == null) {
boundingRect = Imgproc.boundingRect(points);
}
return boundingRect;
}
public Point getCenterPoint() {
return getMinAreaRect().center;
}
}

View File

@@ -1,4 +1,4 @@
package com.chameleonvision.common.vision.base.pipeline;
package com.chameleonvision.common.vision.pipeline;
import java.util.function.Function;
@@ -8,6 +8,7 @@ import java.util.function.Function;
*
* @param <I> Input type for the pipe
* @param <O> Output type for the pipe
* @param <P> Parameters type for the pipe
*/
public abstract class CVPipe<I, O, P> implements Function<I, PipeResult<O>> {

View File

@@ -1,7 +1,7 @@
package com.chameleonvision.common.vision.base.pipeline;
package com.chameleonvision.common.vision.pipeline;
import com.chameleonvision.common.vision.base.pipeline.pipe.ResizeImagePipe;
import com.chameleonvision.common.vision.base.pipeline.pipe.RotateImagePipe;
import com.chameleonvision.common.vision.pipeline.pipe.ResizeImagePipe;
import com.chameleonvision.common.vision.pipeline.pipe.RotateImagePipe;
import edu.wpi.cscore.CameraServerCvJNI;
import org.opencv.core.CvType;
import org.opencv.core.Mat;

View File

@@ -1,4 +1,4 @@
package com.chameleonvision.common.vision.base.pipeline;
package com.chameleonvision.common.vision.pipeline;
public class PipeResult<O> {
O result;

View File

@@ -0,0 +1,53 @@
package com.chameleonvision.common.vision.pipeline.pipe;
import com.chameleonvision.common.vision.pipeline.CVPipe;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
/**
* Pipe that resizes an image to a given resolution
*/
public class ResizeImagePipe extends CVPipe<Mat, Mat, ResizeImagePipe.ResizeImageParams> {
public ResizeImagePipe() {
setParams(ResizeImageParams.DEFAULT);
}
public ResizeImagePipe(ResizeImageParams params) {
setParams(params);
}
/**
* Process this pipe
* @param in {@link Mat} to be resized
* @return Resized {@link Mat}
*/
@Override
protected Mat process(Mat in) {
Imgproc.resize(in, in, params.getSize());
return in;
}
public static class ResizeImageParams {
public static ResizeImageParams DEFAULT = new ResizeImageParams(320, 240);
private Size size;
public int width;
public int height;
public ResizeImageParams() {
this(DEFAULT.width, DEFAULT.height);
}
public ResizeImageParams(int width, int height) {
this.width = width;
this.height = height;
size = new Size(new double[]{width, height});
}
public Size getSize() {
return size;
}
}
}

View File

@@ -0,0 +1,61 @@
package com.chameleonvision.common.vision.pipeline.pipe;
import com.chameleonvision.common.vision.pipeline.CVPipe;
import org.opencv.core.Core;
import org.opencv.core.Mat;
/**
* Pipe that rotates an image to a given orientation
*/
public class RotateImagePipe extends CVPipe<Mat, Mat, RotateImagePipe.RotateImageParams> {
public RotateImagePipe() {
setParams(RotateImageParams.DEFAULT);
}
public RotateImagePipe(RotateImageParams params) {
setParams(params);
}
/**
* Process this pipe
* @param in {@link Mat} to be rotated
* @return Rotated {@link Mat}
*/
@Override
protected Mat process(Mat in) {
Core.rotate(in, in, params.rotation.value);
return in;
}
public static class RotateImageParams {
public static RotateImageParams DEFAULT = new RotateImageParams(ImageRotation.DEG_0);
public ImageRotation rotation;
public RotateImageParams() {
rotation = DEFAULT.rotation;
}
public RotateImageParams(ImageRotation rotation) {
this.rotation = rotation;
}
public enum ImageRotation {
DEG_0(-1),
DEG_90(0),
DEG_180(1),
DEG_270(2);
public final int value;
ImageRotation(int value) {
this.value = value;
}
public boolean isRotated() {
return this.value==DEG_90.value || this.value==DEG_270.value;
}
}
}
}

View File

@@ -1,4 +1,116 @@
package com.chameleonvision.common.vision.target;
import com.chameleonvision.common.util.numbers.DoubleCouple;
import com.chameleonvision.common.vision.opencv.Contour;
import org.apache.commons.math3.util.FastMath;
import org.opencv.core.Point;
import org.opencv.core.RotatedRect;
import java.util.List;
// TODO: banks fix
public class TrackedTarget {
// final Contour externalContour;
//
// private Point targetOffsetPoint = null;
// private Point robotOffsetPoint = null;
//
// // Single Grouped
// public TrackedTarget(Contour inputContour) {
// this.externalContour = inputContour;
//
// }
//
// public TrackedTarget(List<Contour> subContours) {
//
// }
//
//
// private Point calculateOffsetPoint(boolean isLandscape, TargetOffsetPointRegion offsetRegion) {
// Point[] vertices = new Point[4];
//
// RotatedRect minRect = externalContour.getMinAreaRect();
// minRect.points(vertices);
//
// Point bl = getMiddle(vertices[0], vertices[1]);
// Point tl = getMiddle(vertices[1], vertices[2]);
// Point tr = getMiddle(vertices[2], vertices[3]);
// Point br = getMiddle(vertices[3], vertices[0]);
// boolean orientation;
// if (isLandscape) {
// orientation = minRect.size.width > minRect.size.height;
// } else {
// orientation = minRect.size.width < minRect.size.height;
// }
//
// Point result = minRect.center;
// switch (offsetRegion) {
// case Top: {
// result = orientation ? tl : tr;
// break;
// }
// case Bottom: {
// result = orientation ? br : bl;
// break;
// }
// case Left: {
// result = orientation ? bl : tl;
// break;
// }
// case Right: {
// result = orientation ? tr : br;
// break;
// }
// }
// return result;
// }
//
// public Point getTargetOffsetPoint(boolean isLandscape, TargetOffsetPointRegion offsetRegion) {
// if (targetOffsetPoint == null) {
// targetOffsetPoint = calculateOffsetPoint(isLandscape, offsetRegion);
// }
// return targetOffsetPoint;
// }
//
// private Point calculateRobotOffsetPoint(DoubleCouple offsetPoint, DoubleCouple offsetEquationValues, RobotOffsetPointMode offsetMode) {
// switch (offsetMode) {
// case Single:
// if (offsetPoint.isEmpty()) {
// offsetPoint.set(camProps.centerX, camProps.centerY);
// }
//
// t.calibratedX = offsetPoint.getFirst();
// t.calibratedY = offsetPoint.getSecond();
// break;
// case None:
// t.calibratedX = camProps.centerX;
// t.calibratedY = camProps.centerY;
// break;
// case Dual:
// t.calibratedX = (t.point.x - this.calibrationB) / this.calibrationM;
// t.calibratedY = (t.point.y * this.calibrationM) + this.calibrationB;
// break;
// }
// }
//
// private double calculatePitch(double pixelY, double centerY, double verticalFocalLength) {
// double pitch = FastMath.toDegrees(FastMath.atan((pixelY - centerY) / verticalFocalLength));
// return (pitch * -1);
// }
//
// private double calculateYaw(double pixelX, double centerX, double horizontalFocalLength) {
// return FastMath.toDegrees(FastMath.atan((pixelX - centerX) / horizontalFocalLength));
// }
//
// private Point getMiddle(Point p1, Point p2) {
// return new Point(((p1.x + p2.x) / 2), ((p1.y + p2.y) / 2));
// }
//
// public enum TargetOffsetPointRegion {
// Center, Top, Bottom, Left, Right
// }
//
// public enum RobotOffsetPointMode {
// None, Single, Dual
// }
}