Files
PhotonVision/Main/src/main/java/com/chameleonvision/vision/process/CVProcess.java

211 lines
8.8 KiB
Java
Raw Normal View History

package com.chameleonvision.vision.process;
import com.chameleonvision.vision.camera.CameraValues;
import org.apache.commons.math3.util.FastMath;
import org.jetbrains.annotations.NotNull;
import org.opencv.core.*;
2019-09-22 02:49:30 -04:00
import org.opencv.imgproc.Imgproc;
import java.util.*;
@SuppressWarnings("WeakerAccess")
public class CVProcess {
private final CameraValues CamVals;
private HashMap<String, Integer> TargetGrouping = new HashMap<>() {{
put("Single", 1);
put("Dual", 2);
put("Triple", 3);
put("Quadruple", 4);
put("Quintuple", 5);
}};
private Mat Kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5));
private Mat hsvImage = new Mat();
private List<MatOfPoint> FoundContours = new ArrayList<>();
private Mat binaryMat = new Mat();
private List<MatOfPoint> FilteredContours = new ArrayList<>();
private Comparator<RotatedRect> SortByCentermostComparator = Comparator.comparingDouble(this::calcDistance);
private List<RotatedRect> FinalCountours = new ArrayList<>();
private Mat intersectMatA = new Mat();
private Mat intersectMatB = new Mat();
2019-09-22 02:49:30 -04:00
CVProcess(CameraValues camVals) {
CamVals = camVals;
}
2019-09-22 02:49:30 -04:00
void HSVThreshold(Mat srcImage, Mat dst, @NotNull Scalar hsvLower, @NotNull Scalar hsvUpper, boolean shouldErode, boolean shouldDilate) {
Imgproc.cvtColor(srcImage, hsvImage, Imgproc.COLOR_RGB2HSV, 3);
Core.inRange(hsvImage, hsvLower, hsvUpper, dst);
if (shouldErode) {
Imgproc.erode(dst, dst, Kernel);
}
if (shouldDilate) {
Imgproc.dilate(dst, dst, Kernel);
}
hsvImage.release();
}
2019-09-22 02:49:30 -04:00
List<MatOfPoint> FindContours(Mat src) {
src.copyTo(binaryMat);
FoundContours.clear();
Imgproc.findContours(binaryMat, FoundContours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_TC89_L1);
binaryMat.release();
return FoundContours;
}
2019-09-22 02:49:30 -04:00
2019-09-24 02:44:02 +03:00
List<MatOfPoint> FilterContours(List<MatOfPoint> InputContours, List<Integer> area, List<Double> ratio, List<Integer> extent) {
for (MatOfPoint Contour : InputContours) {
try {
2019-09-25 14:41:24 -04:00
double contourArea = Imgproc.contourArea(Contour);
double minAreaSlider = Math.log(area.get(0));
double minArea = (minAreaSlider * CamVals.ImageArea) / 100;
double maxArea = (area.get(1) * CamVals.ImageArea) / 100;
if (contourArea <= minArea || contourArea >= maxArea) {
continue;
}
var rect = Imgproc.minAreaRect(new MatOfPoint2f(Contour.toArray()));
2019-09-25 10:57:29 -07:00
var targetFullness = contourArea;
double minExtent = (double) (extent.get(0) * rect.size.area())/ 100;
double maxExtent = (double) (extent.get(1) * rect.size.area()) / 100;
2019-09-25 14:41:24 -04:00
if (targetFullness <= minExtent || contourArea >= maxExtent) {
continue;
}
2019-09-22 03:40:55 +03:00
double aspectRatio = rect.size.width / rect.size.height;//TODO i think aspectRatio is inverted
if (aspectRatio < ratio.get(0) || aspectRatio > ratio.get(1)) {
continue;
}
FilteredContours.add(Contour);
} catch (Exception e) {
2019-09-22 03:40:55 +03:00
System.err.println("Error while filtering contours");
e.printStackTrace();
}
}
return FilteredContours;
}
2019-09-22 02:49:30 -04:00
private double calcDistance(RotatedRect rect) {
return FastMath.sqrt(FastMath.pow(CamVals.CenterX - rect.center.x, 2) + FastMath.pow(CamVals.CenterY - rect.center.y, 2));
}
2019-09-22 02:49:30 -04:00
RotatedRect SortTargetsToOne(List<RotatedRect> inputRects, String sortMode) {
switch (sortMode) {
case "Largest":
return Collections.max(inputRects, Comparator.comparing(rect -> rect.size.area()));
case "Smallest":
return Collections.min(inputRects, Comparator.comparing(rect -> rect.size.area()));
case "Highest":
return Collections.min(inputRects, Comparator.comparing(rect -> rect.center.y));
case "Lowest":
return Collections.max(inputRects, Comparator.comparing(rect -> rect.center.y));
case "Leftmost":
return Collections.min(inputRects, Comparator.comparing(rect -> rect.center.x));
case "Rightmost":
return Collections.max(inputRects, Comparator.comparing(rect -> rect.center.x));
case "Centermost":
return Collections.min(inputRects, SortByCentermostComparator);
default:
return inputRects.get(0); // default to whatever the first contour is, but this should never happen
}
}
2019-09-22 02:49:30 -04:00
List<RotatedRect> GroupTargets(List<MatOfPoint> InputContours, String IntersectionPoint, String TargetGroup) {
FinalCountours.clear();
if (!TargetGroup.equals("Single")) {
for (var i = 0; i < InputContours.size(); i++) {
List<Point> FinalContourList = new ArrayList<>(InputContours.get(i).toList());
for (var c = 0; c < (TargetGrouping.get(TargetGroup) - 1); c++) {
try {
MatOfPoint firstContour = InputContours.get(i + c);
MatOfPoint secondContour = InputContours.get(i + c + 1);
if (IsIntersecting(firstContour, secondContour, IntersectionPoint)) {
FinalContourList.addAll(secondContour.toList());
}
2019-09-23 12:36:57 -07:00
else{
FinalContourList.clear();
break;
}
firstContour.release();
secondContour.release();
MatOfPoint2f contour = new MatOfPoint2f();
contour.fromList(FinalContourList);
if (contour.cols() != 0 && contour.rows() != 0) {
RotatedRect rect = Imgproc.minAreaRect(contour);
FinalCountours.add(rect);
}
} catch (IndexOutOfBoundsException e) {
FinalContourList.clear();
break;
}
}
}
2019-09-22 02:49:30 -04:00
} else {
for (MatOfPoint inputContour : InputContours) {
MatOfPoint2f contour = new MatOfPoint2f();
contour.fromArray(inputContour.toArray());
if (contour.cols() != 0 && contour.rows() != 0) {
RotatedRect rect = Imgproc.minAreaRect(contour);
FinalCountours.add(rect);
}
}
}
return FinalCountours;
}
2019-09-22 02:49:30 -04:00
private boolean IsIntersecting(MatOfPoint ContourOne, MatOfPoint ContourTwo, String IntersectionPoint) {
if (IntersectionPoint.equals("None")) {
return true;
}
try {
Imgproc.fitLine(ContourOne, intersectMatA, Imgproc.CV_DIST_L2, 0, 0.01, 0.01);
Imgproc.fitLine(ContourTwo, intersectMatB, Imgproc.CV_DIST_L2, 0, 0.01, 0.01);
double vxA = intersectMatA.get(0, 0)[0];
double vyA = intersectMatA.get(1, 0)[0];
double x0A = intersectMatA.get(2, 0)[0];
double y0A = intersectMatA.get(3, 0)[0];
double mA = vyA / vxA;
double vxB = intersectMatB.get(0, 0)[0];
double vyB = intersectMatB.get(1, 0)[0];
double x0B = intersectMatB.get(2, 0)[0];
double y0B = intersectMatB.get(3, 0)[0];
double mB = vyB / vxB;
2019-09-23 12:36:57 -07:00
double bA = y0A - (mA*x0A);
double bB = y0B - (mB*x0B);
double intersectionX = ((mA * x0A) - y0A - (mB * x0B) + y0B )/ (mA - mB);
double intersectionY = (mA * (intersectionX - x0A)) + y0A;
2019-09-23 12:36:57 -07:00
double massX = intersectionX + 1;
double massY = intersectionY + ((mA + bA + mB +bB) / 2);
switch (IntersectionPoint) {
case "Up": {
2019-09-23 12:36:57 -07:00
if (intersectionY < massY) {
return true;
}
break;
}
case "Down": {
2019-09-23 12:36:57 -07:00
if (intersectionY > massY) {
return true;
}
break;
}
case "Left": {
2019-09-23 12:36:57 -07:00
if (intersectionX < massX) {
return true;
}
break;
}
case "Right": {
2019-09-23 12:36:57 -07:00
if (intersectionX > massX) {
return true;
}
break;
}
}
return false;
} catch (Exception e) {
return false;
}
}
}