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 );	
	}
}