00001 /*!@file VFAT/segmentImageMC2.C Basic image segmenter blob finder using color */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: T. Nathan Mundhenk <mundhenk@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/VFAT/segmentImageMC2.C $ 00035 // $Id: segmentImageMC2.C 10794 2009-02-08 06:21:09Z itti $ 00036 // 00037 00038 // ############################################################ 00039 // ############################################################ 00040 // ##### --- VFAT --- 00041 // ##### Vision Feature Analysis Tool: 00042 // ##### T. Nathan Mundhenk nathan@mundhenk.com 00043 // ##### Laurent Itt itti@pollux.usc.edu 00044 // ##### 00045 // ############################################################ 00046 // ############################################################ 00047 00048 #ifndef SEGMENTIMAGEMC2_C_DEFINED 00049 #define SEGMENTIMAGEMC2_C_DEFINED 00050 00051 #include "Util/Assert.H" 00052 #include "VFAT/segmentImageMC2.H" 00053 #include <iostream> 00054 #include <vector> 00055 #include <cstdio> 00056 #include <cstdlib> 00057 00058 /*********************************************************************/ 00059 /*********************************************************************/ 00060 /* PUBLIC methods */ 00061 /*********************************************************************/ 00062 /*********************************************************************/ 00063 00064 /*********************************************************************/ 00065 00066 template SI_TEMPLATE_CLASS 00067 segmentImageMC2<SI_TEMPLATE>::segmentImageMC2() 00068 { 00069 SI_set1 = false; SI_set2 = false; SI_set3 = false; SI_set4 = false; 00070 LINFO("CREATED"); 00071 SI_lowThresh.resize(SI_channels,0); 00072 SI_highThresh.resize(SI_channels,0); 00073 SI_maxIDVal = 0; 00074 SI_killVal = 2; 00075 Image<FLOAT> timage; 00076 SI_infeatureMaps.resize(SI_channels,timage); 00077 SI_useCandidateBandPass = true; 00078 SI_removeSingles = true; 00079 SI_set4 = true; 00080 } 00081 00082 /*********************************************************************/ 00083 00084 template SI_TEMPLATE_CLASS 00085 segmentImageMC2<SI_TEMPLATE>::~segmentImageMC2() 00086 {} 00087 00088 /* set color segmentation values for color and 00089 threshold 00090 */ 00091 00092 /*********************************************************************/ 00093 00094 template SI_TEMPLATE_CLASS inline 00095 void segmentImageMC2<SI_TEMPLATE>::SIsetVal(const typename 00096 std::vector<FLOAT> &val, 00097 const typename 00098 std::vector<FLOAT> &thresh) 00099 { 00100 typename std::vector<FLOAT>::iterator ilowThresh = SI_lowThresh.begin(); 00101 typename std::vector<FLOAT>::iterator ihighThresh = SI_highThresh.begin(); 00102 typename std::vector<FLOAT>::const_iterator ival = val.begin(); 00103 typename std::vector<FLOAT>::const_iterator ithresh = thresh.begin(); 00104 00105 for(INT i = 0; i < val.size(); i++, ++ilowThresh, ++ihighThresh, 00106 ++ival, ++ithresh) 00107 { 00108 *ilowThresh = *ival - *ithresh; 00109 *ihighThresh = *ival + *ithresh; 00110 } 00111 SI_set1 = true; 00112 } 00113 00114 /*********************************************************************/ 00115 00116 template SI_TEMPLATE_CLASS inline 00117 void segmentImageMC2<SI_TEMPLATE>::SIsetValThresh(const typename 00118 std::vector<FLOAT> &high, 00119 const typename 00120 std::vector<FLOAT> &low) 00121 { 00122 SI_lowThresh = low; 00123 SI_highThresh = high; 00124 00125 typename std::vector<FLOAT>::iterator ilow = SI_lowThresh.begin(); 00126 typename std::vector<FLOAT>::iterator ihigh = SI_highThresh.begin(); 00127 00128 // Check to make sure low is less than high for each feature 00129 while(ilow != SI_lowThresh.end()) 00130 { 00131 if(*ilow >= *ihigh) 00132 { 00133 LFATAL("Low threshold must be less than high theshold. Got L %f H %f",*ilow,*ihigh); 00134 } 00135 ++ilow; ++ihigh; 00136 } 00137 00138 SI_set1 = true; 00139 } 00140 /*********************************************************************/ 00141 00142 template SI_TEMPLATE_CLASS inline 00143 void segmentImageMC2<SI_TEMPLATE>::SIresetCandidates(const bool whichWay) 00144 { 00145 Image<bool>::iterator 00146 candidatePixelsIter = SI_candidatePixels.beginw(); 00147 00148 Image<bool>::iterator 00149 preCandidatePixelsIter = SI_preCandidatePixels.beginw(); 00150 00151 // set initial candidates for each pixel 00152 00153 while(candidatePixelsIter != SI_candidatePixels.endw()) 00154 { 00155 *candidatePixelsIter = false; 00156 *preCandidatePixelsIter = whichWay; 00157 ++candidatePixelsIter; ++preCandidatePixelsIter; 00158 } 00159 } 00160 00161 /*********************************************************************/ 00162 /* set size of window frame to inspect in image */ 00163 00164 template SI_TEMPLATE_CLASS inline 00165 void segmentImageMC2<SI_TEMPLATE>::SIsetFrame(int *x, int *y) 00166 { 00167 SI_masterVec.resize(*x*(*y),-1); 00168 SI_reOrderVec.resize(*x*(*y)); 00169 SI_reverseOrderVec.resize(*x*(*y)); 00170 SI_centerX.resize(*x*(*y)); 00171 SI_centerY.resize(*x*(*y)); 00172 SI_Xsum.resize(*x*(*y)); 00173 SI_Ysum.resize(*x*(*y)); 00174 SI_mass.resize(*x*(*y)); 00175 SI_xmin.resize(*x*(*y)); 00176 SI_xmax.resize(*x*(*y)); 00177 SI_ymin.resize(*x*(*y)); 00178 SI_ymax.resize(*x*(*y)); 00179 SI_reset.resize(*x*(*y)); 00180 LINFO("SETTING WIDTH %d, HEIGHT %d",*x,*y); 00181 SI_blobID.resize(*x,*y,-1); 00182 SI_candidatePixels.resize(*x,*y,false); 00183 SI_preCandidatePixels.resize(*x,*y,false); 00184 SI_set2 = true; 00185 } 00186 00187 /*********************************************************************/ 00188 00189 template SI_TEMPLATE_CLASS inline 00190 void segmentImageMC2<SI_TEMPLATE>::SIsetAvg(const INT doAvg) 00191 { 00192 typename std::vector<FLOAT> temp(SI_channels,0); 00193 00194 SI_avg.resize(doAvg,temp); 00195 SI_std.resize(doAvg,temp); 00196 SI_N.resize(doAvg,0); 00197 SI_tempAvg.resize(doAvg,0); 00198 SI_tempStd.resize(doAvg,0); 00199 SI_iter = doAvg; 00200 SI_count = 0; 00201 SI_set3 = true; 00202 } 00203 00204 /*********************************************************************/ 00205 00206 template SI_TEMPLATE_CLASS inline 00207 void segmentImageMC2<SI_TEMPLATE>::SIresetAvg() 00208 { 00209 typename std::vector<FLOAT> temp(SI_channels,0); 00210 00211 for(typename std::vector<std::vector<FLOAT> >::iterator 00212 iavg = SI_avg.begin(); iavg != SI_avg.end(); ++iavg) 00213 *iavg = temp; 00214 for(typename std::vector<std::vector<FLOAT> >::iterator 00215 istd = SI_std.begin(); istd != SI_std.end(); ++istd) 00216 *istd = temp; 00217 00218 typename std::vector<FLOAT>::iterator itempAvg = SI_tempAvg.begin(); 00219 typename std::vector<FLOAT>::iterator itempStd = SI_tempStd.begin(); 00220 typename std::vector<INT>::iterator iN = SI_N.begin(); 00221 00222 for(unsigned int i = 0; i < SI_N.size(); i++, 00223 ++itempAvg, ++itempStd, ++iN) 00224 { 00225 *itempAvg = 0.0F; *itempStd = 0.0F; *iN = 0; 00226 } 00227 SI_count = 0; 00228 00229 } 00230 00231 /*********************************************************************/ 00232 /*********************************************************************/ 00233 /* do image segmentation by calling private memebers to 00234 operate on image. 00235 1. Low pass image 00236 2. Find candidate pixels 00237 3. Eleminate single isolated candidates 00238 4. Link pixels in each blob 00239 5. Clean up 00240 */ 00241 /*********************************************************************/ 00242 /*********************************************************************/ 00243 template SI_TEMPLATE_CLASS 00244 void segmentImageMC2<SI_TEMPLATE>::SIsegment( 00245 Image<PixRGB<byte> > *image, 00246 typename std::vector<Image<FLOAT> > *featureMap, 00247 const bool lowPass) 00248 { 00249 SI_workImage = image; 00250 SIsegment(featureMap,lowPass); 00251 } 00252 00253 /*********************************************************************/ 00254 template SI_TEMPLATE_CLASS 00255 void segmentImageMC2<SI_TEMPLATE>::SIsegment( 00256 typename std::vector<Image<FLOAT> > *featureMap, 00257 const bool lowPass) 00258 { 00259 struct timezone tz; 00260 struct timeval start, stop; 00261 tz.tz_minuteswest = 0; 00262 tz.tz_dsttime = 0; 00263 gettimeofday(&start, &tz); 00264 00265 // Low pass image if desired 00266 if(lowPass) 00267 { 00268 typename std::vector<Image<FLOAT> >::iterator 00269 iimage = featureMap->begin(); 00270 typename std::vector<Image<FLOAT> >::iterator 00271 ifmap = SI_infeatureMaps.begin(); 00272 00273 while(iimage != featureMap->end()) 00274 { 00275 *ifmap = lowPass5(*iimage); 00276 ++ifmap; ++iimage; 00277 } 00278 SI_featureMaps = &SI_infeatureMaps; 00279 } 00280 else 00281 SI_featureMaps = featureMap; 00282 00283 00284 // run doSegment which will call several consecutive methods 00285 SIdoSegment(); 00286 00287 gettimeofday(&stop,&tz); 00288 } 00289 00290 /*********************************************************************/ 00291 template SI_TEMPLATE_CLASS 00292 void segmentImageMC2<SI_TEMPLATE>::SItoggleCandidateBandPass(const bool toggle) 00293 { 00294 SI_useCandidateBandPass = toggle; 00295 } 00296 00297 /*********************************************************************/ 00298 template SI_TEMPLATE_CLASS 00299 void segmentImageMC2<SI_TEMPLATE>::SItoggleRemoveSingles(const bool toggle) 00300 { 00301 SI_removeSingles = toggle; 00302 } 00303 00304 /*********************************************************************/ 00305 template SI_TEMPLATE_CLASS 00306 void segmentImageMC2<SI_TEMPLATE>::SIsetKillValue(const unsigned int kv) 00307 { 00308 SI_killVal = kv; 00309 } 00310 00311 /*********************************************************************/ 00312 /* This method when called will take all remaning blobs from post 00313 processing and create a mother blob 00314 */ 00315 template SI_TEMPLATE_CLASS 00316 Image<INT> segmentImageMC2<SI_TEMPLATE>::SIcreateMother(const Image<INT> &img) const 00317 { 00318 Image<INT> mother; 00319 mother.resize(img.getWidth(),img.getHeight(),ZEROS); 00320 for(int x = SI_frameX1; x < SI_frameX2; x++) 00321 { 00322 for(int y = SI_frameY1; y < SI_frameY2; y++) 00323 { 00324 if(img.getVal(x,y) != 0) 00325 mother.setVal(x,y,1); 00326 else 00327 mother.setVal(x,y,0); 00328 } 00329 } 00330 return mother; 00331 } 00332 00333 /*********************************************************************/ 00334 /* return blob map 00335 */ 00336 template SI_TEMPLATE_CLASS 00337 Image<int> segmentImageMC2<SI_TEMPLATE>::SIreturnBlobs() const 00338 { 00339 return SI_blobID; 00340 } 00341 00342 /*********************************************************************/ 00343 00344 template SI_TEMPLATE_CLASS 00345 Image<bool> segmentImageMC2<SI_TEMPLATE>::SIreturnCandidates() const 00346 { 00347 return SI_candidatePixels; 00348 } 00349 00350 /*********************************************************************/ 00351 00352 template SI_TEMPLATE_CLASS 00353 Image<FLOAT> segmentImageMC2<SI_TEMPLATE>::SIreturnNormalizedCandidates() const 00354 { 00355 Image<FLOAT> NC; 00356 NC.resize(SI_candidatePixels.getWidth(),SI_candidatePixels.getHeight()); 00357 for(int x = 0; x < SI_candidatePixels.getWidth(); x++) 00358 { 00359 for(int y = 0; y < SI_candidatePixels.getHeight(); y++) 00360 { 00361 if(SI_candidatePixels.getVal(x,y)) 00362 NC.setVal(x,y,255); 00363 else 00364 NC.setVal(x,y,0); 00365 } 00366 } 00367 return NC; 00368 } 00369 00370 /*********************************************************************/ 00371 00372 template SI_TEMPLATE_CLASS 00373 Image<PixRGB<FLOAT> > segmentImageMC2<SI_TEMPLATE>::SIreturnWorkImage() const 00374 { 00375 ASSERT((SI_doType == 1) || (SI_doType == 2)); 00376 return *SI_workImage; 00377 } 00378 00379 /*********************************************************************/ 00380 00381 template SI_TEMPLATE_CLASS 00382 INT segmentImageMC2<SI_TEMPLATE>::SInumberBlobs() const 00383 { 00384 return SI_totalBlobs; 00385 } 00386 00387 /*********************************************************************/ 00388 00389 template SI_TEMPLATE_CLASS 00390 std::vector<INT> segmentImageMC2<SI_TEMPLATE>::SIgetBlobMap() const 00391 { 00392 return SI_reOrderVec; 00393 } 00394 00395 /*********************************************************************/ 00396 00397 template SI_TEMPLATE_CLASS 00398 void segmentImageMC2<SI_TEMPLATE>::SIcalcMassCenter() 00399 { 00400 00401 Image<int>::iterator iblobID = SI_blobID.beginw(); 00402 Image<bool>::iterator icandidatePixels = SI_candidatePixels.beginw(); 00403 00404 const ushort width = (ushort)SI_candidatePixels.getWidth(); 00405 const ushort height = (ushort)SI_candidatePixels.getHeight(); 00406 00407 for(ushort y = 0 ; y < height; y++) 00408 { 00409 for(ushort x = 0 ; x < width; x++) 00410 { 00411 if((*icandidatePixels) && (*iblobID != -1)) 00412 { 00413 //std::cerr << "foo " << reverseOrderVec[blobID.getVal(x,y)] << "\n"; 00414 INT *indexBlob = &SI_reverseOrderVec[*iblobID]; 00415 if(SI_reset[*indexBlob]) 00416 { 00417 SI_reset[*indexBlob] = false; 00418 SI_Xsum[*indexBlob] = x; 00419 SI_Ysum[*indexBlob] = y; 00420 SI_mass[*indexBlob] = 1; 00421 SI_xmin[*indexBlob] = x; 00422 SI_ymin[*indexBlob] = y; 00423 SI_xmax[*indexBlob] = x; 00424 SI_ymax[*indexBlob] = y; 00425 } 00426 else 00427 { 00428 SI_Xsum[*indexBlob] += x; 00429 SI_Ysum[*indexBlob] += y; 00430 SI_mass[*indexBlob]++; 00431 00432 if(x <= SI_xmin[*indexBlob]) 00433 SI_xmin[*indexBlob] = x; 00434 if(x >= SI_xmax[*indexBlob]) 00435 SI_xmax[*indexBlob] = x; 00436 if(y <= SI_ymin[*indexBlob]) 00437 SI_ymin[*indexBlob] = y; 00438 if(y >= SI_ymax[*indexBlob]) 00439 SI_ymax[*indexBlob] = y; 00440 } 00441 } 00442 ++iblobID; ++icandidatePixels; 00443 } 00444 } 00445 00446 for(INT b = 0; b < SI_totalBlobs; b++) 00447 { 00448 //std::cerr << "MASS " << mass[x] << "\n"; 00449 if(SI_mass[b] > 0) 00450 { 00451 SI_centerX[b] = SI_Xsum[b]/SI_mass[b]; 00452 SI_centerY[b] = SI_Ysum[b]/SI_mass[b]; 00453 } 00454 } 00455 } 00456 00457 /*********************************************************************/ 00458 00459 template SI_TEMPLATE_CLASS 00460 FLOAT segmentImageMC2<SI_TEMPLATE>::SIgetCenterX(const INT blob) const 00461 { 00462 return SI_centerX[blob]; 00463 } 00464 00465 /*********************************************************************/ 00466 00467 template SI_TEMPLATE_CLASS 00468 FLOAT segmentImageMC2<SI_TEMPLATE>::SIgetCenterY(const INT blob) const 00469 { 00470 return SI_centerY[blob]; 00471 } 00472 00473 /*********************************************************************/ 00474 00475 template SI_TEMPLATE_CLASS 00476 INT segmentImageMC2<SI_TEMPLATE>::SIgetMass(const INT blob) const 00477 { 00478 return SI_mass[blob]; 00479 } 00480 00481 /*********************************************************************/ 00482 00483 template SI_TEMPLATE_CLASS 00484 int segmentImageMC2<SI_TEMPLATE>::SIgetXmin(const INT blob) const 00485 { 00486 return SI_xmin[blob]; 00487 } 00488 00489 /*********************************************************************/ 00490 //! get X max for a blob 00491 template SI_TEMPLATE_CLASS 00492 int segmentImageMC2<SI_TEMPLATE>::SIgetXmax(const INT blob) const 00493 { 00494 return SI_xmax[blob]; 00495 } 00496 00497 /*********************************************************************/ 00498 //! get Y min for a blob 00499 template SI_TEMPLATE_CLASS 00500 int segmentImageMC2<SI_TEMPLATE>::SIgetYmin(const INT blob) const 00501 { 00502 return SI_ymin[blob]; 00503 } 00504 00505 /*********************************************************************/ 00506 //! get Y max for a blob 00507 template SI_TEMPLATE_CLASS 00508 int segmentImageMC2<SI_TEMPLATE>::SIgetYmax(const INT blob) const 00509 { 00510 return SI_ymax[blob]; 00511 } 00512 00513 /*********************************************************************/ 00514 //! get the working image size in X 00515 template SI_TEMPLATE_CLASS 00516 int segmentImageMC2<SI_TEMPLATE>::SIgetImageSizeX() const 00517 { 00518 return SI_candidatePixels.getWidth(); 00519 } 00520 00521 /*********************************************************************/ 00522 //! get the working image size in Y 00523 template SI_TEMPLATE_CLASS 00524 int segmentImageMC2<SI_TEMPLATE>::SIgetImageSizeY() const 00525 { 00526 return SI_candidatePixels.getHeight(); 00527 } 00528 00529 /*********************************************************************/ 00530 template SI_TEMPLATE_CLASS inline 00531 void segmentImageMC2<SI_TEMPLATE>::SIgetValue(INT *blob, 00532 typename std::vector<FLOAT> *mean, 00533 typename std::vector<FLOAT> *std, 00534 INT *in) 00535 { 00536 typename std::vector<FLOAT>::iterator imean = mean->begin(); 00537 typename std::vector<FLOAT>::iterator istd = std->begin(); 00538 typename std::vector<Image<FLOAT> >::iterator 00539 ifeatureMaps = SI_featureMaps->begin(); 00540 00541 bool dothis = true; 00542 *in = 0; 00543 for(INT i = 0; i < SI_featureMaps->size(); i++, ++imean, ++istd, 00544 ++ifeatureMaps) 00545 { 00546 Image<bool>::iterator icandidatePixels = SI_candidatePixels.beginw(); 00547 Image<int>::iterator iblobID = SI_blobID.beginw(); 00548 typename Image<FLOAT>::iterator iifeatureMaps = ifeatureMaps->beginw(); 00549 00550 FLOAT tot = 0; 00551 FLOAT ss = 0; 00552 00553 while(icandidatePixels != SI_candidatePixels.endw()) 00554 { 00555 if((*icandidatePixels) && (*iblobID != -1)) 00556 { 00557 if(SI_reverseOrderVec[*iblobID] == *blob) 00558 { 00559 tot += *iifeatureMaps; 00560 ss += (pow(*iifeatureMaps,2))/SI_mass[*blob]; 00561 } 00562 } 00563 ++icandidatePixels; ++iblobID; ++iifeatureMaps; 00564 } 00565 if(SI_mass[*blob] > 0) 00566 { 00567 *imean = tot/SI_mass[*blob]; 00568 *istd = sqrt(fabs(ss - pow(*imean,2))); 00569 if(dothis == true) 00570 *in = SI_mass[*blob] + *in; 00571 } 00572 dothis = false; 00573 } 00574 } 00575 00576 /*********************************************************************/ 00577 00578 template SI_TEMPLATE_CLASS 00579 void segmentImageMC2<SI_TEMPLATE>::SIgetValueMean(INT *blobListSize, 00580 typename std::vector<INT> *blobList, 00581 typename std::vector<FLOAT> *mean, 00582 typename std::vector<FLOAT> *stdd, 00583 FLOAT *mass) 00584 { 00585 // average feature channels over N iterations for all motherblobs 00586 // modulus on dataset size 00587 if(SI_count == SI_iter) 00588 SI_count = 0; 00589 00590 INT SI_tempN; 00591 00592 typename std::vector<INT>::iterator iN = SI_N.begin(); 00593 typename std::vector<std::vector<FLOAT> >::iterator iavg = SI_avg.begin(); 00594 typename std::vector<std::vector<FLOAT> >::iterator istd = SI_std.begin(); 00595 00596 typename std::vector<FLOAT> *pavg = &SI_avg[SI_count]; 00597 typename std::vector<FLOAT> *pstd = &SI_std[SI_count]; 00598 INT *pcount = &SI_N[SI_count]; 00599 *pcount = 0; 00600 00601 // get mean values etc. for all blobs in this iteration 00602 for(INT i = 0; i < *blobListSize; i++) 00603 { 00604 SIgetValue(&blobList->at(i), &SI_tempAvg, &SI_tempStd, &SI_tempN); 00605 *pcount = SI_tempN + (*pcount); 00606 for(INT f = 0; f < SI_featureMaps->size(); f++) 00607 { 00608 pavg->at(f) += SI_tempAvg[f]*SI_tempN; 00609 pstd->at(f) += SI_tempStd[f]*SI_tempN; 00610 } 00611 } 00612 00613 for(INT f = 0; f < SI_featureMaps->size(); f++) 00614 { 00615 pavg->at(f) = pavg->at(f)/(*pcount); 00616 pstd->at(f) = pstd->at(f)/(*pcount); 00617 } 00618 *mass = *pcount; 00619 SI_count++; 00620 00621 INT massSum = 0; 00622 00623 // combine the results over the last N iterations for everything 00624 for(INT c = 0; c < SI_iter; c++, ++iavg, ++istd, ++iN) 00625 { 00626 massSum += *iN; 00627 typename std::vector<FLOAT>::iterator iiavg = iavg->begin(); 00628 typename std::vector<FLOAT>::iterator iistd = istd->begin(); 00629 typename std::vector<FLOAT>::iterator imean = mean->begin(); 00630 typename std::vector<FLOAT>::iterator istdd = stdd->begin(); 00631 00632 for(INT i = 0; i < SI_featureMaps->size(); i++, ++iiavg, ++iistd, 00633 ++imean, ++istdd) 00634 { 00635 if(*iN != 0) 00636 { 00637 *imean += *iiavg*(*iN); 00638 *istdd += *iistd*(*iN); 00639 } 00640 } 00641 } 00642 00643 // find the total mean and std over all blobs and iterations 00644 typename std::vector<FLOAT>::iterator imean = mean->begin(); 00645 typename std::vector<FLOAT>::iterator istdd = stdd->begin(); 00646 for(INT i = 0; i < SI_featureMaps->size(); i++, ++imean, ++istdd) 00647 { 00648 *imean = *imean/massSum; 00649 *istdd = *istdd/massSum; 00650 } 00651 } 00652 00653 /*********************************************************************/ 00654 /*********************************************************************/ 00655 /* PRIVATE methods */ 00656 /*********************************************************************/ 00657 /*********************************************************************/ 00658 00659 /*********************************************************************/ 00660 00661 /*! find candidate pixels by just checking image pixels to see if they 00662 are within threshold of desired feature values 00663 if so, set candidatePixels image pixel to true 00664 */ 00665 template SI_TEMPLATE_CLASS inline 00666 void segmentImageMC2<SI_TEMPLATE>::SIfindCandidates() 00667 { 00668 // FIX 00669 std::vector<int>::iterator imasterVec = SI_masterVec.begin(); 00670 00671 while(imasterVec != SI_masterVec.end()) 00672 *imasterVec++ = -1; 00673 00674 Image<bool>::iterator 00675 candidatePixelsIter = SI_candidatePixels.beginw(); 00676 00677 Image<bool>::iterator 00678 preCandidatePixelsIter = SI_preCandidatePixels.beginw(); 00679 00680 // set initial candidates for each pixel 00681 00682 while(candidatePixelsIter != SI_candidatePixels.endw()) 00683 { 00684 if((*preCandidatePixelsIter) == true) 00685 { 00686 *candidatePixelsIter = true; 00687 } 00688 else 00689 { 00690 *candidatePixelsIter = false; 00691 } 00692 *preCandidatePixelsIter = true; 00693 ++candidatePixelsIter; ++preCandidatePixelsIter; 00694 } 00695 00696 typename std::vector<Image<FLOAT> >::iterator ifeatureMaps; 00697 typename std::vector<FLOAT>::iterator ilowThresh = SI_lowThresh.begin(); 00698 typename std::vector<FLOAT>::iterator ihighThresh = SI_highThresh.begin(); 00699 // Run over each feature channel 00700 00701 for(ifeatureMaps = SI_featureMaps->begin(); 00702 ifeatureMaps != SI_featureMaps->end(); ++ifeatureMaps, 00703 ++ilowThresh, ++ihighThresh) 00704 { 00705 typename Image<FLOAT>::iterator iifeatureMaps = ifeatureMaps->beginw(); 00706 preCandidatePixelsIter = SI_preCandidatePixels.beginw(); 00707 candidatePixelsIter = SI_candidatePixels.beginw(); 00708 00709 // Run over each pixel 00710 00711 while(iifeatureMaps != ifeatureMaps->endw()) 00712 { 00713 if((*iifeatureMaps > *ihighThresh) || (*iifeatureMaps < *ilowThresh)) 00714 { 00715 *preCandidatePixelsIter = false; 00716 *candidatePixelsIter = false; 00717 } 00718 //else 00719 //LINFO("VAL %f",*iifeatureMaps); 00720 ++preCandidatePixelsIter; ++candidatePixelsIter; ++iifeatureMaps; 00721 } 00722 } 00723 } 00724 00725 /*********************************************************************/ 00726 00727 /*! find candidate pixels by just checking image pixels to see if they 00728 are within threshold of desired feature values 00729 if so, set candidatePixels image pixel to true 00730 */ 00731 template SI_TEMPLATE_CLASS inline 00732 void segmentImageMC2<SI_TEMPLATE>::SIfindCandidatesNoBandPass() 00733 { 00734 // FIX 00735 std::vector<int>::iterator imasterVec = SI_masterVec.begin(); 00736 00737 while(imasterVec != SI_masterVec.end()) 00738 *imasterVec++ = -1; 00739 00740 Image<bool>::iterator 00741 candidatePixelsIter = SI_candidatePixels.beginw(); 00742 00743 // set initial candidates for each pixel 00744 00745 while(candidatePixelsIter != SI_candidatePixels.endw()) 00746 { 00747 *candidatePixelsIter = true; 00748 ++candidatePixelsIter; 00749 } 00750 00751 00752 typename std::vector<Image<FLOAT> >::iterator ifeatureMaps; 00753 typename std::vector<FLOAT>::iterator ilowThresh = SI_lowThresh.begin(); 00754 typename std::vector<FLOAT>::iterator ihighThresh = SI_highThresh.begin(); 00755 // Run over each feature channel 00756 00757 for(ifeatureMaps = SI_featureMaps->begin(); 00758 ifeatureMaps != SI_featureMaps->end(); ++ifeatureMaps, 00759 ++ilowThresh, ++ihighThresh) 00760 { 00761 typename Image<FLOAT>::iterator iifeatureMaps = ifeatureMaps->beginw(); 00762 candidatePixelsIter = SI_candidatePixels.beginw(); 00763 00764 // Run over each pixel 00765 00766 while(iifeatureMaps != ifeatureMaps->endw()) 00767 { 00768 //LINFO("Feature %f, high %f, low %f",*iifeatureMaps,*ihighThresh,*ilowThresh); 00769 if((*iifeatureMaps > *ihighThresh) || (*iifeatureMaps < *ilowThresh)) 00770 *candidatePixelsIter = false; 00771 00772 ++candidatePixelsIter; ++iifeatureMaps; 00773 } 00774 } 00775 } 00776 00777 /*********************************************************************/ 00778 /*! if a pixel does not have an orthogonal neighbor then it is removed 00779 i.e. all pixels without 4 connectivity are removed as noise 00780 */ 00781 template SI_TEMPLATE_CLASS inline 00782 void segmentImageMC2<SI_TEMPLATE>::SIremoveSingles() 00783 { 00784 Image<bool>::iterator icandidatePixels = SI_candidatePixels.beginw(); 00785 00786 const int width = SI_candidatePixels.getWidth(); 00787 const int height = SI_candidatePixels.getHeight(); 00788 for(int y = 0; y < height; y++) 00789 { 00790 for(int x = 0; x < width; x++) 00791 { 00792 if(*icandidatePixels) 00793 { 00794 unsigned int kill = 0; 00795 const int XLeft = x - 1; 00796 const int XRight = x + 1; 00797 const int YTop = y - 1; 00798 const int YBottom = y + 1; 00799 if((XLeft >= 0) && (SI_candidatePixels.getVal(XLeft,y))) 00800 kill++; 00801 if((XRight < width) 00802 && (SI_candidatePixels.getVal(XRight,y))) 00803 kill++; 00804 if((YTop >= 0) && (SI_candidatePixels.getVal(x,YTop))) 00805 kill++; 00806 if((YBottom < height) 00807 && (SI_candidatePixels.getVal(x,YBottom))) 00808 kill++; 00809 if(kill < SI_killVal) 00810 *icandidatePixels = false; 00811 } 00812 ++icandidatePixels; 00813 } 00814 } 00815 } 00816 00817 /*********************************************************************/ 00818 /*! if a pixel does not have an orthogonal neighbor then it is removed 00819 i.e. all pixels without 4 connectivity are removed as noise 00820 */ 00821 template SI_TEMPLATE_CLASS inline 00822 void segmentImageMC2<SI_TEMPLATE>::SIremoveSinglesItr() 00823 { 00824 Image<bool>::iterator icandidatePixels = SI_candidatePixels.beginw(); 00825 00826 const unsigned int width = SI_candidatePixels.getWidth(); 00827 const unsigned int height = SI_candidatePixels.getHeight(); 00828 00829 // hypothetically, this could cause a segmentation fault, but 00830 // we don't access this until the memory is aligned so we are OK 00831 Image<bool>::iterator icandidatePixelsTop = 00832 SI_candidatePixels.beginw() - width; 00833 Image<bool>::iterator icandidatePixelsBottom = 00834 SI_candidatePixels.beginw() + width; 00835 Image<bool>::iterator icandidatePixelsLeft = 00836 SI_candidatePixels.beginw() - 1; 00837 Image<bool>::iterator icandidatePixelsRight = 00838 SI_candidatePixels.beginw() + 1; 00839 00840 for(unsigned int y = 0; y < height; y++) 00841 { 00842 for(unsigned int x = 0; x < width; x++) 00843 { 00844 if(*icandidatePixels) 00845 { 00846 unsigned int kill = 0; 00847 if(x != 0) 00848 if(*icandidatePixelsLeft) 00849 kill++; 00850 if(x < width - 1) 00851 if(*icandidatePixelsRight) 00852 kill++; 00853 if(y != 0) 00854 if(*icandidatePixelsTop) 00855 kill++; 00856 if(y < height - 1) 00857 if(*icandidatePixelsBottom) 00858 kill++; 00859 if(kill < SI_killVal) 00860 *icandidatePixels = false; 00861 } 00862 00863 ++icandidatePixels; 00864 ++icandidatePixelsBottom; ++icandidatePixelsRight; 00865 ++icandidatePixelsTop; ++icandidatePixelsLeft; 00866 } 00867 } 00868 } 00869 00870 /*********************************************************************/ 00871 /*! scan pixels from top to bottom/left to right. Link all pixels in the 00872 same blob. Do this using recursive back tracking to make a blob linked 00873 as one object and not a collection of objects 00874 */ 00875 template SI_TEMPLATE_CLASS inline 00876 void segmentImageMC2<SI_TEMPLATE>::SIdiscreteLinking() 00877 { 00878 const ushort width = (ushort)SI_candidatePixels.getWidth(); 00879 const ushort height = (ushort)SI_candidatePixels.getHeight(); 00880 bool trace = false; 00881 INT pixID = 0; 00882 SI_maxIDVal = 1; 00883 SI_masters = 1; 00884 SI_mastersCount = 0; 00885 int lastNeighbor; 00886 00887 for(ushort x = 0; x < width; x++) 00888 { 00889 trace = false; 00890 lastNeighbor = -2; 00891 for(ushort y = 0; y < height; y++) 00892 { 00893 if(SI_candidatePixels.getVal(x,y)) 00894 { 00895 if(!trace) 00896 { 00897 pixID++; 00898 trace = true; 00899 lastNeighbor = -2; 00900 } 00901 SI_blobID.setVal(x,y,pixID); 00902 if(x > 0) // 1 or 0? 00903 { 00904 if(SI_candidatePixels.getVal(x-1,y)) 00905 { 00906 // relink blob pixels if needed 00907 const INT check = SI_blobID.getVal((x-1),y); 00908 if((signed)check != lastNeighbor) 00909 { 00910 SIbackwardLink(pixID,check); 00911 lastNeighbor = check; 00912 } 00913 else 00914 { 00915 SIbasicLink(pixID); 00916 } 00917 } 00918 else 00919 { 00920 lastNeighbor = -2; 00921 SIbasicLink(pixID); 00922 } 00923 } 00924 else 00925 { 00926 SIbasicLink(pixID); 00927 } 00928 } 00929 else 00930 { 00931 trace = false; 00932 SI_blobID.setVal(x,y,0); 00933 lastNeighbor = -2; 00934 } 00935 } 00936 } 00937 LINFO("Candidate pixels recognized %d",pixID); 00938 SI_num = pixID; 00939 } 00940 00941 /*********************************************************************/ 00942 /*! scan pixels from top to bottom/left to right. Link all pixels in the 00943 same blob. Do this using recursive back tracking to make a blob linked 00944 as one object and not a collection of objects 00945 */ 00946 template SI_TEMPLATE_CLASS inline 00947 void segmentImageMC2<SI_TEMPLATE>::SIdiscreteLinkingOrtho() 00948 { 00949 const int width = SI_candidatePixels.getWidth(); 00950 const int height = SI_candidatePixels.getHeight(); 00951 bool trace = false; 00952 INT pixID = 0; 00953 SI_maxIDVal = 1; 00954 SI_masters = 1; 00955 SI_mastersCount = 0; 00956 int lastNeighbor; 00957 00958 Image<bool>::iterator candidatePixelsItr = SI_candidatePixels.beginw(); 00959 Image<int>::iterator blobIDItr = SI_blobID.beginw(); 00960 00961 for(int y = 0; y < height; y++) 00962 { 00963 trace = false; 00964 lastNeighbor = -2; 00965 for(int x = 0; x < width; x++) 00966 { 00967 // This is: if(SI_candidatePixels.getVal(x,y)) 00968 if(*candidatePixelsItr++) 00969 { 00970 // Start a new pixel row group 00971 if(!trace) 00972 { 00973 pixID++; 00974 trace = true; 00975 lastNeighbor = -2; 00976 } 00977 // This is: SI_blobID.setVal(x,y,pixID); 00978 *blobIDItr++ = pixID; 00979 if(y > 0) // 1 or 0? 00980 { 00981 //This is: if(SI_candidatePixels.getVal(x,y-1)) 00982 if(*(candidatePixelsItr - (1 + width))) 00983 { 00984 // relink blob pixels if needed 00985 //This is: const INT check = SI_blobID.getVal(x,y-1); 00986 const INT check = *(blobIDItr - (1 + width)); 00987 if((signed)check != lastNeighbor) 00988 { 00989 SIbackwardLink(pixID,check); 00990 lastNeighbor = check; 00991 } 00992 else 00993 { 00994 SIbasicLink(pixID); 00995 } 00996 } 00997 else 00998 { 00999 lastNeighbor = -2; 01000 SIbasicLink(pixID); 01001 } 01002 } 01003 else 01004 { 01005 SIbasicLink(pixID); 01006 } 01007 } 01008 else 01009 { 01010 trace = false; 01011 //This is: SI_blobID.setVal(x,y,0); 01012 *blobIDItr++ = 0; 01013 lastNeighbor = -2; 01014 } 01015 } 01016 } 01017 //LINFO("Candidate pixels recognized %d",pixID); 01018 SI_num = pixID; 01019 } 01020 01021 /*********************************************************************/ 01022 /*! relink pixels with new val. This allows pixels that have a new value 01023 to take on the old value of the blob. Slaves take on masters and 01024 masters can take on a slaves master. 01025 01026 */ 01027 template SI_TEMPLATE_CLASS inline 01028 void segmentImageMC2<SI_TEMPLATE>::SIbackwardLink(const INT slave, 01029 const INT master) 01030 { 01031 if(master > SI_maxIDVal) 01032 SI_maxIDVal = master; 01033 if(slave > SI_maxIDVal) 01034 SI_maxIDVal = slave; 01035 01036 // my master has no master 01037 if(SI_masterVec[master] == -1) 01038 { 01039 // I have no master 01040 if(SI_masterVec[slave] == -1) 01041 { 01042 //LINFO("my master has no master/I have no master"); 01043 SI_masterVec[master] = SI_masters; 01044 SI_masterVec[slave] = SI_masters; 01045 SI_masters++; 01046 SI_mastersCount++; 01047 } 01048 // I already have a master 01049 else 01050 { 01051 //LINFO("my master has no master/I already have a master"); 01052 SI_masterVec[master] = SI_masterVec[slave]; 01053 } 01054 } 01055 // my master has a master 01056 else 01057 { 01058 // I have no master 01059 if(SI_masterVec[slave] == -1) 01060 { 01061 //LINFO("my master has a master/I have no master"); 01062 SI_masterVec[slave] = SI_masterVec[master]; 01063 } 01064 // I already have a master 01065 else 01066 { 01067 //LINFO("my master has a master/I already have a master"); 01068 01069 std::vector<int>::iterator masterVecItr = SI_masterVec.begin(); 01070 for(INT i = 0; i <= SI_maxIDVal; i++, ++masterVecItr) 01071 { 01072 if(*masterVecItr == SI_masterVec[slave]) 01073 { 01074 //LINFO("SET %d to %d",masterVec[i],masterVec[slave]); 01075 *masterVecItr = SI_masterVec[master]; 01076 } 01077 } 01078 SI_masterVec[slave] = SI_masterVec[master]; 01079 SI_mastersCount--; 01080 } 01081 } 01082 } 01083 01084 /*********************************************************************/ 01085 template SI_TEMPLATE_CLASS inline 01086 void segmentImageMC2<SI_TEMPLATE>::SIbasicLink(const INT node) 01087 { 01088 if(node > SI_maxIDVal) 01089 SI_maxIDVal = node; 01090 01091 // my master has no master 01092 if(SI_masterVec[node] == -1) 01093 { 01094 //LINFO("my master has no master/I have no master"); 01095 SI_masterVec[node] = SI_masters; 01096 SI_masters++; 01097 SI_mastersCount++; 01098 } 01099 } 01100 01101 /*********************************************************************/ 01102 /*! combine individual elements into a fully combined blob */ 01103 template SI_TEMPLATE_CLASS inline 01104 void segmentImageMC2<SI_TEMPLATE>::SIcombine() 01105 { 01106 SI_totalBlobs = 0; 01107 01108 Image<bool>::iterator candidatePixelsItr = SI_candidatePixels.beginw(); 01109 Image<int>::iterator blobIDItr = SI_blobID.beginw(); 01110 01111 while(candidatePixelsItr != SI_candidatePixels.endw()) 01112 { 01113 if(*candidatePixelsItr++) 01114 *blobIDItr = SI_masterVec[*blobIDItr]; 01115 blobIDItr++; 01116 } 01117 01118 /* 01119 for(int x = 0; x < SI_blobID.getWidth(); x++) 01120 { 01121 for(int y = 0; y < SI_blobID.getHeight(); y++) 01122 { 01123 if(SI_candidatePixels.getVal(x,y)) 01124 SI_blobID.setVal(x,y,SI_masterVec[SI_blobID.getVal(x,y)]); 01125 } 01126 } 01127 */ 01128 01129 std::vector<int>::iterator masterVecItr = SI_masterVec.begin(); 01130 typename std::vector<INT>::iterator reOrderVecItr = SI_reOrderVec.begin(); 01131 std::vector<bool>::iterator resetItr = SI_reset.begin(); 01132 01133 for(INT x = 0; x < SI_num; x++, ++masterVecItr) 01134 { 01135 bool add = true; 01136 //LINFO("Master Vec %d is %d",x,masterVec[x]); 01137 for(INT y = 0; y < SI_totalBlobs; y++) 01138 { 01139 if((int)SI_reOrderVec[y] == *masterVecItr) 01140 add = false; 01141 } 01142 01143 if((add) && (*masterVecItr != -1)) 01144 { 01145 //LINFO("DOING %d",masterVec[x]); 01146 *reOrderVecItr = *masterVecItr; 01147 SI_reverseOrderVec[*masterVecItr] = SI_totalBlobs; 01148 *resetItr = true; 01149 SI_totalBlobs++; ++reOrderVecItr; ++resetItr; 01150 } 01151 } 01152 } 01153 01154 /*********************************************************************/ 01155 01156 template SI_TEMPLATE_CLASS inline 01157 void segmentImageMC2<SI_TEMPLATE>::SIdoSegment() 01158 { 01159 ASSERT(SI_set1); ASSERT(SI_set2); ASSERT(SI_set3); ASSERT(SI_set4); 01160 //LINFO("FINDING CANDIDATES"); 01161 if(SI_useCandidateBandPass == true) 01162 SIfindCandidates(); 01163 else 01164 SIfindCandidatesNoBandPass(); 01165 // 01166 if(SI_removeSingles == true) 01167 { 01168 //LINFO("REMOVING SINGLES"); 01169 SIremoveSinglesItr(); 01170 } 01171 //LINFO("LINKING PIXELS"); 01172 //SIdiscreteLinking(); 01173 SIdiscreteLinkingOrtho(); 01174 //LINFO("RELABELING"); 01175 SIcombine(); 01176 //LINFO("DONE"); 01177 } 01178 01179 #undef SI_TEMPLATE_CLASS 01180 #undef SI_TEMPLATE 01181 01182 template class segmentImageMC2<float, unsigned int, 1>; 01183 template class segmentImageMC2<float, unsigned int, 2>; 01184 template class segmentImageMC2<float, unsigned int, 3>; 01185 template class segmentImageMC2<float, unsigned int, 4>; 01186 01187 // ###################################################################### 01188 /* So things look consistent in everyone's emacs... */ 01189 /* Local Variables: */ 01190 /* indent-tabs-mode: nil */ 01191 /* End: */ 01192 01193 #endif