USFirst.org
follow us on twitter
like us on facebookMNFTC.org
Bookmark and Share

Thanks

The team members of FRC Team 2220 would like to give a big thanks to all of the Sponsors, Mentors and Parents that make having this team possible.

Thank you!

Team Login



TrackAPI.cpp Create PDF Send to Printer Email this Article
Written by TarinB   
Saturday, February 13 2010 16:49

/********************************************************************************

*  Project   : FIRST Motor Controller

*  File Name   : TrackAPI.cpp

*  Contributors   : ELF, DWD

*  Creation Date : August 10, 2008

*  Revision History : Source code & revision history maintained at sourceforge.WPI.edu

*  File Description : Tracking Routines for FIRST Vision API

*/

/*----------------------------------------------------------------------------*/

/*        Copyright (c) FIRST 2008.  All Rights Reserved.                     */

/*  Open Source Software - may be modified and shared by FRC teams. The code  */

/*  must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */

/*----------------------------------------------------------------------------*/

 

#include "string.h"

#include "vxWorks.h"

 

#include "AxisCamera.h"

#include "FrcError.h"

#include "TrackAPI.h"

#include "Utility.h"

#include "VisionAPI.h"

 

int TrackAPI_debugFlag = 0;

#define DPRINTF if(TrackAPI_debugFlag)dprintf

 

/** image quality requirement: particle must be .0025 of pixels */

#define PARTICLE_TO_IMAGE_PERCENT 0.25

 

/**

* @brief Find the largest particle that meets a criteria

* @param binaryImage Image to inspect

* @param rect area to search

* @return 0 = error

*/

bool InArea(Image* binaryImage, int particleIndex, Rect rect)

{

char funcName[]="InArea";

double position;

imaqMeasureParticle(binaryImage, particleIndex, 0,

IMAQ_MT_BOUNDING_RECT_LEFT, &position);

if ( position < (rect.left             ) ) return false; // outside left of rectangle?

imaqMeasureParticle(binaryImage, particleIndex, 0,

IMAQ_MT_BOUNDING_RECT_TOP, &position);

if ( position < (rect.top              ) ) return false; // outside top of rectangle ?

 

imaqMeasureParticle(binaryImage, particleIndex, 0,

IMAQ_MT_BOUNDING_RECT_RIGHT, &position);

if (position > (rect.left + rect.width) ) return false; // outside right of rectangle ?

imaqMeasureParticle(binaryImage, particleIndex, 0,

IMAQ_MT_BOUNDING_RECT_BOTTOM, &position);

if (position > (rect.top + rect.height) ) return false; // outside bottom of rectangle ?

 

DPRINTF(LOG_INFO, "particle %i is in (%i %i) height %i width %i\n",

particleIndex, rect.left, rect.top, rect.height, rect.width);

return true;

}

 

/**

* @brief Find the largest particle that meets a criteria

* @param binaryImage Image to inspect

* @param largestParticleIndex Index of the largest particle

* @param rect area to search

* @return 0 = error

*/

int GetLargestParticle(Image* binaryImage, int* largestParticleIndex)

{ return GetLargestParticle(binaryImage, largestParticleIndex, IMAQ_NO_RECT); }

 

int GetLargestParticle(Image* binaryImage, int* largestParticleIndex, Rect rect)

{

*largestParticleIndex = 0; // points to caller-provided variable

/* determine number of particles in thresholded image */

int numParticles;

int success = frcCountParticles(binaryImage, &numParticles);

if ( !success ) {  return success; }

/* if no particles found we can quit here */

if (numParticles == 0)  {  return 0; }  // unsuccessful if zero particles found

// find the largest particle

double largestParticleArea = 0;

double particleArea;

for (int i = 0; i < numParticles; ++i) {

success = imaqMeasureParticle(binaryImage, i, 0, IMAQ_MT_AREA, &particleArea);

if ( !success ) { return success; }

if (particleArea > largestParticleArea) {

// see if is in the right area

if ( InArea(binaryImage, i, rect) ) {

largestParticleArea = particleArea;

*largestParticleIndex = i;  // return index to caller

}

}

}

return success;

}

 

/**

* @brief Search for a color. Supports IMAQ_IMAGE_HSL.

* @param color Definition for the hue range

* @param trackReport Values for tracking: center of particle, particle size, color

* @return 0 = error

*/

int FindColor(FrcHue color, ParticleAnalysisReport* trackReport)

