blob: 41d40bc3137897516e1691947c2ddfd4da9b62a4 [file] [log] [blame]
jerrymf1579332013-02-07 01:56:28 +00001/********************************************************************************
2* Project : FIRST Motor Controller
3* File Name : TrackAPI.cpp
4* Contributors : ELF, DWD
5* Creation Date : August 10, 2008
6* Revision History : Source code & revision history maintained at sourceforge.WPI.edu
7* File Description : Tracking Routines for FIRST Vision API
8*/
9/*----------------------------------------------------------------------------*/
10/* Copyright (c) FIRST 2008. All Rights Reserved. */
11/* Open Source Software - may be modified and shared by FRC teams. The code */
12/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
13/*----------------------------------------------------------------------------*/
14
15#include "string.h"
16#include "vxWorks.h"
17
18#include "AxisCamera.h"
19#include "FrcError.h"
20#include "TrackAPI.h"
21#include "VisionAPI.h"
22
23int TrackAPI_debugFlag = 0;
24#define DPRINTF if(TrackAPI_debugFlag)dprintf
25
26/**
27* @brief Find the largest particle that meets a criteria
28* @param binaryImage Image to inspect
29* @param rect area to search
30* @return 0 = error
31*/
32bool InArea(Image* binaryImage, int particleIndex, Rect rect)
33{
34 double position;
35
36 imaqMeasureParticle(binaryImage, particleIndex, 0,
37 IMAQ_MT_BOUNDING_RECT_LEFT, &position);
38 if ( position < (rect.left ) ) return false; // outside left of rectangle?
39
40 imaqMeasureParticle(binaryImage, particleIndex, 0,
41 IMAQ_MT_BOUNDING_RECT_TOP, &position);
42 if ( position < (rect.top ) ) return false; // outside top of rectangle ?
43
44 imaqMeasureParticle(binaryImage, particleIndex, 0,
45 IMAQ_MT_BOUNDING_RECT_RIGHT, &position);
46 if (position > (rect.left + rect.width) ) return false; // outside right of rectangle ?
47
48 imaqMeasureParticle(binaryImage, particleIndex, 0,
49 IMAQ_MT_BOUNDING_RECT_BOTTOM, &position);
50 if (position > (rect.top + rect.height) ) return false; // outside bottom of rectangle ?
51
52 DPRINTF(LOG_INFO, "particle %i is in (%i %i) height %i width %i\n",
53 particleIndex, rect.left, rect.top, rect.height, rect.width);
54 return true;
55}
56
57/**
58* @brief Find the largest particle that meets a criteria
59* @param binaryImage Image to inspect
60* @param largestParticleIndex Index of the largest particle
61* @param rect area to search
62* @return 0 = error
63*/
64int GetLargestParticle(Image* binaryImage, int* largestParticleIndex)
65{ return GetLargestParticle(binaryImage, largestParticleIndex, IMAQ_NO_RECT); }
66
67int GetLargestParticle(Image* binaryImage, int* largestParticleIndex, Rect rect)
68{
69 *largestParticleIndex = 0; // points to caller-provided variable
70
71 /* determine number of particles in thresholded image */
72 int numParticles;
73 int success = frcCountParticles(binaryImage, &numParticles);
74 if ( !success ) { return success; }
75
76 /* if no particles found we can quit here */
77 if (numParticles == 0) { return 0; } // unsuccessful if zero particles found
78
79 // find the largest particle
80 double largestParticleArea = 0;
81 double particleArea;
82 for (int i = 0; i < numParticles; ++i) {
83 success = imaqMeasureParticle(binaryImage, i, 0, IMAQ_MT_AREA, &particleArea);
84 if ( !success ) { return success; }
85 if (particleArea > largestParticleArea) {
86 // see if is in the right area
87 if ( InArea(binaryImage, i, rect) ) {
88 largestParticleArea = particleArea;
89 *largestParticleIndex = i; // return index to caller
90 }
91 }
92 }
93
94 return success;
95}
96
97/**
98* @brief Search for a color. Supports IMAQ_IMAGE_HSL.
99* @param color Definition for the hue range
100* @param trackReport Values for tracking: center of particle, particle size, color
101* @return 0 = error
102*/
103int FindColor(FrcHue color, ParticleAnalysisReport* trackReport)
104{
105 int success = 0; // return: 0 = error
106
107 /* track color */
108 // use ACTIVE_LIGHT or WHITE_LIGHT for brightly lit objects
109 TrackingThreshold td = GetTrackingData(color, PASSIVE_LIGHT);
110
111 success = FindColor(IMAQ_HSL, &td.hue, &td.saturation, &td.luminance, trackReport);
112 if ( !success ) {
113 DPRINTF (LOG_INFO, "did not find color - errorCode= %i",GetLastVisionError());
114 return success;
115 }
116
117 //PrintReport(par);
118
119 /* set an image quality restriction */
120 if (trackReport->particleToImagePercent < PARTICLE_TO_IMAGE_PERCENT) {
121 imaqSetError(ERR_PARTICLE_TOO_SMALL, __FUNCTION__);
122 success = 0;
123 }
124 return success;
125}
126
127/**
128* @brief Search for a color. Supports IMAQ_IMAGE_HSL.
129* @param hueRange The range for the first plane
130* @param trackReport Values for tracking: center of particle, particle size, color
131* @return 0 = error
132*/
133int FindColor(const Range* hueRange, ParticleAnalysisReport *trackReport)
134{ return FindColor(hueRange, DEFAULT_SATURATION_THRESHOLD, trackReport); }
135
136/**
137* @brief Search for a color. Supports IMAQ_IMAGE_HSL.
138* @param hueRange The range for the first plane
139* @param minSaturation The lower range saturation
140* @param trackReport Values for tracking: center of particle, particle size, color
141* @return 0 = error
142*/
143int FindColor(const Range* hueRange, int minSaturation, ParticleAnalysisReport *trackReport)
144{
145 Range satRange;
146 satRange.minValue = minSaturation;
147 satRange.maxValue = 255;
148 Range lumRange;
149 lumRange.minValue = 0;
150 lumRange.maxValue = 255;
151 ColorMode cmode = IMAQ_HSL;
152 return FindColor(cmode, hueRange, &satRange, &lumRange, trackReport);
153}
154
155/**
156* @brief Search for a color. Supports IMAQ_IMAGE_HSL and IMAQ_IMAGE_RGB.
157* @param mode Color mode, either IMAQ_HSL or IMAQ_RGB
158* @param plane1Range The range for the first plane (hue or red)
159* @param plane2Range The range for the second plane (saturation or green)
160* @param plane3Range The range for the third plane (luminance or blue)
161* @param trackReport Values for tracking: center of particle, particle size, etc
162* @return 0 = error
163*/
164int FindColor(ColorMode mode, const Range* plane1Range, const Range* plane2Range,
165 const Range* plane3Range, ParticleAnalysisReport *trackReport)
166{
167 return FindColor(mode, plane1Range, plane2Range, plane3Range, trackReport, NULL);
168}
169
170/**
171* @brief Search for a color. Supports IMAQ_IMAGE_HSL and IMAQ_IMAGE_RGB.
172* @param mode Color mode, either IMAQ_HSL or IMAQ_RGB
173* @param plane1Range The range for the first plane (hue or red)
174* @param plane2Range The range for the second plane (saturation or green)
175* @param plane3Range The range for the third plane (luminance or blue)
176* @param trackReport Values for tracking: center of particle, particle size, etc
177* @param colorReport Color charactaristics of the particle
178* @return 0 = error
179*/
180int FindColor(ColorMode mode, const Range* plane1Range, const Range* plane2Range,
181 const Range* plane3Range, ParticleAnalysisReport *trackReport,
182 ColorReport *colorReport)
183{
184 return FindColor(mode, plane1Range, plane2Range, plane3Range, trackReport,
185 NULL, IMAQ_NO_RECT);
186}
187
188/**
189* @brief Search for a color. Supports IMAQ_IMAGE_HSL and IMAQ_IMAGE_RGB.
190* @param mode Color mode, either IMAQ_HSL or IMAQ_RGB
191* @param plane1Range The range for the first plane (hue or red)
192* @param plane2Range The range for the second plane (saturation or green)
193* @param plane3Range The range for the third plane (luminance or blue)
194* @param trackReport Values for tracking: center of particle, particle size, etc
195* @param colorReport Color charactaristics of the particle
196* @param rect Rectangle to confine search to
197* @return 0 = error
198*/
199int FindColor(ColorMode mode, const Range* plane1Range, const Range* plane2Range,
200 const Range* plane3Range, ParticleAnalysisReport *trackReport,
201 ColorReport *colorReport, Rect rect)
202{
203 int errorCode = 0;
204 int success = 0;
205
206 /* create an image object */
207 Image* cameraImage = frcCreateImage(IMAQ_IMAGE_HSL);
208 if (!cameraImage) { return success; }
209
210 /* get image from camera - if the camera has not finished initializing,
211 * this will fail
212 */
213 double imageTime;
214 success = GetImage(cameraImage, &imageTime);
215 if (!success){
216 DPRINTF(LOG_INFO, "No camera Image available Error = %i %s",
217 errorCode, GetVisionErrorText(errorCode));
218 frcDispose(cameraImage);
219 imaqSetError(errorCode, __FUNCTION__); //reset error code for the caller
220 return success;
221 }
222
223 /* save a copy of the image to another image for color thresholding later */
224 Image* histImage = frcCreateImage(IMAQ_IMAGE_HSL);
225 if (!histImage) { frcDispose(cameraImage); return success; }
226 success = frcCopyImage(histImage,cameraImage);
227 if ( !success ) {
228 errorCode = GetLastVisionError();
229 frcDispose(__FUNCTION__,cameraImage,histImage,NULL);
230 return success;
231 }
232
233 /* Color threshold the image */
234 success = frcColorThreshold(cameraImage, cameraImage, mode, plane1Range, plane2Range, plane3Range);
235 if ( !success ) {
236 errorCode = GetLastVisionError();
237 DPRINTF (LOG_DEBUG, "Error = %i %s ", errorCode, GetVisionErrorText(errorCode));
238 frcDispose(__FUNCTION__,cameraImage,histImage,NULL);
239 return success;
240 }
241
242 int largestParticleIndex = 0;
243 success = GetLargestParticle(cameraImage, &largestParticleIndex, rect );
244 if ( !success ) {
245 errorCode = GetLastVisionError();
246 DPRINTF (LOG_DEBUG, "Error after GetLargestParticle = %i %s ", errorCode, GetVisionErrorText(errorCode));
247 frcDispose(__FUNCTION__,cameraImage,histImage,NULL);
248 imaqSetError(ERR_COLOR_NOT_FOUND, __FUNCTION__);
249 return success;
250 }
251 DPRINTF(LOG_INFO, "largestParticleIndex = %i\n", largestParticleIndex);
252
253 /* Particles were found */
254 /*
255 * Fill in report information for largest particle found
256 */
257 success = frcParticleAnalysis(cameraImage, largestParticleIndex, trackReport);
258 trackReport->imageTimestamp = imageTime;
259
260 /* clean up */
261 if (!success) {frcDispose(__FUNCTION__,cameraImage,histImage,NULL); return success;}
262
263 /* particle color statistics */
264 /* only if a color report requested */
265 if (colorReport != NULL)
266 {
267 /* first filter out the other particles */
268 ParticleFilterCriteria2 criteria;
269 ParticleFilterOptions* options = NULL;
270 Rect rect;
271 int numParticles;
272 success = frcParticleFilter(cameraImage, cameraImage, &criteria, 1, options,
273 rect, &numParticles);
274 if ( !success ) {
275 DPRINTF(LOG_INFO, "frcParticleFilter errorCode %i", GetLastVisionError());
276 }
277
278 /* histogram the original image using the thresholded image as a mask */
279 int numClasses = 10; //how many classes?
280 ColorHistogramReport* chrep = imaqColorHistogram2(histImage, numClasses, IMAQ_HSL,
281 NULL, cameraImage);
282 if (chrep == NULL) {
283 DPRINTF(LOG_INFO, "NULL Color Histogram");
284 errorCode = GetLastVisionError();
285 } else {
286 colorReport->particleHueMax = chrep->plane1.max;
287 colorReport->particleHueMin = chrep->plane1.min;
288 colorReport->particleHueMean = chrep->plane1.mean;
289 colorReport->particleSatMax = chrep->plane2.max;
290 colorReport->particleSatMin = chrep->plane2.min;
291 colorReport->particleSatMean = chrep->plane2.mean;
292 colorReport->particleLumMax = chrep->plane3.max;
293 colorReport->particleLumMin = chrep->plane3.min;
294 colorReport->particleLumMean = chrep->plane3.mean;
295 colorReport->numberParticlesFound = numParticles;
296 frcDispose(chrep);
297 }
298 }
299
300 /* clean up */
301 frcDispose(__FUNCTION__,cameraImage,histImage,NULL);
302
303 return success;
304}
305
306
307/**
308 * Data functions for tracking
309 */
310
311
312/**
313 * @brief Get default HSL tracking parameters
314 * Note these parameters are not fully characterized at this point
315 * Get these default values and modify them as needed for your environment
316 * @param hue tasked color
317 * @param light saturation/luminance
318 */
319TrackingThreshold GetTrackingData(FrcHue hue, FrcLight light)
320{
321 TrackingThreshold trackingData;
322
323 //set saturation & luminance
324 switch (light) {
325 default:
326 case FLUORESCENT:
327 trackingData.saturation.minValue = 100;
328 trackingData.saturation.maxValue = 255;
329 trackingData.luminance.minValue = 40;
330 trackingData.luminance.maxValue = 255;
331 if (hue == GREEN) trackingData.luminance.minValue = 100;
332 if (hue == PINK) trackingData.saturation.minValue = 80;
333 if (hue == PINK) trackingData.luminance.minValue = 60;
334 if (hue == PINK) trackingData.luminance.maxValue = 155;
335 break;
336 case PASSIVE_LIGHT:
337 trackingData.saturation.minValue = 50;
338 trackingData.saturation.maxValue = 255;
339 trackingData.luminance.minValue = 20;
340 trackingData.luminance.maxValue = 255;
341 break;
342 case BRIGHT_LIGHT:
343 trackingData.saturation.minValue = 0;
344 trackingData.saturation.maxValue = 100;
345 trackingData.luminance.minValue = 100;
346 trackingData.luminance.maxValue = 255;
347 break;
348 case ACTIVE_LIGHT:
349 trackingData.saturation.minValue = 0;
350 trackingData.saturation.maxValue = 50;
351 trackingData.luminance.minValue = 150;
352 trackingData.luminance.maxValue = 255;
353 break;
354 case WHITE_LIGHT:
355 trackingData.saturation.minValue = 0;
356 trackingData.saturation.maxValue = 20;
357 trackingData.luminance.minValue = 200;
358 trackingData.luminance.maxValue = 255;
359 break;
360 }
361
362 //set hue
363 switch (hue){
364 default:
365 case WHITE:
366 strcpy (trackingData.name, "WHITE");
367 trackingData.hue.minValue = 0;
368 trackingData.hue.maxValue = 255;
369 break;
370 case ORANGE:
371 strcpy (trackingData.name, "ORANGE");
372 trackingData.hue.minValue = 5;
373 trackingData.hue.maxValue = 25;
374 break;
375 case YELLOW:
376 strcpy (trackingData.name, "YELLOW");
377 trackingData.hue.minValue = 30;
378 trackingData.hue.maxValue = 50;
379 break;
380 case GREEN:
381 strcpy (trackingData.name, "GREEN");
382 if (light == FLUORESCENT) {
383 trackingData.hue.minValue = 60;
384 trackingData.hue.maxValue = 110;
385 } else {
386 trackingData.hue.minValue = 90;
387 trackingData.hue.maxValue = 125;
388 }
389 break;
390 case BLUE:
391 strcpy (trackingData.name, "BLUE");
392 trackingData.hue.minValue = 140;
393 trackingData.hue.maxValue = 170;
394 break;
395 case PURPLE:
396 strcpy (trackingData.name, "PURPLE");
397 trackingData.hue.minValue = 180;
398 trackingData.hue.maxValue = 200;
399 break;
400 case PINK:
401 strcpy (trackingData.name, "PINK");
402 trackingData.hue.minValue = 210;
403 trackingData.hue.maxValue = 250;
404 break;
405 case RED:
406 strcpy (trackingData.name, "RED");
407 trackingData.hue.minValue = 240;
408 trackingData.hue.maxValue = 255;
409 break;
410 }
411 return(trackingData);
412}
413
414
415/**
416 * Print particle analysis report
417 * @param myReport Report to print
418 */
419void PrintReport(ParticleAnalysisReport* myReport)
420{
421 dprintf(LOG_INFO, "particle analysis:\n %s%i %s%i\n %s%lf\n %s%i %s%i\n %s%g %s%g\n %s%g\n %s%i %s%i\n %s%i %s%i\n",
422 "imageHeight = ", myReport->imageHeight,
423 "imageWidth = ", myReport->imageWidth,
424 "imageTimestamp = ", myReport->imageTimestamp,
425 "center_mass_x = ", myReport->center_mass_x,
426 "center_mass_y = ", myReport->center_mass_y,
427 "center_mass_x_normalized = ", myReport->center_mass_x_normalized,
428 "center_mass_y_normalized = ", myReport->center_mass_y_normalized,
429 "particleArea = ", myReport->particleArea,
430 "boundingRectangleTop = ", myReport->boundingRect.top,
431 "boundingRectangleLeft = ", myReport->boundingRect.left,
432 "boundingRectangleHeight = ", myReport->boundingRect.height,
433 "boundingRectangleWidth = ", myReport->boundingRect.width);
434
435 dprintf(LOG_INFO, "quality statistics: \n %s%g %s%g \n",
436 "particleToImagePercent = ", myReport->particleToImagePercent,
437 "particleQuality = ", myReport->particleQuality);
438}
439
440/**
441 * Print color report
442 * @param myReport Report to print
443 */
444void PrintReport(ColorReport* myReport)
445{
446 dprintf(LOG_INFO, "particle ranges for %i particles: ",
447 "numberParticlesFound = ", myReport->numberParticlesFound);
448 ;
449 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 -------",
450 "particleHueMax = ", myReport->particleHueMax,
451 "particleHueMin = ", myReport->particleHueMin,
452 "particleHueMean = ", myReport->particleHueMean,
453 "particleSatMax = ", myReport->particleSatMax,
454 "particleSatMin = ", myReport->particleSatMin,
455 "particleSatMean = ", myReport->particleSatMean,
456 "particleLumMax = ", myReport->particleLumMax,
457 "particleLumMin = ", myReport->particleLumMin,
458 "particleLumMean = ", myReport->particleLumMean);
459
460}
461
462/**
463 * Print color report
464 * @param myReport Report to print
465 */
466void PrintReport(TrackingThreshold* myReport)
467{
468 dprintf(LOG_INFO, "name of color: %s", myReport->name);
469
470 dprintf(LOG_INFO, "\n %s%i %s%i\n %s%i %s%i\n %s%i %s%i\n -------",
471 "hueMin = ", myReport->hue.minValue,
472 "hueMax = ", myReport->hue.maxValue,
473 "satMin = ", myReport->saturation.minValue,
474 "satMax = ", myReport->saturation.maxValue,
475 "lumMin = ", myReport->luminance.minValue,
476 "lumMax = ", myReport->luminance.maxValue );
477
478}
479
480
481