-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathTowerTracker.java
More file actions
308 lines (287 loc) · 10.4 KB
/
TowerTracker.java
File metadata and controls
308 lines (287 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import edu.wpi.first.wpilibj.networktables.NetworkTable;
/**
*
* @author Elijah Kaufman
* @version 1.0
* @description Uses opencv and network table 3.0 to detect the vision targets
*
*/
public class TowerTracker {
//This is not TJ code :(
//But it is modified by TJ :D
/**
* static method to load opencv and networkTables
*/
public static String ip = "192.168.1.157";
static{
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//NetworkTable.setClientMode();
//NetworkTable.setClientMode();
//NetworkTable.setIPAddress(ip);
}
// constants for the color RGB values
public static final Scalar
RED = new Scalar(0, 0, 255),
BLUE = new Scalar(255, 0, 0),
GREEN = new Scalar(0, 255, 0),
BLACK = new Scalar(0,0,0),
YELLOW = new Scalar(0, 255, 255),
// these are the threshold values in order
LOWER_BOUNDS = new Scalar(60,216,200),
UPPER_BOUNDS = new Scalar(180,255,255);
// the size for resizing the image
public static final Size resize = new Size(320,240);
// ignore these
public static VideoCapture videoCapture;
public static Mat matOriginal, matHSV, matThresh, clusters, matHeirarchy;
// Constants for known variables
// the height to the top of the target in first stronghold is 97 inches
public static final int TOP_TARGET_HEIGHT = 97;
// the physical height of the camera lens
public static final int TOP_CAMERA_HEIGHT = 31;
// projectile speed in mph
public static final double PROJECTILE_SPEED = 42;
// camera details, can usually be found on the datasheets of the camera
public static final double VERTICAL_FOV = 33.58;
public static final double HORIZONTAL_FOV = 59.78;
public static final double CAMERA_ANGLE = 42;
public static NetworkTable table;
static boolean networkAvailible = false;
static boolean production = false;
static int averageInterval = 5;
static boolean calibrate = false;
public static boolean shouldRun = true;
/**
*
* @param args command line arguments
* just the main loop for the program and the entry points
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
matOriginal = new Mat();
int camLocation = 0;
if(args.length > 0){
ip = args[0];
System.out.println("ip entered is " + ip);
}
if(args.length > 1){
camLocation = Integer.parseInt(args[1]);
System.out.println("camera location is " + args[1]);
}
if(args.length > 2)
{
if(args[2] == "1")
production = true;
if(args[2] == "2")
calibrate = true;
}
matHSV = new Mat();
matThresh = new Mat();
clusters = new Mat();
matHeirarchy = new Mat();
NetworkTable.setClientMode();
NetworkTable.setIPAddress(ip);
try
{
table = NetworkTable.getTable("datatable");
networkAvailible = true;
} catch(java.lang.UnsatisfiedLinkError exception)
{
System.out.println("WOOOPSI DERS AN ERROR: " + exception.getMessage());
networkAvailible = false;}
// main loop of the program
while(shouldRun){
try {
// opens up the camera stream and tries to load it
videoCapture = new VideoCapture(camLocation);
// replaces the ##.## with your team num
//videoCapture.set(Videoio.CAP_PROP_AUTO_EXPOSURE, 1);
//videoCapture.set(Videoio.CAP_PROP_EXPOSURE, 20);
videoCapture.open(camLocation);
// videoCapture.open("C:/Users/David/Pictures/Camera Roll/WIN_20160131_14_58_37_Pro.mp4");
// Example
// cap.open("http://10.30.19.11/mjpg/video.mjpg");
// wait until it is opened
while(!videoCapture.isOpened()){}
// time to actually process the acquired images
processImage();
} catch (Exception e) {
e.printStackTrace();
break;
}
}
// make sure the java process quits when the loop finishes
videoCapture.release();
System.exit(0);
}
/**
*
* reads an image from a live image capture and outputs information to the SmartDashboard or a file
*/
public static void processImage(){
ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
double y,targetX,distance,azimuth;// frame counter
int FrameCount = 0;
Date date1 = new Date();
//long before = System.currentTimeMillis();
// only run for the specified time
//int averageCounter = 0;
int goodValues = 0;
double averageAzimuth = 0, averageShootyAngle = 0, averageDistance = 0;
boolean frameStop = true;
while(frameStop){
Date date2 = new Date();
if(FrameCount >= 9 && !production && !calibrate)
{
frameStop = false;
}
if ( averageInterval != 0 && FrameCount % averageInterval == 0)
{
if (goodValues != 0){
double averageAzimuthOut = averageAzimuth / goodValues;
double averageShootyAngleOut = averageShootyAngle / goodValues;
double averageDistanceOut = averageDistance / goodValues;
System.out.println("Average Azimuth: " + averageAzimuthOut);
System.out.println("Average Shooty Angle: " + averageShootyAngleOut);
System.out.println("Average Distance: " + averageDistanceOut);
Imgcodecs.imwrite("output.png", matOriginal);
date1 = new Date();
if(networkAvailible)
{
table.putNumber("averageAzimuthOut", averageAzimuthOut);
table.putNumber("averageShootyAngleOut", averageShootyAngleOut);
table.putNumber("averageDistanceOut", averageDistanceOut);
}
}
goodValues = 0;
// try that thing later
averageAzimuth =0;
averageShootyAngle = 0;
averageDistance = 0;
}
contours.clear();
// capture from the axis camera
videoCapture.read(matOriginal);
// captures from a static file for testing
// matOriginal = Imgcodecs.imread("someFile.png");
Imgproc.cvtColor(matOriginal,matHSV,Imgproc.COLOR_BGR2HSV);
Core.inRange(matHSV, LOWER_BOUNDS, UPPER_BOUNDS, matThresh);
Imgproc.findContours(matThresh, contours, matHeirarchy, Imgproc.RETR_EXTERNAL,
Imgproc.CHAIN_APPROX_SIMPLE);
// make sure the contours that are detected are at least 20x20
// pixels with an area of 400 and an aspect ration greater then 1
for (Iterator<MatOfPoint> iterator = contours.iterator(); iterator.hasNext();) {
MatOfPoint matOfPoint = (MatOfPoint) iterator.next();
Rect rec = Imgproc.boundingRect(matOfPoint);
if(rec.height < 25 || rec.width < 25){
iterator.remove();
continue;
}
float aspect = (float)rec.width/(float)rec.height;
if(aspect < 1.0)
iterator.remove();
}
for(MatOfPoint mop : contours){
Rect rec = Imgproc.boundingRect(mop);
Imgproc.rectangle(matOriginal, rec.br(), rec.tl(), RED);
}
// if there is only 1 target, then we have found the target we want
if(contours.size() == 1){
Rect rec = Imgproc.boundingRect(contours.get(0));
// "fun" math brought to you by miss daisy (team 341)!
y = rec.br().y + rec.height / 2;
y= -((2 * (y / matOriginal.height())) - 1);
distance = (TOP_TARGET_HEIGHT - TOP_CAMERA_HEIGHT) /
Math.tan((y * VERTICAL_FOV / 2.0 + CAMERA_ANGLE) * Math.PI / 180);
// angle to target...would not rely on this
targetX = rec.tl().x + rec.width / 2;
targetX = (2 * (targetX / matOriginal.width())) - 1;
azimuth = normalize360(targetX*HORIZONTAL_FOV /2.0 + 0);
// drawing info on target
Point center = new Point(rec.br().x-rec.width / 2 - 15,rec.br().y - rec.height / 2);
Point centerw = new Point(rec.br().x-rec.width / 2 - 15,rec.br().y - rec.height / 2 - 20);
Imgproc.putText(matOriginal, ""+(int)distance, center, Core.FONT_HERSHEY_PLAIN, 1, RED);
Imgproc.putText(matOriginal, ""+(int)azimuth, centerw, Core.FONT_HERSHEY_PLAIN, 1, RED);
double shootyAngle = TowerTracker.GetShootyAngle(distance, (double)TOP_TARGET_HEIGHT, PROJECTILE_SPEED, 0);
//System.out.println("Angle needed to shoot:" + shootyAngle);
//System.out.println("Azimuth:" + azimuth);;
//System.out.println("Distance:" + distance);
// averaging
goodValues += 1;
averageShootyAngle =+ shootyAngle;
averageAzimuth += azimuth;
averageDistance += distance;
//if(networkAvailible)
//{
// table.putNumber("0", shootyAngle);
// table.putNumber("1", azimuth);
// table.putNumber("2", distance);
//}
}
table.putNumber("sinceLastUpdate", (date2.getTime() - date1.getTime()) / 1000);
// output an image for debugging
FrameCount++;
System.out.println("Framecount:" + FrameCount);
System.out.println((date2.getTime() - date1.getTime()) / 1000);
if ((int)((date2.getTime() - date1.getTime()) / 1000) % 5 == 0)
{
Imgcodecs.imwrite("output.png", matOriginal);
System.out.println("Image");
}
}
shouldRun = false;
}
/**
* @param angle a nonnormalized angle
*/
//NO LONGER DOES xbox 360, because XBones are better
public static double normalize360(double angle){
while(angle >= 180.0)
{
angle -= 360.0;
}
while(angle < -180.0)
{
angle += 360.0;
}
return angle;
}
//this is TJ code ;)
public static double GetShootyAngle(double distanceToTower, double altitude, double launchSpeed, double thinger)
{
//Returns -1 if the distance is no good
if (distanceToTower <= 0)
{
return -1;
}
//convert speed to inches per second
double inchesPerSecond = launchSpeed * 63360 / 3600;
//convert gravity from meters to inches
double gravityInches = 9.8 * 39.4;
//case 1
double case1 = Math.pow(inchesPerSecond, 4) - gravityInches * (gravityInches * distanceToTower * distanceToTower + 2 * altitude * inchesPerSecond * inchesPerSecond);
if (case1 < 0)
{
return -1;
}
double squareroot1 = Math.sqrt(case1);
double result1 = Math.toDegrees(Math.atan((inchesPerSecond * inchesPerSecond + squareroot1) / (gravityInches * distanceToTower)));
double result2 = Math.toDegrees(Math.atan((inchesPerSecond * inchesPerSecond - squareroot1) / (gravityInches * distanceToTower)));
System.out.println("ShootyAngle result1:" + result1);
System.out.println("ShootyAngle result2:" + result2);
return result2;
}
}