double VisionSubsystemV2::scoreAspectRatio(BinaryImage *image, ParticleAnalysisReport *report, bool outer) {
	double	rectLong,
			rectShort,
			idealAspectRatio,
			aspectRatio;
	if (outer) {
		idealAspectRatio = (62/29);
	} else {
		idealAspectRatio = (62/20);
	}
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (scoreAspectRatio) Using the aspect ratio of %f to score with\n", idealAspectRatio);
	#endif
	imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &rectLong);
	imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &rectShort);
	
	if (report->boundingRect.width > report->boundingRect.height) {
		aspectRatio = 100*(1 - fabs((1 - ((rectLong / rectShort) / idealAspectRatio))));
	} else {
		aspectRatio = 100*(1 - fabs((1 - ((rectShort / rectLong) / idealAspectRatio))));
	}
	double score = (max(0.0, min(aspectRatio, 1000.00)));
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (scoreAspectRatio) The score is %f\n", score);
	#endif
	return score;
}
Пример #2
0
/**
 * NOTE: This is included temporarily until the updated WPILib 
 * is released with the proper headers and immplmentation for 
 * this method. It will be in TrackAPI.cpp.
 * 
* @brief Find the largest particle that meets a criteria 
* @param binaryImage Image to inspect
* @param rect area to search
* @return 0 = error
*/
bool TempInArea(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; 
}
Пример #3
0
double VisionControl::AspectRatio(BinaryImage *image, ParticleAnalysisReport *report)
{
    double rectLong, rectShort, aspectRatio;

    imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &rectLong);
    imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &rectShort);
    
    aspectRatio = rectLong/rectShort;

    return aspectRatio;
}
Пример #4
0
/**
* @brief Conduct measurements for a single particle in an images. 
* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
* 
* @param image image with the particle to analyze. This function modifies the source image. 
* If you need the original image, create a copy of the image using frcCopy() before using this function.
* @param particleNumber The number of the particle to get information on
* @param par on return, a particle analysis report containing information about the particle. This structure must be created by the caller.
* @return On success: 1. On failure: 0. To get extended error information, call GetLastError().
*/
int frcParticleAnalysis(Image* image, int particleNumber, ParticleAnalysisReport* par)				
{
	int success = 0;

	/* image information */
	int height, width;
	if ( ! imaqGetImageSize(image, &width, &height) ) 	{ return success; }	
	par->imageWidth = width;	
	par->imageHeight = height;	
	par->particleIndex = particleNumber;		

	/* center of mass point of the largest particle	*/
	double returnDouble;
	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_CENTER_OF_MASS_X, &returnDouble);
	if ( !success )	{ return success; }
	par->center_mass_x = (int)returnDouble;						// pixel	

	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_CENTER_OF_MASS_Y, &returnDouble);	
	if ( !success )	{ return success; }
	par->center_mass_y = (int)returnDouble;						// pixel		
	
	/* particle size statistics */ 
	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_AREA, &returnDouble);	
	if ( !success )	{ return success; }
	par->particleArea = returnDouble;	
	
	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_TOP, &returnDouble);	
	if ( !success )	{ return success; }
	par->boundingRect.top = (int)returnDouble;
	
	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &returnDouble);	
	if ( !success )	{ return success; }
	par->boundingRect.left = (int)returnDouble;
	
	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_HEIGHT, &returnDouble);	
	if ( !success )	{ return success; }
	par->boundingRect.height = (int)returnDouble;	

	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_BOUNDING_RECT_WIDTH, &returnDouble);	
	if ( !success )	{ return success; }
	par->boundingRect.width = (int)returnDouble;	
	
	/* particle quality statistics */ 
	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &returnDouble);	
	if ( !success )	{ return success; }
	par->particleToImagePercent = returnDouble;

	success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA, &returnDouble);	
	if ( !success )	{ return success; }
	par->particleQuality = returnDouble;
		
	/* normalized position (-1 to 1) */
	par->center_mass_x_normalized = RangeToNormalized(par->center_mass_x, width);
	par->center_mass_y_normalized = RangeToNormalized(par->center_mass_y, height);	
	
	return success;
}
Пример #5
0
double VisionSubsystem::scoreAspectRatio(BinaryImage *image, ParticleAnalysisReport *report, bool outer) {
	double rectLong, rectShort, idealAspectRatio, aspectRatio;
	idealAspectRatio = outer ? (62/29) : (62/20);
	
	imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &rectLong);
	imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &rectShort);
	
	if(report->boundingRect.width > report->boundingRect.height) {
		aspectRatio = 100*(1-fabs((1-((rectLong/rectShort)/idealAspectRatio))));
	} else {
		aspectRatio = 100*(1-fabs((1-((rectShort/rectLong)/idealAspectRatio))));
	}
	return (max(0.0, min(aspectRatio, 1000.00)));
}
Пример #6
0
/**
 * Measure a single parameter for an image.
 * Get the measurement for a single parameter about an image by calling the imaqMeasureParticle
 * function for the selected parameter.
 * @param particleNumber which particle in the set of particles
 * @param whatToMeasure the imaq MeasurementType (what to measure)
 * @param result the value of the measurement
 * @returns true on failure, false on success
 */
bool BinaryImage::ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, double *result)
{
	int success;
	success = imaqMeasureParticle(m_imaqImage, particleNumber, 0, whatToMeasure, result);
	wpi_setImaqErrorWithContext(success, "Error measuring particle");
	return !StatusIsFatal();
}
Пример #7
0
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;
}
Пример #8
0
/**
 * Computes a score (0-100) comparing the aspect ratio to the ideal aspect ratio for the target. This method uses
 * the equivalent rectangle sides to determine aspect ratio as it performs better as the target gets skewed by moving
 * to the left or right. The equivalent rectangle is the rectangle with sides x and y where particle area= x*y
 * and particle perimeter= 2x+2y
 * 
 * @param image The image containing the particle to score, needed to perform additional measurements
 * @param report The Particle Analysis Report for the particle, used for the width, height, and particle number
 * @param outer    Indicates whether the particle aspect ratio should be compared to the ratio for the inner target or the outer
 * @return The aspect ratio score (0-100)
 */
double TargetReport::scoreAspectRatio(BinaryImage *image, ParticleAnalysisReport *report, bool outer){
    double rectLong, rectShort, idealAspectRatio, aspectRatio;
    idealAspectRatio = outer ? (62/29) : (62/20);    //Dimensions of goal opening + 4 inches on all 4 sides for reflective tape
    
    imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &rectLong);
    imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &rectShort);
    
    //Divide width by height to measure aspect ratio
    if(report->boundingRect.width > report->boundingRect.height){
        //particle is wider than it is tall, divide long by short
        aspectRatio = 100*(1-fabs((1-((rectLong/rectShort)/idealAspectRatio))));
    } else {
        //particle is taller than it is wide, divide short by long
        aspectRatio = 100*(1-fabs((1-((rectShort/rectLong)/idealAspectRatio))));
    }
    return (max(0, min(aspectRatio, 100)));        //force to be in range 0-100
}
Пример #9
0
		/**
		 * Computes a score (0-100) comparing the aspect ratio to the ideal aspect ratio for the target. This method uses
		 * the equivalent rectangle sides to determine aspect ratio as it performs better as the target gets skewed by moving
		 * to the left or right. The equivalent rectangle is the rectangle with sides x and y where particle area= x*y
		 * and particle perimeter= 2x+2y
		 * 
		 * @param image The image containing the particle to score, needed to perform additional measurements
		 * @param report The Particle Analysis Report for the particle, used for the width, height, and particle number
		 * @param outer	Indicates whether the particle aspect ratio should be compared to the ratio for the inner target or the outer
		 * @return The aspect ratio score (0-100)
		 */
		inline double scoreAspectRatio(BinaryImage *image, ParticleAnalysisReport *report, bool vertical){
			double rectLong, rectShort, idealAspectRatio, aspectRatio;
			idealAspectRatio = vertical ? (4.0/32) : (23.5/4);	//Vertical reflector 4" wide x 32" tall, horizontal 23.5" wide x 4" tall
			
			imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &rectLong);
			imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &rectShort);
			
			//Divide width by height to measure aspect ratio
			if(report->boundingRect.width > report->boundingRect.height){
				//particle is wider than it is tall, divide long by short
				aspectRatio = ratioToScore(((rectLong/rectShort)/idealAspectRatio));
			} else {
				//particle is taller than it is wide, divide short by long
				aspectRatio = ratioToScore(((rectShort/rectLong)/idealAspectRatio));
			}
			return aspectRatio;		//force to be in range 0-100
		}
