Skip to content
This repository was archived by the owner on Sep 9, 2019. It is now read-only.

Commit 1d3660d

Browse files
committed
Auto merge of #5 - lasarobotics:feature-blob, r=smo-key
Add blob detection and start on the FTC Resq beacon implementation More tweaks and improvements to come - the camera tester app is now fully functional!
2 parents 6724019 + fbc04e1 commit 1d3660d

File tree

14 files changed

+955
-129
lines changed

14 files changed

+955
-129
lines changed

ftc-cameratest/src/main/java/com/lasarobotics/tests/camera/CameraTestActivity.java

+58-25
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77

88
import org.lasarobotics.vision.android.Camera;
99
import org.lasarobotics.vision.android.Cameras;
10-
import org.lasarobotics.vision.image.Drawing;
11-
import org.lasarobotics.vision.util.FPS;
1210
import org.lasarobotics.vision.android.Util;
11+
import org.lasarobotics.vision.detection.ColorBlobDetector;
12+
import org.lasarobotics.vision.detection.Contour;
1313
import org.lasarobotics.vision.detection.ObjectDetection;
14-
import org.lasarobotics.vision.util.Color;
14+
import org.lasarobotics.vision.ftc.resq.Beacon;
15+
import org.lasarobotics.vision.image.Drawing;
16+
import org.lasarobotics.vision.util.FPS;
17+
import org.lasarobotics.vision.util.color.ColorGRAY;
18+
import org.lasarobotics.vision.util.color.ColorHSV;
19+
import org.lasarobotics.vision.util.color.ColorRGBA;
1520
import org.opencv.android.BaseLoaderCallback;
1621
import org.opencv.android.CameraBridgeViewBase;
1722
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
@@ -24,17 +29,17 @@
2429
import org.opencv.highgui.Highgui;
2530

2631
import java.io.File;
32+
import java.util.List;
2733

