| /*----------------------------------------------------------------------------*/ |
| /* 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; |
| } |
| |