Пример #10
0
/**
* @brief Find the largest particles that meet a criteria 
* @param binaryImage Image to inspect
* @param hitReport structure containing arry of hits - first hit is largest 
* @param rect area to search
* @param numParticles Number of particles in array
* @return 0 = error
*/
int GetLargestParticles(Image* binaryImage, ImageHits *hitReport, Rect rect,
		int numberHitsRequested)
{	
	//char funcName[]="GetLargestParticles";
	HitList *hitsInArea = new HitList();  // list to contain all particles within area sorted by size
	int i;
	
	/* determine number of particles in thresholded image */	
	int numParticles = -1;
	int success = frcCountParticles(binaryImage, &numParticles);
	if ( !success )	{ return success; }	
	//DPRINTF(LOG_DEBUG, "particles requested = %i ; particles found in image = %i", numberHitsRequested, numParticles);	
	
	/* if no particles found we can quit here */
	if (numParticles <= 0)  {  return 1; 	}  // successful, but zero particles found

	/* get areas of each particle and insert into list */
	double particleArea;
	int count = 0;
	for (i = 0; i < numParticles; ++i) {		
		success = imaqMeasureParticle(binaryImage, i, 0, IMAQ_MT_AREA, &particleArea);
		if ( !success )	{ return success; }	
		//DPRINTF (LOG_INFO, "size of particle %i = %g", i, particleArea);
		// see if is in the right area and large enough to be considered a possible target
		
		//TODO: Call InArea & delete TempInArea when new WPILib is released
		//if (InArea(binaryImage, i, rect)) {
		//if ( InArea(binaryImage, i, rect) && (particleArea >= FRC_MINIMUM_PIXELS_FOR_TARGET) ) {
		if ( TempInArea(binaryImage, i, rect) && (particleArea >= FRC_MINIMUM_PIXELS_FOR_TARGET) ) {
			hitsInArea->AddNode(i,particleArea);
			count++;
		}
	}
	// see what's in the list 
	hitsInArea->debugDump();
	
	// fill in return structure - number of hits
	if (numParticles < numberHitsRequested) {
		hitReport->numberOfHits = numParticles;		
	} else {
		hitReport->numberOfHits = numberHitsRequested;		
	}
	
	// fill in return structure - indices & areas of largest hits
	HitNode *hitPtr = hitsInArea->head;	
	for (i = 0; i < hitReport->numberOfHits; ++i) {	
		if (hitPtr == NULL) {
			break;
		}
		hitReport->indices[i] = hitPtr->nodeIndex;
		hitReport->areas[i] = hitPtr->nodeArea;
		//DPRINTF (LOG_INFO, "put index %i in hitReport %i", hitPtr->nodeIndex, hitReport->indices[i]);
		hitPtr = hitPtr->next;
	}	
	// dispose of HitList 
	delete hitsInArea;		
	return 1;  // success
}
/**
 * Measure a single parameter for an image.
 * Get the measurement for a single parameter about an image by calling the imaqMeasureParticle
 * function for the selected parameter.
 * @param image the image to measure
 * @param particleNumber which particle in the set of particles
 * @param whatToMeasure the imaq MeasurementType (what to measure)
 * @returns the value of the measurement
 */
double BinaryImage::ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure)
{
	double returnDouble;
	int success;
	success = imaqMeasureParticle(m_imaqImage, particleNumber, 0, whatToMeasure, &returnDouble);
	wpi_imaqAssert(success, "Error measuring particle");
	if (!success) return 0.0;
	else return returnDouble;
}
double VisionSubsystemV2::computeTargetDistance(BinaryImage *image, ParticleAnalysisReport *report, VisionSubsystemV2::Target target) {
	double rectShort, height, targetHeight = -1;
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeDistance) Image Hight: %d, Image Width: %d\n", image->GetHeight(), image->GetWidth());
	#endif	
	if(target == High) {
		targetHeight = (20.0 / 12.0);
		#ifndef VISION_DEBUG_PRINTF_ENABLE
		printf("[VisionSubsystemV2] (computeDistance) Using High target height %f\n", targetHeight);
		#endif
	} else if(target == Middle) {
		targetHeight = (29.0 / 12.0);
		#ifndef VISION_DEBUG_PRINTF_ENABLE
		printf("[VisionSubsystemV2] (computeDistance) Using Middle target height %f\n", targetHeight);
		#endif
	} else if(target == Low) {
		targetHeight = (32.0 / 12.0);
		#ifndef VISION_DEBUG_PRINTF_ENABLE
		printf("[VisionSubsystemV2] (computeDistance) Using Low target height %f\n", targetHeight);
		#endif
	}
	
	//imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &rectShort);
	imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE_FERET, &rectShort);
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeDistance) imaq rectangle short side is %f\n", rectShort);
	#endif

	double repoertedOfBoundingRect = report->boundingRect.height;
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeDistance) Bounding rectangle heigth %f\n", repoertedOfBoundingRect);
	#endif

	height = min(rectShort, repoertedOfBoundingRect);
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeDistance) The Height being used is %f\n", height);
	#endif

	double tangent = VisionSubsystemV2::tanDegress(VIEW_ANGLE_V2 / 2);
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeDistance) tanget of %f is %f\n", (VIEW_ANGLE_V2 / 2), tangent);
	#endif
	//double distance = ((((targetHeight / Y_IMAGE_RES) / height ) / 2) / tangent);
	
	double FOV = (targetHeight * Y_IMAGE_RES) / height;
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeDistance) Field of view is %f\n", FOV);
	#endif

	double distance = (FOV / 2)/ tangent;
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeDistance) Distance is %f\n", distance);
	#endif

	return distance;
}
Пример #13
0
/**
 * Computes the estimated distance to a target using the height of the particle in the image. For more information and graphics
 * showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
 * 
 * @param image The image to use for measuring the particle estimated rectangle
 * @param report The Particle Analysis Report for the particle
 * @param outer True if the particle should be treated as an outer target, false to treat it as a center target
 * @return The estimated distance to the target in Inches.
 */
double TargetReport::computeDistance (BinaryImage *image, ParticleAnalysisReport *report, bool outer) {
    double rectShort, height;
    int targetHeight;

    imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &rectShort);
    //using the smaller of the estimated rectangle short side and the bounding rectangle height results in better performance
    //on skewed rectangles
    height = min(report->boundingRect.height, rectShort);
    targetHeight = outer ? 29 : 21;

    return X_IMAGE_RES * targetHeight / (height * 12 * 2 * tan(VIEW_ANGLE*PI/(180*2)));
}
Пример #14
0
	/**
		 * Computes the estimated distance to a target using the height of the particle in the image. For more information and graphics
		 * showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
		 * 
		 * @param image The image to use for measuring the particle estimated rectangle
		 * @param report The Particle Analysis Report for the particle
		 * @return The estimated distance to the target in feet.
		 */
		inline double computeDistance (BinaryImage *image, ParticleAnalysisReport *report) {
			double rectLong, height;
			int targetHeight;
			
			imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &rectLong);
			//using the smaller of the estimated rectangle long side and the bounding rectangle height results in better performance
			//on skewed rectangles
			height = min(report->boundingRect.height, rectLong);
			targetHeight = 32;
			
			return Y_IMAGE_RES * targetHeight / (height * 12 * 2 * tan(VIEW_ANGLE*PI/(180*2)));
		}