{

char funcName[]="FindColor";

int success = 0; // return: 0 = error

/* track color */

// use ACTIVE_LIGHT or WHITE_LIGHT for brightly lit objects

TrackingThreshold td = GetTrackingData(color, PASSIVE_LIGHT);

 

success = FindColor(IMAQ_HSL, &td.hue, &td.saturation, &td.luminance, trackReport);

if ( !success ) {

DPRINTF (LOG_INFO, "did not find color - errorCode= %i",GetLastVisionError());

return success;

}

 

//PrintReport(par);

/* set an image quality restriction */

if (trackReport->particleToImagePercent < PARTICLE_TO_IMAGE_PERCENT) {

imaqSetError(ERR_PARTICLE_TOO_SMALL, funcName);

success = 0;

}

return success;

}

 

/**

* @brief Search for a color. Supports IMAQ_IMAGE_HSL.

* @param hueRange The range for the first plane

* @param trackReport Values for tracking: center of particle, particle size, color

* @return 0 = error

*/

int FindColor(const Range* hueRange, ParticleAnalysisReport *trackReport)

{ return FindColor(hueRange, DEFAULT_SATURATION_THRESHOLD, trackReport); }

 

/**

* @brief Search for a color. Supports IMAQ_IMAGE_HSL.

* @param hueRange The range for the first plane

* @param minSaturation The lower range saturation

* @param trackReport Values for tracking: center of particle, particle size, color

* @return 0 = error

*/

int FindColor(const Range* hueRange, int minSaturation, ParticleAnalysisReport *trackReport)

{

Range satRange;

satRange.minValue = minSaturation;

satRange.maxValue = 255;

Range lumRange;

lumRange.minValue = 0;

lumRange.maxValue = 255;

ColorMode cmode = IMAQ_HSL;

return FindColor(cmode, hueRange, &satRange, &lumRange, trackReport);

}

 

/**

* @brief Search for a color. Supports IMAQ_IMAGE_HSL and IMAQ_IMAGE_RGB.

* @param mode Color mode, either IMAQ_HSL or IMAQ_RGB

* @param plane1Range The range for the first plane (hue or red)

* @param plane2Range The range for the second plane (saturation or green)

* @param plane3Range The range for the third plane (luminance or blue)

* @param trackReport Values for tracking: center of particle, particle size, etc

* @return 0 = error

*/

int FindColor(ColorMode mode, const Range* plane1Range, const Range* plane2Range,

const Range* plane3Range, ParticleAnalysisReport *trackReport)

{

return FindColor(mode, plane1Range, plane2Range, plane3Range, trackReport, NULL);

}

 

/**

* @brief Search for a color. Supports IMAQ_IMAGE_HSL and IMAQ_IMAGE_RGB.

* @param mode Color mode, either IMAQ_HSL or IMAQ_RGB

* @param plane1Range The range for the first plane (hue or red)

* @param plane2Range The range for the second plane (saturation or green)

* @param plane3Range The range for the third plane (luminance or blue)

* @param trackReport Values for tracking: center of particle, particle size, etc

* @param colorReport Color charactaristics of the particle

* @return 0 = error

*/

int FindColor(ColorMode mode, const Range* plane1Range, const Range* plane2Range,

const Range* plane3Range, ParticleAnalysisReport *trackReport,

ColorReport *colorReport)

{

return FindColor(mode, plane1Range, plane2Range, plane3Range, trackReport,

NULL, IMAQ_NO_RECT);

}

 

/**

* @brief Search for a color. Supports IMAQ_IMAGE_HSL and IMAQ_IMAGE_RGB.

* @param mode Color mode, either IMAQ_HSL or IMAQ_RGB

* @param plane1Range The range for the first plane (hue or red)

* @param plane2Range The range for the second plane (saturation or green)

* @param plane3Range The range for the third plane (luminance or blue)

* @param trackReport Values for tracking: center of particle, particle size, etc

* @param colorReport Color charactaristics of the particle

* @param rect Rectangle to confine search to

* @return 0 = error

*/

int FindColor(ColorMode mode, const Range* plane1Range, const Range* plane2Range,

const Range* plane3Range, ParticleAnalysisReport *trackReport,

ColorReport *colorReport, Rect rect)

