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: */