double VisionSubsystemV2::computeTargetAzimuth(BinaryImage *image, ParticleAnalysisReport *report, Target target, double targetDistance) {
	double targetWidthPixelImaq;
	double targetWidthFeet = -1;
	
	if(target == High) {
		targetWidthFeet = (62.0 / 12.0);
	} else if(target == Middle) {
		targetWidthFeet = (62.0 / 12.0);
	} else if(target == Low) {
		targetWidthFeet = (37.0 / 12.0);
	}
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) The target width in feet is %f\n", targetWidthFeet);
	#endif
	
	imaqMeasureParticle(image->GetImaqImage(), report->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &targetWidthPixelImaq);
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) The imaq pixel width is %f\n", targetWidthPixelImaq);
	#endif

	double targetWidthPixelBoundingRect = report->boundingRect.width;
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) The Bounding rectangle width is %f\n", targetWidthPixelBoundingRect);
	#endif

	double targetWidthPixel = min(targetWidthPixelBoundingRect, targetWidthPixelBoundingRect);
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) The target width being used is %f\n", targetWidthPixel);
	#endif

	double fov = (targetWidthFeet * image->GetWidth()) / targetWidthPixel;
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) The horizontal field of view is %f\n", fov);
	#endif

	double prePreTan = ((report->center_mass_x - (image->GetWidth() / 2)) * (fov / 2)) / image->GetWidth();
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) the distance from teh center in feet is %f\n", prePreTan);
	#endif

	double preTan = prePreTan / targetDistance;
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) The value before tan is %f\n", preTan);
	#endif

	double azimuth = atan(preTan) * (180.0 / PI_V2);
	#ifndef VISION_DEBUG_PRINTF_ENABLE
	printf("[VisionSubsystemV2] (computeTargetAzimuth) the azimuth is %f\n", azimuth);
	#endif

	return azimuth;
}
Пример #16
0
	/****** AUTO FUNCTIONS END *******/
	void Autonomous()
	{
		int counter=0;
		int autonomousEngagement = 0;
		DriverStationLCD *screen = DriverStationLCD::GetInstance();	
		compressor.Start(); //starts compressor class
		rightArmSolenoid.Set(DoubleSolenoid::kReverse); //brings the arms down
		leftArmSolenoid.Set(DoubleSolenoid::kReverse);
		/*** ENSURES THE CATAPULT IS LOADED AND LOADS IF UNLOADED ***/
		if (leftLimitSwitch.Get() == 1 && rightLimitSwitch.Get() == 1)
		{
			winchMotor.Set(0.1); // Gears need to be moving slowly to allow the dog gear to engage properly
			dogSolenoid.Set(DoubleSolenoid::kForward); // Pushes the pneumatic piston forward to engage the dog gear
			Wait(0.2); // Giving the pistons time to engage properly
			winchMotor.Set(0); // Now that the dog gear is engaged, the gears do not have to move
			ratchetSolenoid.Set(DoubleSolenoid::kForward); // Pushes the pneumatic piston forward to engage the ratchet
			Wait(0.2); // Giving the pistons time to engage properly
		}
		while (leftLimitSwitch.Get() == 1 && rightLimitSwitch.Get() == 1) // If Limit Switch Buttons are not pressed
			{
			winchMotor.Set(1); //Now starts the winch motor to load the catapult
			}
		// If the Catapult Left &  Limit Switches are (0,0), (0,1), (1,0)
		{
			winchMotor.Set(0); // Stops the Winch Motor since one or more buttons are pressed
			if ((dogSolenoid.Get() == DoubleSolenoid::kReverse) && (ratchetSolenoid.Get() == DoubleSolenoid::kForward)) // If the Dog Gear is disengaged but the ratchet is engaged
				{
					winchMotor.Set(0.05); // Gears need to be moving slowly to allow the dog gear to engage properly. Might want to test this since the catapult's already loaded.
					dogSolenoid.Set(DoubleSolenoid::kForward); // Engages the dog gear so both dog gear and ratchet are engaged before shooting for safety
					Wait(0.1); // Giving the pistons time to engage properly
					winchMotor.Set(0); // Now that the dog gear is engaged, the gears do not have to move
				}
			else if ((dogSolenoid.Get() == DoubleSolenoid::kForward) && (ratchetSolenoid.Get() == DoubleSolenoid::kReverse)) // If the dog gear is engaged but the ratchet is disengaged
				{
					ratchetSolenoid.Set(DoubleSolenoid::kForward); // Engages the ratchet so that both dog gear and ratchet are engaged before shooting for safety
					Wait(0.1); // Giving the pistons time to engage properly
				}
		}
		/*** DONE LOADING THE CATAPULT ***/
		float pLower = 5; // min height of rectangle for comparison
		float pUpper = 15;	// max height of rectangle for comparison
		int criteriaCount = 1; // number of elements to include/exclude at a time
		int rejectMatches = 1;	// when set to true, particles that do not meet the criteria are discarded
		int connectivity = 1;	// declares connectivity value as 1; so corners are not ignored
		int filterFunction;	// removes small blobs
		int borderSetting;	// variable to store border settings, limit for rectangle
		int borderSize = 1;  // border for the camera frame (if you don't put this, DriverStation gets mad at you)
		ParticleFilterCriteria2 particleCriteria;	
		ParticleFilterOptions2 particleFilterOptions;
		int numParticles;
		particleCriteria.parameter = IMAQ_MT_BOUNDING_RECT_HEIGHT; //The Morphological measurement we use
		particleCriteria.lower = pLower; // The lower bound of the criteria range
		particleCriteria.upper = pUpper; // The upper bound of the criteria range
		particleCriteria.calibrated = FALSE; // We aren't calibrating to real world measurements. We don't need this.
		particleCriteria.exclude = TRUE; // Remove all particles that aren't in specific pLower and pUpper range
		particleFilterOptions.rejectMatches = rejectMatches; // Set to 1 above, so images that do not meet the criteria are discarded
		particleFilterOptions.rejectBorder = 0; // Set to 0 over here so border images are not discarded
		particleFilterOptions.connectivity8 = connectivity; // Sets the image image to 8 bit
		while ((IsAutonomous()))
		{
			if (logitech.GetRawButton(4))
				{
					autonomousEngagement = 1;
				}
			if (autonomousEngagement == 0) // If real autonomous is not engaged start
			{
				if (logitech.GetRawButton(1))
					{
						driveForward();
					}
				if (logitech.GetRawButton(9))
				{
					dogSolenoid.Set(DoubleSolenoid::kForward);		// Brings the pneumatic piston backward to raise the retrieval arm
					winchMotor.Set(0.1);
					Wait(0.3);
					ratchetSolenoid.Set(DoubleSolenoid::kForward);	// Pushes the pneumatic piston forward to lower the retrieval arm
					while(leftLimitSwitch.Get()==1 && rightLimitSwitch.Get()==1)
						{
							winchMotor.Set(1);
						}
				}
				if (logitech.GetRawButton(2))
				{
					autonomousCatapultRelease();
				}
				if (logitech.GetRawButton(3))
				{
					stopDriving();
				}
				if (logitech.GetRawButton(5))
				{
					turnLeft();
				}
				if (logitech.GetRawButton(7))
				{
					turnLeftMore();
				}
				if (logitech.GetRawButton(6))
				{
					turnRight();
				}
				if (logitech.GetRawButton(8))
				{
					turnRightMore();
				}
			}// If real autonomous is not engaged end
			HSLImage* imgpointer; // declares an image container as an HSL (hue-saturation-luminence) image
			imgpointer = camera.GetImage();	//tells camera to capture image
			ringLight.Set(Relay::kForward); //turns ringlight on
			BinaryImage* binIMG = NULL;	// declares a container to hold a binary image
			binIMG = imgpointer -> ThresholdHSL(0, 255, 0, 255, 235, 255);	// thresholds HSL image and places in the binary image container
			delete imgpointer;	// deletes the HSL image to free up memory on the cRIO
			Image* modifiedImage = imaqCreateImage(IMAQ_IMAGE_U8, 0); //create a binary 8-bit format shell for the image
			filterFunction = imaqParticleFilter4(modifiedImage, binIMG -> GetImaqImage(), &particleCriteria, criteriaCount, &particleFilterOptions, NULL, &numParticles); //The Particle Filter Function we use. (The ones before it are outdated)
			borderSetting = imaqSetBorderSize(modifiedImage, borderSize); // Sets a border size so DriverStation is happy
			delete binIMG; //Deletes the Binary image
			int functionCountParticles; // stores number of particles
			int particleAmount; // stores the number of particles for the measure particle function
			functionCountParticles = imaqCountParticles(modifiedImage, TRUE, &particleAmount); // Counts the number of particles
			int functionOne; // The first measuring particle function (specifically for particle #1)
			int functionTwo; // The second measuring particle function (specifically for particle #2)
			double particleOneOrientation; // TRULY ARBITRARY name of the first particle it find
			double particleTwoOrientation; // TRULY ARBITRARY name of the second particle it finds
			functionOne = imaqMeasureParticle(modifiedImage, 0, FALSE, IMAQ_MT_ORIENTATION, &particleOneOrientation); // Measures orientation of particle 1
			functionTwo = imaqMeasureParticle(modifiedImage, 1, FALSE, IMAQ_MT_ORIENTATION, &particleTwoOrientation); // Measures orientation of particle 2
			screen->PrintfLine(DriverStationLCD::kUser_Line2,"P1: %f", particleOneOrientation); // Prints particle 1's orientation
			screen->PrintfLine(DriverStationLCD::kUser_Line3,"P2: %f", particleTwoOrientation); // Prints particle 2's orientation
			imaqDispose(modifiedImage); // Deletes the filtered image
			/**LEFT POSITION**/
			if ((leftPositionSwitch.Get() == 1) && (rightPositionSwitch.Get() == 0)) // Left switch set on,  switch set off
			{
				screen -> PrintfLine(DriverStationLCD::kUser_Line1,"Left Position:F"); // Left position and facing forward			
				if ((particleOneOrientation > 0 && particleOneOrientation < 10) || (particleTwoOrientation > 0 && particleTwoOrientation < 10))
					
					// The target should be hot. Now it goes to the other goal.
					/* Theoretically particle 1 or 2 should register as exactly 0 (the particle is horizontal). We can edit these later. */
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line4,"Left Position Hot!");
					// These DEFINITELY need to be tested. All of them. Forreal.
					turnRight();
					//driveForward();
					Wait(3); 
					stopDriving();
					//autonomousCatapultRelease();
				}
				else // The target isn't hot. So it starts going toward this not hot goal.
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line4,"Left Position Not Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					turnRight();
					driveForward();
					Wait(4);
					stopDriving();
					//autonomousCatapultRelease();
				}		
			}
			/**CENTER POSITION**/
			else if ((leftPositionSwitch.Get() == 0) && (rightPositionSwitch.Get() == 0)) // Left switch off and right switch off
			{
				screen -> PrintfLine(DriverStationLCD::kUser_Line1,"Middle Position:R"); // Middle position and facing 			
				if ((particleOneOrientation > 0 && particleOneOrientation < 10) || (particleTwoOrientation > 0 && particleTwoOrientation < 10)) // The target should be hot. Now it goes to the other goal.
					/* Theoretically particle 1 or 2 should register as exactly 0 (the particle is horizontal). We can edit these later. */
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line4,"Middle Position Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					turnLeftMore();
					driveForward();
					Wait(3); 
					stopDriving();
					autonomousCatapultRelease();
				}
				else // The target isn't hot. So it starts going toward this not hot goal.
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line4,"Middle Position Not Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					driveForward();
					Wait(3);
					stopDriving();
					autonomousCatapultRelease();
				}
			}
			/** RIGHT POSITION**/
			else if ((leftPositionSwitch.Get() == 1) && (rightPositionSwitch.Get() == 1))
			{
				screen -> PrintfLine(DriverStationLCD::kUser_Line1,"Middle Position:R"); // Middle position and facing
				if ((particleOneOrientation > 0 && particleOneOrientation < 10) || (particleTwoOrientation > 0 && particleTwoOrientation < 10)) // The target should be hot. Now it goes to the other goal.
					/* Theoretically particle 1 or 2 should register as exactly 0 (the particle is horizontal). We can edit these later. */
					{
						screen -> PrintfLine(DriverStationLCD::kUser_Line4,"Middle Position Hot");
						// These DEFINITELY need to be tested. All of them. Forreal.
						turnLeftMore();
						driveForward();
						Wait(3); 
						stopDriving();
						autonomousCatapultRelease();
					}
					else // The target isn't hot. So it starts going toward this not hot goal.
					{
						screen -> PrintfLine(DriverStationLCD::kUser_Line4,"Middle Position Not Hot");
						// These DEFINITELY need to be tested. All of them. Forreal.
						driveForward();
						Wait(3);
						stopDriving();
						autonomousCatapultRelease();
					}
			}
			else if (((leftPositionSwitch.Get()) == 1) && ((rightPositionSwitch.Get()) == 0)) // Left switch off and  switch on
			{
				screen -> PrintfLine(DriverStationLCD::kUser_Line1,"Right Position"); //  position and facing forward
				if ((particleOneOrientation > 0 && particleOneOrientation < 10) || ((particleTwoOrientation > 0) && (particleTwoOrientation < 10))) // The target should be hot. Now it goes to the other goal.
					/* Theoretically particle 1 or 2 should register as exactly 0 (the particle is horizontal). We can edit these later. */
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line4,"Right Position Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					turnLeft();
					driveForward();
					Wait(3);
					stopDriving();
					autonomousCatapultRelease();
				}
				else // The target isn't hot. So it starts going toward this not hot goal.
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line4, "Right Position Not Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					driveForward();
					Wait(3);
					stopDriving();
					autonomousCatapultRelease();
				}
			}
			counter++;
			screen -> PrintfLine(DriverStationLCD::kUser_Line5,"R: %f L: %f)", rightFront.Get(), leftFront.Get());
			screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Counter %d", counter);
			screen->UpdateLCD();
		}
		compressor.Stop();
	}