{

char funcName[]="FindColor";

int errorCode = 0;

int success = 0;

/* create an image object */

Image* cameraImage = frcCreateImage(IMAQ_IMAGE_HSL);

if (!cameraImage)  { return success; }

/* get image from camera - if the camera has not finished initializing,

* this will fail

*/

success = GetImage(cameraImage,NULL);

if (!success){

DPRINTF(LOG_INFO, "No camera Image available Error = %i %s",

errorCode, GetVisionErrorText(errorCode));

frcDispose(cameraImage);

imaqSetError(errorCode, funcName); //reset error code for the caller

return success;

}

/* save a copy of the image to another image for color thresholding later */

Image* histImage = frcCreateImage(IMAQ_IMAGE_HSL);

if (!histImage)  { frcDispose(cameraImage); return success; }

success = frcCopyImage(histImage,cameraImage);

if ( !success ) {

errorCode = GetLastVisionError();

frcDispose(funcName,cameraImage,histImage,NULL);

return success;

}

/* Color threshold the image */

success = frcColorThreshold(cameraImage, cameraImage, mode, plane1Range, plane2Range, plane3Range);

if ( !success ) {

errorCode = GetLastVisionError();

DPRINTF (LOG_DEBUG, "Error = %i  %s ", errorCode, GetVisionErrorText(errorCode));

frcDispose(funcName,cameraImage,histImage,NULL);

return success;

}

 

int largestParticleIndex = 0;

success = GetLargestParticle(cameraImage, &largestParticleIndex, rect );

if ( !success ) {

errorCode = GetLastVisionError();

DPRINTF (LOG_DEBUG, "Error after GetLargestParticle = %i  %s ", errorCode, GetVisionErrorText(errorCode));

frcDispose(funcName,cameraImage,histImage,NULL);

imaqSetError(ERR_COLOR_NOT_FOUND, funcName);

return success;

}

DPRINTF(LOG_INFO, "largestParticleIndex = %i\n", largestParticleIndex);

 

/* Particles were found  */

/*

* Fill in report information for largest particle found

*/

success = frcParticleAnalysis(cameraImage, largestParticleIndex, trackReport);

trackReport->imageTimestamp = GetTime();

/* clean up */

if (!success) {frcDispose(funcName,cameraImage,histImage,NULL); return success;}

/* particle color statistics */

/* only if a color report requested */

if (colorReport != NULL)

{

/* first filter out the other particles */

ParticleFilterCriteria2 criteria;

ParticleFilterOptions* options = NULL;

Rect rect;

int numParticles;

success = frcParticleFilter(cameraImage, cameraImage, &criteria, 1, options,

rect, &numParticles);

if ( !success ) {

DPRINTF(LOG_INFO, "frcParticleFilter errorCode %i", GetLastVisionError());

}

/* histogram the original image using the thresholded image as a mask */

int numClasses = 10; //how many classes?

ColorHistogramReport* chrep = imaqColorHistogram2(histImage, numClasses, IMAQ_HSL,

NULL, cameraImage);

if (chrep == NULL) {

DPRINTF(LOG_INFO, "NULL Color Histogram");

errorCode = GetLastVisionError();

} else {

colorReport->particleHueMax = chrep->plane1.max;

colorReport->particleHueMin = chrep->plane1.min;

colorReport->particleHueMean = chrep->plane1.mean;

colorReport->particleSatMax = chrep->plane2.max;

colorReport->particleSatMin  = chrep->plane2.min;

colorReport->particleSatMean = chrep->plane2.mean;

colorReport->particleLumMax = chrep->plane3.max;

colorReport->particleLumMin = chrep->plane3.min;

colorReport->particleLumMean = chrep->plane3.mean;

colorReport->numberParticlesFound = numParticles;

frcDispose(chrep);

}

}

 

/* clean up */

frcDispose(funcName,cameraImage,histImage,NULL);

return success;

}

 

 

/**

* @brief Search for two colors in relation to each other in an Image.

* Supports IMAQ_IMAGE_HSL color mode.

* @param td1 Tracking data for the first color, including hue, sat, lum

* @param td2 Tracking data for the second color, including hue, sat, lum

* @param position Position of second color in relation to the first:

* ABOVE, BELOW, RIGHT or LEFT

* @param trackReport Particle analysis report with averaged positional data (x, y)

* @return 0 = error

*/

int FindTwoColors(TrackingThreshold td1, TrackingThreshold td2,

SecondColorPosition position, ParticleAnalysisReport *trackReport)

