/** * Runs the motors with arcade steering. */ void OperatorControl(void) { HSLImage *Himage; Threshold targetThreshold(247, 255, 60, 140, 10, 50); BinaryImage *matchingPixels; vector<ParticleAnalysisReport> *pReport; //myRobot->SetSafetyEnabled(true); Saftey->SetEnabled(false); AxisCamera &mycam = AxisCamera::GetInstance("10.15.10.11"); mycam.WriteResolution(AxisCamera::kResolution_640x480); mycam.WriteCompression(20); mycam.WriteBrightness(25); Wait(3.0); dsLCD = DriverStationLCD::GetInstance(); dsLCD->Clear(); float X[2]; float Y[2]; float Z[2]; while(IsOperatorControl()) { X[1] = Stick1->GetX(); X[2] = Stick2->GetX(); Y[1] = Stick1->GetY(); Y[2] = Stick2->GetY(); Z[1] = Stick1->GetZ(); Z[2] = Stick2->GetZ(); Jaguar1->Set(Y[1]); Jaguar2->Set(Y[2]); Wait(0.005); if (mycam.IsFreshImage()) { Himage = mycam.GetImage(); matchingPixels = Himage->ThresholdHSL(targetThreshold); pReport = matchingPixels->GetOrderedParticleAnalysisReports(); for (unsigned int i = 0; i < pReport->size(); i++) { printf("Index: %d X Center: %d Y Center: %d \n", i, (*pReport)[i].center_mass_x, (*pReport)[i].center_mass_y); } delete Himage; delete matchingPixels; delete pReport; } } //myRobot->ArcadeDrive(stick); // drive with arcade style (use right stick) //Wait(0.005); // wait for a motor update time }
bool JankyTargeting::DoImageProcessing(void) { bool isSuccessful = false; BinaryImage* firstBinaryImage = NULL; BinaryImage* readyForConvexHull = NULL; firstBinaryImage = hsl.ThresholdHSL(120,186,60,255,0,255); if (firstBinaryImage !=NULL) { // Prune down # particles by removing all small stuff before convexHull. readyForConvexHull = firstBinaryImage->RemoveSmallObjects(false, 2); if (readyForConvexHull != NULL) { samwise = readyForConvexHull->ConvexHull(false); if (samwise != NULL) isSuccessful = true; } } if (readyForConvexHull) delete readyForConvexHull; if (firstBinaryImage) delete firstBinaryImage; return isSuccessful; }
OpenBinaryImage::OpenBinaryImage(const BinaryImage& anImg, unsigned int anOpenSize) throw(QgarErrorDomain) : BinaryImage(anImg) { int sqsize = (2 * anOpenSize) + 1; // Effective mask size if ((sqsize > anImg.width()) || (sqsize > anImg.height())) { std::ostringstream os; os << "Opening size [" << sqsize << " X " << sqsize << "] too large for image [" << anImg.width() << " X " << anImg.height() << "]"; throw QgarErrorDomain(__FILE__, __LINE__, "void qgar::OpenBinaryImage::OpenBinaryImage(const qgar::BinaryImage&, unsigned int)", os.str()); } perform(this, anOpenSize); }
//简单的测试程序,运行ok,结果ok int main() { BinaryImage a(4, 5); //新建一张图片 a.setIndexValue(0, 0, 1); a.setIndexValue(0, 1, 0); a.setIndexValue(0, 2, 0); a.setIndexValue(0, 3, 1); a.setIndexValue(0, 4, 0); a.setIndexValue(1, 0, 1); a.setIndexValue(1, 1, 1); a.setIndexValue(1, 2, 1); a.setIndexValue(1, 3, 0); a.setIndexValue(1, 4, 0); a.setIndexValue(2, 0, 1); a.setIndexValue(2, 1, 0); a.setIndexValue(2, 2, 1); a.setIndexValue(2, 3, 1); a.setIndexValue(2, 4, 0); a.setIndexValue(3, 0, 1); a.setIndexValue(3, 1, 0); a.setIndexValue(3, 2, 0); a.setIndexValue(3, 3, 0); a.setIndexValue(3, 4, 1); Element b(2, 2); //新建一个结构元素 b.ptr[0][0] = 1; b.ptr[0][1] = 1; b.ptr[1][0] = 1; b.ptr[1][1] = 0; b.originX = 1; b.originY = 1; BinaryImage* k=a.dilation(b); //dilation for (int i = 0; i < k->getRow(); i++) { for (int j = 0; j < k->getCol(); j++) cout << k->getIndexValue(i, j) << " "; cout << endl; } cout << "******************************" << endl; BinaryImage* l = a.erosion(b); //erosion for (int i = 0; i < l->getRow(); i++) { for (int j = 0; j < l->getCol(); j++) cout <<l->getIndexValue(i, j) << " "; cout << endl; } cout << "******************************" << endl; system("pause"); }
ErodedBinaryImage::ErodedBinaryImage(BinaryImage& anImg, unsigned int anEroSize) throw(QgarErrorDomain) : BinaryImage(anImg) { int sqsize = (2 * anEroSize) + 1; // Effective mask size if ((sqsize > anImg.width()) || (sqsize > anImg.height())) { std::ostringstream os; os << "Erosion size (" << sqsize << " X " << sqsize << ") too large for an image " << anImg.width() << " X " << anImg.height(); throw QgarErrorDomain(__FILE__, __LINE__, "qgar::ErodedBinaryImage::ErodedBinaryImage(qgar::BinaryImage&, unsigned int)", os.str()); } perform(this, anEroSize); }
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; }
Camera* Camera::calculate(void) { AxisCamera &cam = AxisCamera::GetInstance("10.8.54.11"); if (!cam.IsFreshImage()) return this; take = false; int i = cam.GetImage(&ci); if (i) cerr << "image worked" << endl; else cerr << "image didn't work" << endl; //ci.Write("/colorimage.jpg"); BinaryImage *bi = ci.ThresholdHSL(200, 55, 0, 255, 0, 200); //BinaryImage *bi = ci.ThresholdHSL(Constants::hueMin, Constants::hueMax, // Constants::satMin, Constants::satMax, // Constants::lumMin, Constants::lumMax); //BinaryImage *biggerObjects = bi->RemoveSmallObjects(false, 2); vector <ParticleAnalysisReport> *particleAnalysisList = bi->GetOrderedParticleAnalysisReports(); Rect biggestRectangle = particleAnalysisList->at(0).boundingRect; int x = biggestRectangle.left + biggestRectangle.width/2; int y = biggestRectangle.top + biggestRectangle.height/2; cerr << "T: (" << x << "," << y << ")" << endl; bi->Write("/image.bmp"); //delete bi; //delete particleAnalysisList; return this; }
static bool checkAlignedImage( ConnCompEraserExt const& eraser, BinaryImage const& nonaligned) { BinaryImage const aligned(eraser.computeConnCompImageAligned()); int const pad = aligned.width() - nonaligned.width(); if (pad < 0) { return false; } BinaryImage test1(nonaligned); BinaryImage empty1(test1.size()); empty1.fill(WHITE); rasterOp<RopXor<RopSrc, RopDst> >(test1, test1.rect(), aligned, QPoint(pad, 0)); if (test1 != empty1) { return false; } if (pad > 0) { // Check that padding is white. BinaryImage test2(pad, nonaligned.height()); BinaryImage empty2(test2.size()); empty2.fill(WHITE); rasterOp<RopSrc>(test2, test2.rect(), aligned, QPoint(0, 0)); if (test2 != empty2) { return false; } } return true; }
double CameraHandler::GetDistanceToBall () { unsigned x; int ballNum; double objAngle; double largestArea; double sizeRatio; BinaryImage* binImg; vector<ParticleAnalysisReport>* particles; // ----- Get Image ----- camera->GetImage(img); // ----- Filter out background ----- if (m_ds->GetAlliance() == DriverStation::kBlue) binImg = img->ThresholdHSV(160, 184, 120, 255, 14, 233); else binImg = img->ThresholdRGB(88, 255, 0, 74, 0, 31); // Make picture clear frcMorphology(binImg->GetImaqImage(),binImg->GetImaqImage(),IMAQ_PCLOSE); frcMorphology(binImg->GetImaqImage(),binImg->GetImaqImage(),IMAQ_ERODE); particles = binImg->GetOrderedParticleAnalysisReports(); SmartDashboard::PutNumber("DEBUG Particle size: ", particles->size()); if (particles->size() > 0 && particles->size() < 30) { sort(particles->begin(),particles->end(),particleSort); //Find ball largestArea = 25.0; ballNum = -1; for (x = 0; ((x < particles->size()) && x < 5); x++) { sizeRatio = (double)(*particles)[x].boundingRect.height/(*particles)[x].boundingRect.width; if (((*particles)[x].particleArea > largestArea) && (sizeRatio > 0.75 && sizeRatio < 1.25)) { largestArea = (*particles)[x].particleArea; ballNum = x; } } } if (ballNum >= 0) { objAngle = 0.5*((*particles)[ballNum].boundingRect.width)*(CAMERA_ANGLE/(*particles)[ballNum].imageWidth); return 1/tan(objAngle); } else return -1.0; }
BinaryImage* ConnectedComponents::makeBinaryImg (const std::vector<Component::label_type>& aLabSet) { // Create the resulting image BinaryImage* pImgBin = new BinaryImage(componentImg_.width(), componentImg_.height()); // Pointers to pixel maps Component::label_type* pMapCC = componentImg_.pPixMap(); BinaryImage::pointer pMapBin = pImgBin->pPixMap(); // Current label and color Component::label_type currLab = Component::LABEL_NO_; QGEbw currColor = QGE_BW_WHITE; // Number of pixels of both images int size = componentImg_.width() * componentImg_.height(); // For each pixel of the component image for (int iCnt = 0 ; iCnt< size ; ++iCnt, ++pMapCC, ++pMapBin) { if (*pMapCC != currLab) { // The new pixel belongs to a new component currLab = *pMapCC; std::vector<Component::label_type>::const_iterator it = find(aLabSet.begin(), aLabSet.end(), currLab); if ( (it == aLabSet.end()) || ((*this)[currLab].color() == QGE_BW_WHITE)) { // Current label does not belong to the given set // or belongs to a WHITE component currColor = QGE_BW_WHITE; } else { // Current label belongs to a BLACK component currColor = QGE_BW_BLACK; } } // Set the corresponding pixel of the resulting image // to current color *pMapBin = currColor; } // END for iCnt // Return a pointer to the resulting image return pImgBin; }
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; }
BinaryImage BinaryImage::GetBlock(int iS, int iE, int jS, int jE) { BinaryImage mat; mat.MM = iE - iS + 1; mat.NN = jE - jS + 1; mat.data = new double[mat.MM * mat.NN]; for (int i = 0; i < mat.MM; i++) for (int j = 0; j < mat.NN; j++) mat.Set(i, j, Get(i + iS, j + jS)); return mat; }
/** * Perform a threshold operation on a ColorImage. * Perform a threshold operation on a ColorImage using the ColorMode supplied * as a parameter. * @param colorMode The type of colorspace this operation should be performed in * @returns a pointer to a binary image */ BinaryImage *ColorImage::ComputeThreshold(ColorMode colorMode, int low1, int high1, int low2, int high2, int low3, int high3) { BinaryImage *result = new BinaryImage(); Range range1 = {low1, high1}, range2 = {low2, high2}, range3 = {low3, high3}; int success = imaqColorThreshold(result->GetImaqImage(), m_imaqImage, 1, colorMode, &range1, &range2, &range3); wpi_setImaqErrorWithContext(success, "ImaqThreshold error"); return result; }
BinaryImage::BinaryImage(const BinaryImage& rhs) { //cout<<"BinaryImage::BinaryImage(const BinaryImage&) is invoked..."; m = rhs.getm(); n = rhs.getn(); data = new double[m * n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { data[i * n + j] = rhs.get(i, j); } } }
void TextLineTracer::sanitizeBinaryImage(BinaryImage& image, QRect const& content_rect) { // Kill connected components touching the borders. BinaryImage seed(image.size(), WHITE); seed.fillExcept(seed.rect().adjusted(1, 1, -1, -1), BLACK); BinaryImage touching_border(seedFill(seed.release(), image, CONN8)); rasterOp<RopSubtract<RopDst, RopSrc> >(image, touching_border.release()); // Poor man's despeckle. BinaryImage content_seeds(openBrick(image, QSize(2, 3), WHITE)); rasterOp<RopOr<RopSrc, RopDst> >(content_seeds, openBrick(image, QSize(3, 2), WHITE)); image = seedFill(content_seeds.release(), image, CONN8); // Clear margins. image.fillExcept(content_rect, WHITE); }
void PolynomialSurface::prepareEquationsAndDataPoints( GrayImage const& image, BinaryImage const& mask, std::vector<double>& equations, std::vector<double>& data_points) const { int const width = image.width(); int const height = image.height(); double const xscale = calcScale(width); double const yscale = calcScale(height); uint8_t const* image_line = image.data(); int const image_bpl = image.stride(); uint32_t const* mask_line = mask.data(); int const mask_wpl = mask.wordsPerLine(); int const last_word_idx = (width - 1) >> 5; int const last_word_mask = ~uint32_t(0) << (31 - ((width - 1) & 31)); for (int y = 0; y < height; ++y) { double const y_adjusted = y * yscale; int idx = 0; // Full words. for (; idx < last_word_idx; ++idx) { processMaskWord( image_line, mask_line[idx], idx, y, y_adjusted, xscale, equations, data_points ); } // Last word. processMaskWord( image_line, mask_line[idx] & last_word_mask, idx, y, y_adjusted, xscale, equations, data_points ); image_line += image_bpl; mask_line += mask_wpl; } }
BinaryImage::BinaryImage(const BinaryImage& bin) { MM = bin.MM; NN = bin.NN; data = new double[MM*NN]; for (int i = 0; i < MM; i++) { for (int j = 0; j < NN; j++) { this->Set(i, j, bin.Get(i, j)); } } }
double BinaryImage::SSD(const BinaryImage& T) { Matrix Diff(32, 32, 1); double sum = 0; for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { Diff.Set(i, j, (Get(i, j) - T.Get(i, j))); sum += Diff.Get(i, j)*Diff.Get(i, j); } } return sum; }
BinaryImage BinaryImage::operator=(const BinaryImage& bin) { this->MM = bin.MM; this->NN = bin.NN; this->data = new double[this->MM*this->NN]; for (int i = 0; i < this->MM; i++) { for (int j = 0; j < this->NN; j++) { this->Set(i, j, bin.Get(i, j)); } } return *this; }
BinaryImage BinaryImage::operator-(BinaryImage& in) { BinaryImage out(this->getWidth(), this->getHeight()); for(int i = 0; i < this->getHeight(); i ++) { for(int j = 0; j < this->getWidth(); j ++) { // XOR bool a = this->pixel(i, j) > 0; bool b = in.pixel(i, j) > 0; // Result bool r = (a && !b) || (!a && b); out(i, j) = r ? 1 : 0; } } return out; }
int main() { PngImage Png; Png.Read("./cballs.png"); GrayImage GrayOriginal, GrayBlured, GrayAdjusted; // Convert original PNG to gray-level image GrayOriginal.Convert(Png); // Blur PNG image FastGaussianBlur::Blur<3>(Png, 4.0); Png.Write("./cballs_blured.png"); // Convert Blured PNG image to gray-level image GrayBlured.Convert(Png); GrayAdjusted.CopyFrom(GrayBlured); // Adjust color GrayAdjusted.AdjustColor(1.5, -250); Png.Write(GrayOriginal, "./cballs_gray.png"); Png.Write(GrayBlured, "./cballs_grayblured.png"); Png.Write(GrayAdjusted, "./cballs_grayadjusted.png"); BinaryImage Bin; // Example of Otsu binarization Bin.Otsu(GrayAdjusted); Png.Write(Bin, "./cballs_otsu.png"); // Example of Niblack binarization Bin.Niblack(GrayAdjusted, 150, 1.5); Png.Write(Bin, "./cballs_niblack.png"); // Example of Sauvola binarization Bin.Sauvola(GrayAdjusted, 20, 0.2); Png.Write(Bin, "./cballs_sauvola.png"); Png.Read("./cballs.png"); Cluster Objects; // Clusterize white objects (glowing white balls) uint16_t ClusterAmount = Objects.Clusterize(Bin); for (int i = 0; i < ClusterAmount; ++i) { const ClusterItem &item = Objects.GetCluster(i); Png.DrawCross(item.Cx, item.Cy, 20, PixelType::RGB24(0,255,0)); } Png.Write("./cballs_clusterized.png"); Png.Read("./cballs.png"); PngImage Png2; Png2.Read("./cballs.png"); AffineTransformation Affine; AffineTransformation Affine2; GrayImage Gray1, Gray2; Gray1.Convert(Png); //Affine.Shift(20, 50); Affine.RotateDeg(22.5); Affine2.Scale(2, 2); //Affine.Scale(0.5, 0.5); Affine.Transform(Affine2); AffineTransformationTable AffineTable; AffineTable.Calculate(Gray1.GetWidth(), Gray1.GetHeight(), Affine, true); AffineTable.Apply<InterpolationType::NearestNeighbour>(Gray1, Gray2); Png.Write(Gray2, "./cballs_affine.png"); PngImage testPng; testPng.Read("./cballs.png"); GrayImage testGray, testGray2; testGray.Convert(testPng); testGray2.CopyFrom(testGray); Sobel sobel; sobel.Calculate(testGray); const GenericImage<GenericPixel<int32_t , 1>> *magn = sobel.GetMagnitude(); int32_t min_ = min(*magn); int32_t max_ = max(*magn); int32_t factor = max_ - min_; printf("%d %d %d\n", min_, max_, factor); auto it_src = magn->begin(); auto it_dst = testGray.begin(); for (; it_src != magn->end(); ++it_src, ++it_dst) { if (factor > 0) { int32_t p = it_src[0]; p -= min_; it_dst[0] = (unsigned char) min(255, max(0, (255 * p) / factor)); } else { it_dst[0] = (unsigned char) min(255, max(0, it_src[0])); } } testPng.Write(testGray, "./cballs_sobel.png"); BinaryImage testBin; //GrayImage testGray3; Canny canny; FastGaussianBlur::Blur<3>(testGray2, 1.3); canny.Calculate(testGray2, testBin, 0, 0, 0.33, 0.33); testPng.Write(testBin, "./cballs_canny.png"); HoughLine hough; hough.Calculate(testBin, -30, 30, 0.5, 0, 100, 1, 255); int32_t W = hough.GetWidth(); int32_t H = hough.GetHeight(); printf("%d %d\n", W,H); testGray2.Create(W,H,PixelType::Mono8(0)); printf("%d %d\n", testGray2.GetWidth(), testGray2.GetHeight()); { auto it_src = hough.begin(); auto it_dst = testGray2.begin(); for (; it_src != hough.end(); ++it_src, ++it_dst) { it_dst[0] = it_src[0]; } } testPng.Write(testGray2, "./cballs_hough.png"); return 0; }
void Despeckle::despeckleInPlace( BinaryImage& image, Dpi const& dpi, Level const level, TaskStatus const& status, DebugImages* const dbg) { Settings const settings(Settings::get(level, dpi)); ConnectivityMap cmap(image, CONN8); if (cmap.maxLabel() == 0) { // Completely white image? return; } status.throwIfCancelled(); std::vector<Component> components(cmap.maxLabel() + 1); std::vector<BoundingBox> bounding_boxes(cmap.maxLabel() + 1); int const width = image.width(); int const height = image.height(); uint32_t* const cmap_data = cmap.data(); // Count the number of pixels and a bounding rect of each component. uint32_t* cmap_line = cmap_data; int const cmap_stride = cmap.stride(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { uint32_t const label = cmap_line[x]; ++components[label].num_pixels; bounding_boxes[label].extend(x, y); } cmap_line += cmap_stride; } status.throwIfCancelled(); // Unify big components into one. std::vector<uint32_t> remapping_table(components.size()); uint32_t unified_big_component = 0; uint32_t next_avail_component = 1; for (uint32_t label = 1; label <= cmap.maxLabel(); ++label) { if (bounding_boxes[label].width() < settings.bigObjectThreshold && bounding_boxes[label].height() < settings.bigObjectThreshold) { components[next_avail_component] = components[label]; remapping_table[label] = next_avail_component; ++next_avail_component; } else { if (unified_big_component == 0) { unified_big_component = next_avail_component; ++next_avail_component; components[unified_big_component] = components[label]; // Set num_pixels to a large value so that canBeAttachedTo() // always allows attaching to any such component. components[unified_big_component].num_pixels = width * height; } remapping_table[label] = unified_big_component; } } components.resize(next_avail_component); std::vector<BoundingBox>().swap(bounding_boxes); // We don't need them any more. status.throwIfCancelled(); uint32_t const max_label = next_avail_component - 1; // Remapping individual pixels. cmap_line = cmap_data; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { cmap_line[x] = remapping_table[cmap_line[x]]; } cmap_line += cmap_stride; } if (dbg) { dbg->add(cmap.visualized(), "big_components_unified"); } status.throwIfCancelled(); // Build a Voronoi diagram. std::vector<Distance> distance_matrix; voronoi(cmap, distance_matrix); if (dbg) { dbg->add(cmap.visualized(), "voronoi"); } status.throwIfCancelled(); Distance* const distance_data = &distance_matrix[0] + width + 3; // Now build a bidirectional map of distances between neighboring // connected components. typedef std::map<Connection, uint32_t> Connections; // conn -> sqdist Connections conns; voronoiDistances(cmap, distance_matrix, conns); status.throwIfCancelled(); // Tag connected components with ANCHORED_TO_BIG or ANCHORED_TO_SMALL. BOOST_FOREACH(Connections::value_type const& pair, conns) { Connection const conn(pair.first); uint32_t const sqdist = pair.second; Component& comp1 = components[conn.lesser_label]; Component& comp2 = components[conn.greater_label]; tagSourceComponent(comp1, comp2, sqdist, settings); tagSourceComponent(comp2, comp1, sqdist, settings); }
NiblackBinaryImage::NiblackBinaryImage(const GreyLevelImage& anImg, const int aLowThres, const int aHighThres, const int aMaskSize, const float aK, const float aPostThres) : BinaryImage(anImg.width(), anImg.height()) { // Pointer to mean image FloatImage* pMeanImg = 0; // Compute standard deviation and mean StandardDeviationImage stdImg(FloatImage(anImg), pMeanImg, aMaskSize); // Rows to exchange data GreyLevelImage::pointer bRow = new GreyLevelImage::value_type [_width]; GreyLevelImage::pointer gRow = new GreyLevelImage::value_type [_width]; float* mRow = new float [_width]; float* sRow = new float [_width]; // Binarize for (int i = 0 ; i < _height ; ++i) { // Read means pMeanImg->row(i, mRow); // Read deviations stdImg.row(i, sRow); // Read original anImg.row(i, gRow); // Pointers float* m = mRow; float* s = sRow; GreyLevelImage::pointer g = gRow; GreyLevelImage::pointer b = bRow; // Dynamic thresholding for (int j = 0 ; j < _width ; ++j, ++b, ++g) { if (*g < aLowThres) { *b = 1; } else if (*g > aHighThres) { *b = 0; } else if (*g < (GreyLevelImage::value_type) (*m++ + aK * *s++)) { *b = 1; } else { *b = 0; } } // Save line setRow(i, bRow); } // Post-processing if (aPostThres > 0) { // Copy reference image BinaryImage* contours = new BinaryImage(*this); // Extract connected components ConnectedComponents* compConnexes = new ConnectedComponents(*contours); // Prepare tables int labCnt = compConnexes->componentCnt(); // Tables for the sums of the means of the gradient float* gsum = new float [labCnt]; qgFill(gsum, labCnt, 0.f); // Tables for the numbers of points -- for the mean int* psum = new int [labCnt]; qgFill(psum, labCnt, 0); // Table of labels Component::label_type* labRow = new Component::label_type[_width]; // By construction, first and last pixels are always WHITE labRow[1] = 0; labRow[_width - 1] = 0; // Compute the module of Canny gradient CannyGradientImage* gradImg = new CannyGradientImage(anImg); GradientModuleImage gradModImg(*gradImg); delete gradImg; // Construct the image of the contours of the black components // which thus includes the interesting pixels ErodedBinaryImage* eroImg = new ErodedBinaryImage(*contours); (*contours) -= (*eroImg); delete eroImg; // Create a line of floats float* fRow = new float [_width]; // Pointer to the pixel map of the component image Component::label_type* pMapCCImg = (compConnexes->accessComponentImage()).pPixMap() + _width; for (int i = 1 ; i < (_height - 1) ; ++i) { // Get a line of labels from the component image // and set pixels from white components to 0 pMapCCImg += 2; PRIVATEgetBlackLabels(pMapCCImg, labRow); // Read the corresponding line in the contours contours->row(i, bRow); // Read the corresponding line in the module of the gradient gradModImg.row(i, fRow); Component::label_type* p = labRow; GreyLevelImage::pointer q = bRow; float* r = fRow; for (int j = 0 ; j < _width ; ++j, ++p, ++q, ++r) { if (*q != 0) // We are on a contour { gsum[(int)(*p)] += *r; psum[(int)(*p)] += 1; } } // END for j } // END for i delete contours; // Compute the means for (int i = 0 ; i < labCnt ; ++i) { if (psum[i] != 0) { gsum[i] /= psum[i]; } } // END for // Pointer to the pixel map of the component image pMapCCImg = (compConnexes->accessComponentImage()).pPixMap() + _width; // Delete fake black components for (int i = 1 ; i < _height - 1 ; ++i) { // Read the current line of components pMapCCImg += 2; PRIVATEgetBlackLabels(pMapCCImg, labRow); // Read the corresponding line in the binary image row(i, bRow); // Examine components and delete Component::label_type* p = labRow; GreyLevelImage::pointer pb = bRow; for (int j = 0 ; j < _width ; ++j, ++p, ++pb) { if (((*pb) != 0) && (gsum[(int)(*p)] < aPostThres)) { *pb = 0; } } // Save this line setRow(i, bRow); } // END for // Clean up delete [] fRow; delete [] psum; delete [] gsum; delete compConnexes; } // And clean up delete [] bRow; delete [] gRow; delete [] mRow; delete [] sRow; }
ConnectedComponents::ConnectedComponents(const BinaryImage& aBinImg) : componentImg_(ConnectedComponents::image_type(aBinImg.width(), aBinImg.height())) { // Initialize offset tables used during contour following // to get 4- and 8-connected neighbors in a 3X3 neighborhood. // See class qgar::Component for full details. int w = aBinImg.width(); // 4-CONNECTIVITY // // current (contour) direction // N E S W // 0 1 2 3 4 5 6 7 8 9 10 11 12 // +---+---+---+---+---+---+---+---+---+---+---+---+---+ // | -1| * | -w| * | 1 | * | w | * | -1| * |-w | * | 1 | // +---+---+---+---+---+---+---+---+---+---+---+---+---+ int tmpOff4[13] = { -1, 0, -w, 0, 1, 0, w, 0, -1, 0, -w, 0, 1 }; offset3X3_4_ = new int[13]; memcpy(offset3X3_4_, tmpOff4, 13 * sizeof(int)); // 8-CONNECTIVITY // // search rank // 0 1 2 3 4 5 // +----+----+----+----+----+----+ // / N 0 | -1 | -w |-w-1| 1 |-w+1| w | // | +----+----+----+----+----+----+ // | NE 1 | -w |-w-1| 1 |-w+1| w+1| w-1| // | +----+----+----+----+----+----+ // | E 2 | -w | 1 |-w+1| w | w+1| -1 | // | +----+----+----+----+----+----+ // current | SE 3 | 1 |-w+1| w | w+1| w-1|-w-1| // contour < +----+----+----+----+----+----+ // direction | S 4 | 1 | w | w+1| -1 | w-1| -w | // | +----+----+----+----+----+----+ // | SW 5 | w | w+1| -1 | w-1|-w-1|-w+1| // | +----+----+----+----+----+----+ // | W 6 | w | -1 | w-1| -w |-w-1| 1 | // | +----+----+----+----+----+----+ // \ NW 7 | -1 | w-1| -w |-w-1|-w+1| w+1| // +----+----+----+----+----+----+ int tmpOff8[48] = { -1, -w, -w-1, 1, -w+1, w, -w, -w-1, 1, -w+1, w+1, w-1, -w, 1, -w+1, w, w+1, -1, 1, -w+1, w, w+1, w-1, -w-1, 1, w, w+1, -1, w-1, -w, w, w+1, -1, w-1, -w-1, -w+1, w, -1, w-1, -w, -w-1, 1, -1, w-1, -w, -w-1, -w+1, w+1 }; offset3X3_8_ = new int[48]; memcpy(offset3X3_8_, tmpOff8, 48 * sizeof(int)); // Initialize data in order to run the construction of components ConnectedComponentsImpl ccImpl(aBinImg, &componentImg_, componentTree_, componentTab_, offset3X3_4_, offset3X3_8_); // Run the construction of connected components ccImpl.run(); }
CameraHandler::state_t CameraHandler::getHotGoal () { unsigned x; int largestWidth; // Index of Particle int largestHeight; // Index of Particle int largestWidthVal; // Actual Width int largestHeightVal; // Actual Height BinaryImage* binImg; ColorImage* colImg; vector<ParticleAnalysisReport>* particles; // Get Camera Image camera->GetImage(img); img->Write("bobnormal.bmp"); // Filter out Background binImg = img->ThresholdHSL(103, 156, 252, 255, 33, 109); binImg->Write("bobbin.bmp"); // Make picture clear frcMorphology(binImg->GetImaqImage(),binImg->GetImaqImage(),IMAQ_PCLOSE); frcMorphology(binImg->GetImaqImage(),binImg->GetImaqImage(),IMAQ_DILATE); // Get Particle Analysis particles = binImg->GetOrderedParticleAnalysisReports(); SmartDashboard::PutNumber("Num of Particles: ",particles->size()); if (particles->size() == 1) { // Find Only One Particle return CameraHandler::kNone; } else if (particles->size() > 0 && particles->size() < 30) { // Sort by size sort(particles->begin(), particles->end(), particleSort); // Initialize largestWidth = 0; largestHeight = 0; largestWidthVal = 0; largestHeightVal = 0; for (x=0; (x < 3 && x < particles->size()); x++) { // Find tallest if ((*particles)[x].boundingRect.height > largestHeightVal) { largestHeight = x; largestHeightVal = (*particles)[x].boundingRect.height; } // Find Fattest if ((*particles)[x].boundingRect.width > largestWidthVal) { largestWidth = x; largestWidthVal = (*particles)[x].boundingRect.width; } } if ((*particles)[largestWidth].center_mass_x < (*particles)[largestHeight].center_mass_x) { return kLeft; } else if ((*particles)[largestWidth].center_mass_x > (*particles)[largestHeight].center_mass_x) { return kRight; } else { return kNone; } } else { // Find Too Many Particles or None return kError; } }
double CameraHandler::getCenter() { unsigned x,y; int largestWidth; int largestHeight[2]; int largestWidthVal, largestHeightVal; BinaryImage* binImg; vector<ParticleAnalysisReport>* particles; //m_dsLCD->Printf(DriverStationLCD::kUser_Line1, 1, "Test"); //m_dsLCD->UpdateLCD(); //Get new camera image camera->GetImage(img); //img.Write("bob2.jpg"); //Cannot work with non-RGB images //int Wid = img->GetWidth(); //Sanity Check: Check width of image //Prints width of image from camera //m_dsLCD->Printf(DriverStationLCD::kUser_Line1, 1,"Width of Image: %d",Wid); //Perform HSLThreshold to pull out only blue LED reflection of targets into BinaryImage //BinaryImage* binImg = img->ThresholdHSL(202, 255, 55, 255, 55, 129); //RED LED WORKS TERRRIBLY!!!! binImg = img->ThresholdHSL(52, 255, 71, 188, 76, 219); //RED LED WORKS TERRRIBLY!!!! //BinaryImage* binImg = img->ThresholdHSL(57, 255, 79, 255, 51, 255); //BLUE LED //BinaryImage* binImg = img->ThresholdHSL(159, 255, 0, 255, 71, 255); //RED LED //Perform Morphology on image to remove noise/unwanted particles. Also fills in incomplete particles. frcMorphology(binImg->GetImaqImage(),binImg->GetImaqImage(),IMAQ_PCLOSE); frcMorphology(binImg->GetImaqImage(),binImg->GetImaqImage(),IMAQ_DILATE); //Perform particle analysis on BinaryImage, creates vector with all particles found particles = binImg->GetOrderedParticleAnalysisReports(); printf("Particles found: %d",(int)particles->size()); //Print numbers of particles found to driver station //m_dsLCD->Printf(DriverStationLCD::kUser_Line4, 1, "# Parts:%d ",particles->size()); //m_dsLCD->UpdateLCD(); if(particles->size() > 1 || particles->size() < 30) {// Sort by size sort(particles->begin(), particles->end(), particleSort); // Initialize y=0; largestWidth = 0; largestHeight[0] = -1; largestHeight[1] = -1; largestHeightVal = 0; largestWidthVal = 0; largestHeightVal = 10; for (x=0; (x < 3 && x < particles->size()); x++) { // Find tallest if ((*particles)[x].boundingRect.height > largestHeightVal) { largestHeight[y] = x; largestHeightVal = (*particles)[x].boundingRect.height; y++; } // Find Fattest if ((*particles)[x].boundingRect.width > largestWidthVal) { largestWidth = x; largestWidthVal = (*particles)[x].boundingRect.width; } } // Detect Which Is Hot And Return Normalized Value of X (-1.0 - 1.0) if (fabs((*particles)[largestHeight[0]].center_mass_x - (*particles)[largestWidth].center_mass_x) < fabs((*particles)[largestHeight[1]].center_mass_x - (*particles)[largestWidth].center_mass_x)) { return (*particles)[largestHeight[0]].center_mass_x_normalized; } else { return (*particles)[largestHeight[1]].center_mass_x_normalized; } } else{ return 0; } }
int main(int argc, char* argv[]) { QgarApp app; // PARAMETERS DESCRIPTION // ====================== // app.addParameter("-in", QgarArgs::REQPARAM, QgarArgs::FILEIN, "Source image:"); // app.addParameter("-size", QgarArgs::REQPARAM, QgarArgs::INT, "Maximum width:", 0, "6"); // app.addParameter("-outhick", QgarArgs::REQPARAM, QgarArgs::FILEOUTD, "Image of thick lines:", ".thick.pbm"); // app.addParameter("-outhin", QgarArgs::REQPARAM, QgarArgs::FILEOUTD, "Image of thin lines:", ".thin.pbm"); app.setDescription("Thick-thin separation", QgarArgs::PBM); // COMMAND LINE ANALYSIS // ===================== app.analyzeLine(argc, argv); // Error while parsing parameters? if (app.isError()) { return app._CODE_ERROR; } // Application invoked with flag '-gui'? if (app.isExit()) { return app._CODE_GUI; } app.showProgressBar(); // GET SOURCE IMAGE // ================ cout << "Loading source image..." << endl; PbmFile sourceFile((char*) app.getStringOption("-in")); BinaryImage sourceImg = sourceFile.read(); app.setProgressBar(20); // EXTRACT THICK LINES // =================== cout << "Extracting thick lines..." << endl; OpenBinaryImage thickImg(sourceImg, atoi(app.getStringOption("-size")) / 2); DilatedBinaryImage::perform(&thickImg); BinaryImage::value_type* pMapSource = sourceImg.pPixMap(); BinaryImage::value_type* pMapThick = thickImg.pPixMap(); int size = thickImg.width() * thickImg.height(); for (int iCnt = 0 ; iCnt < size; ++iCnt, ++pMapThick, ++pMapSource) { *pMapThick &= *pMapSource; } app.setProgressBar(50); // SAVE THICK LINES // ================ cout << "Saving thick lines..." << endl; PbmFile thickFile((char*) app.getStringOption("-outhick")); thickFile.write(thickImg); app.setProgressBar(60); // EXTRACT THIN LINES // ================== cout << "Extracting thin lines..." << endl; sourceImg -= thickImg; app.setProgressBar(90); // SAVE THIN LINES // =============== cout <<"Saving thin lines..." << endl; PbmFile thinFile((char*) app.getStringOption("-outhin")); thinFile.write(sourceImg); app.setProgressBar(100); // DISPLAY RESULTS // =============== cout << "Thick-thin separation done." << endl; app.closeProgressBar(); app.showPicture((char*)app.getStringOption("-outhick")); app.showPicture((char*)app.getStringOption("-outhin")); // NORMAL TERMINATION // ================== return app._CODE_END; }
bool tracking (bool use_alternate_score) //camera tracking function { //takes one camera frame and turns towards tallest target //returns true if target is within deadzone, returns false otherwise Threshold tapeThreshold(0, 255, 0, 90, 220, 255); //red hsl as of 20110303, this is the hue, saturation and luminosicity ranges that we want BinaryImage *tapePixels;// Image *convexHull; BinaryImage *convexHullBinaryImage; ParticleAnalysisReport par;//analyzed blob (pre convex hull) ParticleAnalysisReport convexpar;// ONE filled-in blob vector<ParticleAnalysisReport>* pars;//where many analyzed blob goes (pre) vector<ParticleAnalysisReport>* convexpars; //where MANY filled-in blobs go bool foundAnything = false; double best_score = 120; double best_speed; double particle_score; ImageType t; int bs; img = cam->GetImage(); printf("cam->GetImage() returned frame %d x %d\n",img->GetWidth(),img->GetHeight()); tapePixels = img->ThresholdHSL(tapeThreshold); imaqGetBorderSize(tapePixels->GetImaqImage(),&bs); imaqGetImageType(tapePixels->GetImaqImage(),&t); convexHull = imaqCreateImage(t,bs); convexHullBinaryImage = new BinaryImageWrapper(convexHull); convexHullBinaryImage->GetOrderedParticleAnalysisReports(); //tapePixels = img->ThresholdHSL(int 0,int 50,int -100,int -50,int luminenceLow,int luminanceHigh); pars = tapePixels->GetOrderedParticleAnalysisReports(); imaqConvexHull(convexHull,tapePixels->GetImaqImage(),true); convexHullBinaryImage = new BinaryImageWrapper(convexHull); convexpars = convexHullBinaryImage->GetOrderedParticleAnalysisReports(); //imaqGetParticleInfo() //convexpars = convexHull->GetOrderedParticleAnalysisReports(); for (int i=0;i < convexHullBinaryImage->GetNumberParticles();i++) { //par = (*pars)[0]; //convexpar = (*convexpars)[i]; convexpar = convexHullBinaryImage->GetParticleAnalysisReport(i); par = tapePixels->GetParticleAnalysisReport(i); if((convexpar.boundingRect.width < 10) || (convexpar.boundingRect.height < 7)) { continue; } // printf("%d par:%f convex:%f particle area\n",i,par.particleArea,convexpar.particleArea); if ((par.particleArea/convexpar.particleArea > 0.4)) { printf("%d skip max fillness ratio\n",i); continue; } if ((par.particleArea/convexpar.particleArea < 0.10)) { printf("%d skip min fillness ratio\n",i); continue; } if((double)(convexpar.boundingRect.width)/(double)(convexpar.boundingRect.height)>1.8) { printf("%d skip max aspect ratio\n",i); continue; } if((double)(convexpar.boundingRect.width)/(double)(convexpar.boundingRect.height)<.8) { printf("%d skip min aspect ratio\n",i); continue; } //printf("%f center of mass x\n",par.center_mass_x_normalized); //printf("%f center of mass y\n",par.center_mass_y_normalized); distanceInInches = (18.0*179.3)/(convexpar.boundingRect.height); double pwidth = convexpar.boundingRect.width; double mwidth = ((double)convexpar.boundingRect.left+(double)convexpar.boundingRect.width*0.5); double angle = ((180.0/3.14159)*acos (pwidth * distanceInInches/179.3/24.0) ); if(angle != angle) angle = 0.0; // if angle is NaN, set to zero printf("%f distance in inches\n",distanceInInches); //printf("%f angle\n",(180.0/3.14159)*acos (pwidth * distanceInInches/415.0/24.0) ); printf("%d BBctrX:%f CMX:%f\n", i, (double)convexpar.boundingRect.left + (double)convexpar.boundingRect.width*0.5, (double)par.center_mass_x); //printf("%f angle2\n",(((pwidth * distanceInInches)/415.0)/24.0)); //printf("%f center of mass x\n",par.center_mass_x_normalized); printf("%d %f %f center of mass x\n",i,convexpar.center_mass_x_normalized,par.center_mass_x_normalized); printf("%d %f %f center of mass y\n",i,convexpar.center_mass_y_normalized,par.center_mass_y_normalized); printf("%d %f %f rectangle score\n",i,(convexpar.particleArea)/((convexpar.boundingRect.width)*(convexpar.boundingRect.height))*(100),(par.particleArea)/((par.boundingRect.width)*(par.boundingRect.height))*(100)); printf("%d %f fillness ratio\n",i,par.particleArea/convexpar.particleArea); printf("%d %d %d width and height\n",i,(convexpar.boundingRect.width),(convexpar.boundingRect.height)); printf("%d %f aspect ratio\n",i,((convexpar.boundingRect.width)/(double)(convexpar.boundingRect.height))); if ((double)(par.center_mass_x)>mwidth) { angle=angle*(-1.0); } printf("%f true angle\n",angle); //Wait(1.0); double aiming_target_offset = 0.0; //aiming_target_offset = pwidth * angle * (-0.5 / 45.0); numbers are iffy -> NaN double speed = trackingFeedbackFunction(mwidth + aiming_target_offset - 80.0); printf("%f aiming_target_offset due to %f degree angle\n", aiming_target_offset, angle); printf("%f x offset \n",mwidth + aiming_target_offset - 80.0); printf("%f speed \n", speed); foundAnything = true; if (use_alternate_score == false){ particle_score = convexpar.center_mass_y; } else{ particle_score = 2.0*fabs((double)convexpar.center_mass_y - 60.0) + fabs((double)convexpar.center_mass_x - 80.0); } // keep track of the *lowest* score if (best_score > particle_score) { best_score = particle_score; best_speed = speed; } } if(foundAnything == false) { myRobot->TankDrive(0.0, 0.0); } else { myRobot->TankDrive(-best_speed,best_speed); } delete img; delete tapePixels; delete pars; delete convexHullBinaryImage; delete convexpars; //imaqDispose(convexHull); if (foundAnything && best_speed == 0.0){ return true; } else { return false; } }
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; }