Пример #17
0
bool Target::FindParticles()
{
    printf("Target::FindParticles\n");

    long then = (long) GetFPGATime();

    // extract the blue plane
    if (!imaqExtractColorPlanes(m_cameraImage.GetImaqImage(), IMAQ_RGB,
    		NULL, NULL, m_monoImage.GetImaqImage()))
    {
	printf("%s: imaqExtractColorPlanes FAILED\n", __FUNCTION__);
	return false;
    }

    // select interesting particles
    if (!imaqThreshold(m_threshold.GetImaqImage(), m_monoImage.GetImaqImage(),
    	/*rangeMin*/ THRESHOLD, /*rangeMax*/ 255, /*useNewValue*/ 1, /*newValue */ 1))
    {
	printf("%s: imaqThreshold FAILED\n", __FUNCTION__);
	return false;
    }

    if (!imaqConvexHull(m_convexHull.GetImaqImage(), m_threshold.GetImaqImage(),
    	/*connectivity8*/ 1))
    {
	printf("%s: imaqConvexHull FAILED\n", __FUNCTION__);
	return false;
    }

    if (!imaqSizeFilter(m_filtered.GetImaqImage(), m_convexHull.GetImaqImage(),
	/*connectivity8*/ 1, /*erosions*/ 2, /*keepSize*/ IMAQ_KEEP_LARGE,
	/*structuringElement*/ NULL))
    {
	printf("%s: imaqSizeFilter FAILED\n", __FUNCTION__);
	return false;
    }

    int particleCount = 0;
    if (!imaqCountParticles(m_filtered.GetImaqImage(), 1, &particleCount))
    {
	printf("%s: imaqCountParticles FAILED\n", __FUNCTION__);
	return false;
    }

    // select the four largest particles (insertion sort)
    // for now, keep track of only the particle number (index) and size
    memset((void *)m_particles, 0, sizeof m_particles);
    for (int i = 0; i < particleCount; i++) {
	double size;
	if (!imaqMeasureParticle(m_filtered.GetImaqImage(), i, FALSE,
	    IMAQ_MT_PARTICLE_AND_HOLES_AREA, &size))
	{
	    printf("%s: imaqMeasureParticle %d FAILED\n", __FUNCTION__, i);
	    break;
	}
	for (int j = 0; j < 4; j++) {
	    if (size > m_particles[j].size) {
		for (int k = 3; k > j; k--) {
		    m_particles[k].index = m_particles[k-1].index;
		    m_particles[k].size = m_particles[k-1].size;
		}
		m_particles[j].index = i;
		m_particles[j].size = size;
		break;
	    }
	}
    }

    // fill in the rest of the measured data
    for (m_numParticles = 0;
    	 m_numParticles < 4 && m_particles[m_numParticles].size > 0;
	 m_numParticles++)
    {
	Particle* p = &m_particles[m_numParticles];
	imaqMeasureParticle(m_filtered.GetImaqImage(), p->index, FALSE,
			    IMAQ_MT_CENTER_OF_MASS_X, &(p->xCenter));
	imaqMeasureParticle(m_filtered.GetImaqImage(), p->index, FALSE,
			    IMAQ_MT_CENTER_OF_MASS_Y, &(p->yCenter));
	imaqMeasureParticle(m_filtered.GetImaqImage(), p->index, FALSE,
			    IMAQ_MT_BOUNDING_RECT_LEFT, &(p->leftBound));
	imaqMeasureParticle(m_filtered.GetImaqImage(), p->index, FALSE,
			    IMAQ_MT_BOUNDING_RECT_RIGHT, &(p->rightBound));
	imaqMeasureParticle(m_filtered.GetImaqImage(), p->index, FALSE,
			    IMAQ_MT_BOUNDING_RECT_TOP, &(p->topBound)); 
	imaqMeasureParticle(m_filtered.GetImaqImage(), p->index, FALSE,
			    IMAQ_MT_BOUNDING_RECT_BOTTOM, &(p->bottomBound));
	// calculate height/width from bounding box
	p->height = p->bottomBound - p->topBound;
	p->width = p->rightBound - p->leftBound;
    }

    long now = (long) GetFPGATime();
    printf("%s: particle detection took %ld microseconds\n", __FUNCTION__, (now - then));
    printf("%s: found %d particles\n", __FUNCTION__, particleCount);
    printf("%s: returning %d particles\n", __FUNCTION__, m_numParticles);
    for (int i = 0; i < m_numParticles; i++) {
	Particle *p = &m_particles[i];
	printf("  particle %d index %d top %g bottom %g left %g right %g size %g x %g y %g\n",
		i, p->index, p->topBound, p->bottomBound, p->leftBound, p->rightBound,
		p->size, p->xCenter, p->yCenter);
    }

    return true;
}
Пример #18
0
	void Autonomous() override {
		while (IsAutonomous() && IsEnabled())
		{
			//read file in from disk. For this example to run you need to copy image20.jpg from the SampleImages folder to the
			//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
			imaqError = imaqReadFile(frame, "//NewDir//image2.jpg", NULL, NULL);

			//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
			TOTE_HUE_RANGE.minValue = SmartDashboard::GetNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
			TOTE_HUE_RANGE.maxValue = SmartDashboard::GetNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
			TOTE_SAT_RANGE.minValue = SmartDashboard::GetNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
			TOTE_SAT_RANGE.maxValue = SmartDashboard::GetNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
			TOTE_VAL_RANGE.minValue = SmartDashboard::GetNumber("Tote val min", TOTE_VAL_RANGE.minValue);
			TOTE_VAL_RANGE.maxValue = SmartDashboard::GetNumber("Tote val max", TOTE_VAL_RANGE.maxValue);

			//Threshold the image looking for yellow (tote color)
			imaqError = imaqColorThreshold(binaryFrame, frame, 255, IMAQ_HSV, &TOTE_HUE_RANGE, &TOTE_SAT_RANGE, &TOTE_VAL_RANGE);

			//Send particle count to dashboard
			int numParticles = 0;
			imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
			SmartDashboard::PutNumber("Masked particles", numParticles);

			//Send masked image to dashboard to assist in tweaking mask.
			SendToDashboard(binaryFrame, imaqError);

			//filter out small particles
			float areaMin = SmartDashboard::GetNumber("Area min %", AREA_MINIMUM);
			criteria[0] = {IMAQ_MT_AREA_BY_IMAGE_AREA, areaMin, 100, false, false};
			imaqError = imaqParticleFilter4(binaryFrame, binaryFrame, criteria, 1, &filterOptions, NULL, NULL);

			//Send particle count after filtering to dashboard
			imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
			SmartDashboard::PutNumber("Filtered particles", numParticles);

			if(numParticles > 0)
			{
				//Measure particles and sort by particle size
				std::vector<ParticleReport> particles;
				for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
				{
					ParticleReport par;
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &(par.PercentAreaToImageArea));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA, &(par.Area));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_CONVEX_HULL_AREA, &(par.ConvexHullArea));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_TOP, &(par.BoundingRectTop));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &(par.BoundingRectLeft));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_BOTTOM, &(par.BoundingRectBottom));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_RIGHT, &(par.BoundingRectRight));
					particles.push_back(par);
				}
				sort(particles.begin(), particles.end(), CompareParticleSizes);

				//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
				//for the reader. Note that the long and short side scores expect a single tote and will not work for a stack of 2 or more totes.
				//Modification of the code to accommodate 2 or more stacked totes is left as an exercise for the reader.
				scores.Trapezoid = TrapezoidScore(particles.at(0));
				SmartDashboard::PutNumber("Trapezoid", scores.Trapezoid);
				scores.LongAspect = LongSideScore(particles.at(0));
				SmartDashboard::PutNumber("Long Aspect", scores.LongAspect);
				scores.ShortAspect = ShortSideScore(particles.at(0));
				SmartDashboard::PutNumber("Short Aspect", scores.ShortAspect);
				scores.AreaToConvexHullArea = ConvexHullAreaScore(particles.at(0));
				SmartDashboard::PutNumber("Convex Hull Area", scores.AreaToConvexHullArea);
				bool isTote = scores.Trapezoid > SCORE_MIN && (scores.LongAspect > SCORE_MIN || scores.ShortAspect > SCORE_MIN) && scores.AreaToConvexHullArea > SCORE_MIN;
				bool isLong = scores.LongAspect > scores.ShortAspect;

				//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
				SmartDashboard::PutBoolean("IsTote", isTote);
				SmartDashboard::PutNumber("Distance", computeDistance(binaryFrame, particles.at(0), isLong));
			} else {
				SmartDashboard::PutBoolean("IsTote", false);
			}

			Wait(0.005);				// wait for a motor update time
		}
	}