{

char funcName[]="FindTwoColors";

ParticleAnalysisReport secondTrackReport;

// search for the first color

int success = FindColor(IMAQ_HSL,

&td1.hue, &td1.saturation, &td1.luminance,

trackReport, NULL);

if (!success) return success;

DPRINTF(LOG_DEBUG, "found first color: %s", td1.name);

// found the first color, now look for the second color

success = FindColor(IMAQ_HSL,

&td2.hue, &td2.saturation, &td2.luminance,

&secondTrackReport, NULL);

if (!success) return success;

 

// found both colors. check position.

switch (position) {

case ABOVE: //is second color above first color?

if (secondTrackReport.center_mass_y < trackReport->center_mass_y)

{ return true; }

break;

case BELOW: //is second color below first color?

if (secondTrackReport.center_mass_y > trackReport->center_mass_y)

{ return true; }

break;

case RIGHT: //is second color to the right of first color?

if (secondTrackReport.center_mass_x > trackReport->center_mass_x)

{ return true; }

break;

case LEFT:  //is second color to the left of first color?

if (secondTrackReport.center_mass_x < trackReport->center_mass_x)

{ return true; }

break;

default:

DPRINTF(LOG_ERROR, "invalid position parameter");

}

 

DPRINTF(LOG_DEBUG, "%s and %s NOT IN CORRECT POSITION", td1.name, td2.name);

 

return false;

}

 

/**

*   Data functions for tracking

*/

 

 

/**

* @brief Get default HSL tracking parameters

* Note these parameters are not fully characterized at this point

* Get these default values and modify them as needed for your environment

* @param hue tasked color

* @param light saturation/luminance

*/

TrackingThreshold GetTrackingData(FrcHue hue, FrcLight light)

{

//char funcName[]="GetTrackingData";

TrackingThreshold trackingData;

//set saturation & luminance

switch (light) {

default:

case FLUORESCENT:

trackingData.saturation.minValue = 100;

trackingData.saturation.maxValue = 255;

trackingData.luminance.minValue = 40;

trackingData.luminance.maxValue = 255;

if (hue == GREEN) trackingData.luminance.minValue = 100;

if (hue == PINK) trackingData.saturation.minValue = 80;

if (hue == PINK) trackingData.luminance.minValue = 60;

if (hue == PINK) trackingData.luminance.maxValue = 155;

break;

case PASSIVE_LIGHT:

trackingData.saturation.minValue = 50;

trackingData.saturation.maxValue = 255;

trackingData.luminance.minValue = 20;

trackingData.luminance.maxValue = 255;

break;

case BRIGHT_LIGHT:

trackingData.saturation.minValue = 0;

trackingData.saturation.maxValue = 100;

trackingData.luminance.minValue = 100;

trackingData.luminance.maxValue = 255;

break;

case ACTIVE_LIGHT:

trackingData.saturation.minValue = 0;

trackingData.saturation.maxValue = 50;

trackingData.luminance.minValue = 150;

trackingData.luminance.maxValue = 255;

break;

case WHITE_LIGHT:

trackingData.saturation.minValue = 0;

trackingData.saturation.maxValue = 20;

trackingData.luminance.minValue = 200;

trackingData.luminance.maxValue = 255;

break;

case MARIUCCI_PINK_LIGHT:

trackingData.saturation.minValue = 75;

trackingData.saturation.maxValue = 255;

trackingData.luminance.minValue = 85;

trackingData.luminance.maxValue = 255;

break;

case MARIUCCI_GREEN_LIGHT:

trackingData.saturation.minValue = 23;

trackingData.saturation.maxValue = 255;

trackingData.luminance.minValue = 92;

trackingData.luminance.maxValue = 255;

break;

}

//set hue

switch (hue){

default:

case WHITE:

strcpy (trackingData.name, "WHITE");

trackingData.hue.minValue = 0;

trackingData.hue.maxValue = 255;

break;

case ORANGE:

strcpy (trackingData.name, "ORANGE");

trackingData.hue.minValue = 5;

trackingData.hue.maxValue = 25;

break;

case YELLOW:

strcpy (trackingData.name, "YELLOW");

trackingData.hue.minValue = 30;

trackingData.hue.maxValue = 50;

break;

case GREEN:

strcpy (trackingData.name, "GREEN");

if (light == FLUORESCENT) {

trackingData.hue.minValue = 60;

trackingData.hue.maxValue = 110;

} else {

trackingData.hue.minValue = 90;

trackingData.hue.maxValue = 125;

}

break;

case BLUE:

strcpy (trackingData.name, "BLUE");

trackingData.hue.minValue = 140;

trackingData.hue.maxValue = 170;

break;

case PURPLE:

strcpy (trackingData.name, "PURPLE");

trackingData.hue.minValue = 180;

trackingData.hue.maxValue = 200;

break;

case PINK:

strcpy (trackingData.name, "PINK");

trackingData.hue.minValue = 210;

trackingData.hue.maxValue = 250;

break;

case RED:

strcpy (trackingData.name, "RED");

trackingData.hue.minValue = 240;

trackingData.hue.maxValue = 255;

break;

case MARIUCCI_PINK:

strcpy (trackingData.name, "MARIUCCI_PINK");

trackingData.hue.minValue = 220;

trackingData.hue.maxValue = 255;

break;

case MARIUCCI_GREEN:

strcpy (trackingData.name, "MARIUCCI_GREEN");

trackingData.hue.minValue = 55;

trackingData.hue.maxValue = 125;

break;

}

return(trackingData);

}

 

 

