mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-21 01:01:41 +00:00
Major pipeline optimizations, moved grabFrame in to vision thread
This commit is contained in:
@@ -11,6 +11,7 @@ import com.google.gson.GsonBuilder;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.networktables.*;
|
||||
import edu.wpi.first.wpiutil.CircularBuffer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -21,7 +22,6 @@ public class VisionProcess {
|
||||
|
||||
private final CameraCapture cameraCapture;
|
||||
private final List<CVPipeline> pipelines = new ArrayList<>();
|
||||
private final CameraFrameRunnable cameraRunnable;
|
||||
private final CameraStreamerRunnable streamRunnable;
|
||||
private final VisionProcessRunnable visionRunnable;
|
||||
public final CameraStreamer cameraStreamer;
|
||||
@@ -32,13 +32,10 @@ public class VisionProcess {
|
||||
private final CVPipelineSettings driverModeSettings = new CVPipelineSettings();
|
||||
private CVPipeline driverModePipeline = new DriverVisionPipeline(driverModeSettings);
|
||||
|
||||
// shitty stuff
|
||||
private volatile Mat lastCameraFrame = new Mat();
|
||||
private volatile boolean hasUnprocessedFrame = true;
|
||||
private volatile CVPipelineResult lastPipelineResult;
|
||||
|
||||
// network table stuff
|
||||
private NetworkTable defaultTable;
|
||||
private final NetworkTable defaultTable;
|
||||
private NetworkTableEntry ntPipelineEntry;
|
||||
private NetworkTableEntry ntDriverModeEntry;
|
||||
private int ntDriveModeListenerID;
|
||||
@@ -57,14 +54,6 @@ public class VisionProcess {
|
||||
pipelines.add(new CVPipeline2d("New Pipeline"));
|
||||
setPipeline(0, false);
|
||||
|
||||
// Thread to grab frames from the camera
|
||||
// TODO: (HIGH) fix video modes!!!
|
||||
this.cameraRunnable = new CameraFrameRunnable(cameraCapture.getProperties().videoModes.get(0).fps);
|
||||
|
||||
lastPipelineResult = new DriverVisionPipeline.DriverPipelineResult(
|
||||
null, cameraRunnable.getFrame(new Mat()), 0
|
||||
);
|
||||
|
||||
// Thread to put frames on the dashboard
|
||||
this.cameraStreamer = new CameraStreamer(cameraCapture, name);
|
||||
this.streamRunnable = new CameraStreamerRunnable(30, cameraStreamer);
|
||||
@@ -77,18 +66,8 @@ public class VisionProcess {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
System.out.println("Starting NetworkTables");
|
||||
System.out.println("Starting NetworkTables.");
|
||||
initNT(defaultTable);
|
||||
|
||||
System.out.println("Starting camera thread.");
|
||||
new Thread(cameraRunnable).start();
|
||||
while (cameraRunnable.cameraFrame == null) {
|
||||
try {
|
||||
if (lastCameraFrame.cols() > 0) break;
|
||||
} catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
System.out.println("Starting vision thread.");
|
||||
new Thread(visionRunnable).start();
|
||||
System.out.println("Starting stream thread.");
|
||||
@@ -217,7 +196,7 @@ public class VisionProcess {
|
||||
|
||||
//noinspection unchecked
|
||||
List<CVPipeline2d.Target2d> targets = (List<CVPipeline2d.Target2d>) data.targets;
|
||||
ntTimeStampEntry.setDouble(data.processTime);
|
||||
ntTimeStampEntry.setDouble(data.imageTimestamp);
|
||||
ntPitchEntry.setDouble(targets.get(0).pitch);
|
||||
ntYawEntry.setDouble(targets.get(0).yaw);
|
||||
ntAreaEntry.setDouble(targets.get(0).area);
|
||||
@@ -231,12 +210,12 @@ public class VisionProcess {
|
||||
ntYawEntry.setDouble(0.0);
|
||||
ntAreaEntry.setDouble(0.0);
|
||||
ntTimeStampEntry.setDouble(0.0);
|
||||
ntAuxListEntry.setString("");
|
||||
}
|
||||
}
|
||||
|
||||
public void setVideoMode(VideoMode newMode) {
|
||||
cameraCapture.setVideoMode(newMode);
|
||||
cameraRunnable.updateCameraFPS(newMode.fps);
|
||||
cameraStreamer.setNewVideoMode(newMode);
|
||||
}
|
||||
|
||||
@@ -277,59 +256,6 @@ public class VisionProcess {
|
||||
return pipelines.get(pipelineIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* CameraFrameRunnable grabs images from the cameraProcess
|
||||
* at a specified loopTime
|
||||
*/
|
||||
protected class CameraFrameRunnable extends LoopingRunnable {
|
||||
private Mat cameraFrame;
|
||||
private long timestampMicros;
|
||||
|
||||
private final Object frameLock = new Object();
|
||||
|
||||
/**
|
||||
* CameraFrameRunnable grabs images from the cameraProcess
|
||||
* at a specified framerate
|
||||
* @param cameraFPS FPS of camera
|
||||
*/
|
||||
CameraFrameRunnable(int cameraFPS) {
|
||||
// add 2 FPS to allow for a bit of overhead
|
||||
// TODO: (low) test the effect of this
|
||||
super(1000L/(cameraFPS + 2));
|
||||
}
|
||||
|
||||
void updateCameraFPS(int newFPS) {
|
||||
super.loopTimeMs = 1000L / (newFPS + 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
|
||||
// Grab camera frames
|
||||
var camData = cameraCapture.getFrame();
|
||||
if (camData.getLeft().cols() > 0) {
|
||||
// System.out.println("grabbing frame");
|
||||
// synchronized (frameLock) {
|
||||
// cameraFrame = camData.getLeft();
|
||||
// }
|
||||
timestampMicros = camData.getRight();
|
||||
camData.getLeft().copyTo(lastCameraFrame);
|
||||
hasUnprocessedFrame = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Mat getFrame(Mat dst) {
|
||||
if (cameraFrame != null) {
|
||||
dst = cameraFrame;
|
||||
} else {
|
||||
System.out.println("no frame");
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* VisionProcessRunnable will process images as quickly as possible
|
||||
*/
|
||||
@@ -337,48 +263,36 @@ public class VisionProcess {
|
||||
|
||||
volatile Double fps = 0.0;
|
||||
private CircularBuffer fpsAveragingBuffer = new CircularBuffer(7);
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private CVPipelineResult result;
|
||||
private Mat streamBuffer = new Mat();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
var lastUpdateTimeNanos = System.nanoTime();
|
||||
while(!Thread.interrupted()) {
|
||||
// System.out.println("running vision process");
|
||||
|
||||
while(!hasUnprocessedFrame) {
|
||||
try {
|
||||
Thread.sleep(3);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// blocking call, will block until camera has a new frame.
|
||||
Pair<Mat, Long> camData = cameraCapture.getFrame();
|
||||
|
||||
lastCameraFrame.copyTo(streamBuffer); // = //cameraRunnable.getFrame(streamBuffer);
|
||||
hasUnprocessedFrame = false;
|
||||
|
||||
if (streamBuffer.cols() > 0 && streamBuffer.rows() > 0) {
|
||||
result = currentPipeline.runPipeline(streamBuffer);
|
||||
lastPipelineResult = result;
|
||||
Mat camFrame = camData.getLeft();
|
||||
if (camFrame.cols() > 0 && camFrame.rows() > 0) {
|
||||
CVPipelineResult result = currentPipeline.runPipeline(camFrame);
|
||||
|
||||
if (result != null) {
|
||||
result.setTimestamp(camData.getRight());
|
||||
lastPipelineResult = result;
|
||||
updateNetworkTableData(lastPipelineResult);
|
||||
updateUI(lastPipelineResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var deltaTimeNanos = lastUpdateTimeNanos - System.nanoTime();
|
||||
fpsAveragingBuffer.addFirst(1.0 / (deltaTimeNanos * 1E-09));
|
||||
lastUpdateTimeNanos = System.nanoTime();
|
||||
fps = getAverageFPS();
|
||||
|
||||
// TODO: (HIGH) do something with the result
|
||||
}
|
||||
}
|
||||
|
||||
public double getAverageFPS() {
|
||||
double getAverageFPS() {
|
||||
var temp = 0.0;
|
||||
for(int i = 0; i < 7; i++) {
|
||||
temp += fpsAveragingBuffer.get(i);
|
||||
@@ -389,31 +303,32 @@ public class VisionProcess {
|
||||
|
||||
}
|
||||
|
||||
private class CameraStreamerRunnable extends LoopingRunnable {
|
||||
private static class CameraStreamerRunnable extends LoopingRunnable {
|
||||
|
||||
public final CameraStreamer streamer;
|
||||
final CameraStreamer streamer;
|
||||
private Mat streamBuffer = new Mat();
|
||||
|
||||
private CameraStreamerRunnable(int cameraFPS, CameraStreamer streamer) {
|
||||
// add 2 FPS to allow for a bit of overhead
|
||||
// TODO: (low) test the effect of this
|
||||
super(1000L/(cameraFPS + 2));
|
||||
// super(1000L/(cameraFPS + 2));
|
||||
super(10L);
|
||||
this.streamer = streamer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process() {
|
||||
// System.out.println("running camera streamer");
|
||||
Mat latestMat = lastPipelineResult.outputMat; //visionRunnable.result;
|
||||
if (latestMat != null && latestMat.cols() > 0) {
|
||||
latestMat.copyTo(streamBuffer);
|
||||
streamer.runStream(streamBuffer);
|
||||
streamBuffer.release();
|
||||
// Mat latestMat = lastPipelineResult.outputMat; //visionRunnable.result;
|
||||
// if (latestMat != null && latestMat.cols() > 0) {
|
||||
// latestMat.copyTo(streamBuffer);
|
||||
// streamer.runStream(streamBuffer);
|
||||
// streamBuffer.release();
|
||||
// if (toStreamMat != null && toStreamMat.cols() > 0) {
|
||||
// } else {
|
||||
// System.out.println("fuuuuck");
|
||||
// }
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,8 @@ public class USBCameraCapture implements CameraCapture {
|
||||
@Override
|
||||
public Pair<Mat, Long> getFrame() {
|
||||
Long deltaTime;
|
||||
synchronized (cvSink) {
|
||||
deltaTime = cvSink.grabFrame(imageBuffer) * 1000L;
|
||||
}
|
||||
// TODO: Why multiply by 1000 here?
|
||||
deltaTime = cvSink.grabFrame(imageBuffer) * 1000L;
|
||||
return Pair.of(imageBuffer, deltaTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,11 +89,10 @@ public class CVPipeline2d extends CVPipeline<CVPipeline2dResult, CVPipeline2dSet
|
||||
if (cameraCapture == null) {
|
||||
throw new RuntimeException("Pipeline was not initialized before being run!");
|
||||
}
|
||||
if(inputMat.cols() <= 1) {
|
||||
if (inputMat.cols() <= 1) {
|
||||
throw new RuntimeException("Input Mat is empty!");
|
||||
}
|
||||
|
||||
|
||||
pipelineTimeStringBuilder = new StringBuilder();
|
||||
|
||||
inputMat.copyTo(rawCameraMat);
|
||||
|
||||
@@ -9,6 +9,7 @@ public abstract class CVPipelineResult<T> {
|
||||
public final boolean hasTarget;
|
||||
public final Mat outputMat;
|
||||
public final long processTime;
|
||||
public long imageTimestamp = 0;
|
||||
|
||||
public CVPipelineResult(List<T> targets, Mat outputMat, long processTime) {
|
||||
this.targets = targets;
|
||||
@@ -16,4 +17,8 @@ public abstract class CVPipelineResult<T> {
|
||||
this.outputMat = outputMat;
|
||||
this.processTime = processTime;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
imageTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class BlurPipe implements Pipe<Mat, Mat> {
|
||||
try {
|
||||
Imgproc.blur(processBuffer, processBuffer, new Size(blurSize, blurSize));
|
||||
processBuffer.copyTo(outputMat);
|
||||
processBuffer.release();
|
||||
} catch (CvException e) {
|
||||
System.err.println("(BlurPipe) Exception thrown by OpenCV: \n" + e.getMessage());
|
||||
}
|
||||
@@ -37,10 +38,7 @@ public class BlurPipe implements Pipe<Mat, Mat> {
|
||||
input.copyTo(outputMat);
|
||||
}
|
||||
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
Pair<Mat, Long> output = Pair.of(outputMat, processTime);
|
||||
processBuffer.release();
|
||||
return output;
|
||||
return Pair.of(outputMat, processTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,30 +43,32 @@ public class Collect2dTargetsPipe implements Pipe<List<RotatedRect>, List<CVPipe
|
||||
|
||||
targets.clear();
|
||||
|
||||
input.forEach(r -> {
|
||||
CVPipeline2d.Target2d t = new CVPipeline2d.Target2d();
|
||||
t.rawPoint = r;
|
||||
switch (calibrationMode) {
|
||||
case None:
|
||||
t.calibratedX = camProps.centerX;
|
||||
t.calibratedY = camProps.centerY;
|
||||
break;
|
||||
case Single:
|
||||
t.calibratedX = calibrationPoint.get(0).doubleValue();
|
||||
t.calibratedY = calibrationPoint.get(1).doubleValue();
|
||||
break;
|
||||
case Dual:
|
||||
t.calibratedX = (r.center.y - calibrationB) / calibrationM;
|
||||
t.calibratedY = (r.center.x * calibrationM) + calibrationB;
|
||||
break;
|
||||
if (input.size() > 0) {
|
||||
for (RotatedRect r : input) {
|
||||
CVPipeline2d.Target2d t = new CVPipeline2d.Target2d();
|
||||
t.rawPoint = r;
|
||||
switch (calibrationMode) {
|
||||
case None:
|
||||
t.calibratedX = camProps.centerX;
|
||||
t.calibratedY = camProps.centerY;
|
||||
break;
|
||||
case Single:
|
||||
t.calibratedX = calibrationPoint.get(0).doubleValue();
|
||||
t.calibratedY = calibrationPoint.get(1).doubleValue();
|
||||
break;
|
||||
case Dual:
|
||||
t.calibratedX = (r.center.y - calibrationB) / calibrationM;
|
||||
t.calibratedY = (r.center.x * calibrationM) + calibrationB;
|
||||
break;
|
||||
}
|
||||
|
||||
t.pitch = calculatePitch(r.center.y, t.calibratedY);
|
||||
t.yaw = calculateYaw(r.center.x, t.calibratedX);
|
||||
t.area = r.size.area();
|
||||
|
||||
targets.add(t);
|
||||
}
|
||||
|
||||
t.pitch = calculatePitch(r.center.y, t.calibratedY);
|
||||
t.yaw = calculateYaw(r.center.x, t.calibratedX);
|
||||
t.area = r.size.area();
|
||||
|
||||
targets.add(t);
|
||||
});
|
||||
}
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
return Pair.of(targets, processTime);
|
||||
|
||||
@@ -17,6 +17,7 @@ public class Draw2dContoursPipe implements Pipe<Pair<Mat, List<RotatedRect>>, Ma
|
||||
private final Draw2dContoursSettings settings;
|
||||
private CaptureStaticProperties camProps;
|
||||
|
||||
private Mat processBuffer = new Mat();
|
||||
private Mat outputMat = new Mat();
|
||||
|
||||
public Draw2dContoursPipe(Draw2dContoursSettings settings, CaptureStaticProperties camProps) {
|
||||
@@ -32,48 +33,51 @@ public class Draw2dContoursPipe implements Pipe<Pair<Mat, List<RotatedRect>>, Ma
|
||||
public Pair<Mat, Long> run(Pair<Mat, List<RotatedRect>> input) {
|
||||
long processStartNanos = System.nanoTime();
|
||||
|
||||
outputMat = input.getLeft();
|
||||
if (settings.showCrosshair || settings.showCentroid || settings.showMaximumBox || settings.showRotatedBox) {
|
||||
input.getLeft().copyTo(processBuffer);
|
||||
|
||||
if (input.getRight() != null && !input.getRight().isEmpty()) {
|
||||
for (RotatedRect r : input.getRight()) {
|
||||
if (r == null) continue;
|
||||
if (input.getRight().size() > 0) {
|
||||
for (RotatedRect r : input.getRight()) {
|
||||
if (r == null) continue;
|
||||
|
||||
List<MatOfPoint> drawnContour = new ArrayList<>();
|
||||
Point[] vertices = new Point[4];
|
||||
r.points(vertices);
|
||||
MatOfPoint contour = new MatOfPoint(vertices);
|
||||
drawnContour.add(contour);
|
||||
List<MatOfPoint> drawnContour = new ArrayList<>();
|
||||
Point[] vertices = new Point[4];
|
||||
r.points(vertices);
|
||||
MatOfPoint contour = new MatOfPoint(vertices);
|
||||
drawnContour.add(contour);
|
||||
|
||||
if (settings.showCentroid) {
|
||||
Imgproc.circle(outputMat, r.center, 3, Helpers.colorToScalar(settings.centroidColor));
|
||||
}
|
||||
if (settings.showCentroid) {
|
||||
Imgproc.circle(processBuffer, r.center, 3, Helpers.colorToScalar(settings.centroidColor));
|
||||
}
|
||||
|
||||
if (settings.showRotatedBox) {
|
||||
Imgproc.drawContours(outputMat, drawnContour, 0, Helpers.colorToScalar(settings.rotatedBoxColor), settings.boxOutlineSize);
|
||||
}
|
||||
if (settings.showRotatedBox) {
|
||||
Imgproc.drawContours(processBuffer, drawnContour, 0, Helpers.colorToScalar(settings.rotatedBoxColor), settings.boxOutlineSize);
|
||||
}
|
||||
|
||||
if (settings.showMaximumBox) {
|
||||
Rect box = Imgproc.boundingRect(contour);
|
||||
Imgproc.rectangle(outputMat, new Point(box.x, box.y), new Point((box.x + box.width), (box.y + box.height)), Helpers.colorToScalar(settings.maximumBoxColor), settings.boxOutlineSize);
|
||||
if (settings.showMaximumBox) {
|
||||
Rect box = Imgproc.boundingRect(contour);
|
||||
Imgproc.rectangle(processBuffer, new Point(box.x, box.y), new Point((box.x + box.width), (box.y + box.height)), Helpers.colorToScalar(settings.maximumBoxColor), settings.boxOutlineSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.showCrosshair) {
|
||||
Point xMax = new Point(camProps.centerX + 10, camProps.centerY);
|
||||
Point xMin = new Point(camProps.centerX - 10, camProps.centerY);
|
||||
Point yMax = new Point(camProps.centerX, camProps.centerY + 10);
|
||||
Point yMin = new Point(camProps.centerX, camProps.centerY - 10);
|
||||
if (outputMat != null && outputMat.cols() > 0) {
|
||||
Imgproc.line(outputMat, xMax, xMin, Helpers.colorToScalar(settings.crosshairColor), 2);
|
||||
Imgproc.line(outputMat, yMax, yMin, Helpers.colorToScalar(settings.crosshairColor), 2);
|
||||
if (settings.showCrosshair) {
|
||||
Point xMax = new Point(camProps.centerX + 10, camProps.centerY);
|
||||
Point xMin = new Point(camProps.centerX - 10, camProps.centerY);
|
||||
Point yMax = new Point(camProps.centerX, camProps.centerY + 10);
|
||||
Point yMin = new Point(camProps.centerX, camProps.centerY - 10);
|
||||
Imgproc.line(processBuffer, xMax, xMin, Helpers.colorToScalar(settings.crosshairColor), 2);
|
||||
Imgproc.line(processBuffer, yMax, yMin, Helpers.colorToScalar(settings.crosshairColor), 2);
|
||||
}
|
||||
|
||||
processBuffer.copyTo(outputMat);
|
||||
processBuffer.release();
|
||||
} else {
|
||||
input.getLeft().copyTo(outputMat);
|
||||
}
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
Pair<Mat, Long> output = Pair.of(outputMat, processTime);
|
||||
outputMat.release();
|
||||
return output;
|
||||
return Pair.of(outputMat, processTime);
|
||||
}
|
||||
|
||||
public static class Draw2dContoursSettings {
|
||||
|
||||
@@ -30,20 +30,24 @@ public class ErodeDilatePipe implements Pipe<Mat, Mat> {
|
||||
public Pair<Mat, Long> run(Mat input) {
|
||||
long processStartNanos = System.nanoTime();
|
||||
|
||||
input.copyTo(processBuffer);
|
||||
if (erode || dilate) {
|
||||
input.copyTo(processBuffer);
|
||||
|
||||
if (erode) {
|
||||
Imgproc.erode(processBuffer, processBuffer, kernel);
|
||||
}
|
||||
if (erode) {
|
||||
Imgproc.erode(processBuffer, processBuffer, kernel);
|
||||
}
|
||||
|
||||
if (dilate) {
|
||||
Imgproc.erode(processBuffer, processBuffer, kernel);
|
||||
if (dilate) {
|
||||
Imgproc.erode(processBuffer, processBuffer, kernel);
|
||||
}
|
||||
|
||||
processBuffer.copyTo(outputMat);
|
||||
processBuffer.release();
|
||||
} else {
|
||||
input.copyTo(outputMat);
|
||||
}
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
processBuffer.copyTo(outputMat);
|
||||
Pair<Mat, Long> output = Pair.of(outputMat, processTime);
|
||||
processBuffer.release();
|
||||
return output;
|
||||
return Pair.of(outputMat, processTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,30 +40,32 @@ public class FilterContoursPipe implements Pipe<List<MatOfPoint>, List<MatOfPoin
|
||||
|
||||
filteredContours.clear();
|
||||
|
||||
for (MatOfPoint Contour : input) {
|
||||
try {
|
||||
double contourArea = Imgproc.contourArea(Contour);
|
||||
double AreaRatio = (contourArea / camProps.imageArea) * 100;
|
||||
double minArea = (MathHandler.sigmoid(area.get(0)));
|
||||
double maxArea = (MathHandler.sigmoid(area.get(1)));
|
||||
if (AreaRatio < minArea || AreaRatio > maxArea) {
|
||||
continue;
|
||||
if (input.size() > 0) {
|
||||
for (MatOfPoint Contour : input) {
|
||||
try {
|
||||
double contourArea = Imgproc.contourArea(Contour);
|
||||
double AreaRatio = (contourArea / camProps.imageArea) * 100;
|
||||
double minArea = (MathHandler.sigmoid(area.get(0)));
|
||||
double maxArea = (MathHandler.sigmoid(area.get(1)));
|
||||
if (AreaRatio < minArea || AreaRatio > maxArea) {
|
||||
continue;
|
||||
}
|
||||
var rect = Imgproc.minAreaRect(new MatOfPoint2f(Contour.toArray()));
|
||||
double minExtent = (extent.get(0).doubleValue() * rect.size.area()) / 100;
|
||||
double maxExtent = (extent.get(1).doubleValue() * 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.get(0).doubleValue() || aspectRatio > ratio.get(1).doubleValue()) {
|
||||
continue;
|
||||
}
|
||||
filteredContours.add(Contour);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error while filtering contours");
|
||||
e.printStackTrace();
|
||||
}
|
||||
var rect = Imgproc.minAreaRect(new MatOfPoint2f(Contour.toArray()));
|
||||
double minExtent = (extent.get(0).doubleValue() * rect.size.area()) / 100;
|
||||
double maxExtent = (extent.get(1).doubleValue() * 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.get(0).doubleValue() || aspectRatio > ratio.get(1).doubleValue()) {
|
||||
continue;
|
||||
}
|
||||
filteredContours.add(Contour);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error while filtering contours");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,56 +41,59 @@ public class GroupContoursPipe implements Pipe<List<MatOfPoint>, List<RotatedRec
|
||||
|
||||
groupedContours.clear();
|
||||
|
||||
List<MatOfPoint> sorted = new ArrayList<>(input);
|
||||
sorted.sort(sortByMomentsX);
|
||||
if (input.size() > 0) {
|
||||
|
||||
Collections.reverse(sorted);
|
||||
List<MatOfPoint> sorted = new ArrayList<>(input);
|
||||
sorted.sort(sortByMomentsX);
|
||||
|
||||
switch (group) {
|
||||
case Single: {
|
||||
input.forEach(c -> {
|
||||
MatOfPoint2f contour = new MatOfPoint2f();
|
||||
contour.fromArray(c.toArray());
|
||||
if (contour.cols() != 0 && contour.rows() != 0) {
|
||||
RotatedRect rect = Imgproc.minAreaRect(contour);
|
||||
groupedContours.add(rect);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Dual: {
|
||||
for (var i = 0; i < input.size(); i++) {
|
||||
List<Point> finalContourList = new ArrayList<>(input.get(i).toList());
|
||||
|
||||
try {
|
||||
MatOfPoint firstContour = input.get(i);
|
||||
MatOfPoint secondContour = input.get(i + 1);
|
||||
|
||||
if (isIntersecting(firstContour, secondContour)) {
|
||||
finalContourList.addAll(secondContour.toList());
|
||||
} else {
|
||||
finalContourList.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
intersectMatA.release();
|
||||
intersectMatB.release();
|
||||
firstContour.release();
|
||||
secondContour.release();
|
||||
Collections.reverse(sorted);
|
||||
|
||||
switch (group) {
|
||||
case Single: {
|
||||
input.forEach(c -> {
|
||||
MatOfPoint2f contour = new MatOfPoint2f();
|
||||
contour.fromList(finalContourList);
|
||||
|
||||
contour.fromArray(c.toArray());
|
||||
if (contour.cols() != 0 && contour.rows() != 0) {
|
||||
RotatedRect rect = Imgproc.minAreaRect(contour);
|
||||
groupedContours.add(rect);
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
System.err.println("GroupContours: WTF");
|
||||
finalContourList.clear();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Dual: {
|
||||
for (var i = 0; i < input.size(); i++) {
|
||||
List<Point> finalContourList = new ArrayList<>(input.get(i).toList());
|
||||
|
||||
try {
|
||||
MatOfPoint firstContour = input.get(i);
|
||||
MatOfPoint secondContour = input.get(i + 1);
|
||||
|
||||
if (isIntersecting(firstContour, secondContour)) {
|
||||
finalContourList.addAll(secondContour.toList());
|
||||
} else {
|
||||
finalContourList.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
intersectMatA.release();
|
||||
intersectMatB.release();
|
||||
firstContour.release();
|
||||
secondContour.release();
|
||||
|
||||
MatOfPoint2f contour = new MatOfPoint2f();
|
||||
contour.fromList(finalContourList);
|
||||
|
||||
if (contour.cols() != 0 && contour.rows() != 0) {
|
||||
RotatedRect rect = Imgproc.minAreaRect(contour);
|
||||
groupedContours.add(rect);
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
System.err.println("GroupContours: WTF");
|
||||
finalContourList.clear();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,11 +38,11 @@ public class HsvPipe implements Pipe<Mat, Mat> {
|
||||
System.err.println("(HsvPipe) Exception thrown by OpenCV: \n" + e.getMessage());
|
||||
}
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
processBuffer.copyTo(outputMat);
|
||||
Pair<Mat, Long> output = Pair.of(outputMat, processTime);
|
||||
processBuffer.release();
|
||||
return output;
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
return Pair.of(outputMat, processTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,15 +28,27 @@ public class RotateFlipPipe implements Pipe<Mat, Mat> {
|
||||
public Pair<Mat, Long> run(Mat input) {
|
||||
long processStartNanos = System.nanoTime();
|
||||
|
||||
input.copyTo(processBuffer);
|
||||
boolean shouldFlip = !flip.equals(ImageFlipMode.NONE);
|
||||
boolean shouldRotate = !rotation.equals(ImageRotation.DEG_0);
|
||||
|
||||
Core.flip(processBuffer, processBuffer, flip.value);
|
||||
Core.rotate(processBuffer, processBuffer, rotation.value);
|
||||
if (shouldFlip || shouldRotate) {
|
||||
input.copyTo(processBuffer);
|
||||
|
||||
if (shouldFlip) {
|
||||
Core.flip(processBuffer, processBuffer, flip.value);
|
||||
}
|
||||
|
||||
if (shouldRotate) {
|
||||
Core.rotate(processBuffer, processBuffer, rotation.value);
|
||||
}
|
||||
|
||||
processBuffer.copyTo(outputMat);
|
||||
processBuffer.release();
|
||||
} else {
|
||||
input.copyTo(outputMat);
|
||||
}
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
processBuffer.copyTo(outputMat);
|
||||
Pair<Mat, Long> output = Pair.of(outputMat, processTime);
|
||||
processBuffer.release();
|
||||
return output;
|
||||
return Pair.of(outputMat, processTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,32 +44,35 @@ public class SortContoursPipe implements Pipe<List<RotatedRect>, List<RotatedRec
|
||||
long processStartNanos = System.nanoTime();
|
||||
|
||||
sortedContours.clear();
|
||||
sortedContours.addAll(input);
|
||||
|
||||
switch (sort) {
|
||||
case Largest:
|
||||
sortedContours.sort(SortByLargestComparator);
|
||||
break;
|
||||
case Smallest:
|
||||
sortedContours.sort(SortBySmallestComparator);
|
||||
break;
|
||||
case Highest:
|
||||
sortedContours.sort(SortByHighestComparator);
|
||||
break;
|
||||
case Lowest:
|
||||
sortedContours.sort(SortByLowestComparator);
|
||||
break;
|
||||
case Leftmost:
|
||||
sortedContours.sort(SortByLeftmostComparator);
|
||||
break;
|
||||
case Rightmost:
|
||||
sortedContours.sort(SortByRightmostComparator);
|
||||
break;
|
||||
case Centermost:
|
||||
sortedContours.sort(SortByCentermostComparator);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (input.size() > 0) {
|
||||
sortedContours.addAll(input);
|
||||
|
||||
switch (sort) {
|
||||
case Largest:
|
||||
sortedContours.sort(SortByLargestComparator);
|
||||
break;
|
||||
case Smallest:
|
||||
sortedContours.sort(SortBySmallestComparator);
|
||||
break;
|
||||
case Highest:
|
||||
sortedContours.sort(SortByHighestComparator);
|
||||
break;
|
||||
case Lowest:
|
||||
sortedContours.sort(SortByLowestComparator);
|
||||
break;
|
||||
case Leftmost:
|
||||
sortedContours.sort(SortByLeftmostComparator);
|
||||
break;
|
||||
case Rightmost:
|
||||
sortedContours.sort(SortByRightmostComparator);
|
||||
break;
|
||||
case Centermost:
|
||||
sortedContours.sort(SortByCentermostComparator);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
long processTime = System.nanoTime() - processStartNanos;
|
||||
|
||||
@@ -27,19 +27,21 @@ public class SpeckleRejectPipe implements Pipe<List<MatOfPoint>, List<MatOfPoint
|
||||
|
||||
despeckledContours.clear();
|
||||
|
||||
double averageArea = 0.0;
|
||||
if (input.size() > 0) {
|
||||
double averageArea = 0.0;
|
||||
|
||||
for (MatOfPoint c : input) {
|
||||
averageArea += Imgproc.contourArea(c);
|
||||
}
|
||||
for (MatOfPoint c : input) {
|
||||
averageArea += Imgproc.contourArea(c);
|
||||
}
|
||||
|
||||
averageArea /= input.size();
|
||||
averageArea /= input.size();
|
||||
|
||||
double minAllowedArea = minPercentOfAvg / 100.0 * averageArea;
|
||||
double minAllowedArea = minPercentOfAvg / 100.0 * averageArea;
|
||||
|
||||
for (MatOfPoint c : input) {
|
||||
if (Imgproc.contourArea(c) >= minAllowedArea) {
|
||||
despeckledContours.add(c);
|
||||
for (MatOfPoint c : input) {
|
||||
if (Imgproc.contourArea(c) >= minAllowedArea) {
|
||||
despeckledContours.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user