blob: f4cd1bbae0d304b1131b5a5eef395f33b63e5361 [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* 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 "BinaryImage.h"
#include "WPIErrors.h"
#include <cstring>
/** Private NI function needed to write to the VxWorks target */
IMAQ_FUNC int Priv_SetWriteFileAllowed(uint32_t enable);
BinaryImage::BinaryImage() : MonoImage()
{
}
BinaryImage::~BinaryImage()
{
}
/**
* Get then number of particles for the image.
* @returns the number of particles found for the image.
*/
int BinaryImage::GetNumberParticles()
{
int numParticles = 0;
int success = imaqCountParticles(m_imaqImage, 1, &numParticles);
wpi_setImaqErrorWithContext(success, "Error counting particles");
return numParticles;
}
/**
* Get a single particle analysis report.
* Get one (of possibly many) particle analysis reports for an image.
* @param particleNumber Which particle analysis report to return.
* @returns the selected particle analysis report
*/
ParticleAnalysisReport BinaryImage::GetParticleAnalysisReport(int particleNumber)
{
ParticleAnalysisReport par;
GetParticleAnalysisReport(particleNumber, &par);
return par;
}
/**
* Get a single particle analysis report.
* Get one (of possibly many) particle analysis reports for an image.
* This version could be more efficient when copying many reports.
* @param particleNumber Which particle analysis report to return.
* @param par the selected particle analysis report
*/
void BinaryImage::GetParticleAnalysisReport(int particleNumber, ParticleAnalysisReport *par)
{
int success;
int numParticles = 0;
success = imaqGetImageSize(m_imaqImage, &par->imageWidth, &par->imageHeight);
wpi_setImaqErrorWithContext(success, "Error getting image size");
if (StatusIsFatal())
return;
success = imaqCountParticles(m_imaqImage, 1, &numParticles);
wpi_setImaqErrorWithContext(success, "Error counting particles");
if (StatusIsFatal())
return;
if (particleNumber >= numParticles)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "particleNumber");
return;
}
par->particleIndex = particleNumber;
// Don't bother measuring the rest of the particle if one fails
bool good = ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_X, &par->center_mass_x);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_Y, &par->center_mass_y);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA, &par->particleArea);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_TOP, &par->boundingRect.top);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_LEFT, &par->boundingRect.left);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_HEIGHT, &par->boundingRect.height);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_WIDTH, &par->boundingRect.width);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA_BY_IMAGE_AREA, &par->particleToImagePercent);
good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA, &par->particleQuality);
if (good)
{
/* normalized position (-1 to 1) */
par->center_mass_x_normalized = NormalizeFromRange(par->center_mass_x, par->imageWidth);
par->center_mass_y_normalized = NormalizeFromRange(par->center_mass_y, par->imageHeight);
}
}
/**
* Get an ordered vector of particles for the image.
* Create a vector of particle analysis reports sorted by size for an image.
* The vector contains the actual report structures.
* @returns a pointer to the vector of particle analysis reports. The caller must delete the
* vector when finished using it.
*/
vector<ParticleAnalysisReport>* BinaryImage::GetOrderedParticleAnalysisReports()
{
vector<ParticleAnalysisReport>* particles = new vector<ParticleAnalysisReport>;
int particleCount = GetNumberParticles();
for(int particleIndex = 0; particleIndex < particleCount; particleIndex++)
{
particles->push_back(GetParticleAnalysisReport(particleIndex));
}
// TODO: This is pretty inefficient since each compare in the sort copies
// both reports being compared... do it manually instead... while we're
// at it, we should provide a version that allows a preallocated buffer of
// ParticleAnalysisReport structures
sort(particles->begin(), particles->end(), CompareParticleSizes);
return particles;
}
/**
* Write a binary image to flash.
* Writes the binary image to flash on the cRIO for later inspection.
* @param fileName the name of the image file written to the flash.
*/
void BinaryImage::Write(const char *fileName)
{
RGBValue colorTable[256];
Priv_SetWriteFileAllowed(1);
memset(colorTable, 0, sizeof(colorTable));
colorTable[0].R = 0;
colorTable[1].R = 255;
colorTable[0].G = colorTable[1].G = 0;
colorTable[0].B = colorTable[1].B = 0;
colorTable[0].alpha = colorTable[1].alpha = 0;
imaqWriteFile(m_imaqImage, fileName, colorTable);
}
/**
* 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 false on failure, true on success
*/
bool BinaryImage::ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, int *result)
{
double resultDouble;
bool success = ParticleMeasurement(particleNumber, whatToMeasure, &resultDouble);
*result = (int)resultDouble;
return 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 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();
}
//Normalizes to [-1,1]
double BinaryImage::NormalizeFromRange(double position, int range)
{
return (position * 2.0 / (double)range) - 1.0;
}
/**
* The compare helper function for sort.
* This function compares two particle analysis reports as a helper for the sort function.
* @param particle1 The first particle to compare
* @param particle2 the second particle to compare
* @returns true if particle1 is greater than particle2
*/
bool BinaryImage::CompareParticleSizes(ParticleAnalysisReport particle1, ParticleAnalysisReport particle2)
{
//we want descending sort order
return particle1.particleToImagePercent > particle2.particleToImagePercent;
}
BinaryImage *BinaryImage::RemoveSmallObjects(bool connectivity8, int erosions)
{
BinaryImage *result = new BinaryImage();
int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage, connectivity8, erosions, IMAQ_KEEP_LARGE, NULL);
wpi_setImaqErrorWithContext(success, "Error in RemoveSmallObjects");
return result;
}
BinaryImage *BinaryImage::RemoveLargeObjects(bool connectivity8, int erosions)
{
BinaryImage *result = new BinaryImage();
int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage, connectivity8, erosions, IMAQ_KEEP_SMALL, NULL);
wpi_setImaqErrorWithContext(success, "Error in RemoveLargeObjects");
return result;
}
BinaryImage *BinaryImage::ConvexHull(bool connectivity8)
{
BinaryImage *result = new BinaryImage();
int success = imaqConvexHull(result->GetImaqImage(), m_imaqImage, connectivity8);
wpi_setImaqErrorWithContext(success, "Error in convex hull operation");
return result;
}
BinaryImage *BinaryImage::ParticleFilter(ParticleFilterCriteria2 *criteria, int criteriaCount)
{
BinaryImage *result = new BinaryImage();
int numParticles;
ParticleFilterOptions2 filterOptions = {0, 0, 0, 1};
int success = imaqParticleFilter4(result->GetImaqImage(), m_imaqImage, criteria, criteriaCount, &filterOptions, NULL, &numParticles);
wpi_setImaqErrorWithContext(success, "Error in particle filter operation");
return result;
}