Hello CD,
Can anyone please provide help or an explanation of how to implement the sample Vision Code for tracking the Targets in this year’s game? For example, how can we take the provided “scoring methods” and use them to make a motor run faster, or rotate an arm at an angle? Thank you very much.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.usfirst.frc4.Team4Ascent.subsystems;
import edu.wpi.first.wpilibj.camera.AxisCamera;
import edu.wpi.first.wpilibj.image.BinaryImage;
import edu.wpi.first.wpilibj.image.ColorImage;
import edu.wpi.first.wpilibj.image.CriteriaCollection;
import edu.wpi.first.wpilibj.image.LinearAverages;
import edu.wpi.first.wpilibj.image.NIVision;
import edu.wpi.first.wpilibj.image.NIVision.MeasurementType;
import edu.wpi.first.wpilibj.image.NIVision.Rect;
import edu.wpi.first.wpilibj.image.NIVisionException;
import edu.wpi.first.wpilibj.image.ParticleAnalysisReport;
import edu.wpi.first.wpilibj.image.RGBImage;
/**
*
* @author robotics4
*/
public class Camera{
//Constants from WPI
final int XMAXSIZE = 24;
final int XMINSIZE = 24;
final int YMAXSIZE = 24;
final int YMINSIZE = 48;
final double xMax] = {1, 1, 1, 1, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, 1, 1, 1, 1};
final double xMin] = {.4, .6, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, .1, 0.6, 0};
final double yMax] = {1, 1, 1, 1, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, 1, 1, 1, 1};
final double yMin] = {.4, .6, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05,
.05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05, .05,
.05, .05, .6, 0};
final int RECTANGULARITY_LIMIT = 60;
final int ASPECT_RATIO_LIMIT = 75;
final int X_EDGE_LIMIT = 40;
final int Y_EDGE_LIMIT = 60;
final int X_IMAGE_RES = 320; //X Image resolution in pixels, should be 160, 320 or 640
// final double VIEW_ANGLE = 43.5; //Axis 206 camera
final double VIEW_ANGLE = 48; //Axis M1011 camera
AxisCamera camera; // the axis camera object (connected to the switch)
CriteriaCollection cc; // the criteria for doing the particle filter operation
public class Scores {
double rectangularity;
double aspectRatioInner;
double aspectRatioOuter;
double xEdge;
double yEdge;
}
public void init() {
cc = new CriteriaCollection();
cc.addCriteria(MeasurementType.IMAQ_MT_AREA, 500, 65535, false);
}
public void auton(){
try {
//ColorImage image = camera.getImage(); // comment if using stored images
ColorImage image; // next 2 lines read image from flash on cRIO
image = new RGBImage("/testImage.jpg"); // get the sample image from the cRIO flash
BinaryImage thresholdImage = image.thresholdHSV(60, 100, 90, 255, 20, 255); // keep only red objects
//thresholdImage.write("/threshold.bmp");
BinaryImage convexHullImage = thresholdImage.convexHull(false); // fill in occluded rectangles
//convexHullImage.write("/convexHull.bmp");
BinaryImage filteredImage = convexHullImage.particleFilter(cc); //filteredImage.write("/filteredImage.bmp");
//filteredImage.write("/filteredImage.bmp");
Scores scores] = new Scores[filteredImage.getNumberParticles()];
for (int i = 0; i < scores.length; i++) {
ParticleAnalysisReport report = filteredImage.getParticleAnalysisReport(i);
scores* = new Scores();
scores*.rectangularity = scoreRectangularity(report);
scores*.aspectRatioOuter = scoreAspectRatio(filteredImage,report, i, true);
scores*.aspectRatioInner = scoreAspectRatio(filteredImage, report, i, false);
scores*.xEdge = scoreXEdge(thresholdImage, report);
scores*.yEdge = scoreYEdge(thresholdImage, report);
if(scoreCompare(scores*, false))
{
System.out.println("particle: " + i + "is a High Goal centerX: " + report.center_mass_x_normalized + "centerY: " + report.center_mass_y_normalized);
System.out.println("Distance: " + computeDistance(thresholdImage, report, i, false));
} else if (scoreCompare(scores*, true)) {
System.out.println("particle: " + i + "is a Middle Goal centerX: " + report.center_mass_x_normalized + "centerY: " + report.center_mass_y_normalized);
System.out.println("Distance: " + computeDistance(thresholdImage, report, i, true));
} else {
System.out.println("particle: " + i + "is not a goal centerX: " + report.center_mass_x_normalized + "centerY: " + report.center_mass_y_normalized);
}
System.out.println("rect: " + scores*.rectangularity + "ARinner: " + scores*.aspectRatioInner);
System.out.println("ARouter: " + scores*.aspectRatioOuter + "xEdge: " + scores*.xEdge + "yEdge: " + scores*.yEdge);
filteredImage.free();
convexHullImage.free();
thresholdImage.free();
image.free();
}
} catch (NIVisionException ex) {
ex.printStackTrace();
}
}
double computeDistance (BinaryImage image, ParticleAnalysisReport report, int particleNumber, boolean outer) throws NIVisionException {
double rectShort, height;
int targetHeight;
rectShort = NIVision.MeasureParticle(image.image, particleNumber, false, MeasurementType.IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE);
//using the smaller of the estimated rectangle short side and the bounding rectangle height results in better performance
//on skewed rectangles
height = Math.min(report.boundingRectHeight, rectShort);
targetHeight = outer ? 29 : 21;
return X_IMAGE_RES * targetHeight / (height * 12 * 2 * Math.tan(VIEW_ANGLE*Math.PI/(180*2)));
}
public double scoreAspectRatio(BinaryImage image, ParticleAnalysisReport report, int particleNumber, boolean outer) throws NIVisionException
{
double rectLong, rectShort, aspectRatio, idealAspectRatio;
rectLong = NIVision.MeasureParticle(image.image, particleNumber, false, MeasurementType.IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE);
rectShort = NIVision.MeasureParticle(image.image, particleNumber, false, MeasurementType.IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE);
idealAspectRatio = outer ? (62/29) : (62/20); //Dimensions of goal opening + 4 inches on all 4 sides for reflective tape
//Divide width by height to measure aspect ratio
if(report.boundingRectWidth > report.boundingRectHeight){
//particle is wider than it is tall, divide long by short
aspectRatio = 100*(1-Math.abs((1-((rectLong/rectShort)/idealAspectRatio))));
} else {
//particle is taller than it is wide, divide short by long
aspectRatio = 100*(1-Math.abs((1-((rectShort/rectLong)/idealAspectRatio))));
}
return (Math.max(0, Math.min(aspectRatio, 100.0))); //force to be in range 0-100
}
/**
* Compares scores to defined limits and returns true if the particle appears to be a target
*
* @param scores The structure containing the scores to compare
* @param outer True if the particle should be treated as an outer target, false to treat it as a center target
*
* @return True if the particle meets all limits, false otherwise
*/
boolean scoreCompare(Scores scores, boolean outer){
boolean isTarget = true;
isTarget &= scores.rectangularity > RECTANGULARITY_LIMIT;
if(outer){
isTarget &= scores.aspectRatioOuter > ASPECT_RATIO_LIMIT;
} else {
isTarget &= scores.aspectRatioInner > ASPECT_RATIO_LIMIT;
}
isTarget &= scores.xEdge > X_EDGE_LIMIT;
isTarget &= scores.yEdge > Y_EDGE_LIMIT;
return isTarget;
}
/**
* Computes a score (0-100) estimating how rectangular the particle is by comparing the area of the particle
* to the area of the bounding box surrounding it. A perfect rectangle would cover the entire bounding box.
*
* @param report The Particle Analysis Report for the particle to score
* @return The rectangularity score (0-100)
*/
double scoreRectangularity(ParticleAnalysisReport report){
if(report.boundingRectWidth*report.boundingRectHeight !=0){
return 100*report.particleArea/(report.boundingRectWidth*report.boundingRectHeight);
} else {
return 0;
}
}
/**
* Computes a score based on the match between a template profile and the particle profile in the X direction. This method uses the
* the column averages and the profile defined at the top of the sample to look for the solid vertical edges with
* a hollow center.
*
* @param image The image to use, should be the image before the convex hull is performed
* @param report The Particle Analysis Report for the particle
*
* @return The X Edge Score (0-100)
*/
public double scoreXEdge(BinaryImage image, ParticleAnalysisReport report) throws NIVisionException
{
double total = 0;
LinearAverages averages;
Rect rect = new Rect(report.boundingRectTop, report.boundingRectLeft, report.boundingRectHeight, report.boundingRectWidth);
averages = NIVision.getLinearAverages(image.image, LinearAverages.LinearAveragesMode.IMAQ_COLUMN_AVERAGES, rect);
float columnAverages] = averages.getColumnAverages();
for(int i=0; i < (columnAverages.length); i++){
if(xMin(i*(XMINSIZE-1)/columnAverages.length)] < columnAverages*
&& columnAverages* < xMax*){
total++;
}
}
total = 100*total/(columnAverages.length);
return total;
}
/**
* Computes a score based on the match between a template profile and the particle profile in the Y direction. This method uses the
* the row averages and the profile defined at the top of the sample to look for the solid horizontal edges with
* a hollow center
*
* @param image The image to use, should be the image before the convex hull is performed
* @param report The Particle Analysis Report for the particle
*
* @return The Y Edge score (0-100)
*
*/
public double scoreYEdge(BinaryImage image, ParticleAnalysisReport report) throws NIVisionException
{
double total = 0;
LinearAverages averages;
Rect rect = new Rect(report.boundingRectTop, report.boundingRectLeft, report.boundingRectHeight, report.boundingRectWidth);
averages = NIVision.getLinearAverages(image.image, LinearAverages.LinearAveragesMode.IMAQ_ROW_AVERAGES, rect);
float rowAverages] = averages.getRowAverages();
for(int i=0; i < (rowAverages.length); i++){
if(yMin(i*(YMINSIZE-1)/rowAverages.length)] < rowAverages*
&& rowAverages* < yMax*){
total++;
}
}
total = 100*total/(rowAverages.length);
return total;
}
}