blob: ef29e36070a9241c8c5d00cd4ad32fef44fbfde2 [file] [log] [blame]
jerrymcb7a06a2013-02-17 22:32:29 +00001package org.frc971;
2
jerrymcd2c3322013-02-18 08:49:01 +00003import java.awt.BorderLayout;
4import java.awt.GridLayout;
jerrymcb7a06a2013-02-17 22:32:29 +00005import java.awt.event.KeyEvent;
6import java.io.File;
7import java.io.IOException;
8
9import javax.imageio.ImageIO;
jerrymcd2c3322013-02-18 08:49:01 +000010import javax.swing.JPanel;
11import javax.swing.JSlider;
jerrymcb7a06a2013-02-17 22:32:29 +000012import javax.swing.WindowConstants;
jerrymcd2c3322013-02-18 08:49:01 +000013import javax.swing.event.ChangeEvent;
14import javax.swing.event.ChangeListener;
jerrymcb7a06a2013-02-17 22:32:29 +000015
16import com.googlecode.javacv.CanvasFrame;
17
18import edu.wpi.first.wpijavacv.WPIColorImage;
jerrym6ebe6452013-02-18 03:00:31 +000019import edu.wpi.first.wpijavacv.WPIImage;
jerrymcb7a06a2013-02-17 22:32:29 +000020
21/* REQUIRED JAVA LIBRARIES:
jerrym38d8af12013-02-18 06:54:13 +000022 * external_jars/
23 * javacpp.jar
24 * javacv-YOUR_OS.jar
25 * javacv.jar
26 * WPIJavaCV.jar
jerrymcb7a06a2013-02-17 22:32:29 +000027 *
jerrym38d8af12013-02-18 06:54:13 +000028 * REQUIRED NATIVE CODE LIBRARIES ON $PATH:
29 * Program Files/WPIJavaCV/ [for example]
jerrymcb7a06a2013-02-17 22:32:29 +000030 * JavaCV_2.2.0/javacv-bin/javacv-YOUR_OS.jar
31 * OpenCV_2.2.0/bin/*
jerrym38d8af12013-02-18 06:54:13 +000032 *
33 * The native libraries and javacv-YOUR_OS.jar must match the 32 vs. 64-bit JVM.
jerrymcb7a06a2013-02-17 22:32:29 +000034 */
35/**
36 * FRC 2013 vision-target recognizer tuner app.
37 *
jerrymf96c32c2013-02-18 19:30:45 +000038 * <p>
39 * See {@link #processEvents()} for the keystroke commands.
40 *
jerrymcb7a06a2013-02-17 22:32:29 +000041 * @author jerry
42 */
43public class VisionTuner {
44 private String[] testImageFilenames;
45 private WPIColorImage[] testImages;
jerrymcb7a06a2013-02-17 22:32:29 +000046 private int currentIndex = 0;
jerrym6ebe6452013-02-18 03:00:31 +000047 private Recognizer recognizer = new Recognizer2013();
jerrymcb7a06a2013-02-17 22:32:29 +000048
jerrymcd2c3322013-02-18 08:49:01 +000049 private final CanvasFrame cameraFrame = new CanvasFrame("Camera");
50 private final JPanel panel = new JPanel();
51 private final JSlider hueMinSlider = new JSlider();
52 private final JSlider hueMaxSlider = new JSlider();
53 private final JSlider satMinSlider = new JSlider();
54 private final JSlider valMinSlider = new JSlider();
55
jerrymf0c84552013-02-19 00:51:20 +000056 private int totalFrames = -1; // don't count the first (warmup) frame
jerrymf96c32c2013-02-18 19:30:45 +000057 private double totalMsec;
58 private double minMsec = Double.MAX_VALUE;
59 private double maxMsec;
jerryma1cd68d2013-02-18 09:16:19 +000060
jerrymcb7a06a2013-02-17 22:32:29 +000061 public VisionTuner(String[] imageFilenames) {
62 cameraFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jerrymcb7a06a2013-02-17 22:32:29 +000063
64 loadTestImages(imageFilenames);
jerrymf96c32c2013-02-18 19:30:45 +000065 recognizer.showIntermediateStages(true);
jerrymcd2c3322013-02-18 08:49:01 +000066
67 cameraFrame.getContentPane().add(panel, BorderLayout.SOUTH);
68 panel.setLayout(new GridLayout(0, 2, 0, 0));
69
70 ChangeListener sliderListener = new ChangeListener() {
71 @Override
jerrymdda60132013-02-18 09:25:03 +000072 public void stateChanged(ChangeEvent e) {
73 System.out.println("New HSV range ["
74 + hueMinSlider.getValue() + " .. "
jerrymcd2469c2013-02-18 20:15:28 +000075 + hueMaxSlider.getValue() + "] "
76 + satMinSlider.getValue() + "+ "
77 + valMinSlider.getValue() + "+");
jerrymdda60132013-02-18 09:25:03 +000078 recognizer.setHSVRange(
79 hueMinSlider.getValue(), hueMaxSlider.getValue(),
80 satMinSlider.getValue(),
81 valMinSlider.getValue());
82 processCurrentImage();
jerrymcd2c3322013-02-18 08:49:01 +000083 }
84 };
85
jerrymcd2c3322013-02-18 08:49:01 +000086 hueMinSlider.setToolTipText("minimum HSV hue");
87 hueMinSlider.setMaximum(255);
jerrymcd2469c2013-02-18 20:15:28 +000088 hueMinSlider.setValue(recognizer.getHueMin());
jerrymcd2c3322013-02-18 08:49:01 +000089 panel.add(hueMinSlider);
90
jerrymcd2c3322013-02-18 08:49:01 +000091 hueMaxSlider.setToolTipText("maximum HSV hue");
92 hueMaxSlider.setMaximum(255);
jerrymcd2469c2013-02-18 20:15:28 +000093 hueMaxSlider.setValue(recognizer.getHueMax());
jerrymcd2c3322013-02-18 08:49:01 +000094 panel.add(hueMaxSlider);
95
jerrymcd2c3322013-02-18 08:49:01 +000096 satMinSlider.setToolTipText("minimum HSV color saturation");
97 satMinSlider.setMaximum(255);
jerrymcd2469c2013-02-18 20:15:28 +000098 satMinSlider.setValue(recognizer.getSatMin());
jerrymcd2c3322013-02-18 08:49:01 +000099 panel.add(satMinSlider);
100
jerrymcd2c3322013-02-18 08:49:01 +0000101 valMinSlider.setToolTipText("minimum HSV brightness value");
102 valMinSlider.setMaximum(255);
jerrymcd2469c2013-02-18 20:15:28 +0000103 valMinSlider.setValue(recognizer.getValMin());
jerrymcd2c3322013-02-18 08:49:01 +0000104 panel.add(valMinSlider);
105
jerrymcd2469c2013-02-18 20:15:28 +0000106 System.out.println("Initial HSV range ["
107 + hueMinSlider.getValue() + " .. "
108 + hueMaxSlider.getValue() + "] "
109 + satMinSlider.getValue() + "+ "
110 + valMinSlider.getValue() + "+");
111
jerrymcd2c3322013-02-18 08:49:01 +0000112 hueMinSlider.addChangeListener(sliderListener);
113 hueMaxSlider.addChangeListener(sliderListener);
114 satMinSlider.addChangeListener(sliderListener);
115 valMinSlider.addChangeListener(sliderListener);
jerrymcb7a06a2013-02-17 22:32:29 +0000116 }
117
jerrymcb7a06a2013-02-17 22:32:29 +0000118 /**
119 * Loads the named test image files.
120 * Sets testImageFilenames and testImages.
121 */
122 private void loadTestImages(String[] imageFilenames) {
jerrymdda60132013-02-18 09:25:03 +0000123 testImageFilenames = imageFilenames;
124 testImages = new WPIColorImage[testImageFilenames.length];
125 currentIndex = 0;
jerrymcb7a06a2013-02-17 22:32:29 +0000126
jerrymdda60132013-02-18 09:25:03 +0000127 for (int i = 0; i < testImageFilenames.length; i++) {
jerrymcb7a06a2013-02-17 22:32:29 +0000128 String imageFilename = testImageFilenames[i];
129
130 System.out.println("Loading image file: " + imageFilename);
131 WPIColorImage rawImage = null;
132 try {
jerrym6ebe6452013-02-18 03:00:31 +0000133 rawImage = new WPIColorImage(ImageIO.read(
jerrymdda60132013-02-18 09:25:03 +0000134 new File(imageFilename)));
jerrymcb7a06a2013-02-17 22:32:29 +0000135 } catch (IOException e) {
jerrym6ebe6452013-02-18 03:00:31 +0000136 System.err.println("Couldn't load image file: " + imageFilename
jerrymdda60132013-02-18 09:25:03 +0000137 + ": " + e.getMessage());
jerrymcb7a06a2013-02-17 22:32:29 +0000138 System.exit(1);
139 return;
140 }
141 testImages[i] = rawImage;
jerrymdda60132013-02-18 09:25:03 +0000142 }
jerrymcb7a06a2013-02-17 22:32:29 +0000143 }
144
145 private void processCurrentImage() {
jerrym6ebe6452013-02-18 03:00:31 +0000146 WPIColorImage cameraImage = testImages[currentIndex];
jerrymcb7a06a2013-02-17 22:32:29 +0000147 cameraFrame.setTitle(testImageFilenames[currentIndex]);
jerrym6ebe6452013-02-18 03:00:31 +0000148
jerryma1cd68d2013-02-18 09:16:19 +0000149 long startTime = System.nanoTime();
jerrym6ebe6452013-02-18 03:00:31 +0000150 WPIImage processedImage = recognizer.processImage(cameraImage);
jerryma1cd68d2013-02-18 09:16:19 +0000151 long endTime = System.nanoTime();
152
jerrym6ebe6452013-02-18 03:00:31 +0000153 cameraFrame.showImage(processedImage.getBufferedImage());
jerryma1cd68d2013-02-18 09:16:19 +0000154
155 double milliseconds = (endTime - startTime) / 1e6;
jerrymf0c84552013-02-19 00:51:20 +0000156 if (++totalFrames > 0) {
157 totalMsec += milliseconds;
158 minMsec = Math.min(minMsec, milliseconds);
159 maxMsec = Math.max(maxMsec, milliseconds);
160 System.out.format("The recognizer took %.2f ms, %.2f fps, %.2f avg%n",
161 milliseconds, 1000 / milliseconds,
162 1000 * totalFrames / totalMsec);
163 }
jerrymcb7a06a2013-02-17 22:32:29 +0000164 }
165
166 private void previousImage() {
jerrymdda60132013-02-18 09:25:03 +0000167 if (currentIndex > 0) {
168 --currentIndex;
169 }
170 processCurrentImage();
jerrymcb7a06a2013-02-17 22:32:29 +0000171 }
172
173 private void nextImage() {
jerrymdda60132013-02-18 09:25:03 +0000174 if (currentIndex + 1 < testImages.length) {
175 ++currentIndex;
176 }
177 processCurrentImage();
jerrymcb7a06a2013-02-17 22:32:29 +0000178 }
179
180 private void processEvents() {
jerrymdda60132013-02-18 09:25:03 +0000181 KeyEvent e = cameraFrame.waitKey();
jerrymcb7a06a2013-02-17 22:32:29 +0000182
jerrymdda60132013-02-18 09:25:03 +0000183 switch (e.getKeyCode()) {
jerrymf96c32c2013-02-18 19:30:45 +0000184 case KeyEvent.VK_LEFT: // left arrow key: go to previous image
jerrymdda60132013-02-18 09:25:03 +0000185 previousImage();
186 break;
jerrymf96c32c2013-02-18 19:30:45 +0000187 case KeyEvent.VK_RIGHT: // right arrow key: go to next image
jerrymdda60132013-02-18 09:25:03 +0000188 nextImage();
189 break;
jerrymf96c32c2013-02-18 19:30:45 +0000190 case KeyEvent.VK_Q: // Q: print time measurements then quit
191 System.out.format("The recognizer took %.2f ms avg, %.2f min,"
192 + " %.2f max, %.2f fps avg%n",
193 totalMsec / totalFrames,
194 minMsec, maxMsec,
195 1000 * totalFrames / totalMsec);
196 System.exit(0);
jerrymdda60132013-02-18 09:25:03 +0000197 }
jerrymcb7a06a2013-02-17 22:32:29 +0000198 }
199
200 public static void main(final String[] args) {
201 if (args.length == 0) {
202 System.err.println("Usage: " + VisionTuner.class.getName()
jerrymdda60132013-02-18 09:25:03 +0000203 + " test image filenames...");
jerrymcb7a06a2013-02-17 22:32:29 +0000204 System.exit(1);
205 }
206
207 VisionTuner tuner = new VisionTuner(args);
208 tuner.processCurrentImage();
209
210 for (;;) {
211 tuner.processEvents();
212 }
213 }
214
215}