Пример #19
0
	Vision_Out Run(Vision_In in)
	{
		Vision_Out out;

		//read file in from disk. For this example to run you need to copy image.jpg from the SampleImages folder to the
		//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
		//imaqError = imaqReadFile(frame, "//home//lvuser//SampleImages//image.jpg", NULL, NULL);

		imaqError = camera->GetImage(frame);

		//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
		/*
					RING_HUE_RANGE.minValue = SmartDashboard::GetNumber("Tote hue min", RING_HUE_RANGE.minValue);
					RING_HUE_RANGE.maxValue = SmartDashboard::GetNumber("Tote hue max", RING_HUE_RANGE.maxValue);
					RING_SAT_RANGE.minValue = SmartDashboard::GetNumber("Tote sat min", RING_SAT_RANGE.minValue);
					RING_SAT_RANGE.maxValue = SmartDashboard::GetNumber("Tote sat max", RING_SAT_RANGE.maxValue);
					RING_VAL_RANGE.minValue = SmartDashboard::GetNumber("Tote val min", RING_VAL_RANGE.minValue);
					RING_VAL_RANGE.maxValue = SmartDashboard::GetNumber("Tote val max", RING_VAL_RANGE.maxValue);
		 */

		if(in.shouldProcess)
		{
			//Threshold the image looking for ring light color
			imaqError = imaqColorThreshold(binaryFrame, frame, 255, IMAQ_HSV, &RING_HUE_RANGE, &RING_SAT_RANGE, &RING_VAL_RANGE);

			//Send particle count to dashboard
			int numParticles = 0;
			imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
			SmartDashboard::PutNumber("Masked particles", numParticles);

			//Send masked image to dashboard to assist in tweaking mask.
			SendToDashboard(binaryFrame, imaqError);

			//filter out small particles
			float areaMin = SmartDashboard::GetNumber("Area min %", AREA_MINIMUM);
			criteria[0] = {IMAQ_MT_AREA_BY_IMAGE_AREA, areaMin, 100, false, false};
			imaqError = imaqParticleFilter4(binaryFrame, binaryFrame, criteria, 1, &filterOptions, NULL, NULL);

			//Send particle count after filtering to dashboard
			imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
			SmartDashboard::PutNumber("Filtered particles", numParticles);

			if(numParticles > 0)
			{
				//Measure particles and sort by particle size
				std::vector<ParticleReport> particles;
				for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
				{
					ParticleReport par;
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &(par.PercentAreaToImageArea));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA, &(par.Area));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_TOP, &(par.BoundingRectTop));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &(par.BoundingRectLeft));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_BOTTOM, &(par.BoundingRectBottom));
					imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_RIGHT, &(par.BoundingRectRight));
					particles.push_back(par);
				}
				sort(particles.begin(), particles.end(), CompareParticleSizes);

				//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
				//for the reader. Note that this scores and reports information about a single particle (single L shaped target). To get accurate information
				//about the location of the tote (not just the distance) you will need to correlate two adjacent targets in order to find the true center of the tote.
				scores.Aspect = AspectScore(particles.at(0));
				SmartDashboard::PutNumber("Aspect", scores.Aspect);
				scores.Area = AreaScore(particles.at(0));
				SmartDashboard::PutNumber("Area", scores.Area);
				bool isTarget = scores.Area > SCORE_MIN && scores.Aspect > SCORE_MIN;

				//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
				SmartDashboard::PutBoolean("IsTarget", isTarget);
				double distance = computeDistance(binaryFrame, particles.at(0));
				SmartDashboard::PutNumber("Distance", computeDistance(binaryFrame, particles.at(0)));

				out.returnDistanceToTote = distance;
				out.returnIsTote = true;
				return out;
			}
			else
			{
				out.returnIsTote = false;
				SmartDashboard::PutBoolean("IsTarget", false);
			}
		}
		return out;
	}
