This program was written for (and on) a MacBook Air. It takes a photo with the webcam, processes the image, and identifies targets (rectangles of certain sizes).
/* vision.java written by Michael K. Pellegrino */ /* 2018 03 15 */ import java.awt.FlowLayout; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.util.List; import java.util.Vector; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; 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.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import org.opencv.videoio.VideoCapture; public class vision { private static BufferedImage Mat2BufferedImage(Mat m) { int type = BufferedImage.TYPE_BYTE_GRAY; if ( m.channels() > 1 ) { type = BufferedImage.TYPE_3BYTE_BGR; } int bufferSize = m.channels()*m.cols()*m.rows(); byte [] b = new byte[bufferSize]; m.get(0,0,b); // get all the pixels BufferedImage image = new BufferedImage(m.cols(),m.rows(), type); final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); System.arraycopy(b, 0, targetPixels, 0, b.length); return image; } private static void saveImage(String fname, Mat img) { Imgcodecs.imwrite(fname, img); } private static Mat readImage(String fname) { return Imgcodecs.imread( fname ); } private static void displayImage(Image img2) { ImageIcon icon=new ImageIcon(img2); JFrame frame=new JFrame(); frame.setLayout(new FlowLayout()); frame.setSize(img2.getWidth(null)+50, img2.getHeight(null)+50); JLabel lbl=new JLabel(); lbl.setIcon(icon); frame.add(lbl); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } // ============================================== public static void main(String[] args) { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); //double hue[]= {21.0, 57.3}; //double sat[]= {16.1, 98.3}; //double lum[]= {160.5, 255.0}; List<MatOfPoint> contours = new Vector<MatOfPoint>(); List<Rect> possibleTargets = new Vector<Rect>(); contours.clear(); possibleTargets.clear(); Mat hierarchy = new Mat(); VideoCapture vc = new VideoCapture(0); Mat m = new Mat(); Mat original = new Mat(); int t=0; try { Thread.sleep(90); } catch (InterruptedException ie) { ie.printStackTrace(); } while( !vc.read(original) ) { t++; } if(!vc.isOpened()) System.out.println("Video capturing hasn't been correctly initialized."); else { System.out.println("The camera has been correctly initialized."); //vc.read(original); } original = readImage("green-tape.jpg"); vc.release(); // Resize the image //Imgproc.resize(original, original, new Size(640,480)); //saveImage( "original.jpg", original); // Blur it a bit //Imgproc.blur(original, m, new Size(5,5)); // Convert it from Blue-Green-Red to Greyscale //Imgproc.cvtColor(m, m, Imgproc.COLOR_BGR2GRAY); // Only take the brightest from each channel //Core.inRange(m, new Scalar(218,241,213), new Scalar(255,255,255), m); Core.inRange(original, new Scalar(209,0,0), new Scalar(255,255,255), m); // Make the image negative Core.bitwise_not(m, m); // Now look for Rectangles Imgproc.findContours( m, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE); System.out.println( "Number of Contours: " + contours.size()); // Convert it back to Blue-Green-Red //Imgproc.cvtColor(m, m, Imgproc.COLOR_GRAY2BGR); // Look at each of the contours that were found // If their height to width ratio is roughly that // of the retro-reflective tape, put a GREEN rectangle around it // if it's not the same ratio, then put a red // rectangle around it. // // The tape on the field consists of two 2"x16" pieces // of retro-reflective tape that are 4" apart. // for( int i=0; i<contours.size(); i++) { Rect r = Imgproc.boundingRect(contours.get(i)); //if( Math.abs( (r.height/r.width) - 8 ) < 1 ) // <<--- for competitions because the tape is 2"x16"... not 2"x15" //but it's really going to depend on the camera... they vary with different x:y ratios. if( Math.abs( (r.height/r.width) - 6 ) < 1.5 ) { Imgproc.rectangle(original, new Point(r.x, r.y), new Point( r.x+r.width, r.y+r.height), new Scalar( 0,0,255)); possibleTargets.add(r); } else { Imgproc.rectangle(original, new Point(r.x, r.y), new Point( r.x+r.width, r.y+r.height), new Scalar( 0,255,0)); //possibleTargets.add(r); // take this out later - it's just for debugging } } // Now go through the possible targets and check to see if: // * two of them are close in height/width // * the center-y's of those two are within 5 or so pixels // * the center-x's of those two are around 3 widths apart if( possibleTargets.size() > 1) { for( int i=0; i<possibleTargets.size(); i++) { Rect r = possibleTargets.get(i); double cx = (2*r.x + r.width)/2; double cy = (2*r.y + r.height)/2; double cw = r.width; for( int j=i; j<possibleTargets.size(); j++ ) { Rect r2 = possibleTargets.get(j); double cx2 = (2*r2.x + r2.width)/2; double cy2 = (2*r2.y + r2.height)/2; double cw2 = r2.width; double cwA = 3*(cw+cw2)/2; // this is about how far apart the targets should be - the (average of the 2 widths) * 3 // ================================= if( !( (cx==cx2) && (cy==cy2) ) && (Math.abs( cy-cy2) < 5) && (Math.abs(cw-cw2) < 5) && ((Math.abs(cx2-cx)-cwA) < 5 )) { // this means that the centerY's are within 5 pixels of each other // and that the centers aren't the same // and that the two targets' widths are within 5 pixels // and that the distance between them is about 3 widths System.out.println( "I found two reflective strips that meet the required parameters!"); Imgproc.arrowedLine(original, new Point(320,470), new Point( cx, cy ) , new Scalar(0,0,255),1,4,0,0.05); Imgproc.arrowedLine(original, new Point(320,470), new Point( cx2, cy2 ) , new Scalar(0,0,255),1,4,0,0.05); } // ================================= } // Put a circle in the center of possible target and draw an arrow to it Imgproc.circle( original, new Point( cx, cy), 2, new Scalar(0,0,255), 6); System.out.println( "Center of possible target " + (i+1) + " is (" + cx + ", " + cy + ") H: " + r.height + " W: " + r.width ); } } else if( possibleTargets.size()==1) { Rect r = possibleTargets.get(0); double cx = (2*r.x + r.width)/2; double cy = (2*r.y + r.height)/2; double cw = r.width; Imgproc.circle( original, new Point( cx, cy), 2, new Scalar(0,0,255), 6); System.out.println( "Center of sole possible target " + (1) + " is (" + cx + ", " + cy + ") H: " + r.height + " W: " + cw); } else { System.out.println( "There are no possible targets visible to me." ); } // Put a big rectangle around the image Imgproc.rectangle(original, new Point(10,10), new Point(630,470), new Scalar(0,0,255)); // Put Team Number on Image Imgproc.putText(original,"Team 5752", new Point(15,30),0, 0.5, new Scalar( 0,0,255) ); // ===================== // saveImage( "processed.jpg", original); BufferedImage b = Mat2BufferedImage(original); displayImage( b ); } }