blob: 4a30a79d96ff23c7795ad3d94666e3f0916468b3 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
Brian Silverman1a675112016-02-20 20:42:49 -05002/* Copyright (c) FIRST 2014-2016. All Rights Reserved. */
Brian Silverman26e4e522015-12-17 01:56:40 -05003/* Open Source Software - may be modified and shared by FRC teams. The code */
Brian Silverman1a675112016-02-20 20:42:49 -05004/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
Brian Silverman26e4e522015-12-17 01:56:40 -05006/*----------------------------------------------------------------------------*/
7
8#include "Vision/BinaryImage.h"
9#include "WPIErrors.h"
10#include <cstring>
11
12using namespace std;
13
14/**
15 * Get then number of particles for the image.
16 * @returns the number of particles found for the image.
17 */
18int BinaryImage::GetNumberParticles() {
19 int numParticles = 0;
20 int success = imaqCountParticles(m_imaqImage, 1, &numParticles);
21 wpi_setImaqErrorWithContext(success, "Error counting particles");
22 return numParticles;
23}
24
25/**
26 * Get a single particle analysis report.
27 * Get one (of possibly many) particle analysis reports for an image.
28 * @param particleNumber Which particle analysis report to return.
29 * @returns the selected particle analysis report
30 */
31ParticleAnalysisReport BinaryImage::GetParticleAnalysisReport(
32 int particleNumber) {
33 ParticleAnalysisReport par;
34 GetParticleAnalysisReport(particleNumber, &par);
35 return par;
36}
37
38/**
39 * Get a single particle analysis report.
40 * Get one (of possibly many) particle analysis reports for an image.
41 * This version could be more efficient when copying many reports.
42 * @param particleNumber Which particle analysis report to return.
43 * @param par the selected particle analysis report
44 */
45void BinaryImage::GetParticleAnalysisReport(int particleNumber,
46 ParticleAnalysisReport *par) {
47 int success;
48 int numParticles = 0;
49
50 success = imaqGetImageSize(m_imaqImage, &par->imageWidth, &par->imageHeight);
51 wpi_setImaqErrorWithContext(success, "Error getting image size");
52 if (StatusIsFatal()) return;
53
54 success = imaqCountParticles(m_imaqImage, 1, &numParticles);
55 wpi_setImaqErrorWithContext(success, "Error counting particles");
56 if (StatusIsFatal()) return;
57
58 if (particleNumber >= numParticles) {
59 wpi_setWPIErrorWithContext(ParameterOutOfRange, "particleNumber");
60 return;
61 }
62
63 par->particleIndex = particleNumber;
64 // Don't bother measuring the rest of the particle if one fails
65 bool good = ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_X,
66 &par->center_mass_x);
67 good = good && ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_Y,
68 &par->center_mass_y);
69 good = good &&
70 ParticleMeasurement(particleNumber, IMAQ_MT_AREA, &par->particleArea);
71 good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_TOP,
72 &par->boundingRect.top);
73 good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_LEFT,
74 &par->boundingRect.left);
75 good =
76 good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_HEIGHT,
77 &par->boundingRect.height);
78 good =
79 good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_WIDTH,
80 &par->boundingRect.width);
81 good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA_BY_IMAGE_AREA,
82 &par->particleToImagePercent);
83 good = good && ParticleMeasurement(particleNumber,
84 IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA,
85 &par->particleQuality);
86
87 if (good) {
88 /* normalized position (-1 to 1) */
89 par->center_mass_x_normalized =
90 NormalizeFromRange(par->center_mass_x, par->imageWidth);
91 par->center_mass_y_normalized =
92 NormalizeFromRange(par->center_mass_y, par->imageHeight);
93 }
94}
95
96/**
97 * Get an ordered vector of particles for the image.
98 * Create a vector of particle analysis reports sorted by size for an image.
99 * The vector contains the actual report structures.
100 * @returns a pointer to the vector of particle analysis reports. The caller
101 * must delete the
102 * vector when finished using it.
103 */
104vector<ParticleAnalysisReport> *
105BinaryImage::GetOrderedParticleAnalysisReports() {
106 auto particles = new vector<ParticleAnalysisReport>;
107 int particleCount = GetNumberParticles();
108 for (int particleIndex = 0; particleIndex < particleCount; particleIndex++) {
109 particles->push_back(GetParticleAnalysisReport(particleIndex));
110 }
111 // TODO: This is pretty inefficient since each compare in the sort copies
112 // both reports being compared... do it manually instead... while we're
113 // at it, we should provide a version that allows a preallocated buffer of
114 // ParticleAnalysisReport structures
115 sort(particles->begin(), particles->end(), CompareParticleSizes);
116 return particles;
117}
118
119/**
120 * Write a binary image to flash.
121 * Writes the binary image to flash on the cRIO for later inspection.
122 * @param fileName the name of the image file written to the flash.
123 */
124void BinaryImage::Write(const char *fileName) {
125 RGBValue colorTable[256];
126 memset(colorTable, 0, sizeof(colorTable));
127 colorTable[0].R = 0;
128 colorTable[1].R = 255;
129 colorTable[0].G = colorTable[1].G = 0;
130 colorTable[0].B = colorTable[1].B = 0;
131 colorTable[0].alpha = colorTable[1].alpha = 0;
132 imaqWriteFile(m_imaqImage, fileName, colorTable);
133}
134
135/**
136 * Measure a single parameter for an image.
137 * Get the measurement for a single parameter about an image by calling the
138 * imaqMeasureParticle
139 * function for the selected parameter.
140 * @param particleNumber which particle in the set of particles
141 * @param whatToMeasure the imaq MeasurementType (what to measure)
142 * @param result the value of the measurement
143 * @returns false on failure, true on success
144 */
145bool BinaryImage::ParticleMeasurement(int particleNumber,
146 MeasurementType whatToMeasure,
147 int *result) {
148 double resultDouble;
149 bool success =
150 ParticleMeasurement(particleNumber, whatToMeasure, &resultDouble);
151 *result = (int)resultDouble;
152 return success;
153}
154
155/**
156 * Measure a single parameter for an image.
157 * Get the measurement for a single parameter about an image by calling the
158 * imaqMeasureParticle
159 * function for the selected parameter.
160 * @param particleNumber which particle in the set of particles
161 * @param whatToMeasure the imaq MeasurementType (what to measure)
162 * @param result the value of the measurement
163 * @returns true on failure, false on success
164 */
165bool BinaryImage::ParticleMeasurement(int particleNumber,
166 MeasurementType whatToMeasure,
167 double *result) {
168 int success;
169 success = imaqMeasureParticle(m_imaqImage, particleNumber, 0, whatToMeasure,
170 result);
171 wpi_setImaqErrorWithContext(success, "Error measuring particle");
172 return !StatusIsFatal();
173}
174
175// Normalizes to [-1,1]
176double BinaryImage::NormalizeFromRange(double position, int range) {
177 return (position * 2.0 / (double)range) - 1.0;
178}
179
180/**
181 * The compare helper function for sort.
182 * This function compares two particle analysis reports as a helper for the sort
183 * function.
184 * @param particle1 The first particle to compare
185 * @param particle2 the second particle to compare
186 * @returns true if particle1 is greater than particle2
187 */
188bool BinaryImage::CompareParticleSizes(ParticleAnalysisReport particle1,
189 ParticleAnalysisReport particle2) {
190 // we want descending sort order
191 return particle1.particleToImagePercent > particle2.particleToImagePercent;
192}
193
194BinaryImage *BinaryImage::RemoveSmallObjects(bool connectivity8, int erosions) {
195 auto result = new BinaryImage();
196 int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage,
197 connectivity8, erosions, IMAQ_KEEP_LARGE, nullptr);
198 wpi_setImaqErrorWithContext(success, "Error in RemoveSmallObjects");
199 return result;
200}
201
202BinaryImage *BinaryImage::RemoveLargeObjects(bool connectivity8, int erosions) {
203 auto result = new BinaryImage();
204 int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage,
205 connectivity8, erosions, IMAQ_KEEP_SMALL, nullptr);
206 wpi_setImaqErrorWithContext(success, "Error in RemoveLargeObjects");
207 return result;
208}
209
210BinaryImage *BinaryImage::ConvexHull(bool connectivity8) {
211 auto result = new BinaryImage();
212 int success =
213 imaqConvexHull(result->GetImaqImage(), m_imaqImage, connectivity8);
214 wpi_setImaqErrorWithContext(success, "Error in convex hull operation");
215 return result;
216}
217
218BinaryImage *BinaryImage::ParticleFilter(ParticleFilterCriteria2 *criteria,
219 int criteriaCount) {
220 auto result = new BinaryImage();
221 int numParticles;
222 ParticleFilterOptions2 filterOptions = {0, 0, 0, 1};
223 int success =
224 imaqParticleFilter4(result->GetImaqImage(), m_imaqImage, criteria,
225 criteriaCount, &filterOptions, nullptr, &numParticles);
226 wpi_setImaqErrorWithContext(success, "Error in particle filter operation");
227 return result;
228}