Пример #20
0
inline void processTaskFunc(UINT32 hotGoalPtr...)
{
			bool hotGoal = (bool *) hotGoalPtr;
			Scores *scores;
			TargetReport target;
			int verticalTargets[MAX_PARTICLES];
			int horizontalTargets[MAX_PARTICLES];
			int verticalTargetCount, horizontalTargetCount;
			Threshold threshold(0, 255, 0, 255, 220, 255);	//HSV threshold criteria, ranges are in that order ie. Hue is 60-100
			ParticleFilterCriteria2 criteria[] = {
					{IMAQ_MT_AREA, AREA_MINIMUM, 65535, false, false}
			};												//Particle filter criteria, used to filter out small particles
			//AxisCamera &camera = AxisCamera::GetInstance();	//To use the Axis camera uncomment this line
			
	            /**
	             * Do the image capture with the camera and apply the algorithm described above. This
	             * sample will either get images from the camera or from an image file stored in the top
	             * level directory in the flash memory on the cRIO. The file name in this case is "testImage.jpg"
	             */
				ColorImage *image;
				image = new RGBImage("/testImage.jpg");		// get the sample image from the cRIO flash

				//image = camera.GetImage();				//To get the images from the camera comment the line above and uncomment this one
				BinaryImage *thresholdImage = image->ThresholdHSV(threshold);	// get just the green target pixels
				//thresholdImage->Write("/threshold.bmp");
				BinaryImage *filteredImage = thresholdImage->ParticleFilter(criteria, 1);	//Remove small particles
				//filteredImage->Write("Filtered.bmp");

				vector<ParticleAnalysisReport> *reports = filteredImage->GetOrderedParticleAnalysisReports();  //get a particle analysis report for each particle

				verticalTargetCount = horizontalTargetCount = 0;
				//Iterate through each particle, scoring it and determining whether it is a target or not
				if(reports->size() > 0)
				{
					scores = new Scores[reports->size()];
					for (unsigned int i = 0; i < MAX_PARTICLES && i < reports->size(); i++) {
						ParticleAnalysisReport *report = &(reports->at(i));
						
						//Score each particle on rectangularity and aspect ratio
						scores[i].rectangularity = scoreRectangularity(report);
						scores[i].aspectRatioVertical = scoreAspectRatio(filteredImage, report, true);
						scores[i].aspectRatioHorizontal = scoreAspectRatio(filteredImage, report, false);			
						
						//Check if the particle is a horizontal target, if not, check if it's a vertical target
						if(scoreCompare(scores[i], false))
						{
							printf("particle: %d  is a Horizontal Target centerX: %d  centerY: %d \n", i, report->center_mass_x, report->center_mass_y);
							horizontalTargets[horizontalTargetCount++] = i; //Add particle to target array and increment count
						} else if (scoreCompare(scores[i], true)) {
							printf("particle: %d  is a Vertical Target centerX: %d  centerY: %d \n", i, report->center_mass_x, report->center_mass_y);
							verticalTargets[verticalTargetCount++] = i;  //Add particle to target array and increment count
						} else {
							printf("particle: %d  is not a Target centerX: %d  centerY: %d \n", i, report->center_mass_x, report->center_mass_y);
						}
						printf("Scores rect: %f  ARvert: %f \n", scores[i].rectangularity, scores[i].aspectRatioVertical);
						printf("ARhoriz: %f  \n", scores[i].aspectRatioHorizontal);	
					}

					//Zero out scores and set verticalIndex to first target in case there are no horizontal targets
					target.totalScore = target.leftScore = target.rightScore = target.tapeWidthScore = target.verticalScore = 0;
					target.verticalIndex = verticalTargets[0];
					for (int i = 0; i < verticalTargetCount; i++)
					{
						ParticleAnalysisReport *verticalReport = &(reports->at(verticalTargets[i]));
						for (int j = 0; j < horizontalTargetCount; j++)
						{
							ParticleAnalysisReport *horizontalReport = &(reports->at(horizontalTargets[j]));
							double horizWidth, horizHeight, vertWidth, leftScore, rightScore, tapeWidthScore, verticalScore, total;
		
							//Measure equivalent rectangle sides for use in score calculation
							imaqMeasureParticle(filteredImage->GetImaqImage(), horizontalReport->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_LONG_SIDE, &horizWidth);
							imaqMeasureParticle(filteredImage->GetImaqImage(), verticalReport->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &vertWidth);
							imaqMeasureParticle(filteredImage->GetImaqImage(), horizontalReport->particleIndex, 0, IMAQ_MT_EQUIVALENT_RECT_SHORT_SIDE, &horizHeight);
							
							//Determine if the horizontal target is in the expected location to the left of the vertical target
							leftScore = ratioToScore(1.2*(verticalReport->boundingRect.left - horizontalReport->center_mass_x)/horizWidth);
							//Determine if the horizontal target is in the expected location to the right of the  vertical target
							rightScore = ratioToScore(1.2*(horizontalReport->center_mass_x - verticalReport->boundingRect.left - verticalReport->boundingRect.width)/horizWidth);
							//Determine if the width of the tape on the two targets appears to be the same
							tapeWidthScore = ratioToScore(vertWidth/horizHeight);
							//Determine if the vertical location of the horizontal target appears to be correct
							verticalScore = ratioToScore(1-(verticalReport->boundingRect.top - horizontalReport->center_mass_y)/(4*horizHeight));
							total = leftScore > rightScore ? leftScore:rightScore;
							total += tapeWidthScore + verticalScore;
							
							//If the target is the best detected so far store the information about it
							if(total > target.totalScore)
							{
								target.horizontalIndex = horizontalTargets[j];
								target.verticalIndex = verticalTargets[i];
								target.totalScore = total;
								target.leftScore = leftScore;
								target.rightScore = rightScore;
								target.tapeWidthScore = tapeWidthScore;
								target.verticalScore = verticalScore;
							}
						}
						//Determine if the best target is a Hot target
						target.Hot = hotOrNot(target);
					}
					
					if(verticalTargetCount > 0)
					{
						//Information about the target is contained in the "target" structure
						//To get measurement information such as sizes or locations use the
						//horizontal or vertical index to get the particle report as shown below
						ParticleAnalysisReport *distanceReport = &(reports->at(target.verticalIndex));
						double distance = computeDistance(filteredImage, distanceReport);
						if(target.Hot)
						{
							printf("Hot target located \n");
							printf("Distance: %f \n", distance);
							hotGoal = true;
						} else {
							printf("No hot target present \n");
							printf("Distance: %f \n", distance);
							hotGoal = false;
						}
					}
				}

				// be sure to delete images after using them
				delete filteredImage;
				delete thresholdImage;
				delete image;
				
				//delete allocated reports and Scores objects also
				delete scores;
				delete reports;
}
////////////////////////////////////////////////////////////////////////////////
//
// Function Name: IVA_Particle
//
// Description  : Computes the number of particles detected in a binary image and
//                a 2D array of requested measurements about the particle.
//
// Parameters   : image                      -  Input image
//                connectivity               -  Set this parameter to 1 to use
//                                              connectivity-8 to determine
//                                              whether particles are touching.
//                                              Set this parameter to 0 to use
//                                              connectivity-4 to determine
//                                              whether particles are touching.
//                pixelMeasurements          -  Array of measuremnets parameters
//                numPixelMeasurements       -  Number of elements in the array
//                calibratedMeasurements     -  Array of measuremnets parameters
//                numCalibratedMeasurements  -  Number of elements in the array
//                ivaData                    -  Internal Data structure
//                stepIndex                  -  Step index (index at which to store
//                                              the results in the resuts array)
//
// Return Value : success
//
////////////////////////////////////////////////////////////////////////////////
static int IVA_Particle(Image* image,
                                 int connectivity,
                                 int pPixelMeasurements[],
                                 int numPixelMeasurements,
                                 int pCalibratedMeasurements[],
                                 int numCalibratedMeasurements,
                                 IVA_Data* ivaData,
                                 int stepIndex)
{
    int success = 1;
    int numParticles;
    double* pixelMeasurements = NULL;
    double* calibratedMeasurements = NULL;
    unsigned int visionInfo;
    IVA_Result* particleResults;
    int i;
    int j;
    double centerOfMassX;
    double centerOfMassY;


    //-------------------------------------------------------------------//
    //                         Particle Analysis                         //
    //-------------------------------------------------------------------//

    // Counts the number of particles in the image.
    VisionErrChk(imaqCountParticles(image, connectivity, &numParticles));

    // Allocate the arrays for the measurements.
    pixelMeasurements = (double*)malloc(numParticles * numPixelMeasurements * sizeof(double));
    calibratedMeasurements = (double*)malloc(numParticles * numCalibratedMeasurements * sizeof(double));

    // Delete all the results of this step (from a previous iteration)
    IVA_DisposeStepResults(ivaData, stepIndex);

    // Check if the image is calibrated.
    VisionErrChk(imaqGetVisionInfoTypes(image, &visionInfo));

    // If the image is calibrated, we also need to log the calibrated position (x and y)
    ivaData->stepResults[stepIndex].numResults = (visionInfo & IMAQ_VISIONINFO_CALIBRATION ?
                                                  numParticles * 4 + 1 : numParticles * 2 + 1);
    ivaData->stepResults[stepIndex].results = malloc (sizeof(IVA_Result) * ivaData->stepResults[stepIndex].numResults);
    
    particleResults = ivaData->stepResults[stepIndex].results;

    #if defined (IVA_STORE_RESULT_NAMES)
        sprintf(particleResults->resultName, "Object #");
    #endif
    particleResults->type = IVA_NUMERIC;
    particleResults->resultVal.numVal = numParticles;
    particleResults++;
    
    for (i = 0 ; i < numParticles ; i++)
    {
        // Computes the requested pixel measurements about the particle.
        for (j = 0 ; j < numPixelMeasurements ; j++)
        {
            VisionErrChk(imaqMeasureParticle(image, i, FALSE, pPixelMeasurements[j], &pixelMeasurements[i*numPixelMeasurements + j]));
        }

        // Computes the requested calibrated measurements about the particle.
        for (j = 0 ; j < numCalibratedMeasurements ; j++)
        {
            VisionErrChk(imaqMeasureParticle(image, i, TRUE, pCalibratedMeasurements[j], &calibratedMeasurements[i*numCalibratedMeasurements + j]));
        }
        
        #if defined (IVA_STORE_RESULT_NAMES)
            sprintf(particleResults->resultName, "Particle %d.X Position (Pix.)", i + 1);
        #endif
        particleResults->type = IVA_NUMERIC;
        VisionErrChk(imaqMeasureParticle(image, i, FALSE, IMAQ_MT_CENTER_OF_MASS_X, &centerOfMassX));
        particleResults->resultVal.numVal = centerOfMassX;
        particleResults++;

        #if defined (IVA_STORE_RESULT_NAMES)
            sprintf(particleResults->resultName, "Particle %d.Y Position (Pix.)", i + 1);
        #endif
        particleResults->type = IVA_NUMERIC;
        VisionErrChk(imaqMeasureParticle(image, i, FALSE, IMAQ_MT_CENTER_OF_MASS_Y, &centerOfMassY));
        particleResults->resultVal.numVal = centerOfMassY;
        particleResults++;

        if (visionInfo & IMAQ_VISIONINFO_CALIBRATION)
        {
            #if defined (IVA_STORE_RESULT_NAMES)
                sprintf(particleResults->resultName, "Particle %d.X Position (Calibrated)", i + 1);
            #endif
            particleResults->type = IVA_NUMERIC;
            VisionErrChk(imaqMeasureParticle(image, i, TRUE, IMAQ_MT_CENTER_OF_MASS_X, &centerOfMassX));
            particleResults->resultVal.numVal = centerOfMassX;
            particleResults++;

            #if defined (IVA_STORE_RESULT_NAMES)
                sprintf(particleResults->resultName, "Particle %d.Y Position (Calibrated)", i + 1);
            #endif
            particleResults->type = IVA_NUMERIC;
            VisionErrChk(imaqMeasureParticle(image, i, TRUE, IMAQ_MT_CENTER_OF_MASS_Y, &centerOfMassY));
            particleResults->resultVal.numVal = centerOfMassY;
            particleResults++;
        }
    }

Error:
    free(pixelMeasurements);
    free(calibratedMeasurements);

    return success;
}
Пример #22
0
	void Autonomous()
	{
		DriverStationLCD *screen = DriverStationLCD::GetInstance(); 
		while ((IsAutonomous()))
		{
			HSLImage* imgpointer; // declares an image container as an HSL (hue-saturation-luminence) image
			imgpointer = camera.GetImage();	//tells camera to capture image
			backpack.Set(Relay::kForward); //turns ringlight on
			BinaryImage* binIMG = NULL;	// declares a container to hold a binary image
			binIMG = imgpointer -> ThresholdHSL(0, 255, 0, 255, 235, 255);	// thresholds HSL image and places in the binary image container
			delete imgpointer;	// deletes the HSL image to free up memory on the cRIO
			Image* Kirby = imaqCreateImage(IMAQ_IMAGE_U8, 0); //create 8 bit image
			Image* KirbyTwo = imaqCreateImage(IMAQ_IMAGE_U8, 0); // creates the second 8-bit image that we can use separately for counting particles. 
																 // (The first image gets eaten by the measureparticle function)
			float pLower = 175; // min height of rectangle for comparison
			float pUpper = 500;	// max height of rectangle for comparison
			int criteriaCount = 1; // number of elements to include/exclude at a time
			int rejectMatches = 1;	// when set to true, particles that do not meet the criteria are discarded
			int connectivity = 1;	// declares connectivity value as 1; so corners are not ignored
			int Polturgust3000;	// removes small blobs
			int borderSetting;	// variable to store border settings, limit for rectangle
			int cloningDevice; // we create another image because the ParticleMeasuring steals the image from particlecounter
			int borderSize = 1;  // border for the camera frame (if you don't put this, DriverStation gets mad at you)
			ParticleFilterCriteria2 particleCriteria;	
			ParticleFilterOptions2 particleFilterOptions;
			int numParticles;
			particleCriteria.parameter = IMAQ_MT_AREA; //The Morphological measurement we use
			particleCriteria.lower = pLower; // The lower bound of the criteria range
			particleCriteria.upper = pUpper; // The upper bound of the criteria range
			particleCriteria.calibrated = FALSE; // We aren't calibrating to real world measurements. We don't need this.
			particleCriteria.exclude = TRUE; // Remove all particles that aren't in specific pLower and pUpper range
			particleFilterOptions.rejectMatches = rejectMatches; // Set to 1 above, so images that do not meet the criteria are discarded
			particleFilterOptions.rejectBorder = 0; // Set to 0 over here so border images are not discarded
			particleFilterOptions.connectivity8 = connectivity; // Sets the image image to 8 bit
			
			Polturgust3000 = imaqParticleFilter4(Kirby, binIMG -> GetImaqImage(), &particleCriteria, criteriaCount, &particleFilterOptions, NULL, &numParticles); //The Particle Filter Function we use. (The ones before it are outdated)
			borderSetting = imaqSetBorderSize(Kirby, borderSize); // Sets a border size
			cloningDevice =  imaqDuplicate(KirbyTwo, Kirby); //Officially creating a duplicate of the first image to count the number of particles.
			delete binIMG; //Deletes the Binary image
			int ParticleCounter;	// stores number of particles
			int* countparticles; // stores the number of particles for the measure particle function

			ParticleCounter = imaqCountParticles(Kirby, TRUE, countparticles); // Counts the number of particles to be sent off later to the MeasureParticle function. Then it gets eaten by the measureparticle function
			int TinyRuler; // TRULY ARBITRARY name of the first measuring particle function (specifically for particle #1)
			int BabyYardstick; // TRULY ARBITRARY Name of the second measuring particle function (specifically for particle #2)
			double* unowidth; // TRULY ARBITRARY name of the first particle it find
			double* doswidth; // TRULY ARBITRARY name of the second particle it finds

			TinyRuler = imaqMeasureParticle(Kirby, 0, FALSE, IMAQ_MT_BOUNDING_RECT_WIDTH, unowidth); // Function of measuring rectangle width is applied to particle 1 (unowidth)
			BabyYardstick = imaqMeasureParticle(Kirby, 1, FALSE, IMAQ_MT_BOUNDING_RECT_WIDTH, doswidth); // Function of measuring width is applied to particle 2 (doswidth)

			
			screen->PrintfLine(DriverStationLCD::kUser_Line3,"W1: %f",*unowidth); // Prints the applied information to particle 1. (Rectangle width)
			screen->PrintfLine(DriverStationLCD::kUser_Line4,"W2: %f",*doswidth);
			imaqDispose(Kirby);
			imaqDispose(KirbyTwo);
			if (((togglebuttonOne.Get()) == 0) && ((togglebuttonTwo.Get()) == 1))
			{
				screen -> PrintfLine(DriverStationLCD::kUser_Line1,"Left Position");
				if (*unowidth > 20) // The target should be hot. Now it goes to the other goal.
					// Even this needs to be tested
				{	
					screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Left Position Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					turnRight();
					driveForward();
					Wait(3); 
					stopDriving();
					shootCatapult();
				}
				else // The target isn't hot. So it starts going toward this not hot goal.
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Left Position Not Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					driveForward();
					Wait(3);
					stopDriving();
					shootCatapult();
				}
			}
			//both on
			else if (((togglebuttonOne.Get()) == 1) && ((togglebuttonTwo.Get()) == 1))
			{
				screen -> PrintfLine(DriverStationLCD::kUser_Line1,"Middle Position");
				if (*unowidth > 20) // The target should be hot. Now it goes to the other goal.
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Middle Position Hot");
					// These DEFINITELY need to be tested. All of them. Forreal.
					turnLeftMore();
					driveForward();
					Wait(3); 
					stopDriving();
					shootCatapult();
				}
				else if (((togglebuttonOne.Get()) == 0) && ((togglebuttonTwo.Get()) == 0))
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line1,"Middle Position");
					if (*unowidth > 20) // The target should be hot. Now it goes to the other goal.
					{
						screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Middle Position Hot");
						// These DEFINITELY need to be tested. All of them. Forreal.
						turnRightMore();
						driveForward();
						Wait(3); 
						stopDriving();
						shootCatapult();
					}
					else // The target isn't hot. So it starts going toward this not hot goal.
					{
						screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Middle Position Not Hot");
						// These DEFINITELY need to be tested. All of them. Forreal.
						driveForward();
						Wait(3);
						stopDriving();
						shootCatapult();
						driveForward();
						Wait(3);
						stopDriving();
					}
				}
				//Left button on && right off
				else if (((togglebuttonOne.Get()) == 1) && ((togglebuttonTwo.Get()) == 0))
				{
					screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Right Position");
					if (*unowidth > 20) // The target should be hot. Now it goes to the other goal.
					{
						screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Right Position Hot");
						// These DEFINITELY need to be tested. All of them. Forreal.
						turnLeft();
						driveForward();
						Wait(3); 
						stopDriving();
						shootCatapult();
					}
					else // The target isn't hot. So it starts going toward this not hot goal.
					{
						screen -> PrintfLine(DriverStationLCD::kUser_Line6,"Right Position Not Hot");
						// These DEFINITELY need to be tested. All of them. Forreal.
						driveForward();
						Wait(3);
						stopDriving();
						shootCatapult();
					}
				}
			Wait(0.005);
			screen -> UpdateLCD();
			}
		}
	}