2834
public class CameraTestActivity extends Activity implements CvCameraViewListener2 {
2935

3036
private Mat mRgba; //RGBA scene image
3137
private Mat mGray; //Grayscale scene image
38+
private CameraBridgeViewBase mOpenCvCameraView;
3239

3340
private float focalLength; //Camera lens focal length
3441

35-
private CameraBridgeViewBase mOpenCvCameraView;
36-
37-
private ObjectDetection.ObjectAnalysis objectAnalysis;
42+
//private ObjectDetection.ObjectAnalysis objectAnalysis;
3843
private FPS fpsCounter;
3944

4045
private void initialize()
@@ -48,7 +53,7 @@ private void initialize()
4853

4954
//GET OBJECT IMAGE
5055
//Read the target image file
51-
String dir = Util.getDCIMDirectory();
56+
/*String dir = Util.getDCIMDirectory();
5257
File file = new File(dir + "/beacon.png");
5358
5459
if (!file.exists())
@@ -63,13 +68,13 @@ private void initialize()
6368
// print error and abort execution
6469
Log.e("CameraTester", "FAILED TO LOAD IMAGE FILE!");
6570
System.exit(1);
66-
}
71+
}*/
6772

6873
//ANALYZE OBJECT
69-
ObjectDetection detection = new ObjectDetection(ObjectDetection.FeatureDetectorType.GFTT,
70-
ObjectDetection.DescriptorExtractorType.BRIEF,
71-
ObjectDetection.DescriptorMatcherType.BRUTEFORCE_HAMMING);
72-
objectAnalysis = detection.analyzeObject(mTarget);
74+
//ObjectDetection detection = new ObjectDetection(ObjectDetection.FeatureDetectorType.GFTT,
75+
// ObjectDetection.DescriptorExtractorType.ORB,
76+
// ObjectDetection.DescriptorMatcherType.BRUTEFORCE_HAMMING);
77+
//objectAnalysis = detection.analyzeObject(mTarget);
7378

7479
//UPDATE COUNTER
7580
fpsCounter = new FPS();
@@ -83,7 +88,7 @@ public void onManagerConnected(int status) {
8388
{
8489
// OpenCV loaded successfully!
8590
// Load native library AFTER OpenCV initialization
86-
91+
8792
initialize();
8893

8994
mOpenCvCameraView.enableView();
@@ -135,9 +140,22 @@ public void onDestroy() {
135140
mOpenCvCameraView.disableView();
136141
}
137142

143+
private ColorBlobDetector detectorRed;
144+
private ColorBlobDetector detectorBlue;
145+
private static final ColorHSV colorRadius = new ColorHSV(50, 75, 127);
146+
147+
private static final ColorHSV lowerBoundRed = new ColorHSV( (int)(305 / 360.0 * 255.0), (int)(0.200 * 255.0), (int)(0.300 * 255.0));
148+
private static final ColorHSV upperBoundRed = new ColorHSV( (int)((360.0+5.0) / 360.0 * 255.0), 255 , 255);
149+
150+
private static final ColorHSV lowerBoundBlue = new ColorHSV((int)(187.0 / 360.0 * 255.0), (int)(0.750 * 255.0), (int)(0.750 * 255.0));
151+
private static final ColorHSV upperBoundBlue = new ColorHSV((int)(227.0 / 360.0 * 255.0), 255 , 255);
152+
138153
public void onCameraViewStarted(int width, int height) {
139154
mRgba = new Mat(height, width, CvType.CV_8UC4);
140155
mGray = new Mat(height, width, CvType.CV_8UC1);
156+
157+
detectorRed = new ColorBlobDetector(lowerBoundRed, upperBoundRed);
158+
detectorBlue = new ColorBlobDetector(lowerBoundBlue, upperBoundBlue);
141159
}
142160

143161
public void onCameraViewStopped() {
@@ -146,31 +164,46 @@ public void onCameraViewStopped() {
146164
}
147165

148166
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
149-
// input frame has RBGA format
167+
// input frame has RGBA format
150168
mRgba = inputFrame.rgba();
151169
mGray = inputFrame.gray();
152170

153171
fpsCounter.update();
154172

155-
ObjectDetection detection = new ObjectDetection(ObjectDetection.FeatureDetectorType.FAST_DYNAMIC,
156-
ObjectDetection.DescriptorExtractorType.BRIEF,
157-
ObjectDetection.DescriptorMatcherType.BRUTEFORCE_HAMMING);
173+
//ObjectDetection detection = new ObjectDetection(ObjectDetection.FeatureDetectorType.ORB,
174+
// ObjectDetection.DescriptorExtractorType.ORB,
175+
// ObjectDetection.DescriptorMatcherType.BRUTEFORCE_HAMMING);
158176

159177
try {
160-
ObjectDetection.SceneAnalysis sceneAnalysis = detection.analyzeScene(mGray, objectAnalysis, mRgba);
161-
ObjectDetection.drawKeypoints(mRgba, sceneAnalysis);
162-
ObjectDetection.drawDebugInfo(mRgba, sceneAnalysis);
163-
ObjectDetection.drawObjectLocation(mRgba, objectAnalysis, sceneAnalysis);
178+
//ObjectDetection.SceneAnalysis sceneAnalysis = detection.analyzeScene(mGray, objectAnalysis, mRgba);
179+
//ObjectDetection.drawKeypoints(mRgba, sceneAnalysis);
180+
//ObjectDetection.drawDebugInfo(mRgba, sceneAnalysis);
181+
//ObjectDetection.drawObjectLocation(mRgba, objectAnalysis, sceneAnalysis);
182+
183+
mRgba = inputFrame.rgba();
184+
185+
//Process the frame for the color blobs
186+
detectorRed.process(mRgba);
187+
detectorBlue.process(mRgba);
188+
189+
//Get the list of contours
190+
List<Contour> contoursRed = detectorRed.getContours();
191+
List<Contour> contoursBlue = detectorBlue.getContours();
192+
193+
Beacon.BeaconColorAnalysis colorAnalysis = Beacon.analyzeColor(contoursRed, contoursBlue, mRgba);
194+
195+
Drawing.drawContours(mRgba, contoursRed, new ColorRGBA(255, 0, 0), 3);
196+
Drawing.drawContours(mRgba, contoursBlue, new ColorRGBA(0, 0, 255), 3);
197+
Drawing.drawText(mRgba, colorAnalysis.getStateLeft().toString() + ", " + colorAnalysis.getStateRight().toString(),
198+
new Point(0, 8), 1.0f, new ColorGRAY(255), Drawing.Anchor.BOTTOMLEFT);
164199
}
165200
catch (Exception e)
166201
{
167-
Drawing.drawText(mRgba, "Analysis Error", new Point(0, 8), 1.0f, new Color("#F44336"), Drawing.Anchor.BOTTOMLEFT);
202+
Drawing.drawText(mRgba, "Analysis Error", new Point(0, 8), 1.0f, new ColorRGBA("#F44336"), Drawing.Anchor.BOTTOMLEFT);
168203
e.printStackTrace();
169204
}
170205

171-
Drawing.drawText(mRgba, "FPS: " + fpsCounter.getFPSString(), new Point(0, 24), 1.0f, new Color("#2196F3"));
172-
173-
//Features.highlightFeatures(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr());
206+
Drawing.drawText(mRgba, "FPS: " + fpsCounter.getFPSString(), new Point(0, 24), 1.0f, new ColorRGBA("#2196F3"));
174207

175208
return mRgba;
176209
}

ftc-visionlib-java/src/main/java/org/lasarobotics/vision/detection/BlobDetection.java

-8
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.lasarobotics.vision.detection;
2+
3+
/**
4+
* Created by arthur on 10/9/15.
5+
*/
6+
public class CascadeObjectDetection {
7+
public CascadeObjectDetection() {
8+
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package org.lasarobotics.vision.detection;
2+
3+
import org.lasarobotics.vision.image.Drawing;
4+
import org.lasarobotics.vision.util.color.Color;
5+
import org.lasarobotics.vision.util.color.ColorHSV;
6+
import org.lasarobotics.vision.util.color.ColorSpace;
7+
import org.opencv.core.Core;
8+
import org.opencv.core.Mat;
9+
import org.opencv.core.MatOfPoint;
10+
import org.opencv.core.Scalar;
11+
import org.opencv.imgproc.Imgproc;
12+
13+
import java.util.ArrayList;
14+
import java.util.Iterator;
15+
import java.util.List;
16+
17+
/**
18+
* Implements blob (regional) detection based on color
19+
*/
20+
public class ColorBlobDetector {
21+
22+
//Lower bound for range checking
23+
private ColorHSV lowerBound = new ColorHSV(0, 0, 0);
24+
//Upper bound for range checking
25+
private ColorHSV upperBound = new ColorHSV(0, 0, 0);
26+
//Minimum contour area in percent for contours filtering
27+
private static double minContourArea = 0.1;
28+
//Color radius for range checking
29+
private Scalar colorRadius = new Scalar(75, 75, 75, 0);
30+
//Currently selected color
31+
private Color color;
32+
//True if radius is set, false if lower and upper bound is set
33+
private boolean isRadiusSet = true;
34+
35+
private List<Contour> contours = new ArrayList<>();
36+
private double maxArea;
37+
38+
public ColorBlobDetector(Color color)
39+
{
40+
setColor(color);
41+
}
42+
public ColorBlobDetector(Color color, Color colorRadius)
43+
{
44+
this.colorRadius = colorRadius.convertColorScalar(ColorSpace.HSV);
45+
setColor(color);
46+
}
47+
public ColorBlobDetector(ColorHSV colorMinimum, ColorHSV colorMaximum)
48+
{
49+
setColorRadius(colorMinimum, colorMaximum);
50+
}
51+
52+
public void setColor(Color color)
53+
{
54+
if (color == null)
55+
throw new IllegalArgumentException("Color must not be null!");
56+
57+
if (isRadiusSet)
58+
return;
59+
60+
this.color = color;
61+
Scalar hsvColor = color.convertColorScalar(ColorSpace.HSV);
62+
63+
//calculate min and max hues
64+
double minH = (hsvColor.val[0] >= colorRadius.val[0]) ? hsvColor.val[0] - colorRadius.val[0] : 0;
65+
double maxH = (hsvColor.val[0] + colorRadius.val[0] <= 255) ? hsvColor.val[0] + colorRadius.val[0] : 255;
66+
67+
Scalar lowerBoundScalar = lowerBound.getScalar();
68+
Scalar upperBoundScalar = upperBound.getScalar();
69+
70+
lowerBoundScalar.val[0] = minH;
71+
upperBoundScalar.val[0] = maxH;
72+
73+
lowerBoundScalar.val[1] = hsvColor.val[1] - colorRadius.val[1];
74+
upperBoundScalar.val[1] = hsvColor.val[1] + colorRadius.val[1];
75+
76+
lowerBoundScalar.val[2] = hsvColor.val[2] - colorRadius.val[2];
77+
upperBoundScalar.val[2] = hsvColor.val[2] + colorRadius.val[2];
78+
79+
lowerBoundScalar.val[3] = 0;
80+
upperBoundScalar.val[3] = 255;
81+
82+
lowerBound = new ColorHSV(lowerBoundScalar);
83+
upperBound = new ColorHSV(upperBoundScalar);
84+
}
85+
86+
//TODO test this method - set a color radius in the contructor and solve for the true min and max bound
87+
public void setColorRadius(Color lowerBound, Color upperBound)
88+
{
89+
isRadiusSet = false;
90+
Scalar lower = lowerBound.convertColorScalar(ColorSpace.HSV);
91+
Scalar upper = upperBound.convertColorScalar(ColorSpace.HSV);
92+
93+
this.lowerBound = new ColorHSV(lower);
94+
this.upperBound = new ColorHSV(upper);
95+
}
96+
97+
public void setColorRadius(ColorHSV radius) {
98+
isRadiusSet = true;
99+
this.colorRadius = radius.getScalar();
100+
//Update the bounds again
101+
setColor(color);
102+
}
103+
104+
public void setMinContourArea(double area) {
105+
minContourArea = area;
106+
//Update the bounds again
107+
setColor(color);
108+
}
109+
110+
public void process(Mat rgbaImage) {
111+
Imgproc.pyrDown(rgbaImage, mPyrDownMat);
112+
Imgproc.pyrDown(mPyrDownMat, mPyrDownMat);
113+
114+
Imgproc.cvtColor(mPyrDownMat, mHsvMat, Imgproc.COLOR_RGB2HSV_FULL);
115+
116+
//Test whether we need two inRange operations (only if the hue crosses over 255)
117+
if (upperBound.getScalar().val[0] <= 255) {
118+
Core.inRange(mHsvMat, lowerBound.getScalar(), upperBound.getScalar(), mMask);
119+
} else
120+
{
121+
//We need two operations - we're going to OR the masks together
122+
Scalar lower = lowerBound.getScalar().clone();
123+
Scalar upper = upperBound.getScalar().clone();
124+
while (upper.val[0] > 255)
125+
upper.val[0] -= 255;
126+
double tmp = lower.val[0];
127+
lower.val[0] = 0;
128+
//Mask 1 - from 0 to n
129+
Core.inRange(mHsvMat, lower, upper, mMaskOne);
130+
//Mask 2 - from 255-n to 255
131+
lower.val[0] = tmp;
132+
upper.val[0] = 255;
133+
Core.inRange(mHsvMat, lower, upper, mMask);
134+
//OR the two masks
135+
Core.bitwise_or(mMaskOne, mMask, mMask);
136+
}
137+
138+
//Dilate (blur) the mask to decrease processing power
139+
Imgproc.dilate(mMask, mDilatedMask, new Mat());
140+
141+
List<MatOfPoint> contourListTemp = new ArrayList<>();
142+
143+
Imgproc.findContours(mDilatedMask, contourListTemp, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
144+
145+
// Find max contour area
146+
double maxArea = 0;
147+
List<Contour> contourList = new ArrayList<>();
148+
for (MatOfPoint c : contourListTemp) {
149+
Contour con = new Contour(c);
150+
contourList.add(con);
151+
double area = con.area();
152+
if (area > maxArea)
153+
maxArea = area;
154+
}
155+
this.maxArea = maxArea;
156+
157+
// Filter contours by area and resize to fit the original image size
158+
contours.clear();
159+
for (Contour c : contourList ) {
160+
if (Imgproc.contourArea(c) > minContourArea*maxArea) {
161+
Core.multiply(c, new Scalar(4,4), c);
162+
contours.add(new Contour(c));
163+
}
164+
}
165+
}
166+
167+
public double getContourMaxArea()
168+
{
169+
return maxArea;
170+
}
171+
172+
public void drawContours(Mat img, Color color)
173+
{
174+
Drawing.drawContours(img, contours, color);
175+
}
176+
public void drawContours(Mat img, Color color, int thickness)
177+
{
178+
Drawing.drawContours(img, contours, color, thickness);
179+
}
180+
181+
public List<Contour> getContours() {
182+
return contours;
183+
}
184+
185+
// Cache
186+
private Mat mPyrDownMat = new Mat();
187+
private Mat mHsvMat = new Mat();
188+
private Mat mMaskOne = new Mat();
189+
private Mat mMask = new Mat();
190+
private Mat mDilatedMask = new Mat();
191+
private Mat mHierarchy = new Mat();
192+
}

0 commit comments

Comments
 (0)