/**

* Print particle analysis report

* @param myReport Report to print

*/

void PrintReport(ParticleAnalysisReport* myReport)

{

char funcName[]="PrintReport";

dprintf(LOG_INFO, "particle analysis:\n    %s%i  %s%i\n    %s%d\n    %s%i  %s%i\n    %s%g  %s%g\n    %s%g\n    %s%i  %s%i\n    %s%i  %s%i\n",

"imageHeight = ", myReport->imageHeight,

"imageWidth = ", myReport->imageWidth,

"imageTimestamp = ", myReport->imageTimestamp,

"center_mass_x = ", myReport->center_mass_x,

"center_mass_y = ", myReport->center_mass_y,

"center_mass_x_normalized = ", myReport->center_mass_x_normalized,

"center_mass_y_normalized = ", myReport->center_mass_y_normalized,

"particleArea = ", myReport->particleArea,

"boundingRectangleTop = ", myReport->boundingRect.top,

"boundingRectangleLeft = ", myReport->boundingRect.left,

"boundingRectangleHeight = ", myReport->boundingRect.height,

"boundingRectangleWidth = ", myReport->boundingRect.width);

 

dprintf(LOG_INFO, "quality statistics:  \n    %s%g %s%g \n",

"particleToImagePercent = ", myReport->particleToImagePercent,

"particleQuality = ", myReport->particleQuality);

}

 

/**

* Print color report

* @param myReport Report to print

*/

void PrintReport(ColorReport* myReport)

{

char funcName[]="PrintReport";

dprintf(LOG_INFO, "particle ranges for %i particles:",

"numberParticlesFound = ", myReport->numberParticlesFound);

;

dprintf(LOG_INFO, "\n    %s%f  %s%f  %s%f\n    %s%f %s%f  %s%f\n    %s%f  %s%f  %s%f\n -------",

"particleHueMax = ", myReport->particleHueMax,

"particleHueMin = ", myReport->particleHueMin,

"particleHueMean = ", myReport->particleHueMean,

"particleSatMax = ", myReport->particleSatMax,

"particleSatMin = ", myReport->particleSatMin,

"particleSatMean = ", myReport->particleSatMean,

"particleLumMax = ", myReport->particleLumMax,

"particleLumMin = ", myReport->particleLumMin,

"particleLumMean = ", myReport->particleLumMean);

 

}

 

/**

* Print color report

* @param myReport Report to print

*/

void PrintReport(TrackingThreshold* myReport)

{

char funcName[]="PrintReport";

dprintf(LOG_INFO, "name of color: %s", myReport->name);

 

dprintf(LOG_INFO, "\n    %s%i  %s%i\n    %s%i %s%i\n    %s%i  %s%i\n -------",

"hueMin = ", myReport->hue.minValue,

"hueMax = ", myReport->hue.maxValue,

"satMin = ", myReport->saturation.minValue,

"satMax = ", myReport->saturation.maxValue,

"lumMin = ", myReport->luminance.minValue,

"lumMax = ", myReport->luminance.maxValue );

 

}

 

 

 

 
Lockheed Martin
Valid XHTML 1.0 Transitional Valid CSS!
Blanda
Blanda
Copyright © 2012 Eagan Robotics. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.