contrib/brl/bseg/bmdl/bmdl_classify.txx
Go to the documentation of this file.
00001 // This is brl/bseg/bmdl/bmdl_classify.txx
00002 #ifndef bmdl_classify_txx_
00003 #define bmdl_classify_txx_
00004 //:
00005 // \file
00006 // \brief Classify each pixel in lidar images
00007 //
00008 // \author Matt Leotta
00009 // \date Oct. 14, 2008
00010 
00011 #include "bmdl_classify.h"
00012 #include <vcl_limits.h>
00013 #include <vnl/vnl_math.h>
00014 #include <vcl_algorithm.h>
00015 #include <vcl_utility.h>
00016 #include <vcl_set.h>
00017 #include <vcl_map.h>
00018 #include <vcl_cassert.h>
00019 
00020 #include <vil/algo/vil_binary_dilate.h>
00021 #include <vil/algo/vil_binary_erode.h>
00022 #include <vgl/vgl_box_2d.h>
00023 #include <vgl/vgl_point_2d.h>
00024 
00025 //: Constructor
00026 // \param height_noise_stdev is the standard deviation in lidar height
00027 //  This parameter can be set manually or estimated
00028 // \param area_threshold is the minimum area allowed for buildings
00029 // \param height_resolution is the height difference below which buildings are merged
00030 // \param gnd_threshold is the minimum height above the ground considered for buildings
00031 // \param veg_threshold is the minimum distance between first and last returns for vegetation
00032 template <class T>
00033 bmdl_classify<T>::bmdl_classify(unsigned int area_threshold,
00034                                 T height_resolution,
00035                                 T gnd_threshold,
00036                                 T veg_threshold)
00037 : hgt_stdev_(T(0.01)),
00038   area_threshold_(area_threshold),
00039   height_resolution_(height_resolution),
00040   gnd_threshold_(gnd_threshold),
00041   veg_threshold_(veg_threshold),
00042   first_min_( vcl_numeric_limits<T>::infinity()),
00043   first_max_(-vcl_numeric_limits<T>::infinity()),
00044   last_min_ ( vcl_numeric_limits<T>::infinity()),
00045   last_max_ (-vcl_numeric_limits<T>::infinity())
00046 {
00047 }
00048 
00049 
00050 //: Set the first and last return images
00051 template <class T>
00052 void bmdl_classify<T>::set_lidar_data(const vil_image_view<T>& first_return,
00053                                       const vil_image_view<T>& last_return)
00054 {
00055   assert(first_return.ni() == last_return.ni());
00056   assert(first_return.nj() == last_return.nj());
00057   first_return_ = first_return;
00058   last_return_ = last_return;
00059 
00060   // find the range of finite values in the images
00061   bmdl_classify::range(first_return_,first_min_,first_max_);
00062   bmdl_classify::range(last_return_,last_min_,last_max_);
00063 }
00064 
00065 
00066 //: Set the bare earth image
00067 template <class T>
00068 void bmdl_classify<T>::set_bare_earth(const vil_image_view<T>& bare_earth)
00069 {
00070   assert(first_return_.ni() == bare_earth.ni());
00071   assert(first_return_.nj() == bare_earth.nj());
00072   bare_earth_ = bare_earth;
00073 }
00074 
00075 
00076 //: Estimate a constant bare earth model
00077 // This function returns the constant height
00078 // and also uses it to fill the bare earth image
00079 template <class T>
00080 T bmdl_classify<T>::estimate_bare_earth()
00081 {
00082   vcl_vector<T> data;
00083   unsigned int ni = last_return_.ni();
00084   unsigned int nj = last_return_.nj();
00085 
00086   for (unsigned int j=0; j<nj; ++j) {
00087     for (unsigned int i=0; i<ni; ++i) {
00088       if (vnl_math_isfinite(last_return_(i,j))) {
00089         data.push_back(last_return_(i,j));
00090       }
00091     }
00092   }
00093 
00094   T mean = 0.0;
00095   T gnd_stdev = 0.0;
00096   fit_gaussian_to_peak(data,last_min_,last_max_,mean,gnd_stdev);
00097 
00098   bare_earth_.set_size(ni,nj);
00099   bare_earth_.fill(mean);
00100 
00101   return mean;
00102 }
00103 
00104 
00105 //: Estimate the standard deviation in height due to noise
00106 //  Returns the estimate and stores it internally for later use
00107 template <class T>
00108 T bmdl_classify<T>::estimate_height_noise_stdev()
00109 {
00110 // Warning: this code is a rough approximation
00111 // it probably should be rewritten to do it correctly
00112 
00113   unsigned int ni = first_return_.ni();
00114   unsigned int nj = first_return_.nj();
00115   // test that all inputs have been set
00116   assert(ni>0 && nj>0);
00117   assert(last_return_.ni() == ni);
00118   assert(last_return_.nj() == nj);
00119 
00120   vcl_vector<T> diff;
00121   for (unsigned int j=0; j<nj; ++j) {
00122     for (unsigned int i=0; i<ni; ++i) {
00123       if (!vnl_math_isfinite(last_return_(i,j)) || !vnl_math_isfinite(first_return_(i,j)))
00124         continue;
00125       diff.push_back(first_return_(i,j) - last_return_(i,j));
00126     }
00127   }
00128   vcl_sort(diff.begin(),diff.end());
00129   // discard top 5% of points for robustness
00130   T max_diff = diff[int(diff.size()*.95)];
00131 
00132   if (max_diff == 0.0)
00133     vcl_cout << "Warning: first and last return images are identical" << vcl_endl;
00134 
00135   T mean = 0.0;
00136   fit_gaussian_to_peak(diff,0,max_diff,mean,hgt_stdev_);
00137 
00138   vcl_cout << "diff mean = "<< mean << " stdev = "<< hgt_stdev_ << vcl_endl;
00139   return hgt_stdev_;
00140 }
00141 
00142 
00143 //: Classify each pixel as Ground (0), Vegetation (1), or Building (>=2)
00144 // Each building is given an index sequentially starting with 2
00145 // and sorted by mean height.
00146 template <class T>
00147 void bmdl_classify<T>::label_lidar()
00148 {
00149   unsigned int ni = first_return_.ni();
00150   unsigned int nj = first_return_.nj();
00151   // test that all inputs have been set
00152   assert(ni>0 && nj>0);
00153   assert(last_return_.ni() == ni);
00154   assert(last_return_.nj() == nj);
00155   assert(bare_earth_.ni() == ni);
00156   assert(bare_earth_.nj() == nj);
00157   assert(hgt_stdev_ > 0.0);
00158 
00159   // 1. First segment the image into ground, buildings, and vegetation
00160   segment();
00161 
00162   // 2. Cluster the pixels for buildings and apply unique labels
00163   cluster_buildings();
00164 
00165   // 3. Merge similar sections
00166   while (greedy_merge())
00167     ;
00168 
00169   // Replace small holes in buildings from ground to vegetation labels
00170   fill_ground_holes(5);
00171 
00172   // 4. Remove holes
00173   while (dilate_buildings(5))
00174     ;
00175 
00176   // 5. Remove buildings that are two small in area
00177   threshold_building_area();
00178 
00179   // 6. Remove holes
00180   while (dilate_buildings(5))
00181     ;
00182 
00183   // 7. Merge similar sections
00184   while (greedy_merge())
00185     ;
00186 
00187   // 8. Refine building regions with various morphological operations
00188   refine_buildings();
00189 
00190   // 9. Determine the heights to use for meshing
00191   heights_.set_size(ni,nj);
00192   for (unsigned int j=0; j<nj; ++j) {
00193     for (unsigned int i=0; i<ni; ++i) {
00194       if (labels_(i,j) > 1)
00195         heights_(i,j) = building_mean_hgt_[labels_(i,j)-2];
00196       else if (labels_(i,j) == 1)
00197         heights_(i,j) = first_return_(i,j);
00198       else
00199         heights_(i,j) = bare_earth_(i,j);
00200     }
00201   }
00202 }
00203 
00204 
00205 //: Perform an initial segmentation at each pixel using thresholds
00206 //  Classify each pixel as Ground (0), Vegetation (1), or Building (2)
00207 //  Results are stored in the labels image
00208 template <class T>
00209 void bmdl_classify<T>::segment()
00210 {
00211   unsigned int ni = first_return_.ni();
00212   unsigned int nj = first_return_.nj();
00213   // test that all inputs have been set
00214   assert(ni>0 && nj>0);
00215   assert(last_return_.ni() == ni);
00216   assert(last_return_.nj() == nj);
00217   assert(bare_earth_.ni() == ni);
00218   assert(bare_earth_.nj() == nj);
00219   assert(hgt_stdev_ > 0.0);
00220 
00221   // resize the outputs
00222   labels_.set_size(ni,nj);
00223 
00224   for (unsigned int j=0; j<nj; ++j) {
00225     for (unsigned int i=0; i<ni; ++i) {
00226       // test for ground
00227       if (!vnl_math_isfinite(first_return_(i,j)) ||
00228           first_return_(i,j) - bare_earth_(i,j) < gnd_threshold_)
00229         labels_(i,j) = 0;
00230       // test for vegetation
00231       else if ((first_return_(i,j) - last_return_(i,j)) > veg_threshold_)
00232         labels_(i,j) = 1;
00233       // otherwise building
00234       else
00235         labels_(i,j) = 2;
00236     }
00237   }
00238 }
00239 
00240 
00241 //: Cluster pixels on building into groups of adjacent pixels with similar heights.
00242 //  Assign a new label to each groups.
00243 //  Returns building mean heights and pixel counts by reference
00244 template <class T>
00245 void bmdl_classify<T>::cluster_buildings()
00246 {
00247   unsigned int ni=first_return_.ni();
00248   unsigned int nj=first_return_.nj();
00249   // test that all inputs have been set
00250   assert(ni>0 && nj>0);
00251   assert(last_return_.ni() == ni);
00252   assert(last_return_.nj() == nj);
00253   assert(labels_.ni() == ni);
00254   assert(labels_.nj() == nj);
00255   assert(hgt_stdev_ > 0.0);
00256 
00257   building_mean_hgt_.clear();
00258   building_area_.clear();
00259 
00260   // bin building heights
00261   vil_image_view<unsigned int> bin_img = bin_heights(height_resolution_);
00262 
00263   vcl_vector<unsigned int> merge_map;
00264   vcl_vector<unsigned int> count;
00265   vcl_vector<T> mean;
00266 
00267   // handle first pixel
00268   if (labels_(0,0) >= 2)
00269   {
00270     count.push_back(1);
00271     merge_map.push_back(merge_map.size());
00272     mean.push_back(last_return_(0,0));
00273     labels_(0,0) = count.size()+1;
00274   }
00275   // handle first row
00276   for (unsigned int i=1; i<ni; ++i) {
00277     if (labels_(i,0)!=2)
00278       continue;
00279 
00280     int idx = labels_(i-1,0)-2;
00281     while (idx>=0 && merge_map[idx] != (unsigned int)idx )
00282       idx = merge_map[idx];
00283     T val = last_return_(i,0);
00284     if (idx>=0 && bin_img(i-1,0) == bin_img(i,0)) {
00285       labels_(i,0) = idx+2;
00286       mean[idx] = (mean[idx]*count[idx] + val)/(count[idx]+1);
00287       ++count[idx];
00288     }
00289     else{
00290       count.push_back(1);
00291       merge_map.push_back(merge_map.size());
00292       mean.push_back(val);
00293       labels_(i,0) = count.size()+1;
00294     }
00295   }
00296   // handle first column
00297   for (unsigned int j=1; j<nj; ++j) {
00298     if (labels_(0,j)!=2)
00299       continue;
00300 
00301     int idx = labels_(0,j-1)-2;
00302     while (idx>=0 && merge_map[idx] != (unsigned int)idx )
00303       idx = merge_map[idx];
00304     T val = last_return_(0,j);
00305     if (idx>=0 && bin_img(0,j-1) == bin_img(0,j)) {
00306       labels_(0,j) = idx+2;
00307       mean[idx] = (mean[idx]*count[idx] + val)/(count[idx]+1);
00308       ++count[idx];
00309     }
00310     else {
00311       count.push_back(1);
00312       merge_map.push_back(merge_map.size());
00313       mean.push_back(val);
00314       labels_(0,j) = count.size()+1;
00315     }
00316   }
00317 
00318   // handle the rest of the image
00319   for (unsigned int j=1; j<nj; ++j)
00320   {
00321     for (unsigned int i=1; i<ni; ++i)
00322     {
00323       if (labels_(i,j)!=2)
00324         continue;
00325 
00326       T val = last_return_(i,j);
00327       int idx1 = labels_(i-1,j)-2, idx2 = labels_(i,j-1)-2;
00328       while (idx1>=0 && merge_map[idx1] != (unsigned int)idx1 )
00329         idx1 = merge_map[idx1];
00330       while (idx2>=0 && merge_map[idx2] != (unsigned int)idx2 )
00331         idx2 = merge_map[idx2];
00332       int idx = -1;
00333       if (idx1>=0 && (bin_img(i-1,j) == bin_img(i,j)) )
00334         idx = idx1;
00335       if (idx2>=0 && (bin_img(i,j-1) == bin_img(i,j)) )
00336       {
00337         if (idx == -1 || idx1 == idx2)
00338           idx = idx2;
00339         else {
00340           labels_(i,j) = idx1+2;
00341           mean[idx1] = (mean[idx1]*count[idx1] + mean[idx2]*count[idx2] + val)
00342                       /(count[idx1]+count[idx2]+1);
00343           count[idx1] += count[idx2]+1;
00344           count[idx2] = 0;
00345           merge_map[idx2] = idx1;
00346           continue;
00347         }
00348       }
00349 
00350       if (idx >= 0 ) {
00351         labels_(i,j) = idx+2;
00352         mean[idx] = (mean[idx]*count[idx] + val)/(count[idx]+1);
00353         ++count[idx];
00354       }
00355       else {
00356         count.push_back(1);
00357         merge_map.push_back(merge_map.size());
00358         mean.push_back(val);
00359         labels_(i,j) = count.size()+1;
00360       }
00361     }
00362   }
00363   vcl_cout << "num labels = "<<count.size() << vcl_endl;
00364 
00365   // simplify merge map
00366   vcl_vector<vcl_pair<T,int> > unique;
00367   for (unsigned int i=0; i<merge_map.size(); ++i)
00368   {
00369     if (merge_map[i] == i) {
00370       unique.push_back(vcl_pair<T,int>(mean[i],i));
00371       continue;
00372     }
00373     unsigned int i2 = i;
00374     while (merge_map[i2] != i2 )
00375       merge_map[i] = i2 = merge_map[i2];
00376   }
00377   vcl_sort(unique.begin(), unique.end());
00378 
00379   building_mean_hgt_.resize(unique.size(),0.0);
00380   building_area_.resize(unique.size(),0);
00381 
00382   vcl_vector<unsigned int> unique_map(merge_map.size(),0);
00383   for (unsigned int i=0; i<unique.size(); ++i) {
00384     unique_map[unique[i].second] = i;
00385     building_mean_hgt_[i] = unique[i].first;
00386     building_area_[i] = count[unique[i].second];
00387   }
00388   for (unsigned int i=0; i<unique_map.size(); ++i)
00389     if (merge_map[i] != i)
00390       unique_map[i] = unique_map[merge_map[i]];
00391 
00392   for (unsigned int j=0; j<nj; ++j) {
00393     for (unsigned int i=0; i<ni; ++i) {
00394       if (labels_(i,j)>=2)
00395         labels_(i,j) = unique_map[labels_(i,j)-2]+2;
00396     }
00397   }
00398 
00399   vcl_cout << "After clustering there are "
00400            <<building_area_.size()<<" buildings" << vcl_endl;
00401 }
00402 
00403 
00404 //: Replace small holes in buildings from ground to vegetation labels
00405 // Holes are switch if area is less than \a min_size
00406 template <class T>
00407 void bmdl_classify<T>::fill_ground_holes(unsigned int min_size)
00408 {
00409   unsigned int ni = labels_.ni();
00410   unsigned int nj = labels_.nj();
00411 
00412   vil_image_view<unsigned int> labels(ni,nj);
00413   for (unsigned int j=0; j<nj; ++j) {
00414     for (unsigned int i=0; i<ni; ++i) {
00415       if (labels_(i,j)>0)
00416         labels(i,j) = 0;
00417       else
00418         labels(i,j) = 1;
00419     }
00420   }
00421 
00422   vcl_vector<unsigned int> merge_map;
00423   vcl_vector<unsigned int> count;
00424 
00425   // handle first pixel
00426   if (labels(0,0) > 0)
00427   {
00428     count.push_back(1);
00429     merge_map.push_back(merge_map.size());
00430     labels(0,0) = count.size();
00431   }
00432   // handle first row
00433   for (unsigned int i=1; i<ni; ++i) {
00434     if (labels(i,0)!=1)
00435       continue;
00436 
00437     int idx = labels(i-1,0)-1;
00438     while (idx>=0 && merge_map[idx] != (unsigned int)idx )
00439       idx = merge_map[idx];
00440     if (idx>=0) {
00441       labels(i,0) = idx+1;
00442       ++count[idx];
00443     }
00444     else{
00445       count.push_back(1);
00446       merge_map.push_back(merge_map.size());
00447       labels(i,0) = count.size();
00448     }
00449   }
00450   // handle first column
00451   for (unsigned int j=1; j<nj; ++j) {
00452     if (labels(0,j)!=1)
00453       continue;
00454 
00455     int idx = labels(0,j-1)-1;
00456     while (idx>=0 && merge_map[idx] != (unsigned int)idx )
00457       idx = merge_map[idx];
00458     if (idx>=0) {
00459       labels(0,j) = idx+1;
00460       ++count[idx];
00461     }
00462     else {
00463       count.push_back(1);
00464       merge_map.push_back(merge_map.size());
00465       labels(0,j) = count.size();
00466     }
00467   }
00468 
00469   // handle the rest of the image
00470   for (unsigned int j=1; j<nj; ++j)
00471   {
00472     for (unsigned int i=1; i<ni; ++i)
00473     {
00474       if (labels(i,j)!=1)
00475         continue;
00476 
00477       int idx1 = labels(i-1,j)-1, idx2 = labels(i,j-1)-1;
00478       while (idx1>=0 && merge_map[idx1] != (unsigned int)idx1 )
00479         idx1 = merge_map[idx1];
00480       while (idx2>=0 && merge_map[idx2] != (unsigned int)idx2 )
00481         idx2 = merge_map[idx2];
00482       int idx = -1;
00483       if (idx1>=0)
00484         idx = idx1;
00485       if (idx2>=0)
00486       {
00487         if (idx == -1 || idx1 == idx2)
00488           idx = idx2;
00489         else {
00490           labels(i,j) = idx1+1;
00491           count[idx1] += count[idx2]+1;
00492           count[idx2] = 0;
00493           merge_map[idx2] = idx1;
00494           continue;
00495         }
00496       }
00497 
00498       if (idx >= 0 ) {
00499         labels(i,j) = idx+1;
00500         ++count[idx];
00501       }
00502       else {
00503         count.push_back(1);
00504         merge_map.push_back(merge_map.size());
00505         labels(i,j) = count.size();
00506       }
00507     }
00508   }
00509   vcl_cout << "num ground labels = "<<count.size() << vcl_endl;
00510 
00511   // update the original labels
00512   for (unsigned int j=0; j<nj; ++j) {
00513     for (unsigned int i=0; i<ni; ++i) {
00514       if (labels(i,j)==0)
00515         continue;
00516 
00517       int idx = labels(i,j)-1;
00518       while (idx>=0 && merge_map[idx] != (unsigned int)idx )
00519         idx = merge_map[idx];
00520       if (count[idx] <= min_size){
00521         labels_(i,j) = 1;
00522       }
00523     }
00524   }
00525 }
00526 
00527 
00528 //: Threshold buildings by area
00529 // All buildings with area (in pixels) less than the threshold
00530 // are removed and replace with a vegetation label
00531 template <class T>
00532 void bmdl_classify<T>::threshold_building_area()
00533 {
00534   vcl_vector<T> new_means;
00535   vcl_vector<unsigned int> new_areas;
00536   vcl_vector<unsigned int> label_map(building_area_.size(),1);
00537 
00538   for (unsigned int i=0; i<building_area_.size(); ++i){
00539     if (building_area_[i] >= area_threshold_)
00540     {
00541       label_map[i] = new_areas.size()+2;
00542       new_means.push_back(building_mean_hgt_[i]);
00543       new_areas.push_back(building_area_[i]);
00544     }
00545   }
00546 
00547   // update label image with new labels
00548   unsigned int ni = labels_.ni();
00549   unsigned int nj = labels_.nj();
00550   for (unsigned int j=0; j<nj; ++j) {
00551     for (unsigned int i=0; i<ni; ++i) {
00552       if (labels_(i,j)>=2)
00553         labels_(i,j) = label_map[labels_(i,j)-2];
00554     }
00555   }
00556   building_area_.swap(new_areas);
00557   building_mean_hgt_.swap(new_means);
00558 
00559   vcl_cout << "After thresholding by area there are "
00560            <<building_area_.size()<<" buildings" << vcl_endl;
00561 }
00562 
00563 
00564 //: Refine the building regions
00565 template <class T>
00566 void bmdl_classify<T>::refine_buildings()
00567 {
00568   // test that all inputs have been set
00569   assert(first_return_.ni() > 0);
00570   assert(first_return_.nj() > 0);
00571   assert(last_return_.ni() == first_return_.ni());
00572   assert(last_return_.nj() == first_return_.nj());
00573   assert(labels_.ni() == first_return_.ni());
00574   assert(labels_.nj() == first_return_.nj());
00575   assert(hgt_stdev_ > 0.0);
00576 
00577   expand_buildings(building_mean_hgt_, building_area_);
00578 
00579   //while (expand_buildings(building_mean_hgt_, building_area_)) ;
00580 
00581 #if 0
00582   vcl_vector<bool> valid = close_buildings(building_mean_hgt_.size());
00583 
00584   vcl_vector<T> new_means;
00585   vcl_vector<unsigned int> idx_map(building_mean_hgt_.size(),0);
00586   unsigned cnt = 1;
00587   for (unsigned int i=0; i<valid.size(); ++i) {
00588     if (valid[i]) {
00589       new_means.push_back(building_mean_hgt_[i]);
00590       idx_map[i] = ++cnt;
00591     }
00592   }
00593   building_mean_hgt_.swap(new_means);
00594 
00595   // relabel buildings with reduced label set
00596   for (unsigned int j=0; j<nj; ++j) {
00597     for (unsigned int i=0; i<ni; ++i) {
00598       if (labels_(i,j) > 1)
00599         labels_(i,j) = idx_map[labels_(i,j)-2];
00600     }
00601   }
00602 #endif // 0
00603 }
00604 
00605 
00606 //=========================================================
00607 // Private helper functions
00608 
00609 
00610 // Merge map helper class
00611 //=======================
00612 
00613 //: Constructor
00614 template <class T>
00615 bmdl_classify<T>::merge_map::merge_map(bmdl_classify<T>* c)
00616 : classifier_(c)
00617 {
00618   assert(c);
00619   idx_map_.resize(classifier_->building_mean_hgt_.size());
00620   for (unsigned int i=0; i<idx_map_.size(); ++i)
00621     idx_map_[i] = i;
00622 }
00623 
00624 //: Destructor - simplify merge map and apply to classifier
00625 template <class T>
00626 bmdl_classify<T>::merge_map::~merge_map()
00627 {
00628   vcl_vector<T>& mean = classifier_->building_mean_hgt_;
00629   vcl_vector<unsigned int>& count = classifier_->building_area_;
00630 
00631   // simplify merge map
00632   vcl_vector<vcl_pair<T,int> > unique;
00633   for (unsigned int i=0; i<idx_map_.size(); ++i)
00634   {
00635     if (idx_map_[i] == i) {
00636       unique.push_back(vcl_pair<T,int>(mean[i],i));
00637       continue;
00638     }
00639     unsigned int i2 = i;
00640     while (idx_map_[i2] != i2 )
00641       idx_map_[i] = i2 = idx_map_[i2];
00642   }
00643   vcl_sort(unique.begin(), unique.end());
00644 
00645   vcl_vector<T> new_mean(unique.size(),0.0);
00646   vcl_vector<unsigned int> new_count(unique.size(),0);
00647 
00648   vcl_vector<unsigned int> unique_map(idx_map_.size(),0);
00649   for (unsigned int i=0; i<unique.size(); ++i) {
00650     unique_map[unique[i].second] = i;
00651     new_mean[i] = unique[i].first;
00652     new_count[i] = count[unique[i].second];
00653   }
00654   for (unsigned int i=0; i<unique_map.size(); ++i)
00655     if (idx_map_[i] != i)
00656       unique_map[i] = unique_map[idx_map_[i]];
00657 
00658   vil_image_view<unsigned int>& labels = classifier_->labels_;
00659   unsigned int ni = labels.ni();
00660   unsigned int nj = labels.nj();
00661   for (unsigned int j=0; j<nj; ++j) {
00662     for (unsigned int i=0; i<ni; ++i) {
00663       if (labels(i,j)>=2)
00664         labels(i,j) = unique_map[labels(i,j)-2]+2;
00665     }
00666   }
00667   mean.swap(new_mean);
00668   count.swap(new_count);
00669 }
00670 
00671 //: translate old index to temporary merged index
00672 template <class T>
00673 unsigned int
00674 bmdl_classify<T>::merge_map::translate(unsigned int idx) const
00675 {
00676   while (idx_map_[idx] != idx) idx = idx_map_[idx];
00677   return idx;
00678 }
00679 
00680 //: merge two indices
00681 template <class T>
00682 void bmdl_classify<T>::merge_map::merge(unsigned int idx1, unsigned int idx2)
00683 {
00684   idx1 = translate(idx1);
00685   idx2 = translate(idx2);
00686   if (idx1 == idx2)
00687     return;
00688 
00689   vcl_vector<T>& mean = classifier_->building_mean_hgt_;
00690   vcl_vector<unsigned int>& count = classifier_->building_area_;
00691 
00692   mean[idx1] = (mean[idx1]*count[idx1] + mean[idx2]*count[idx2])
00693                /(count[idx1]+count[idx2]);
00694   count[idx1] += count[idx2];
00695   count[idx2] = 0;
00696   idx_map_[idx2] = idx1;
00697 }
00698 
00699 //=======================
00700 
00701 //: Parabolic interpolation of 3 points \p y_0, \p y_1, \p y_2
00702 //  \returns the peak value by reference in \p y_peak
00703 //  \returns the peak location offset from the x of \p y_0
00704 template <class T>
00705 T bmdl_classify<T>::interpolate_parabola(T y_1, T y_0, T y_2, T& root_offset) const
00706 {
00707   T diff1 = y_2 - y_1;      // first derivative
00708   T diff2 = 2 * y_0 - y_1 - y_2; // second derivative
00709   //y_peak = y_0 + diff1 * diff1 / (8 * diff2);        // interpolate y as max/min
00710   root_offset = vcl_abs(vcl_sqrt(diff1*diff1/4 + 2*diff2*y_0) / diff2);
00711   return diff1 / (2 * diff2);   // interpolate x offset
00712 }
00713 
00714 
00715 //: compute a histogram of the data
00716 // Does not reset the initial bin values to zero
00717 template <class T>
00718 void bmdl_classify<T>::histogram(const vcl_vector<T>& data, vcl_vector<unsigned int>& bins,
00719                                  T minv, T maxv) const
00720 {
00721   assert(maxv > minv);
00722   int num_bins = bins.size();
00723   assert(num_bins > 0);
00724   T binsize = (maxv-minv)/num_bins;
00725 
00726   for (unsigned int i=0; i<data.size(); ++i)
00727   {
00728     int bin = static_cast<int>((data[i]-minv)/binsize);
00729     if (bin >= num_bins || bin < 0)
00730       continue;
00731     ++bins[bin];
00732   }
00733 }
00734 
00735 
00736 //: find a peak in the data and fit a gaussian to it
00737 // Search in the range \a minv to \a maxv
00738 template <class T>
00739 void bmdl_classify<T>::fit_gaussian_to_peak(const vcl_vector<T>& data, T minv, T maxv,
00740                                             T& mean, T& stdev) const
00741 {
00742   unsigned int num_bins = 100;
00743   T binsize = (maxv-minv)/num_bins;
00744   vcl_vector<unsigned int> hist(100,0);
00745   histogram(data,hist,minv,maxv);
00746 
00747   //find peak
00748   unsigned int peakv = 0;
00749   unsigned int peaki = 0;
00750   for (unsigned int i=0; i<num_bins; ++i) {
00751     if (hist[i] > peakv) {
00752       peakv = hist[i];
00753       peaki = i;
00754     }
00755   }
00756 
00757   // fit a parabola to estimate the range of the peak
00758   unsigned int pp = (peaki+1 < num_bins)?hist[peaki+1]:0;
00759   unsigned int pm = (peaki >= 1)        ?hist[peaki-1]:0;
00760   T ro;
00761   T shift = interpolate_parabola(T(pm),T(peakv),T(pp),ro);
00762   ro *= 3;
00763   maxv = (peaki+shift+ro)*binsize+minv;
00764   minv = (peaki+shift-ro)*binsize+minv;
00765 
00766   // fit a Gaussian around this peak
00767   mean = 0;
00768   stdev = 0;
00769   unsigned count = 0;
00770   for (unsigned int i=0; i<data.size(); ++i) {
00771     if (data[i] > minv && data[i] < maxv) {
00772       ++count;
00773       mean += data[i];
00774       stdev += data[i]*data[i];
00775     }
00776   }
00777 
00778   mean /= count;
00779   stdev /= count;
00780   stdev -= mean*mean;
00781   stdev = vcl_sqrt(stdev);
00782 }
00783 
00784 
00785 //: Expand the range (minv, maxv) with the data in \a image
00786 // Only finite values count
00787 template <class T>
00788 void bmdl_classify<T>::range(const vil_image_view<T>& image,
00789                              T& minv, T& maxv) const
00790 {
00791   const unsigned int ni= image.ni();
00792   const unsigned int nj = image.nj();
00793   for (unsigned int j=0; j<nj; ++j) {
00794     for (unsigned int i=0; i<ni; ++i) {
00795       const T& val = image(i,j);
00796       if (vnl_math_isfinite(val)) {
00797         if (val > maxv) maxv = val;
00798         if (val < minv) minv = val;
00799       }
00800     }
00801   }
00802 }
00803 
00804 
00805 //: Search for nearby pixel that can be added to each building
00806 //  Return true if any changes are made
00807 template <class T>
00808 bool bmdl_classify<T>::expand_buildings(vcl_vector<T>& means,
00809                                         vcl_vector<unsigned int>& sizes)
00810 {
00811   bool changed = false;
00812   unsigned int ni=first_return_.ni();
00813   unsigned int nj=last_return_.nj();
00814 
00815   T zthresh = T(0.01);
00816   assert(zthresh > 0); // make sure that T is not an integral type
00817 
00818   merge_map merge(this);
00819 
00820   for (unsigned int j=0; j<nj; ++j)
00821   {
00822     for (unsigned int i=0; i<ni; ++i)
00823     {
00824       // only expand into non-buildings
00825       if (labels_(i,j) != 1)
00826         continue;
00827 
00828       // collect all adjacent building labels
00829       vcl_set<int> n;
00830       if (i>0 && labels_(i-1,j) > 1) {
00831         unsigned int idx = merge.translate(labels_(i-1,j)-2);
00832         if (vnl_math_sqr(first_return_(i,j) - first_return_(i-1,j)) < zthresh ||
00833             vnl_math_sqr(last_return_(i,j) - last_return_(i-1,j)) < zthresh )
00834           n.insert(idx);
00835       }
00836       if (j>0 && labels_(i,j-1) > 1) {
00837         unsigned int idx = merge.translate(labels_(i,j-1)-2);
00838         if (vnl_math_sqr(first_return_(i,j) - first_return_(i,j-1)) < zthresh ||
00839             vnl_math_sqr(last_return_(i,j) - last_return_(i,j-1)) < zthresh )
00840           n.insert(idx);
00841       }
00842       if (i<ni-1 && labels_(i+1,j) > 1) {
00843         unsigned int idx = merge.translate(labels_(i+1,j)-2);
00844         if (vnl_math_sqr(first_return_(i,j) - first_return_(i+1,j)) < zthresh ||
00845             vnl_math_sqr(last_return_(i,j) - last_return_(i+1,j)) < zthresh )
00846           n.insert(idx);
00847       }
00848       if (j<nj-1 && labels_(i,j+1) > 1) {
00849         unsigned int idx = merge.translate(labels_(i,j+1)-2);
00850         if (vnl_math_sqr(first_return_(i,j) - first_return_(i,j+1)) < zthresh ||
00851             vnl_math_sqr(last_return_(i,j) - last_return_(i,j+1)) < zthresh )
00852           n.insert(idx);
00853       }
00854       if (n.empty())
00855         continue;
00856 
00857       for (vcl_set<int>::iterator itr=n.begin(); itr!=n.end(); ++itr) {
00858         // test for merge
00859         if (labels_(i,j) > 1) {
00860           unsigned int other = labels_(i,j)-2;
00861           merge.merge(other,*itr);
00862         }
00863         else {
00864           labels_(i,j) = *itr+2;
00865           changed = true;
00866         }
00867       }
00868     }
00869   }
00870 
00871   return changed;
00872 }
00873 
00874 
00875 //: Dilate buildings into unclaimed (vegetation) pixel
00876 //  Only claim a vegetation pixel if surrounded by
00877 //  \a num pixels from the same building
00878 template <class T>
00879 bool bmdl_classify<T>::dilate_buildings(unsigned int num)
00880 {
00881   bool changed = false;
00882   unsigned int ni=first_return_.ni()-1;
00883   unsigned int nj=last_return_.nj()-1;
00884 
00885   for (unsigned int j=1; j<nj; ++j)
00886   {
00887     for (unsigned int i=1; i<ni; ++i)
00888     {
00889       // only expand into non-buildings
00890       if (labels_(i,j) != 1)
00891         continue;
00892 
00893       // collect all adjacent building labels
00894       vcl_map<int,unsigned int> n;
00895       int offset_x[] = {-1, 0, 0, 1,-1, 1, 1,-1};
00896       int offset_y[] = { 0,-1, 1, 0,-1,-1, 1, 1};
00897       unsigned total = 0;
00898       for (unsigned int k=0; k<8; ++k)
00899       {
00900         int idx = labels_(i+offset_x[k], j+offset_y[k])-2;
00901         if (idx<0) continue;
00902         ++total;
00903         vcl_map<int,unsigned int>::iterator itr = n.find(idx);
00904         if (itr == n.end() && k < 4) // only consider 4-con neighbors
00905           n.insert(vcl_pair<int,unsigned int>(idx,1));
00906         else
00907           ++(itr->second);
00908       }
00909 
00910       if (n.empty())
00911         continue;
00912 
00913       if (total < num)
00914         continue;
00915 
00916       unsigned int max = 0;
00917       for (vcl_map<int,unsigned>::iterator itr=n.begin(); itr!=n.end(); ++itr)
00918       {
00919         if (itr->second > max) {
00920           max = itr->second;
00921           labels_(i,j) = itr->first+2;
00922           ++building_area_[itr->first];
00923           changed = true;
00924         }
00925       }
00926     }
00927   }
00928 
00929   return changed;
00930 }
00931 
00932 
00933 //: Greedy merging of adjacent buildings
00934 template <class T>
00935 bool bmdl_classify<T>::greedy_merge()
00936 {
00937   unsigned int ni=labels_.ni();
00938   unsigned int nj=labels_.nj();
00939   // map of number of pixels adjacent between two buildings
00940   typedef vcl_pair<unsigned int,unsigned int> upair;
00941   vcl_set<upair> adjacent;
00942 
00943   for (unsigned int j=0; j<nj; ++j) {
00944     for (unsigned int i=1; i<ni; ++i) {
00945       unsigned int l1 = labels_(i,j);
00946       unsigned int l2 = labels_(i-1,j);
00947       if (l1<2 || l2<2 || l1==l2) continue;
00948       if (l1>l2) vcl_swap(l1,l2);
00949       adjacent.insert(upair(l1-2,l2-2));
00950     }
00951   }
00952 
00953   for (unsigned int j=1; j<nj; ++j) {
00954     for (unsigned int i=0; i<ni; ++i) {
00955       unsigned int l1 = labels_(i,j);
00956       unsigned int l2 = labels_(i,j-1);
00957       if (l1<2 || l2<2 || l1==l2) continue;
00958       if (l1>l2) vcl_swap(l1,l2);
00959       adjacent.insert(upair(l1-2,l2-2));
00960     }
00961   }
00962 
00963   vcl_vector<upair> to_merge;
00964   for (vcl_set<upair>::iterator itr=adjacent.begin();
00965        itr!=adjacent.end(); ++itr)
00966   {
00967     unsigned int idx1 = itr->first;
00968     unsigned int idx2 = itr->second;
00969     if (vcl_abs(building_mean_hgt_[idx1] - building_mean_hgt_[idx2]) < height_resolution_)
00970       to_merge.push_back(*itr);
00971   }
00972 
00973   if (to_merge.empty())
00974     return false;
00975 
00976   merge_map merge(this);
00977 
00978   for (unsigned int i=0; i<to_merge.size(); ++i)
00979   {
00980     unsigned int idx1 = merge.translate(to_merge[i].first);
00981     unsigned int idx2 = merge.translate(to_merge[i].second);
00982     merge.merge(idx1,idx2);
00983   }
00984 
00985   return true;
00986 }
00987 
00988 
00989 //: Morphological clean up on each building independently
00990 template <class T>
00991 vcl_vector<bool>
00992 bmdl_classify<T>::close_buildings(unsigned int num_labels)
00993 {
00994   unsigned int ni=labels_.ni();
00995   unsigned int nj=labels_.nj();
00996   vil_image_view<unsigned int> new_labels(ni,nj);
00997   vcl_vector<vgl_box_2d<int> > building_bounds(num_labels);
00998 
00999   // transfer vegetation labels to the output
01000   // and build a bounding box around each building
01001   for (unsigned int j=0; j<nj; ++j) {
01002     for (unsigned int i=0; i<ni; ++i) {
01003       new_labels(i,j) = (labels_(i,j)==1)?1:0;
01004       if (labels_(i,j)>1)
01005         building_bounds[labels_(i,j)-2].add(vgl_point_2d<int>(i,j));
01006     }
01007   }
01008 
01009   // find the maximum of all building sizes
01010   unsigned int bni=0, bnj=0;
01011   for (unsigned int l=0; l<num_labels; ++l) {
01012     unsigned int w = building_bounds[l].width()+1;
01013     unsigned int h = building_bounds[l].height()+1;
01014     if (w > bni) bni = w;
01015     if (h > bnj) bnj = h;
01016   }
01017 
01018   vcl_cout << "max bounds = " << bni<<", "<<bnj<<vcl_endl;
01019   bni += 4;
01020   bnj += 4;
01021   vil_image_view<bool> full_mask(bni,bnj);
01022   vil_image_view<bool> full_work(bni,bnj);
01023 
01024   vil_structuring_element se;
01025   se.set_to_disk(1);
01026   vcl_vector<bool> valid(num_labels,false);
01027   for (unsigned l=0; l<num_labels; ++l) {
01028     const vgl_box_2d<int>& bbox = building_bounds[l];
01029     // skip buildings that vanish with a binary dilate
01030     if (bbox.width()==0 || bbox.height()==0)
01031       continue;
01032 
01033 #ifdef DEBUG
01034     vcl_cout << "closing "<<l<<" of "<<num_labels<<vcl_endl;
01035     vcl_cout << "  from "<<building_bounds[l].min_point()<<" to "<<building_bounds[l].max_point()<<vcl_endl;
01036 #endif // DEBUG
01037 
01038     int min_x=bbox.min_x()-2, min_y=bbox.min_y()-2;
01039     if (min_x < 0) min_x = 0;
01040     if (min_y < 0) min_y = 0;
01041     int max_x=bbox.max_x()+2, max_y=bbox.max_y()+2;
01042     if (max_x >= int(ni)) max_x = ni-1;
01043     if (max_y >= int(nj)) max_y = nj-1;
01044 
01045     unsigned lni = max_x - min_x + 1;
01046     unsigned lnj = max_y - min_y + 1;
01047 
01048     // create cropped views of the working image space
01049     vil_image_view<bool> mask(lni,lnj);// = vil_crop(full_mask,0,lni,0,lnj);
01050     vil_image_view<bool> work(lni,lnj);// = vil_crop(full_work,0,lni,0,lnj);
01051 
01052     // copy data into a binary mask
01053     for (unsigned int j=0; j<lnj; ++j) {
01054       for (unsigned int i=0; i<lni; ++i) {
01055         mask(i,j) = labels_(min_x+i,min_y+j)-2 == l;
01056       }
01057     }
01058     // binary closing
01059     vil_binary_dilate(mask,work,se);
01060     vil_binary_erode(work,mask,se);
01061     // binary opening
01062     vil_binary_erode(mask,work,se);
01063     vil_binary_dilate(work,mask,se);
01064 
01065     // copy mask back to labels
01066     for (unsigned int j=0; j<lnj; ++j) {
01067       for (unsigned int i=0; i<lni; ++i) {
01068         if (mask(i,j)){
01069           new_labels(min_x+i,min_y+j) = l+2;
01070           valid[l] = true;
01071         }
01072       }
01073     }
01074   }
01075   labels_ = new_labels;
01076   return valid;
01077 }
01078 
01079 
01080 //: Group building pixel by height into bins of size \a binsize
01081 template <class T>
01082 vil_image_view<unsigned int> bmdl_classify<T>::bin_heights(T binsize)
01083 {
01084   unsigned int ni=last_return_.ni();
01085   unsigned int nj=last_return_.nj();
01086 
01087   vil_image_view<unsigned int> bin_img(ni,nj);
01088 
01089   for (unsigned int j=0; j<nj; ++j)
01090   {
01091     for (unsigned int i=0; i<ni; ++i)
01092     {
01093       if (labels_(i,j) < 2)
01094       {
01095         bin_img(i,j) = labels_(i,j);
01096         continue;
01097       }
01098       int bin = static_cast<int>((last_return_(i,j) - last_min_)/binsize);
01099       bin_img(i,j) = bin+2;
01100     }
01101   }
01102   return bin_img;
01103 }
01104 
01105 
01106 //------------------------------------------------------------------------------
01107 
01108 #define BMDL_CLASSIFY_INSTANTIATE(T) \
01109 template class bmdl_classify<T >
01110 
01111 
01112 #endif // bmdl_classify_txx_