Fixed up code, made it prettier, it now follows the stylguide better and is more readable.
git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4182 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/971cv/src/org/spartanrobotics/VisionTuner.java b/971cv/src/org/spartanrobotics/VisionTuner.java
new file mode 100644
index 0000000..95c3b8d
--- /dev/null
+++ b/971cv/src/org/spartanrobotics/VisionTuner.java
@@ -0,0 +1,296 @@
+package org.spartanrobotics;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.WindowConstants;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import com.googlecode.javacv.CanvasFrame;
+import edu.wpi.first.wpijavacv.WPIColorImage;
+import edu.wpi.first.wpijavacv.WPIImage;
+
+/* REQUIRED JAVA LIBRARIES:
+ * external_jars/
+ * javacpp.jar
+ * javacv-YOUR_OS.jar
+ * javacv.jar
+ * WPIJavaCV.jar
+ *
+ * REQUIRED NATIVE CODE LIBRARIES ON $PATH:
+ * Program Files/WPIJavaCV/ [for example]
+ * JavaCV_2.2.0/javacv-bin/javacv-YOUR_OS.jar
+ * OpenCV_2.2.0/bin/*
+ *
+ * The native libraries and javacv-YOUR_OS.jar must match the 32 vs. 64-bit JVM.
+ */
+/**
+ * FRC 2013 vision-target recognizer tuner app.
+ *
+ * <p>
+ * See {@link #processEvents()} for the keystroke commands.
+ *
+ * @author jerry
+ * @author daniel
+ */
+public class VisionTuner {
+ private Recognizer recognizer = new Recognizer2013();
+
+ private final static Logger LOG = Logger.getLogger(
+ VisionTuner.class.getName());
+
+ private final CanvasFrame cameraFrame = new CanvasFrame("Camera");
+ private final JPanel panel = new JPanel();
+ private final JSlider hueMinSlider = new JSlider();
+ private final JSlider hueMaxSlider = new JSlider();
+ private final JSlider satMinSlider = new JSlider();
+ private final JSlider valMinSlider = new JSlider();
+ private final JButton showCalibration = new JButton("Calibrate");
+ private final JLabel frameRate = new JLabel("0");
+
+ private ResultSender sender = null;
+
+ private int totalFrames = -1; // don't count the first (warm-up) frame
+ private double totalMsec;
+ private double minMsec = Double.MAX_VALUE;
+ private double maxMsec;
+
+ private TestImageGetter getter;
+
+ private WPIColorImage current;
+
+ private boolean debug = false;
+
+ long startTime;
+ long endTime;
+
+ public VisionTuner() {
+ //set logger to log everything
+ LOG.setLevel(Level.ALL);
+ try {
+ LogHandler handler = new LogHandler("ds_vision.log");
+ TimeFormatter formatter = new TimeFormatter();
+ handler.setFormatter(formatter);
+ LOG.addHandler(handler);
+ } catch (FileNotFoundException e) {
+ Messages.warning("Logging initialization failed.");
+ }
+
+ //initialize result sender
+ try {
+ sender = new ResultSender();
+ } catch (IOException e) {
+ LOG.severe("Server initialization failed: " + e.getMessage() + ". Result reporting disabled.");
+ }
+ cameraFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+ cameraFrame.getContentPane().add(panel, BorderLayout.SOUTH);
+ panel.setLayout(new GridLayout(0, 2, 0, 0));
+
+ showCalibration.setToolTipText("Click here if the system is not finding targets well enough.");
+ panel.add(showCalibration);
+ showCalibration.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showCalibrationWindow();
+ }
+ });
+
+ panel.add(frameRate);
+
+ }
+
+ /** Shows a calibration window when the user clicks the Calibrate button. */
+ private void showCalibrationWindow() {
+ final CanvasFrame calibrationWindow = new CanvasFrame("Calibration");
+ calibrationWindow.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+
+ final JPanel panel = new JPanel();
+ calibrationWindow.getContentPane().add(panel, BorderLayout.SOUTH);
+ panel.setLayout(new GridLayout(3, 3, 0, 0));
+
+ hueMinSlider.setToolTipText("minimum HSV hue");
+ hueMinSlider.setMaximum(255);
+ hueMinSlider.setValue(recognizer.getHueMin());
+ panel.add(hueMinSlider);
+
+ panel.add(new JLabel("min hue max hue"));
+
+ hueMaxSlider.setToolTipText("maximum HSV hue");
+ hueMaxSlider.setMaximum(255);
+ hueMaxSlider.setValue(recognizer.getHueMax());
+ panel.add(hueMaxSlider);
+
+ satMinSlider.setToolTipText("minimum HSV color saturation");
+ satMinSlider.setMaximum(255);
+ satMinSlider.setValue(recognizer.getSatMin());
+ panel.add(satMinSlider);
+
+ panel.add(new JLabel("min saturation max saturation"));
+
+ valMinSlider.setToolTipText("minimum HSV brightness value");
+ valMinSlider.setMaximum(255);
+ valMinSlider.setValue(recognizer.getValMin());
+ panel.add(valMinSlider);
+
+ panel.add(new JLabel("")); //empty cells can cause problems
+
+ final JButton done = new JButton("Close");
+ panel.add(done);
+ done.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ calibrationWindow.dispose();
+ }
+ });
+
+ panel.add(new JLabel("")); //empty cells can cause problems
+
+ ChangeListener sliderListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LOG.fine("New HSV range ["
+ + hueMinSlider.getValue() + " .. "
+ + hueMaxSlider.getValue() + "] "
+ + satMinSlider.getValue() + "+ "
+ + valMinSlider.getValue() + "+");
+ recognizer.setHSVRange(
+ hueMinSlider.getValue(), hueMaxSlider.getValue(),
+ satMinSlider.getValue(),
+ valMinSlider.getValue());
+ if (debug) {
+ processImage(current, getter.getName());
+ }
+ }
+ };
+
+ hueMinSlider.addChangeListener(sliderListener);
+ hueMaxSlider.addChangeListener(sliderListener);
+ satMinSlider.addChangeListener(sliderListener);
+ valMinSlider.addChangeListener(sliderListener);
+
+ calibrationWindow.pack();
+
+ }
+
+ /**
+ * Loads the named test image files.
+ * Sets testImageFilenames and testImages.
+ */
+ private void processImage(WPIColorImage cameraImage, String title) {
+ current = cameraImage;
+
+ //set window title if it needs to be changed
+ cameraFrame.setTitle(title);
+
+ Target target = recognizer.processImage(cameraImage);
+ WPIImage processedImage = target.editedPicture;
+ endTime = System.nanoTime();
+
+ cameraFrame.showImage(processedImage.getBufferedImage());
+
+ double milliseconds = (endTime - startTime) / 1e6;
+ if (++totalFrames > 0) {
+ totalMsec += milliseconds;
+ minMsec = Math.min(minMsec, milliseconds);
+ maxMsec = Math.max(maxMsec, milliseconds);
+ LOG.fine("The recognizer took " + milliseconds + " ms, " +
+ (1000 * totalFrames / totalMsec) + " fps avg");
+ }
+
+ //send results to atom. (and any connected clients)
+ if (sender != null) {
+ sender.send(target.azimuth, target.elevation, target.range);
+ }
+
+ //show average fps
+ double fps = (1000 / milliseconds);
+ frameRate.setText("FPS: " + String.valueOf(fps));
+
+ }
+
+ private void previousImage() {
+ WPIColorImage toProcess = getter.getPrev();
+ if (toProcess != null)
+ processImage(toProcess, getter.getName());
+ }
+
+ private void nextImage() {
+ WPIColorImage toProcess = getter.getFrame();
+ if (toProcess != null)
+ processImage(toProcess, getter.getName());
+ }
+
+ private void processEvents() {
+ KeyEvent e = cameraFrame.waitKey();
+
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_LEFT: // left arrow key: go to previous image
+ previousImage();
+ break;
+ case KeyEvent.VK_RIGHT: // right arrow key: go to next image
+ nextImage();
+ break;
+ case KeyEvent.VK_Q: // Q: print time measurements then quit
+ LOG.fine("The recognizer took " + (totalMsec / totalFrames) + "ms avg, " + minMsec +" min,"
+ + maxMsec + " max, " + (1000 * totalFrames / totalMsec) + " fps avg");
+ System.exit(0);
+ }
+ }
+
+ public static void main(final String[] args) {
+ VisionTuner tuner = new VisionTuner();
+ Messages.SetWindow(tuner.cameraFrame);
+
+ String atomIP = null;
+ try {
+ atomIP = args[0];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Usage: VisionTuner [atom IP address]");
+ System.exit(0);
+ }
+
+ if (Arrays.asList(args).contains("-debug")) {
+ //debug mode has been requested
+ tuner.debug = true;
+
+ //show debugging windows
+ tuner.recognizer.showIntermediateStages(true);
+ tuner.getter = new TestImageGetter(".");
+ WPIColorImage to_process = tuner.getter.getFrame();
+ tuner.processImage(to_process, tuner.getter.getName());
+ for (;;) {
+ tuner.processEvents();
+ }
+ } else {
+ try {
+ HTTPClient client = new HTTPClient(atomIP);
+ for (;;) {
+ tuner.startTime = System.nanoTime();
+ WPIColorImage to_process = client.getFrame();
+ if (to_process != null) {
+ tuner.processImage(to_process, client.getName());
+ LOG.fine("Captured time: " + Double.toString(client.getTimestamp()));
+ }
+ }
+ } catch (IOException e) {
+ LOG.severe("Client initialization failed: " + e.getMessage() + ".");
+ Messages.severe("Client initialization failed: " + e.getMessage() + ".");
+ }
+ }
+ }
+
+}