Пример #23
0
void Camera::GetInfo() {

	Image* binFrame;

	binFrame = imaqCreateImage(IMAQ_IMAGE_U8, 0);

	Range Hue = {hueMin, hueMax};
	Range Sat = {satMin, satMax};
	Range Val = {valMin, valMax};

	ParticleFilterCriteria2 criteria[1];

	//ParticleFilterOptions2 filterOptions = {0, 0, 1, 1};


	criteria[0] = {IMAQ_MT_AREA, 0, (float) aireMin, false, true};


	int nbParticles(0);

	IMAQdxGrab(session, frame, true, NULL);
	imaqScale(frame, frame, 2, 2, ScalingMode::IMAQ_SCALE_SMALLER, IMAQ_NO_RECT);
	imaqColorThreshold(binFrame, frame, 255, IMAQ_HSV, &Hue, &Sat, &Val);
	imaqMorphology(binFrame, binFrame, IMAQ_DILATE, NULL);

	//imaqParticleFilter4(binFrame, binFrame, &criteria[0], 1, &filterOptions, NULL, &nbParticles);

	imaqCountParticles(binFrame, 0, &nbParticles);


	CameraServer::GetInstance()->SetImage(binFrame);

	int indexMax(0);
	double aireMax(0);

	if(nbParticles > 0) {

		for(int particleIndex = 0; particleIndex < nbParticles; particleIndex++){

			double aire (0);
			imaqMeasureParticle(binFrame, particleIndex, 0, IMAQ_MT_AREA, &aire);

			if (aire > aireMax){
				aireMax = aire;
				indexMax = particleIndex;
			}
		}


		double hypothenuse(0);

		int hauteurImage(0);
		int largeurImage(0);

		double centreX(0);
		double centreY(0);
		double angleX(0);
		double angleY(0);
		double offset(0);


		imaqMeasureParticle(binFrame, indexMax, 0, IMAQ_MT_CENTER_OF_MASS_X, &centreX);
		imaqMeasureParticle(binFrame, indexMax, 0, IMAQ_MT_CENTER_OF_MASS_Y, &centreY);

		imaqGetImageSize(binFrame, &largeurImage, &hauteurImage);
		double dHauteurImage(hauteurImage);
		double dLargeurImage(largeurImage);


		//Normalisation
		centreX = ((2 * centreX) / dLargeurImage) - 1;
		centreY = ((-2 * centreY) / dHauteurImage) + 1;


		angleX = atan(centreX * tan(FOV_X * acos(-1) / 180.0));
		angleY = atan(centreY * tan(FOV_Y * acos(-1) / 180.0));

		distance = (TARGET_HEIGHT - CAMERA_HEIGHT) / tan(CAMERA_ANGLE * acos(-1) / 180 + angleY);
		hypothenuse = sqrt(pow(distance, 2) + pow(TARGET_HEIGHT - CAMERA_HEIGHT, 2));

		offset = hypothenuse * tan(angleX);

		ecart = offset - CAMERA_OFFSET;



		SmartDashboard::PutNumber("Distance Cible", distance);
		SmartDashboard::PutNumber("Angle Cible", ecart);
		SmartDashboard::PutNumber("Aire Particule", aireMax);
		SmartDashboard::PutNumber("Nombre Particules", nbParticles);
		SmartDashboard::PutNumber("Hypothenuse", hypothenuse);
		SmartDashboard::PutNumber("Largeur image", largeurImage);
	}


}