BitObject.C

Go to the documentation of this file.
00001 /*!@file MBARI/BitObject.C class that holds an object defined by a shape in a
00002 bit image */
00003 
00004 // //////////////////////////////////////////////////////////////////// //
00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003   //
00006 // by the University of Southern California (USC) and the iLab at USC.  //
00007 // See http://iLab.usc.edu for information about this project.          //
00008 // //////////////////////////////////////////////////////////////////// //
00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00011 // in Visual Environments, and Applications'' by Christof Koch and      //
00012 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00013 // pending; application number 09/912,225 filed July 23, 2001; see      //
00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00015 // //////////////////////////////////////////////////////////////////// //
00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00017 //                                                                      //
00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00019 // redistribute it and/or modify it under the terms of the GNU General  //
00020 // Public License as published by the Free Software Foundation; either  //
00021 // version 2 of the License, or (at your option) any later version.     //
00022 //                                                                      //
00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00026 // PURPOSE.  See the GNU General Public License for more details.       //
00027 //                                                                      //
00028 // You should have received a copy of the GNU General Public License    //
00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00031 // Boston, MA 02111-1307 USA.                                           //
00032 // //////////////////////////////////////////////////////////////////// //
00033 //
00034 // Primary maintainer for this file: Dirk Walther <walther@caltech.edu>
00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/MBARI/BitObject.C $
00036 // $Id: BitObject.C 9412 2008-03-10 23:10:15Z farhan $
00037 //
00038 
00039 #include "MBARI/BitObject.H"
00040 
00041 #include "Image/CutPaste.H"    // for crop()
00042 #include "Image/IO.H"
00043 #include "Image/Image.H"
00044 #include "Image/MathOps.H"
00045 #include "Image/Pixels.H"
00046 #include "Image/Transforms.H"
00047 #include "Raster/GenericFrame.H"
00048 #include "Raster/PnmParser.H"
00049 #include "Raster/PnmWriter.H"
00050 #include "Util/Assert.H"
00051 #include "Util/MathFunctions.H"
00052 
00053 #include <cmath>
00054 #include <istream>
00055 #include <ostream>
00056 
00057 // ######################################################################
00058 BitObject::BitObject()
00059 {
00060   freeMem();
00061 }
00062 
00063 // ######################################################################
00064 BitObject::BitObject(const Image<byte>& img, const Point2D<int> location,
00065                      const byte threshold)
00066 {
00067   reset(img, location, threshold);
00068 }
00069 
00070 // ######################################################################
00071 BitObject::BitObject(const Image<byte>& img)
00072 {
00073   reset(img);
00074 }
00075 
00076 // ######################################################################
00077 BitObject::BitObject(std::istream& is)
00078 {
00079   readFromStream(is);
00080 }
00081 
00082 // ######################################################################
00083 Image<byte> BitObject::reset(const Image<byte>& img, const Point2D<int> location,
00084                              const byte threshold)
00085 {
00086   ASSERT(img.initialized());
00087 
00088   // first, reset everything to defaults
00089   freeMem();
00090 
00091   // now, flood img to get the object
00092   Image<byte> dest;
00093   int area = floodCleanBB(img, dest, location, threshold,
00094                           byte(1), itsBoundingBox);
00095 
00096   // no object found? return -1
00097   if (area == -1)
00098     {
00099       itsCentroidXY.reset(location);
00100       return Image<byte>();
00101     }
00102 
00103   // set the dimensions of the original image
00104   itsImageDims = img.getDims();
00105 
00106   // crop the object mask from the flooding destination
00107   itsObjectMask = crop(dest, itsBoundingBox);
00108 
00109   // get the area, the centroid, and the bounding box
00110   std::vector<float> sumx, sumy;
00111   itsArea = (int)sumXY(itsObjectMask, sumx, sumy);
00112   if (area != itsArea)
00113     LFATAL("area %i doesn't match the one from flooding %i", itsArea, area);
00114 
00115   int firstX, lastX, firstY, lastY;
00116   float cX, cY;
00117   bool success = (getCentroidFirstLast(sumx, cX, firstX, lastX) |
00118                   getCentroidFirstLast(sumy, cY, firstY, lastY));
00119   itsCentroidXY.reset(cX,cY);
00120 
00121   if (!success) LFATAL("determining the centroid failed");
00122 
00123   if ((firstX != 0) || (lastX != itsObjectMask.getWidth()-1) ||
00124       (firstY != 0) || (lastY != itsObjectMask.getHeight()-1))
00125     LFATAL("boundary box doesn't match the one from flooding");
00126 
00127   itsCentroidXY += Vector2D(itsBoundingBox.left(),itsBoundingBox.top());
00128 
00129   return dest;
00130 }
00131 
00132 // ######################################################################
00133 int BitObject::reset(const Image<byte>& img)
00134 {
00135   ASSERT(img.initialized());
00136 
00137   // first, reset everything to defaults
00138   freeMem();
00139 
00140   // set the dimensions of the original image
00141   itsImageDims = img.getDims();
00142 
00143   // get the area, the centroid, and the bounding box
00144   std::vector<float> sumx, sumy;
00145   itsArea = (int)sumXY(img, sumx, sumy);
00146 
00147   if (itsArea == 0)
00148     {
00149       freeMem();
00150       return -1;
00151     }
00152 
00153   int firstX, lastX, firstY, lastY;
00154   float cX, cY;
00155   bool success = (getCentroidFirstLast(sumx, cX, firstX, lastX) |
00156                   getCentroidFirstLast(sumy, cY, firstY, lastY));
00157   itsCentroidXY.reset(cX,cY);
00158 
00159   if (!success)
00160     {
00161       freeMem();
00162       return -1;
00163     }
00164 
00165   itsBoundingBox = Rectangle::tlbrI(firstY, firstX, lastY, lastX);
00166 
00167   // cut out the object mask
00168   itsObjectMask = crop(img, itsBoundingBox);
00169 
00170   //LINFO("BB: size: %i; %s; dims: %s",itsBoundingBox.width()*itsBoundingBox.height(),
00171   //    toStr(itsBoundingBox).c_str(),toStr(itsObjectMask.getDims()).c_str());
00172 
00173   return itsArea;
00174 }
00175 
00176 // ######################################################################
00177 void BitObject::computeSecondMoments()
00178 {
00179   ASSERT(isValid());
00180 
00181   int w = itsObjectMask.getWidth();
00182   int h = itsObjectMask.getHeight();
00183 
00184   // The bounding box is stored in image coordinates, and so is the centroid. For
00185   // computing the second moments, however we need the centroid in object coords.
00186   float cenX = itsCentroidXY.x() - itsBoundingBox.left();
00187   float cenY = itsCentroidXY.y() - itsBoundingBox.top();
00188 
00189   // compute the second moments
00190   std::vector<float> diffX(w), diffX2(w), diffY(h), diffY2(h);
00191   for (int y = 0; y < h; ++y)
00192     {
00193       diffY[y] = y - cenY;
00194       diffY2[y] = diffY[y] * diffY[y];
00195     }
00196   for (int x = 0; x < w; ++x)
00197     {
00198       diffX[x] = x - cenX;
00199       diffX2[x] = diffX[x] * diffX[x];
00200     }
00201 
00202   Image<byte>::const_iterator optr = itsObjectMask.begin();
00203   for (int y = 0; y < h; ++y)
00204     for(int x = 0; x < w; ++x)
00205       {
00206         if (*optr != 0)
00207           {
00208             itsUxx += diffX2[x];
00209             itsUyy += diffY2[y];
00210             itsUxy += (diffX[x] * diffY[y]);
00211           }
00212         ++optr;
00213       }
00214   itsUxx /= itsArea;
00215   itsUyy /= itsArea;
00216   itsUxy /= itsArea;
00217 
00218   // compute the parameters d, e and f for the ellipse:
00219   // d*x^2 + 2*e*x*y + f*y^2 <= 1
00220   float coeff = 1 / (4 * (itsUxx * itsUyy - itsUxy * itsUxy));
00221   float d =  coeff * itsUyy;
00222   float e = -coeff * itsUxy;
00223   float f =  coeff * itsUxx;
00224 
00225   // from these guys, compute the paramters d and f for the
00226   // ellipse when it is rotated so that x is the main axis
00227   // and figure out the angle of rotation for this.
00228   float expr = sqrt(4*e*e + squareOf(d - f));
00229   float d2 = 0.5 * (d + f + expr);
00230   float f2 = 0.5 * (d + f - expr);
00231 
00232   // the angle is defined in clockwise (image) coordinates:
00233   //  --  is 0
00234   //  \   is 45
00235   //  |   is 90
00236   //  /   is 135
00237   itsOriAngle = 90 * atan(2 * e / (d - f)) / M_PI;
00238   if (itsUyy > itsUxx) itsOriAngle += 90.0F;
00239   if (itsOriAngle < 0.0F) itsOriAngle += 180.0F;
00240 
00241   // this checks if itsOriAngle is nan
00242   if (itsOriAngle != itsOriAngle) itsOriAngle = 0.0F;
00243 
00244   // now get the length of the major and the minor axes:
00245   itsMajorAxis = 2 / sqrt(f2);
00246   itsMinorAxis = 2 / sqrt(d2);
00247   itsElongation = itsMajorAxis / itsMinorAxis;
00248 
00249   // We're done
00250   haveSecondMoments = true;
00251   return;
00252 }
00253 
00254 // ######################################################################
00255 void BitObject::freeMem()
00256 {
00257   itsObjectMask.freeMem();
00258   itsBoundingBox = Rectangle();
00259   itsCentroidXY = Vector2D();
00260   itsArea = 0;
00261   itsUxx = 0.0F;
00262   itsUyy = 0.0F;
00263   itsUxy = 0.0F;
00264   itsMajorAxis = 0.0F;
00265   itsMinorAxis = 0.0F;
00266   itsElongation = 0.0F;
00267   itsOriAngle = 0.0F;
00268   itsImageDims = Dims(0,0);
00269   itsMaxIntensity = -1.0F;
00270   itsMinIntensity = -1.0F;
00271   itsAvgIntensity = -1.0F;
00272   haveSecondMoments = false;
00273 }
00274 // ######################################################################
00275 void BitObject::writeToStream(std::ostream& os) const
00276 {
00277   // bounding box
00278   if (itsBoundingBox.isValid())
00279     {
00280       os << itsBoundingBox.top() << " "
00281          << itsBoundingBox.left() << " "
00282          << itsBoundingBox.bottomI() << " "
00283          << itsBoundingBox.rightI() << "\n";
00284     }
00285   else
00286     {
00287       os << "-1 -1 -1 -1\n";
00288     }
00289 
00290   // image dimensions
00291   os << itsImageDims.w() << " " << itsImageDims.h() << "\n";
00292 
00293   // centroid
00294   itsCentroidXY.writeToStream(os);
00295 
00296   // area
00297   os << itsArea << "\n";
00298 
00299   // have second moments?
00300   if (haveSecondMoments) os << "0\n";
00301   else os <<"1\n";
00302 
00303   // second moments
00304   os << itsUxx << " " << itsUyy << " " << itsUxy << "\n";
00305 
00306   // axes, elongation, angle
00307   os << itsMajorAxis << " "
00308      << itsMinorAxis << " "
00309      << itsElongation << " "
00310      << itsOriAngle << "\n";
00311 
00312   // max, min and avg intensity
00313   os << itsMaxIntensity << " "
00314      << itsMinIntensity << " "
00315      << itsAvgIntensity << "\n";
00316 
00317   // the object mask
00318   PnmWriter::writeAsciiBW(itsObjectMask, 1, os);
00319 
00320   os << "\n";
00321 
00322   // done
00323   return;
00324 }
00325 
00326 // ######################################################################
00327 void BitObject::readFromStream(std::istream& is)
00328 {
00329   // bounding box
00330   int t, l, b, r;
00331   is >> t; is >> l; is >> b; is >> r;
00332   if (t >= 0)
00333     itsBoundingBox = Rectangle::tlbrI(t, l, b, r);
00334   else
00335     itsBoundingBox = Rectangle();
00336 
00337 
00338   // image dims
00339   int w, h;
00340   is >> w; is >> h;
00341   itsImageDims = Dims(w,h);
00342 
00343   // centroid
00344   itsCentroidXY = Vector2D(is);
00345 
00346   // area
00347   is >> itsArea;
00348 
00349   // have second moments?
00350   int hs; is >> hs;
00351   haveSecondMoments = (hs == 1);
00352 
00353   // second moments
00354   is >> itsUxx; is >> itsUyy; is >> itsUxy;
00355 
00356   // axes, elongation, angle
00357   is >> itsMajorAxis; is >> itsMinorAxis;
00358   is >> itsElongation; is >> itsOriAngle;
00359 
00360   // max, min, avg intensity
00361   is >> itsMaxIntensity;
00362   is >> itsMinIntensity;
00363   is >> itsAvgIntensity;
00364 
00365   // object mask
00366   PnmParser pp(is);
00367   itsObjectMask = pp.getFrame().asGray();
00368 }
00369 
00370 // ######################################################################
00371 template <class T>
00372 void BitObject::setMaxMinAvgIntensity(const Image<T>& img)
00373 {
00374   ASSERT(img.getDims() == itsImageDims);
00375   if (!isValid()) return;
00376 
00377   float sum = 0.0F;
00378   int num = 0;
00379 
00380   // loop over bounding box
00381   const int iw = img.getWidth();
00382   typename Image<byte>::const_iterator bptr = itsObjectMask.begin();
00383   typename Image<T>::const_iterator iptr2, iptr = img.begin();
00384   iptr += (iw * itsBoundingBox.top() + itsBoundingBox.left());
00385 
00386   for (int y = itsBoundingBox.top(); y < itsBoundingBox.bottomO(); ++y)
00387     {
00388       iptr2 = iptr;
00389       for (int x = itsBoundingBox.left(); x < itsBoundingBox.rightO(); ++x)
00390         {
00391           // check if we're indeed inside the object
00392           if (*bptr > byte(0))
00393             {
00394               sum += (float)(*iptr2);
00395               ++num;
00396               if ((itsMaxIntensity == -1.0F) || (*iptr2 > itsMaxIntensity))
00397                 itsMaxIntensity = *iptr2;
00398               if ((itsMinIntensity == -1.0F) || (*iptr2 < itsMinIntensity))
00399                 itsMinIntensity = *iptr2;
00400             }
00401           ++bptr;
00402           ++iptr2;
00403         }
00404       iptr += iw;
00405     }
00406 
00407   if (sum == 0) itsAvgIntensity = 0.0F;
00408   else itsAvgIntensity = sum / (float)num;
00409 
00410 }
00411 
00412 // ######################################################################
00413 void BitObject::getMaxMinAvgIntensity(float& maxIntensity,
00414                                       float& minIntensity,
00415                                       float& avgIntensity)
00416 {
00417   maxIntensity = itsMaxIntensity;
00418   minIntensity = itsMinIntensity;
00419   avgIntensity = itsAvgIntensity;
00420 }
00421 
00422 // ######################################################################
00423 Rectangle BitObject::getBoundingBox(const BitObject::Coords coords) const
00424 {
00425   switch(coords)
00426     {
00427     case OBJECT: return Rectangle::tlbrI(0, 0, itsBoundingBox.height() - 1,
00428                                   itsBoundingBox.width() - 1);
00429     case IMAGE: return itsBoundingBox;
00430     default: LFATAL("Unknown Coords type - don't know what to do.");
00431     }
00432   //this is never reached but we have to make the compiler happy
00433   return Rectangle();
00434 }
00435 
00436 // ######################################################################
00437 Image<byte> BitObject::getObjectMask(const byte value,
00438                                      const BitObject::Coords coords) const
00439 {
00440   ASSERT(isValid());
00441   Image<byte> objectCopy = replaceVals(itsObjectMask,byte(1),value);
00442 
00443   switch (coords)
00444     {
00445     case OBJECT: return objectCopy;
00446 
00447     case IMAGE:
00448       {
00449         Image<byte> result(itsImageDims, ZEROS);
00450         pasteImage(result, objectCopy, byte(0), getObjectOrigin());
00451         return result;
00452       }
00453 
00454     default: LFATAL("Unknown Coords type - don't know what to do.");
00455     }
00456 
00457   //this is never reached but we have to make the compiler happy
00458   return Image<byte>();
00459 }
00460 
00461 // ######################################################################
00462 Dims BitObject::getObjectDims() const
00463 { return itsObjectMask.getDims(); }
00464 
00465 // ######################################################################
00466 Point2D<int> BitObject::getObjectOrigin() const
00467 {
00468   return Point2D<int>(itsBoundingBox.left(),itsBoundingBox.top());
00469 }
00470 
00471 // ######################################################################
00472 Point2D<int> BitObject::getCentroid(const BitObject::Coords coords) const
00473 {
00474   return getCentroidXY().getPoint2D();
00475 }
00476 
00477 // ######################################################################
00478 Vector2D BitObject::getCentroidXY(const BitObject::Coords coords) const
00479 {
00480   switch (coords)
00481     {
00482     case OBJECT: return (itsCentroidXY - Vector2D(getObjectOrigin()));
00483     case IMAGE: return itsCentroidXY;
00484     default: LFATAL("Unknown Coords type - don't know what to do.");
00485     }
00486   //this is never reached but we have to make the compiler happy
00487   return Vector2D();
00488 }
00489 
00490 // ######################################################################
00491 int BitObject::getArea() const
00492 { return itsArea; }
00493 
00494 // ######################################################################
00495 void BitObject::getSecondMoments(float& uxx, float& uyy, float& uxy)
00496 {
00497   if (!haveSecondMoments) computeSecondMoments();
00498   uxx = itsUxx; uyy = itsUyy; uxy = itsUxy;
00499 }
00500 
00501 // ######################################################################
00502 float BitObject::getMajorAxis()
00503 {
00504   if (!haveSecondMoments) computeSecondMoments();
00505   return itsMajorAxis;
00506 }
00507 
00508 // ######################################################################
00509 float BitObject::getMinorAxis()
00510 {
00511   if (!haveSecondMoments) computeSecondMoments();
00512   return itsMinorAxis;
00513 }
00514 
00515 // ######################################################################
00516 float BitObject::getElongation()
00517 {
00518   if (!haveSecondMoments) computeSecondMoments();
00519   return itsElongation;
00520 }
00521 
00522 // ######################################################################
00523 float BitObject::getOriAngle()
00524 {
00525   return itsOriAngle;
00526 }
00527 
00528 // ######################################################################
00529 bool BitObject::isValid() const
00530 { return ((itsArea > 0) && itsBoundingBox.isValid()); }
00531 
00532 // ######################################################################
00533 bool BitObject::doesIntersect(const BitObject& other) const
00534 {
00535   // are this and other actually valid? no -> return false
00536   if (!(isValid() && other.isValid()))
00537     {
00538       LINFO("no interesect, because one of the objects is invalid.");
00539       return false;
00540     }
00541 
00542   Rectangle tBB = getBoundingBox(IMAGE);
00543   Rectangle oBB = other.getBoundingBox(IMAGE);
00544 
00545   // compute the intersecting bounding box params
00546   int ll = std::max(tBB.left(),oBB.left());
00547   int rr = std::min(tBB.rightI(),oBB.rightI());
00548   int tt = std::max(tBB.top(),oBB.top());
00549   int bb = std::min(tBB.bottomI(),oBB.bottomI());
00550 
00551   //LINFO("this.ObjMask.dims = %s; other.ObjMask.dims = %s",
00552   //    toStr(getObjectMask(byte(1),OBJECT).getDims()).c_str(),
00553   //    toStr(other.getObjectMask(byte(1),OBJECT).getDims()).c_str());
00554 
00555   // is this a valid rectangle?
00556   if ((ll > rr)||(tt > bb))
00557     {
00558       //LINFO("No intersect because the bounding boxes don't overlap: %s and %s",
00559       //    toStr(tBB).c_str(),toStr(oBB).c_str());
00560       return false;
00561     }
00562 
00563   // get the rectangles in order to crop the object masks
00564   Rectangle tCM = Rectangle::tlbrI(tt - tBB.top(), ll - tBB.left(),
00565                                   bb - tBB.top(), rr - tBB.left());
00566   Rectangle oCM = Rectangle::tlbrI(tt - oBB.top(), ll - oBB.left(),
00567                                   bb - oBB.top(), rr - oBB.left());
00568 
00569   //LINFO("tCM = %s; oCM = %s; this.ObjMask.dims = %s; other.ObjMask.dims = %s",
00570   //    toStr(tCM).c_str(),toStr(oCM).c_str(),
00571   //    toStr(getObjectMask(byte(1),OBJECT).getDims()).c_str(),
00572   //    toStr(other.getObjectMask(byte(1),OBJECT).getDims()).c_str());
00573   //LINFO("tCM: P2D = %s, dims = %s; oCM: P2D = %s, dims = %s",
00574   //    toStr(Point2D<int>(tCM.left(), tCM.top())).c_str(),
00575   //    toStr(Dims(tCM.width(), tCM.height())).c_str(),
00576   //    toStr(Point2D<int>(oCM.left(), oCM.top())).c_str(),
00577   //    toStr(Dims(oCM.width(), oCM.height())).c_str());
00578 
00579   // crop the object masks and get the intersecting image
00580   Image<byte> cor = takeMin(crop(getObjectMask(byte(1),OBJECT),tCM),
00581                             crop(other.getObjectMask(byte(1),OBJECT),oCM));
00582   double s = sum(cor);
00583 
00584   //LINFO("tCM = %s; oCM = %s; this.ObjMask.dims = %s; other.ObjMask.dims = %s; sum = %g",
00585   //    toStr(tCM).c_str(),toStr(oCM).c_str(),
00586   //    toStr(getObjectMask(byte(1),OBJECT).getDims()).c_str(),
00587   //    toStr(other.getObjectMask(byte(1),OBJECT).getDims()).c_str(),s);
00588 
00589   return (s > 0.0);
00590 }
00591 
00592 // ######################################################################
00593 template <class T_or_RGB>
00594 void BitObject::drawShape(Image<T_or_RGB>& img,
00595                           const T_or_RGB& color,
00596                           float opacity)
00597 {
00598   ASSERT(isValid());
00599   ASSERT(img.initialized());
00600   ASSERT(img.getDims() == itsImageDims);
00601 
00602   int w = img.getWidth();
00603   float op2 = 1.0F - opacity;
00604 
00605   typename Image<T_or_RGB>::iterator iptr, iptr2;
00606   Image<byte>::const_iterator mptr = itsObjectMask.begin();
00607   iptr2 = img.beginw() + itsBoundingBox.top() * w + itsBoundingBox.left();
00608   for (int y = itsBoundingBox.top(); y < itsBoundingBox.bottomO(); ++y)
00609     {
00610       iptr = iptr2;
00611       for (int x = itsBoundingBox.left(); x < itsBoundingBox.rightO(); ++x)
00612         {
00613           if (*mptr > 0) *iptr = T_or_RGB(*iptr * op2 + color * opacity);
00614           ++iptr; ++mptr;
00615         }
00616       iptr2 += w;
00617     }
00618 }
00619 
00620 // ######################################################################
00621 template <class T_or_RGB>
00622 void BitObject::drawOutline(Image<T_or_RGB>& img,
00623                             const T_or_RGB& color,
00624                             float opacity)
00625 {
00626   ASSERT(isValid());
00627   ASSERT(img.initialized());
00628   ASSERT(img.getDims() == itsImageDims);
00629   float op2 = 1.0F - opacity;
00630 
00631   Image<byte> marked(img.getDims(), ZEROS);
00632 
00633   int t = itsBoundingBox.top();
00634   int b = itsBoundingBox.bottomO();
00635   int l = itsBoundingBox.left();
00636   int r = itsBoundingBox.rightO();
00637 
00638   for (int y = t; y < b; ++y)
00639     for (int x = l; x < r; ++x)
00640       {
00641         if (itsObjectMask.getVal(x-l,y-t) == 0) continue;
00642         for (int dy = -1; dy <= 1; ++dy)
00643           for (int dx = -1; dx <= 1; ++dx)
00644             {
00645               if ((dy == 0) && (dx == 0)) continue;
00646               Point2D<int> pim(x+dx,y+dy);
00647               if (!img.coordsOk(pim)) continue;
00648 
00649               bool isBoundary = false;
00650               Point2D<int> pm(x+dx-l,y+dy-t);
00651               if (!itsObjectMask.coordsOk(pm)) isBoundary = true;
00652               else if (itsObjectMask.getVal(pm) == 0) isBoundary = true;
00653 
00654               if (isBoundary && marked.getVal(pim) == 0)
00655                 {
00656                   img.setVal(pim,T_or_RGB(img.getVal(pim) * op2 + color * opacity));
00657                   marked.setVal(pim,byte(1));
00658                 }
00659             } // end for dx,dy
00660       } // end for x,y
00661 } // end drawOutline
00662 
00663 
00664 // ######################################################################
00665 template <class T_or_RGB>
00666 void BitObject::drawBoundingBox(Image<T_or_RGB>& img,
00667                                 const T_or_RGB& color,
00668                                 float opacity)
00669 {
00670   ASSERT(isValid());
00671   ASSERT(img.initialized());
00672   ASSERT(img.getDims() == itsImageDims);
00673 
00674   float op2 = 1.0F - opacity;
00675   int t = itsBoundingBox.top();
00676   int b = itsBoundingBox.bottomI();
00677   int l = itsBoundingBox.left();
00678   int r = itsBoundingBox.rightI();
00679 
00680   for (int x = l; x <= r; ++x)
00681     {
00682       Point2D<int> p1(x,t), p2(x,b);
00683       img.setVal(p1,img.getVal(p1) * op2 + color * opacity);
00684       img.setVal(p2,img.getVal(p2) * op2 + color * opacity);
00685     }
00686   for (int y = t+1; y < b; ++y)
00687     {
00688       Point2D<int> p1(l,y), p2(r,y);
00689       img.setVal(p1,img.getVal(p1) * op2 + color * opacity);
00690       img.setVal(p2,img.getVal(p2) * op2 + color * opacity);
00691     }
00692 }
00693 
00694 // ######################################################################
00695 template <class T_or_RGB>
00696 void BitObject::draw(BitObjectDrawMode mode, Image<T_or_RGB>& img,
00697                      const T_or_RGB& color, float opacity)
00698 {
00699   switch(mode)
00700     {
00701     case BODMnone:    break;
00702     case BODMshape:   drawShape(img, color, opacity); break;
00703     case BODMoutline: drawOutline(img, color, opacity); break;
00704     case BODMbbox:    drawBoundingBox(img, color, opacity); break;
00705     default: LERROR("Unknown BitObjectDrawMode: %i - ignoring!",mode);
00706     }
00707 }
00708 
00709 // ######################################################################
00710 // Instantiation of template functions
00711 // ######################################################################
00712 template void BitObject::setMaxMinAvgIntensity(const Image<byte>& img);
00713 template void BitObject::setMaxMinAvgIntensity(const Image<float>& img);
00714 
00715 #define INSTANTIATE(T_or_RGB) \
00716 template void BitObject::drawShape(Image< T_or_RGB >& img, \
00717                                    const T_or_RGB& color, \
00718                                    float opacity); \
00719 template void BitObject::drawOutline(Image< T_or_RGB >& img, \
00720                                      const T_or_RGB& color, \
00721                                      float opacity); \
00722 template void BitObject::drawBoundingBox(Image< T_or_RGB >& img, \
00723                                          const T_or_RGB& color, \
00724                                          float opacity); \
00725 template void BitObject::draw(BitObjectDrawMode mode, \
00726                               Image< T_or_RGB >& img, \
00727                               const T_or_RGB& color, \
00728                               float opacity);
00729 
00730 INSTANTIATE(PixRGB<byte>);
00731 INSTANTIATE(PixRGB<float>);
00732 INSTANTIATE(byte);
00733 INSTANTIATE(float);
00734 
00735 // ######################################################################
00736 /* So things look consistent in everyone's emacs... */
00737 /* Local Variables: */
00738 /* indent-tabs-mode: nil */
00739 /* End: */
Generated on Sun May 8 08:40:59 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3