summaryrefslogtreecommitdiff
path: root/WPIJavaCV/src/edu/wpi/first/wpijavacv
diff options
context:
space:
mode:
Diffstat (limited to 'WPIJavaCV/src/edu/wpi/first/wpijavacv')
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java277
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java26
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java64
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java158
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java100
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java119
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java151
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java53
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java76
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java41
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java28
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java34
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java56
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java126
-rw-r--r--WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java52
15 files changed, 1361 insertions, 0 deletions
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java
new file mode 100644
index 0000000..1b862f9
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java
@@ -0,0 +1,277 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.wpijavacv;
+
+import java.util.ArrayList;
+import static com.googlecode.javacv.cpp.opencv_imgproc.*;
+import static com.googlecode.javacv.cpp.opencv_core.*;
+
+/**
+ * A {@link WPIBinaryImage} object is a black and white image.
+ *
+ * @author Greg Granito
+ */
+public class WPIBinaryImage extends WPIImage {
+
+ /**
+ * Instantiates a {@link WPIBinaryImage} from the given {@link IplImage}.
+ * The resulting image will be directly wrapped around the given image, and so any modifications
+ * to the {@link WPIBinaryImage} will reflect on the given image.
+ * @param image the image to wrap
+ */
+ protected WPIBinaryImage(IplImage image) {
+ super(image);
+ }
+
+ /**
+ * Returns the result of "and-ing" the given image with the calling image. Every pixel in the image
+ * will be "and-ed" with the corresponding pixel in the other image to form a new image. The result
+ * of and-ing two pixels is pictured in the table below.
+ *
+ * <table border="1"><tr>
+ * <th></th>
+ * <th>White</th>
+ * <th>Black</th>
+ * </tr><tr>
+ * <th>White</th>
+ * <td>White</td>
+ * <td>Black</td>
+ * </tr><tr>
+ * <th>Black</th>
+ * <td>Black</td>
+ * <td>Black</td>
+ * </tr></table>
+ *
+ * @param image2 a second {@link WPIBinaryImage}
+ * @return an image that is the pixel-wise and of the two images.
+ */
+ public WPIBinaryImage getAnd(WPIBinaryImage image2) {
+ validateDisposed();
+
+ IplImage result = IplImage.create(image.cvSize(), image.depth(), 1);
+ cvAnd(image, image2.image, result, null);
+ return new WPIBinaryImage(result);
+ }
+
+ /**
+ * "and-s" this image with the given image. Every pixel in this image
+ * will be "and-ed" with the corresponding pixel in the other image. The result
+ * of and-ing two pixels is pictured in the table below.
+ *
+ * <table border="1"><tr>
+ * <th></th>
+ * <th>White</th>
+ * <th>Black</th>
+ * </tr><tr>
+ * <th>White</th>
+ * <td>White</td>
+ * <td>Black</td>
+ * </tr><tr>
+ * <th>Black</th>
+ * <td>Black</td>
+ * <td>Black</td>
+ * </tr></table>
+ *
+ * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getAnd(wpijavacv.WPIBinaryImage) getAnd(...)}
+ * instead.
+ *
+ * @param image2 a second {@link WPIBinaryImage}
+ */
+ public void and(WPIBinaryImage image2) {
+ validateDisposed();
+
+ cvAnd(image, image2.image, image, null);
+ }
+
+ /**
+ * Returns the result of "or-ing" the given image with the calling image. Every pixel in the image
+ * will be "or-ed" with the corresponding pixel in the other image to form a new image. The result
+ * of or-ing two pixels is pictured in the table below.
+ *
+ * <table border="1"><tr>
+ * <th></th>
+ * <th>White</th>
+ * <th>Black</th>
+ * </tr><tr>
+ * <th>White</th>
+ * <td>White</td>
+ * <td>White</td>
+ * </tr><tr>
+ * <th>Black</th>
+ * <td>White</td>
+ * <td>Black</td>
+ * </tr></table>
+ *
+ * @param image2 a second {@link WPIBinaryImage}
+ * @return an image that is the pixel-wise or of the two images.
+ */
+ public WPIBinaryImage getOr(WPIBinaryImage image2) {
+ validateDisposed();
+
+ IplImage result = IplImage.create(image.cvSize(), image.depth(), 1);
+ cvOr(image, image2.image, result, null);
+ return new WPIBinaryImage(result);
+ }
+
+ /**
+ * "or-s" this image with the given image. Every pixel in this image
+ * will be "or-ed" with the corresponding pixel in the other image. The result
+ * of or-ing two pixels is pictured in the table below. This will modify the image.
+ *
+ * <table border="1"><tr>
+ * <th></th>
+ * <th>White</th>
+ * <th>Black</th>
+ * </tr><tr>
+ * <th>White</th>
+ * <td>White</td>
+ * <td>White</td>
+ * </tr><tr>
+ * <th>Black</th>
+ * <td>White</td>
+ * <td>Black</td>
+ * </tr></table>
+ *
+ * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getOr(wpijavacv.WPIBinaryImage) getOr(...)}
+ * instead.
+ *
+ * @param image2 a second {@link WPIBinaryImage}
+ */
+ public void or(WPIBinaryImage image2) {
+ validateDisposed();
+
+ cvOr(image, image2.image, image, null);
+ }
+
+ /**
+ * Inverts this image. Everything which was white will now be black and
+ * everything which was black will now be white.
+ * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getInverse() getInverse()} instead.
+ * instead.
+ */
+ public void invert() {
+ validateDisposed();
+
+ cvInv(image, image);
+ }
+
+ /**
+ * Returns an image that is the inverse of the calling one. In other words, it returns a copy of this image except
+ * that the copy will replace every black pixel with white one and every white pixel with a black one.
+ * @return a new {@link WPIBinaryImage} that is the inverse of the image
+ */
+ public WPIBinaryImage getInverse() {
+ validateDisposed();
+
+ IplImage result = IplImage.create(image.cvSize(), image.depth(), 1);
+ cvInv(image, result);
+ return new WPIBinaryImage(result);
+ }
+
+ /**
+ * Dilates the image the specified number of times. Every time the image is dilated, every pixel which borders a white pixel
+ * will itself become white. The effect is that white pixels in the image begin to spread.
+ * This is useful if you wish to remove small "holes" from the image.
+ *
+ * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getDilated(int) getDilated(...)} instead.
+ * instead.
+ *
+ * @param iterations the number of times to perform the dilation. For example, if this is 3, then every pixel within three
+ * spaces of a white pixel will become white.
+ */
+ public void dilate(int iterations) {
+ validateDisposed();
+
+ cvDilate(image, image, null, iterations);
+ }
+
+ /**
+ * Returns an image that is the result of dilating the specified number of times. Every time an image is dilated, every pixel which borders a white pixel
+ * will itself become white. The effect is that white pixels in the image begin to spread.
+ * This is useful if you wish to remove small "holes" from the image.
+ *
+ * @param iterations the number of times to perform the dilation. For example, if this is 3, then every pixel within three
+ * @return returns a new {@link WPIBinaryImage} in which the surrounding pixels to each white pixel
+ * are changed to white
+ */
+ public WPIBinaryImage getDilated(int iterations) {
+ validateDisposed();
+
+ IplImage result = IplImage.create(image.cvSize(), image.depth(), 1);
+ cvDilate(image, result, null, iterations);
+ return new WPIBinaryImage(result);
+ }
+
+ /**
+ * Erodes the image the specified number of times. Every time the image is eroded, every pixel which borders a black pixel
+ * will itself become black. The effect is that black pixels in the image begin to spread.
+ * This is useful if you wish to shrink or remove white blobs from an image.
+ *
+ * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getEroded(int) getEroded(...)} instead.
+ * instead.
+ *
+ * @param iterations the number of times to perform the erosion. For example, if this is 3, then every pixel within three
+ * spaces of a black pixel will become black.
+ */
+ public void erode(int iterations) {
+ validateDisposed();
+
+ cvErode(image, image, null, iterations);
+ }
+
+ /**
+ * Returns an image that is the result of eroding the specified number of times. Every time the image is eroded, every pixel which borders a black pixel
+ * will itself become black. The effect is that black pixels in the image begin to spread.
+ * This is useful if you wish to shrink or remove white blobs from an image.
+ *
+ * @param iterations the number of times to perform the erosion. For example, if this is 3, then every pixel within three
+ * spaces of a black pixel will become black.
+ * @return a new {@link WPIBinaryImage} in which the surrounding pixels to each black pixel
+ * are changed to black
+ */
+ public WPIBinaryImage getEroded(int iterations) {
+ validateDisposed();
+
+ IplImage result = IplImage.create(image.cvSize(), image.depth(), 1);
+ cvErode(image, result, null, iterations);
+ return new WPIBinaryImage(result);
+ }
+
+ /**
+ * Finds all the "contours" in the image. A contour is basically an outline.
+ * @return an array of {@link WpiContour} that is all of the edges in the image
+ */
+ public WPIContour[] findContours() {
+ validateDisposed();
+
+ IplImage tempImage = IplImage.create(image.cvSize(), image.depth(), 1);
+
+ cvCopy(image, tempImage);
+
+ final CvMemStorage storage = CvMemStorage.create();
+ WPIMemoryPool pool = new WPIMemoryPool() {
+
+ @Override
+ protected void disposed() {
+ cvClearMemStorage(storage);
+ storage.release();
+ }
+ };
+
+ CvSeq contours = new CvSeq();
+ cvFindContours(tempImage, storage, contours, 256, CV_RETR_LIST, CV_CHAIN_APPROX_TC89_KCOS);
+ ArrayList<WPIContour> results = new ArrayList();
+ while (!isNull(contours)) {
+ WPIContour contour = new WPIContour(cvCloneSeq(contours, storage));
+ results.add(contour);
+ pool.addToPool(contour);
+ contours = contours.h_next();
+ }
+
+ tempImage.release();
+ WPIContour[] array = new WPIContour[results.size()];
+ return results.toArray(array);
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java
new file mode 100644
index 0000000..ed9752f
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java
@@ -0,0 +1,26 @@
+package edu.wpi.first.wpijavacv;
+
+/**
+ * A class used to gather images from the robot's camera.
+ * @author Joe Grinstead and Greg Granito
+ */
+public class WPICamera extends WPIFFmpegVideo {
+
+ private static final int DEFAULT_ENDING_IP = 11;
+
+ public WPICamera(String loginName, String password, int team) {
+ this(loginName + ":" + password + "@10." + (team / 100) + "." + (team % 100) + "." + DEFAULT_ENDING_IP);
+ }
+
+ public WPICamera(int team) {
+ this("10." + (team / 100) + "." + (team % 100) + "." + DEFAULT_ENDING_IP);
+ }
+
+ public WPICamera(String loginName, String password, String ip) {
+ this(loginName + ":" + password + "@" + ip);
+ }
+
+ public WPICamera(String ip) {
+ super("http://" + ip + "/mjpg/video.mjpg");
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java
new file mode 100644
index 0000000..50f585b
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java
@@ -0,0 +1,64 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package edu.wpi.first.wpijavacv;
+
+import java.awt.Color;
+import static com.googlecode.javacv.cpp.opencv_core.*;
+/**
+ * A class of colors used for drawing function
+ * @author Greg Granito
+ */
+public class WPIColor {
+ public static final WPIColor BLACK = new WPIColor(CvScalar.BLACK, Color.BLACK);
+ public static final WPIColor BLUE = new WPIColor(CvScalar.BLUE, Color.BLUE);
+ public static final WPIColor CYAN = new WPIColor(CvScalar.CYAN, Color.CYAN);
+ public static final WPIColor GRAY = new WPIColor(CvScalar.GRAY, Color.GRAY);
+ public static final WPIColor GREEN = new WPIColor(CvScalar.GREEN, Color.GREEN);
+ public static final WPIColor MAGENTA = new WPIColor(CvScalar.MAGENTA, Color.MAGENTA);
+ public static final WPIColor ONE = new WPIColor(CvScalar.ONE);
+ public static final WPIColor ONEHALF = new WPIColor(CvScalar.ONEHALF);
+ public static final WPIColor RED = new WPIColor(CvScalar.RED, Color.RED);
+ public static final WPIColor WHITE = new WPIColor(CvScalar.WHITE, Color.WHITE);
+ public static final WPIColor YELLOW = new WPIColor(CvScalar.YELLOW, Color.YELLOW);
+ public static final WPIColor ZERO = new WPIColor(CvScalar.ZERO);
+
+ private final CvScalar scalar;
+ private Color color;
+
+ WPIColor(CvScalar scalar) {
+ this.scalar = scalar;
+ }
+
+ WPIColor(CvScalar scalar, Color color) {
+ this(scalar);
+ this.color = color;
+ }
+
+ /**
+ * Creates a new WPIColor with the specified rgb values
+ * @param red red value, 0 - 255
+ * @param green green value, 0 - 255
+ * @param blue blue value, 0 - 255
+ */
+ public WPIColor(int red, int green, int blue){
+ this(CV_RGB(red, green, blue));
+ }
+
+ public WPIColor(Color color) {
+ this(CV_RGB(color.getRed(), color.getGreen(), color.getBlue()), color);
+ }
+
+ CvScalar toCvScalar(){
+ return scalar;
+ }
+
+ public Color toColor() {
+ if (color == null) {
+ color = new Color((int) scalar.red(), (int) scalar.green(), (int) scalar.blue());
+ }
+ return color;
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java
new file mode 100644
index 0000000..b643efa
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java
@@ -0,0 +1,158 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.wpijavacv;
+
+import java.awt.image.BufferedImage;
+import static com.googlecode.javacv.cpp.opencv_core.*;
+
+/**
+ * A color image
+ * @author Greg Granito
+ */
+public class WPIColorImage extends WPIImage {
+
+ private WPIGrayscaleImage red;
+ private WPIGrayscaleImage blue;
+ private WPIGrayscaleImage green;
+
+ /**
+ * creates a WpiColorImage based on the BufferedImage source
+ * @param imageSrc the source image
+ */
+ public WPIColorImage(BufferedImage imageSrc) {
+ super(imageSrc);
+ }
+
+ WPIColorImage(IplImage imageSrc) {
+ super(imageSrc);
+ }
+
+ /**
+ * Draws a WpiContour
+ * @param c the contour
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawContour(WPIContour c, WPIColor color, int thickness) {
+ cvDrawContours(image, c.getCVSeq(), color.toCvScalar(), color.toCvScalar(), 100, thickness, 8);
+ }
+
+ /**
+ * Draws all the WpiContours in an array
+ * @param c the contours
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawContours(WPIContour[] c, WPIColor color, int thickness){
+ for(WPIContour con:c){
+ drawContour(con, color, thickness);
+ }
+ }
+
+ /**
+ * Draws a line between the two specified WpiPoints
+ * @param p1 Point 1
+ * @param p2 Point 2
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawLine(WPIPoint p1, WPIPoint p2, WPIColor color, int thickness){
+ cvLine(image, p1.getCvPoint(), p2.getCvPoint(), color.toCvScalar(), thickness, 8, 0);
+ }
+
+ /**
+ * Draws a WpiPolygon
+ * @param p the WpiPolygon
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawPolygon(WPIPolygon p, WPIColor color, int thickness){
+ cvDrawContours(image, p.getCVSeq(), color.toCvScalar(), color.toCvScalar(), 100, thickness, 8);
+ }
+
+ /**
+ * Draws a WpiPoint
+ * @param p the WpiPoint
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawPoint(WPIPoint p, WPIColor color, int thickness){
+ cvDrawCircle(image,p.getCvPoint(), thickness, color.toCvScalar(), CV_FILLED, 8, 0);
+ }
+ /**
+ * Draws all the WpiPoints in an array
+ * @param p the WpiPoint array
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawPoints(WPIPoint[] p, WPIColor color, int thickness){
+ for(int i = 0; i< p.length; i++)
+ drawPoint(p[i], color, thickness);
+ }
+ /**
+ * Draws all the WpiPolygons in an array
+ * @param p the WpiPolygon array
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawPolygons(WPIPolygon[] p, WPIColor color, int thickness){
+ for(WPIPolygon polygon : p){
+ if(polygon != null && !polygon.getCVSeq().isNull())
+ drawPolygon(polygon, color, thickness);
+ }
+ }
+ /**
+ * Draws a rectangle
+ * @param x the top left corner x coord
+ * @param y the top left corner y coord
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ * @param color the desired WPIColor
+ * @param thickness the thickness in pixels
+ */
+ public void drawRect(int x, int y, int width, int height, WPIColor color, int thickness){
+ cvDrawRect(image, cvPoint(x, y),cvPoint(x+width, y+height), color.toCvScalar(), thickness, 8, 0);
+ }
+
+ private void generateChannels() {
+ if (red == null) {
+ IplImage redChannel = IplImage.create(image.cvSize(), 8, 1);
+ IplImage greenChannel = IplImage.create(image.cvSize(), 8, 1);
+ IplImage blueChannel = IplImage.create(image.cvSize(), 8, 1);
+ cvSplit(image, blueChannel, greenChannel, redChannel, null);
+ red = new WPIGrayscaleImage(redChannel);
+ blue = new WPIGrayscaleImage(blueChannel);
+ green = new WPIGrayscaleImage(greenChannel);
+ }
+ }
+
+ /**
+ * Gets the red channel from the color image
+ * @return a WpiGrayscaleImage that represents the red channel of the WPIColor image
+ */
+ public WPIGrayscaleImage getRedChannel() {
+ generateChannels();
+ return red;
+ }
+
+ /**
+ * Gets the blue channel from the color image
+ * @return a WpiGrayscaleImage that represents the blue channel of the WPIColor image
+ */
+ public WPIGrayscaleImage getBlueChannel() {
+ generateChannels();
+ return blue;
+ }
+
+ /**
+ * Gets the green channel from the color image
+ * @return a WpiGrayscaleImage that represents the green channel of the WPIColor image
+ */
+
+ public WPIGrayscaleImage getGreenChannel() {
+ generateChannels();
+ return green;
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java
new file mode 100644
index 0000000..5cd9fc4
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java
@@ -0,0 +1,100 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package edu.wpi.first.wpijavacv;
+
+import static com.googlecode.javacv.cpp.opencv_core.*;
+import static com.googlecode.javacv.cpp.opencv_imgproc.*;
+/**
+ * This is a class that represents contours, it can only be obtained from
+ * the findContours() method of WpiBinaryImage
+ * @author Greg Granito
+ */
+public class WPIContour extends WPIDisposable {
+
+ CvSeq contours;
+ CvRect rect;
+
+ WPIContour(CvSeq contours) {
+ this.contours = contours;
+// memStorage = contours.storage();
+
+ }
+
+ CvSeq getCVSeq(){
+ return contours;
+ }
+
+ /**
+ *
+ * @return the height of the bounding rectangle of the contour
+ */
+ public int getHeight(){
+ if(rect == null || rect.isNull())
+ rect = cvBoundingRect(contours, 1);
+
+ return rect.height();
+ }
+
+ /**
+ *
+ * @return the width of the bounding rectangle of the contour
+ */
+ public int getWidth(){
+ if(rect == null || rect.isNull())
+ rect = cvBoundingRect(contours, 1);
+
+ return rect.width();
+ }
+
+ /**
+ *
+ * @return the x coord of the top left corner of the bounding rectangle
+ */
+ public int getX(){
+ if(rect == null || rect.isNull())
+ rect = cvBoundingRect(contours, 1);
+
+ return rect.x();
+ }
+
+ /**
+ *
+ * @return the y coord of the top left corner of the bounding rectangle
+ */
+ public int getY(){
+ if(rect == null || rect.isNull())
+ rect = cvBoundingRect(contours, 1);
+
+ return rect.y();
+ }
+
+ /**
+ *
+ * @param percentAccuracy the percentage the perimeter of the polygon can be off
+ * the perimeter of the contour. The higher the value, the fewer points the polygon
+ * will have. A value of 4-5 is recommended.
+ * @return the approximated WpiPolygon
+ */
+ public synchronized WPIPolygon approxPolygon(double percentAccuracy){
+ WPIPolygon polygon = new WPIPolygon(cvApproxPoly(contours, contours.header_size(), contours.storage(), CV_POLY_APPROX_DP, percentAccuracy, 0));
+ if (getPool() != null) {
+ getPool().addToPool(polygon);
+ }
+ return polygon;
+ }
+
+ /**
+ *
+ * @return the perimeter of the contour
+ */
+ public int getlength(){
+ return (int) cvContourPerimeter(contours);
+ }
+
+ public void disposed() {
+ contours.deallocate();
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java
new file mode 100644
index 0000000..6d4b23b
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java
@@ -0,0 +1,119 @@
+package edu.wpi.first.wpijavacv;
+
+import com.googlecode.javacpp.Pointer;
+
+/**
+ * This class is an abstract class defining disposable elements.
+ * It is the superclass of most things which links directly to the javacv core.
+ *
+ * @author Joe Grinstead
+ */
+public abstract class WPIDisposable {
+
+ /** The memory pool to report to when disposed, or null if there is none */
+ private WPIMemoryPool pool = null;
+
+ /** Whether or not this is disposed */
+ private boolean disposed = false;
+
+ /**
+ * Sets the {@link WPIMemoryPool} that this disposable is linked to.
+ * @param pool the pool to link to (null is allowed)
+ */
+ protected void setPool(WPIMemoryPool pool) {
+ this.pool = pool;
+ }
+
+ protected WPIMemoryPool getPool() {
+ return pool;
+ }
+
+ /**
+ * Disposes this object. This may be called multiple times.
+ *
+ * Programmers should call this when they no longer need an object. However, even if they don't, this will be called
+ * when this object is collected by the garbage collector.
+ */
+ public void dispose() {
+ if (!disposed) {
+ disposed = true;
+ disposed();
+ if (pool != null) {
+ pool.removeFromPool(this);
+ }
+ }
+ }
+
+ /**
+ * This is called when {@link WPIDisposable#dispose() dispose()} is called for the first time.
+ * Subclasses should clear out whatever internal resources they are using.
+ */
+ protected abstract void disposed();
+
+ /**
+ * Returns whether or not this object is disposed.
+ * @return whether or not this object is disposed
+ *
+ * @see WPIDisposable#dispose() dispose()
+ */
+ public boolean isDisposed() {
+ return disposed;
+ }
+
+ /**
+ * Checks if this {@link WPIDisposable} has already been disposed. If it has,
+ * then it will throw a {@link DisposedException} with a default message.
+ */
+ protected void validateDisposed() {
+ if (disposed) {
+ throw new DisposedException(this + " has been disposed");
+ }
+ }
+
+ /**
+ * Checks if this {@link WPIDisposable} has already been disposed. If it has,
+ * then it will throw a {@link DisposedException} with the given message.
+ * @param message the message to give the exception
+ */
+ protected void validateDisposed(String message) {
+ if (disposed) {
+ throw new DisposedException(message);
+ }
+ }
+
+ /**
+ * Returns whether or not the given pointer is null in either the java sense or the javacv sense.
+ * @param pointer the pointer
+ * @return whether it is null
+ */
+ protected static boolean isNull(Pointer pointer) {
+ return pointer == null || pointer.isNull();
+ }
+
+ /**
+ * Attempts to free (in the c sense) the given pointer. Does nothing if given null.
+ * @param pointer the pointer to free
+ */
+ protected static void free(Pointer pointer) {
+ if (pointer != null && !pointer.isNull()) {
+ pointer.deallocate();
+ }
+ }
+
+ /**
+ * An exception to be thrown if an element has already been disposed and the user attempts to
+ * perform an operation on it.
+ */
+ public static class DisposedException extends RuntimeException {
+
+ public DisposedException(String message) {
+ super(message);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ dispose();
+ super.finalize();
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java
new file mode 100644
index 0000000..e52bced
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java
@@ -0,0 +1,151 @@
+package edu.wpi.first.wpijavacv;
+
+import com.googlecode.javacv.FFmpegFrameGrabber;
+import static com.googlecode.javacv.cpp.opencv_core.*;
+import com.googlecode.javacv.cpp.opencv_core.IplImage;
+
+/**
+ * A class used to gather images from the robot's camera.
+ * @author Joe Grinstead and Greg Granito
+ */
+public class WPIFFmpegVideo extends WPIDisposable {
+
+ private FFmpegFrameGrabber grabber;
+ private IplImage image;
+ private boolean readImage = true;
+ private boolean badConnection = false;
+ private final Object imageLock = new Object();
+ private final Object grabberLock = new Object();
+
+ public WPIFFmpegVideo(final String path) {
+ new Thread() {
+
+ @Override
+ public void run() {
+ grabber = new FFmpegFrameGrabber(path);
+ grabber.setFrameRate(1.0);
+ try {
+ grabber.start();
+
+ while (!isDisposed()) {
+ try {
+ IplImage newest;
+ synchronized (grabberLock) {
+ if (isDisposed()) {
+ return;
+ }
+ newest = grabber.grab();
+ }
+ if (isNull(newest)) {
+ synchronized (imageLock) {
+ badConnection = true;
+ imageLock.notify();
+ }
+ return;
+ } else {
+ synchronized (imageLock) {
+ if (image == null) {
+ image = cvCreateImage(newest.cvSize(), newest.depth(), newest.nChannels());
+ }
+ cvCopy(newest, image);
+ readImage = false;
+ badConnection = false;
+ imageLock.notify();
+ }
+ }
+ } catch (Exception ex) {
+ synchronized (imageLock) {
+ badConnection = true;
+ imageLock.notify();
+ }
+ ex.printStackTrace();
+ return;
+ }
+ try {
+ Thread.sleep(20);
+ } catch (InterruptedException ex) {
+ }
+ }
+ } catch (Exception ex) {
+ synchronized (imageLock) {
+ badConnection = true;
+ imageLock.notify();
+ }
+ ex.printStackTrace();
+ }
+ }
+ }.start();
+ }
+
+ public WPIImage getImage() throws BadConnectionException {
+ validateDisposed();
+
+ synchronized (imageLock) {
+ if (badConnection) {
+ throw new BadConnectionException();
+ } else if (image == null) {
+ return null;
+ } else if (image.nChannels() == 1) {
+ return new WPIGrayscaleImage(image.clone());
+ } else {
+ assert image.nChannels() == 3;
+ return new WPIColorImage(image.clone());
+ }
+ }
+ }
+
+ public WPIImage getNewImage(double timeout) throws BadConnectionException {
+ validateDisposed();
+
+ synchronized (imageLock) {
+ readImage = true;
+ while (readImage && !badConnection) {
+ try {
+ badConnection = true;
+ imageLock.wait((long) (timeout * 1000));
+ } catch (InterruptedException ex) {
+ }
+ }
+ readImage = true;
+
+ if (badConnection) {
+ throw new BadConnectionException();
+ } else if (image.nChannels() == 1) {
+ return new WPIGrayscaleImage(image.clone());
+ } else {
+ assert image.nChannels() == 3;
+ return new WPIColorImage(image.clone());
+ }
+ }
+ }
+
+ public WPIImage getNewImage() throws BadConnectionException {
+ return getNewImage(0);
+ }
+
+ @Override
+ protected void disposed() {
+ try {
+ synchronized (imageLock) {
+ if (!isNull(image)) {
+ image.release();
+ }
+ image = null;
+ }
+ } catch (Exception ex) {
+ }
+ }
+
+ /**
+ * An exception that occurs when the camera can not be reached.
+ * @author Greg Granito
+ */
+ public static class BadConnectionException extends Exception {
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ grabber.stop();
+ super.finalize();
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java
new file mode 100644
index 0000000..facbb09
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java
@@ -0,0 +1,53 @@
+/*
+ * To change this template, choose Tools | Templates
+ * getAnd open the template in the editor.
+ */
+package edu.wpi.first.wpijavacv;
+
+import static com.googlecode.javacv.cpp.opencv_core.*;
+import static com.googlecode.javacv.cpp.opencv_imgproc.*;
+
+/**
+ * A grayscale image
+ *
+ * @author Greg Granito
+ */
+public class WPIGrayscaleImage extends WPIImage {
+
+ WPIGrayscaleImage(IplImage imageSrc) {
+ super(imageSrc);
+ }
+
+ /**
+ * Returns a black and white image where every pixel that is higher (in the 0-255 scale) than the given threshold is <bold>white</bold>,
+ * and everything below is <bold>black</bold>.
+ * @param threshold a value 0-255. if a pixel has a value below the theshold, it becomes black
+ * if the pixel value is above or equal to the threshold, the pixel becomes white
+ * @return a new {@link WPIBinaryImage} that represents the threshold
+ */
+ public WPIBinaryImage getThreshold(int threshold) {
+ validateDisposed();
+
+ IplImage bin = IplImage.create(image.cvSize(), 8, 1);
+ cvThreshold(image, bin, threshold, 255, CV_THRESH_BINARY);
+ return new WPIBinaryImage(bin);
+ }
+
+ /**
+ * Returns a black and white image where every pixel that is higher (in the 0-255 scale) than the given threshold is <bold>black</bold>,
+ * and everything below is <bold>white</bold>.
+ *
+ * In other words, this will return the inverted image of {@link WpiGrayscaleImage#getThreshold(int) getThreshold(...)} but is
+ * more efficient than calling {@link WpiGrayscaleImage#getThreshold(int) getThreshold(...)}.{@link WPIBinaryImage#getInverse() getInverse()}
+ * @param threshold a value 0-255. if a pixel has a value below the theshold, it becomes black
+ * if the pixel value is above or equal to the threshold, the pixel becomes white
+ * @return a new {@link WPIBinaryImage} that represents the threshold
+ */
+ public WPIBinaryImage getThresholdInverted(int threshold) {
+ validateDisposed();
+
+ IplImage bin = IplImage.create(image.cvSize(), 8, 1);
+ cvThreshold(image, bin, threshold, 255, CV_THRESH_BINARY_INV);
+ return new WPIBinaryImage(bin);
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java
new file mode 100644
index 0000000..609aaaa
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java
@@ -0,0 +1,76 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.wpijavacv;
+
+import static com.googlecode.javacv.cpp.opencv_core.*;
+import java.awt.image.BufferedImage;
+
+/**
+ * This class is the superclass of all images.
+ *
+ * @author Greg Granito
+ */
+public class WPIImage extends WPIDisposable {
+
+ /** The underlying {@link IplImage} */
+ protected IplImage image;
+
+ /**
+ * Instantiates a {@link WPIImage} from a {@link BufferedImage}.
+ * Useful for interacting with swing.
+ * This will not keep a reference to the given image, so any modification to this
+ * {@link WPIImage} will not change the given image.
+ * @param image the image to copy into a {@link WPIImage}
+ */
+ public WPIImage(BufferedImage image) {
+ this(IplImage.createFrom(image));
+ }
+
+ /**
+ * Instantiates a {@link WPIImage} from the given {@link IplImage}.
+ * The resulting image will be directly wrapped around the given image, and so any modifications
+ * to the {@link WPIImage} will reflect on the given image.
+ * @param image the image to wrap
+ */
+ public WPIImage(IplImage image) {
+ this.image = image;
+ }
+
+ /**
+ * Returns the width of the image.
+ * @return the width in pixels of the image
+ */
+ public int getWidth() {
+ validateDisposed();
+
+ return image.width();
+ }
+
+ /**
+ * Returns the height of the image.
+ * @return the height in pixels of the image
+ */
+ public int getHeight() {
+ validateDisposed();
+
+ return image.height();
+ }
+
+ /**
+ * Copies this {@link WPIImage} into a {@link BufferedImage}.
+ * This method will always generate a new image.
+ * @return a copy of the image
+ */
+ public BufferedImage getBufferedImage() {
+ validateDisposed();
+
+ return image.getBufferedImage();
+ }
+
+ @Override
+ protected void disposed() {
+ image.release();
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java
new file mode 100644
index 0000000..b31da02
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java
@@ -0,0 +1,41 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package edu.wpi.first.wpijavacv;
+
+import static com.googlecode.javacv.cpp.opencv_highgui.*;
+
+/**
+ * A class with utility functions. Use this instead of Thread.sleep()
+ * @author Greg Granito
+ */
+public class WPIJavaCVUtils {
+
+ /**
+ * Waits for the specified key to be pressed
+ * @param key the key (case sensitive)
+ */
+ public static void waitForKey(char key){
+ while(cvWaitKey() != key);
+ }
+
+ /**
+ * waits until any key has been pressed
+ * @return the key that was pressed
+ */
+ public static char waitForAnyKey(){
+ return (char)cvWaitKey();
+ }
+
+ /**
+ * Waits until the timeout or until a key is pressed
+ * @param key the case sensitive key
+ * @param timeoutMillis the timeout
+ * @return returns whether the key was pressed (false if the timeout occurs)
+ */
+ public static boolean keyIsPressed(char key, int timeoutMillis){
+ return cvWaitKey(timeoutMillis) == key;
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java
new file mode 100644
index 0000000..1414b35
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java
@@ -0,0 +1,28 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package edu.wpi.first.wpijavacv;
+
+import static com.googlecode.javacv.cpp.opencv_highgui.*;
+/**
+ * A class used to gather images from cameras connected to the laptop
+ * @author Greg
+ */
+public class WPILaptopCamera extends WPIDisposable {
+ CvCapture cam;
+
+ public WPILaptopCamera() {
+ cam = cvCreateCameraCapture(0);
+ }
+
+ public WPIColorImage getCurrentFrame(){
+ return new WPIColorImage(cvQueryFrame(cam));
+ }
+
+ @Override
+ protected void disposed() {
+ }
+
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java
new file mode 100644
index 0000000..1d9a3b4
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java
@@ -0,0 +1,34 @@
+package edu.wpi.first.wpijavacv;
+
+/**
+ * This class allows a bunch of disposable items to be put into a pool which will do something if all of them are disposed.
+ * Subclasses should override the dispose method to react to when everything in the pool is disposed.
+ * @author Joe Grinstead
+ */
+public abstract class WPIMemoryPool extends WPIDisposable {
+
+ /** The number of elements remaining in the pool */
+ private int remaining;
+
+ /**
+ * Adds the given disposable item to the memory pool
+ * @param disposable the item
+ */
+ public synchronized void addToPool(WPIDisposable disposable) {
+ validateDisposed();
+ disposable.setPool(this);
+ remaining++;
+ }
+
+ /**
+ * Removes the given disposable item from the memory pool
+ * @param disposable
+ */
+ public synchronized void removeFromPool(WPIDisposable disposable) {
+ validateDisposed();
+ disposable.setPool(null);
+ if (--remaining <= 0) {
+ dispose();
+ }
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java
new file mode 100644
index 0000000..a5a2016
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java
@@ -0,0 +1,56 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package edu.wpi.first.wpijavacv;
+
+import com.googlecode.javacv.cpp.opencv_core.CvPoint;
+
+/**
+ * A class representing a point
+ * @author Greg Granito
+ */
+public class WPIPoint {
+
+ CvPoint p;
+ /**
+ * Creates a new WpiPoint with the specified x and y coordinates
+ * @param x the x coord
+ * @param y the y coord
+ */
+ public WPIPoint(int x, int y) {
+ p = new CvPoint(x, y);
+ }
+
+ WPIPoint(CvPoint c) {
+ p = c;
+ }
+
+ /**
+ *
+ * @return the x coord
+ */
+ public int getX(){
+ return p.x();
+ }
+
+ /**
+ *
+ * @return the y coord
+ */
+ public int getY(){
+ return p.y();
+ }
+
+ CvPoint getCvPoint(){
+ return p;
+ }
+
+ @Override
+ public String toString() {
+ return p.toString();
+ }
+
+
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java
new file mode 100644
index 0000000..a54a297
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java
@@ -0,0 +1,126 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.wpijavacv;
+
+import static com.googlecode.javacv.cpp.opencv_imgproc.*;
+import static com.googlecode.javacv.cpp.opencv_core.*;
+
+/**
+ * A class that represents a polygon, can be obtained from approxPolygon()
+ * in WpiContour
+ * @author Greg Granito
+ */
+public class WPIPolygon extends WPIDisposable {
+
+ CvSeq polygon;
+ CvRect boundingRect;
+
+ WPIPolygon(CvSeq data) {
+ polygon = data;
+ }
+
+ CvSeq getCVSeq() {
+ return polygon;
+ }
+
+ /**
+ *
+ * @return an array of WpiPoints of the vertices of the polygon
+ */
+ public WPIPoint[] getPoints() {
+ CvPoint points = new CvPoint(getNumVertices());
+ WPIPoint[] wpiPoints= new WPIPoint[getNumVertices()];
+ cvCvtSeqToArray(polygon, points.position(0), CV_WHOLE_SEQ);
+ for (int j = 0; j < getNumVertices(); j++) {
+
+ wpiPoints[j] = new WPIPoint(points.position(j).x(), points.position(j).y());
+
+ }
+ return wpiPoints;
+ }
+
+ /**
+ *
+ * @return the width of the bounding rectangle of the polygon
+ */
+ public int getWidth() {
+ if (boundingRect == null) {
+ boundingRect = cvBoundingRect(polygon, 0);
+ }
+ return boundingRect.width();
+ }
+
+ /**
+ *
+ * @return the height of the bounding rectangle of the polygon
+ */
+ public int getHeight() {
+ if (boundingRect == null) {
+ boundingRect = cvBoundingRect(polygon, 0);
+ }
+ return boundingRect.height();
+ }
+
+ /**
+ *
+ * @return the x coord of the top left corner of the bounding
+ * rectangle of the polygon
+ */
+ public int getX() {
+ if (boundingRect == null) {
+ boundingRect = cvBoundingRect(polygon, 0);
+ }
+ return boundingRect.x();
+ }
+
+ /**
+ *
+ * @return the y coord of the top left corner of the bounding
+ * rectangle of the polygon
+ */
+ public int getY() {
+ if (boundingRect == null) {
+ boundingRect = cvBoundingRect(polygon, 0);
+ }
+ return boundingRect.y();
+ }
+
+ /**
+ *
+ * @return the number of vertices in the polygon
+ */
+ public int getNumVertices() {
+ return polygon.total();
+
+ }
+
+ /**
+ *
+ * @return whether or not the polygon is convex
+ */
+ public boolean isConvex() {
+ return cvCheckContourConvexity(polygon) == 0 ? false : true;
+ }
+
+ /**
+ *
+ * @return the area in pixels of the polygon
+ */
+ public int getArea() {
+ return Math.abs((int) cvContourArea(polygon, CV_WHOLE_SEQ, -1));
+ }
+
+ /**
+ *
+ * @return the perimeter in pixels of the polygon
+ */
+ public int getPerimeter() {
+ return (int) cvArcLength(polygon, CV_WHOLE_SEQ, -1);
+ }
+
+ public void disposed() {
+ polygon.deallocate();
+ }
+}
diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java
new file mode 100644
index 0000000..f15db45
--- /dev/null
+++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java
@@ -0,0 +1,52 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.wpijavacv;
+
+import static com.googlecode.javacv.cpp.opencv_highgui.*;
+
+/**
+ * A class that can be used for displaying images
+ * @author Greg Granito
+ */
+public class WPIWindow {
+
+ private static int count = 0;
+
+ private String name;
+
+ /**
+ * Creates a new window with a default name that will be in the format
+ * "Window " + windowNumber
+ */
+ public WPIWindow() {
+ count++;
+
+ name = "Window " +count;
+ cvNamedWindow(name);
+
+ }
+
+ /**
+ * Creates a new window with the specified name must be unique
+ * (including the windows named using the default constructor)
+ * @param name the desired name
+ */
+ public WPIWindow(String name) {
+ this.name = name;
+ }
+
+
+ /**
+ * Shows the specified image, must be the same resolution and depth
+ * as the first image the window shows
+ * @param image the image
+ */
+ public void showImage(WPIImage image) {
+
+ if(image != null)
+ cvShowImage(name, image.image);
+ else cvShowImage(name, null);
+ }
+}