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