blob: 663b2f191efc2c6957632505886c2209572d70f3 [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 *
38 * @author jerry
39 */
40public class VisionTuner {
41 private String[] testImageFilenames;
42 private WPIColorImage[] testImages;
jerrymcb7a06a2013-02-17 22:32:29 +000043 private int currentIndex = 0;
jerrym6ebe6452013-02-18 03:00:31 +000044 private Recognizer recognizer = new Recognizer2013();
jerrymcb7a06a2013-02-17 22:32:29 +000045
jerrymcd2c3322013-02-18 08:49:01 +000046 private final CanvasFrame cameraFrame = new CanvasFrame("Camera");
47 private final JPanel panel = new JPanel();
48 private final JSlider hueMinSlider = new JSlider();
49 private final JSlider hueMaxSlider = new JSlider();
50 private final JSlider satMinSlider = new JSlider();
51 private final JSlider valMinSlider = new JSlider();
52
jerryma1cd68d2013-02-18 09:16:19 +000053 private static int totalFrames;
54 private static double totalMsec;
jerrymdda60132013-02-18 09:25:03 +000055 // private static double minMsec = Double.MAX_VALUE;
56 // private static double maxMsec;
jerryma1cd68d2013-02-18 09:16:19 +000057
jerrymcb7a06a2013-02-17 22:32:29 +000058 public VisionTuner(String[] imageFilenames) {
59 cameraFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jerrymcb7a06a2013-02-17 22:32:29 +000060
61 loadTestImages(imageFilenames);
jerrymcd2c3322013-02-18 08:49:01 +000062
63 cameraFrame.getContentPane().add(panel, BorderLayout.SOUTH);
64 panel.setLayout(new GridLayout(0, 2, 0, 0));
65
66 ChangeListener sliderListener = new ChangeListener() {
67 @Override
jerrymdda60132013-02-18 09:25:03 +000068 public void stateChanged(ChangeEvent e) {
69 System.out.println("New HSV range ["
70 + hueMinSlider.getValue() + " .. "
71 + hueMaxSlider.getValue() + "], ["
72 + satMinSlider.getValue() + " .. 255], ["
73 + valMinSlider.getValue() + " .. 255]");
74 recognizer.setHSVRange(
75 hueMinSlider.getValue(), hueMaxSlider.getValue(),
76 satMinSlider.getValue(),
77 valMinSlider.getValue());
78 processCurrentImage();
jerrymcd2c3322013-02-18 08:49:01 +000079 }
80 };
81
82 hueMinSlider.setValue(recognizer.getHueMin());
83 hueMinSlider.setToolTipText("minimum HSV hue");
84 hueMinSlider.setMaximum(255);
85 panel.add(hueMinSlider);
86
87 hueMaxSlider.setValue(recognizer.getHueMax());
88 hueMaxSlider.setToolTipText("maximum HSV hue");
89 hueMaxSlider.setMaximum(255);
90 panel.add(hueMaxSlider);
91
92 satMinSlider.setValue(recognizer.getSatMin());
93 satMinSlider.setToolTipText("minimum HSV color saturation");
94 satMinSlider.setMaximum(255);
95 panel.add(satMinSlider);
96
97 valMinSlider.setValue(recognizer.getValMin());
98 valMinSlider.setToolTipText("minimum HSV brightness value");
99 valMinSlider.setMaximum(255);
100 panel.add(valMinSlider);
101
102 hueMinSlider.addChangeListener(sliderListener);
103 hueMaxSlider.addChangeListener(sliderListener);
104 satMinSlider.addChangeListener(sliderListener);
105 valMinSlider.addChangeListener(sliderListener);
jerrymcb7a06a2013-02-17 22:32:29 +0000106 }
107
jerrymcb7a06a2013-02-17 22:32:29 +0000108 /**
109 * Loads the named test image files.
110 * Sets testImageFilenames and testImages.
111 */
112 private void loadTestImages(String[] imageFilenames) {
jerrymdda60132013-02-18 09:25:03 +0000113 testImageFilenames = imageFilenames;
114 testImages = new WPIColorImage[testImageFilenames.length];
115 currentIndex = 0;
jerrymcb7a06a2013-02-17 22:32:29 +0000116
jerrymdda60132013-02-18 09:25:03 +0000117 for (int i = 0; i < testImageFilenames.length; i++) {
jerrymcb7a06a2013-02-17 22:32:29 +0000118 String imageFilename = testImageFilenames[i];
119
120 System.out.println("Loading image file: " + imageFilename);
121 WPIColorImage rawImage = null;
122 try {
jerrym6ebe6452013-02-18 03:00:31 +0000123 rawImage = new WPIColorImage(ImageIO.read(
jerrymdda60132013-02-18 09:25:03 +0000124 new File(imageFilename)));
jerrymcb7a06a2013-02-17 22:32:29 +0000125 } catch (IOException e) {
jerrym6ebe6452013-02-18 03:00:31 +0000126 System.err.println("Couldn't load image file: " + imageFilename
jerrymdda60132013-02-18 09:25:03 +0000127 + ": " + e.getMessage());
jerrymcb7a06a2013-02-17 22:32:29 +0000128 System.exit(1);
129 return;
130 }
131 testImages[i] = rawImage;
jerrymdda60132013-02-18 09:25:03 +0000132 }
jerrymcb7a06a2013-02-17 22:32:29 +0000133 }
134
135 private void processCurrentImage() {
jerrym6ebe6452013-02-18 03:00:31 +0000136 WPIColorImage cameraImage = testImages[currentIndex];
jerrymcb7a06a2013-02-17 22:32:29 +0000137 cameraFrame.setTitle(testImageFilenames[currentIndex]);
jerrym6ebe6452013-02-18 03:00:31 +0000138
jerryma1cd68d2013-02-18 09:16:19 +0000139 long startTime = System.nanoTime();
jerrym6ebe6452013-02-18 03:00:31 +0000140 WPIImage processedImage = recognizer.processImage(cameraImage);
jerryma1cd68d2013-02-18 09:16:19 +0000141 long endTime = System.nanoTime();
142
jerrym6ebe6452013-02-18 03:00:31 +0000143 cameraFrame.showImage(processedImage.getBufferedImage());
jerryma1cd68d2013-02-18 09:16:19 +0000144
145 double milliseconds = (endTime - startTime) / 1e6;
146 ++totalFrames;
147 totalMsec += milliseconds;
jerrymdda60132013-02-18 09:25:03 +0000148 // minMsec = Math.min(minMsec, milliseconds);
149 // maxMsec = Math.max(maxMsec, milliseconds);
jerryma1cd68d2013-02-18 09:16:19 +0000150 System.out.format("Processing took %.2f ms, %.2f fps, %.2f avg%n",
jerrymdda60132013-02-18 09:25:03 +0000151 milliseconds, 1000 / milliseconds,
152 1000 * totalFrames / totalMsec);
jerrymcb7a06a2013-02-17 22:32:29 +0000153 }
154
155 private void previousImage() {
jerrymdda60132013-02-18 09:25:03 +0000156 if (currentIndex > 0) {
157 --currentIndex;
158 }
159 processCurrentImage();
jerrymcb7a06a2013-02-17 22:32:29 +0000160 }
161
162 private void nextImage() {
jerrymdda60132013-02-18 09:25:03 +0000163 if (currentIndex + 1 < testImages.length) {
164 ++currentIndex;
165 }
166 processCurrentImage();
jerrymcb7a06a2013-02-17 22:32:29 +0000167 }
168
169 private void processEvents() {
jerrymdda60132013-02-18 09:25:03 +0000170 KeyEvent e = cameraFrame.waitKey();
jerrymcb7a06a2013-02-17 22:32:29 +0000171
jerrymdda60132013-02-18 09:25:03 +0000172 switch (e.getKeyCode()) {
173 case KeyEvent.VK_LEFT:
174 previousImage();
175 break;
176 case KeyEvent.VK_RIGHT:
177 nextImage();
178 break;
179 }
jerrymcb7a06a2013-02-17 22:32:29 +0000180 }
181
182 public static void main(final String[] args) {
183 if (args.length == 0) {
184 System.err.println("Usage: " + VisionTuner.class.getName()
jerrymdda60132013-02-18 09:25:03 +0000185 + " test image filenames...");
jerrymcb7a06a2013-02-17 22:32:29 +0000186 System.exit(1);
187 }
188
189 VisionTuner tuner = new VisionTuner(args);
190 tuner.processCurrentImage();
191
192 for (;;) {
193 tuner.processEvents();
194 }
195 }
196
197}