contrib/brl/bseg/sdet/sdet_atmospheric_image_classifier.cxx
Go to the documentation of this file.
00001 #include "sdet_atmospheric_image_classifier.h"
00002 //
00003 #include <vnl/vnl_numeric_traits.h>
00004 #include <vul/vul_timer.h>
00005 #include <vbl/io/vbl_io_smart_ptr.h>
00006 #include <vcl_iostream.h>
00007 #include <vcl_cassert.h>
00008 #include <vil/vil_load.h>
00009 // test if a given texture category is an atmospheric effect
00010 bool sdet_atmospheric_image_classifier::atmos_cat(vcl_string const& cat)
00011 {
00012   for (vcl_vector<vcl_string>::iterator cit = atmos_categories_.begin();
00013        cit != atmos_categories_.end(); ++cit)
00014     if (cat == *cit)
00015       return true;
00016   return false;
00017 }
00018 //
00019 // compute the subjective logic opinion,
00020 // omega =  {b_no_atmos, b_atmos, uncertainty, a}
00021 // a = 0.5. For the moment, uncertainty is assigned the
00022 // probability of desert texture since that category is
00023 // easily confused with haze, a true atmospheric effect
00024 // admittedly this approach is not on solid grounds but
00025 // seems to work well in a large number of situations
00026 //
00027 void sdet_atmospheric_image_classifier::
00028 category_quality_color_mix(vcl_map<vcl_string, float>& probs,
00029                            vnl_vector_fixed<float, 3> const& uncert,
00030                            vnl_vector_fixed<float, 3>& color_mix)
00031 {
00032   //start with max prob color
00033   vcl_map<vcl_string, vcl_vector<float> >::iterator hit = category_histograms_.begin();
00034   // accumulate p_atmos, p_no_atmos, and p_haze
00035   float atmos_sum = 0.0f;
00036   float no_atmos_sum = 0.0f;
00037   float prob_sum = 0.0f;
00038   float up = 0.0f;
00039   for (; hit != category_histograms_.end(); ++hit) {
00040     const vcl_string& cat = (*hit).first;
00041     float p = probs[cat];
00042     prob_sum += p;
00043     if (atmos_cat(cat))
00044       atmos_sum += p;
00045     else if (cat=="des") // cludge for the moment-JLM
00046       up = p;
00047     else
00048       no_atmos_sum += p;
00049   }
00050   // define uncertainty as p_haze
00051   float u = up/prob_sum;
00052   // normalize sums to probabilities
00053   float p_bad  = atmos_sum/(prob_sum-up);
00054   float p_good = no_atmos_sum/(prob_sum-up);
00055   // potentially reduce uncertainty so that beliefs are not negative
00056   float p_min = p_bad;
00057   if (p_good<p_min) p_min = p_good;
00058   if ((p_min-0.5f*u) < 0.0f) u = 2.0f*p_min;
00059   // form the beliefs
00060   float b_bad = p_bad - 0.5f*u;
00061   float b_good = p_good - 0.5f*u;
00062   vnl_vector_fixed<float, 3> good, bad;
00063   bad[0]  = 1.0f;   bad[1] = 0.0f;  bad[2] = 0.0f;
00064   good[0] = 0.0f;  good[1] = 1.0f; good[2] = 0.0f;
00065   if (up<prob_sum)
00066     color_mix = b_good*good + b_bad*bad +  u*uncert;
00067   else
00068     color_mix = u*uncert;
00069 }
00070 
00071 #if 0 //=====debug====
00072 static bool required_block(int bidxu, int bidxv, int i,
00073                            int j, int block_size, int margin)
00074 {
00075   int idxu = (i-margin)/block_size, idxv = (j-margin)/block_size;
00076   return bidxu == idxu && bidxv == idxv;
00077 }
00078 #endif // 0
00079 
00080 vil_image_view<float> sdet_atmospheric_image_classifier::
00081 classify_image_blocks_qual(vcl_string const& img_path)
00082 {
00083   vil_image_resource_sptr resc = vil_load_image_resource(img_path.c_str());
00084   vil_image_view<float> img = scale_image(resc); // map to [0, 1]
00085   vcl_cout << "Classifying quality on image " << img_path << '\n' << vcl_flush;
00086   return classify_image_blocks_qual(img);
00087 }
00088 
00089 vil_image_view<float>
00090 sdet_atmospheric_image_classifier::classify_image_blocks_qual(vil_image_view<float> const& image)
00091 {
00092   vcl_cout << "image size(" << image.ni()<< ' ' << image.nj() << ")pixels:["
00093            << texton_dictionary_.size() << "]categories \n" << vcl_flush;
00094   vul_timer t;
00095   if (!color_map_valid_)
00096     this->init_color_map();
00097   if (!texton_index_valid_)
00098     this->compute_texton_index();
00099   this->compute_filter_bank(image);
00100   unsigned dim = filter_responses_.n_levels();
00101   vcl_cout << "texton dimension " << dim +2<< '\n';
00102 
00103   int margin = static_cast<int>(this->max_filter_radius());
00104   vcl_cout << "filter kernel margin " << margin << '\n';
00105   int ni = static_cast<int>(image.ni());
00106   int nj = static_cast<int>(image.nj());
00107   if ((ni-margin)<=0 || (nj-margin)<=0) {
00108     vcl_cout << "Image smaller than filter margin\n";
00109     return vil_image_view<float>(0, 0);
00110   }
00111   //number of pixels in a block
00112   unsigned block_area = block_size_*block_size_;
00113   float weight = 1.0f/static_cast<float>(block_area);
00114 
00115   vil_image_view<float> prob(ni, nj, 3);
00116   // fill image with the uncertainty = 1 color
00117   vnl_vector_fixed<float, 3> unct;
00118   unct[0] = 0.0f;  unct[1] = 0.0f; unct[2] = 1.0f;
00119   for (unsigned j = 0; j<image.nj(); ++j)
00120     for (unsigned i = 0; i<image.ni(); ++i)
00121       for (unsigned p = 0; p<3; ++p)
00122         prob(i,j,p) = unct[p];
00123 
00124   unsigned nh = texton_index_.size();
00125   int bidxv = 0;
00126   for (int j = margin; j<(nj-margin); j+=block_size_, ++bidxv) {
00127     int bidxu = 0;
00128     for (int i = margin; i<(ni-margin); i+=block_size_, ++bidxu) {
00129       vcl_vector<float> h(nh, 0.0f);
00130       for (unsigned r = 0; r<block_size_; ++r)
00131         for (unsigned c = 0; c<block_size_; ++c) {
00132           vnl_vector<double> temp(dim+2);
00133           for (unsigned f = 0; f<dim; ++f)
00134             temp[f]=filter_responses_.response(f)(i+c,j+r);
00135           temp[dim]=laplace_(i+c,j+r); temp[dim+1]=gauss_(i+c,j+r);
00136           //hist bins are probabilities
00137           //i.e., sum h[i] = 1.0 so weight should typically be 1/Nupdates
00138           this->update_hist(temp, weight, h);
00139         }
00140 
00141       //finished a block - compute category probabilites from the histogram
00142       vcl_map<vcl_string, float> texture_probs =
00143         this->texture_probabilities(h);
00144 
00145 #if 0 //=====debug====
00146       int ii = 7518, jj = 2909;
00147       if (required_block(bidxu, bidxv, ii, jj, block_size_, margin)) {
00148         vcl_cout << "probs(" << i << ' ' << j << ")\n";
00149         float psum = 0.0;
00150         for (vcl_map<vcl_string, float>::iterator cit = texture_probs.begin();
00151              cit != texture_probs.end(); ++cit)
00152           psum += (*cit).second;
00153 
00154         for (vcl_map<vcl_string, float>::iterator cit =texture_probs.begin();
00155              cit != texture_probs.end(); ++cit)
00156           vcl_cout << (*cit).first << ' ' << ((*cit).second)/psum << '\n';
00157 #ifdef DEBUG
00158         vcl_cout << " hist\n";
00159         for (unsigned i = 0; i<nh; ++i)
00160           vcl_cout << h[i]<< '\n';
00161 #endif
00162       }
00163 #endif
00164       vnl_vector_fixed<float, 3> color;
00165       //colorize output according to probabilities of each category
00166       this->category_quality_color_mix(texture_probs,unct, color);
00167       for (unsigned r = 0; r<block_size_; ++r)
00168         for (unsigned c = 0; c<block_size_; ++c)
00169           for (unsigned b = 0; b<3; ++b)
00170             prob(i+c,j+r,b) = color[b];
00171     }
00172     vcl_cout << '.' << vcl_flush;
00173   }
00174   vcl_cout << "\nBlock classification took " << t.real()/1000.0 << " seconds\n" << vcl_flush;
00175   return prob;
00176 }
00177 
00178 vil_image_view<float> sdet_atmospheric_image_classifier::
00179 classify_image_blocks_expected(vcl_string const& img_path,
00180                                vcl_string const& exp_path)
00181 {
00182   vil_image_resource_sptr resc = vil_load_image_resource(img_path.c_str());
00183   vil_image_view<float> img = scale_image(resc); // map to [0, 1]
00184   vil_image_resource_sptr resce = vil_load_image_resource(exp_path.c_str());
00185   vil_image_view<float> exp = scale_image(resce); // map to [0, 1]
00186   vcl_cout << "Classifying quality on image " << img_path << " using expected image " << exp_path << '\n' << vcl_flush;
00187   return classify_image_blocks_expected(img, exp);
00188 }
00189 
00190 vil_image_view<float> sdet_atmospheric_image_classifier::
00191 classify_image_blocks_expected(vil_image_view<float> const& image,
00192                                vil_image_view<float> const& exp
00193                               )
00194 {
00195   int ni = static_cast<int>(image.ni());
00196   int nj = static_cast<int>(image.nj());
00197   int ni_exp = static_cast<int>(exp.ni());
00198   int nj_exp = static_cast<int>(exp.nj());
00199   if ((ni != ni_exp) || (nj != nj_exp)) {
00200     vcl_cout << "Incoming image and expected image not of same size\n"
00201              << vcl_flush;
00202     return vil_image_view<float>();
00203   }
00204   vcl_cout << "image size(" << ni << ' '
00205            << nj << ")pixels \n" << vcl_flush;
00206 
00207   vul_timer t;
00208 
00209   if (!texton_index_valid_)
00210     this->compute_texton_index();
00211   this->compute_filter_bank(image);
00212   unsigned dim = filter_responses_.n_levels();
00213   vcl_cout << "texton dimension " << dim +2<< '\n';
00214 
00215   int margin = static_cast<int>(this->max_filter_radius());
00216   vcl_cout << "filter kernel margin " << margin << '\n';
00217   if ((ni-margin)<=0 || (nj-margin)<=0) {
00218     vcl_cout << "Image smaller than filter margin\n";
00219     return vil_image_view<float>(0, 0);
00220   }
00221   //cached filter outputs for the input image
00222   vcl_vector<vil_image_view<float> > image_resps =
00223     filter_responses_.responses();
00224   vil_image_view<float> laplace = laplace_;
00225   vil_image_view<float> gauss = gauss_;
00226 
00227   //compute new filter outputs for the expected image
00228   this->compute_filter_bank(exp);
00229 
00230   vil_image_view<float> prob(ni, nj, 3);
00231   // fill image with the uncertainty = 1 color
00232   vnl_vector_fixed<float, 3> unct;
00233   unct[0] = 0.0f;  unct[1] = 0.0f; unct[2] = 1.0f;
00234   for (unsigned j = 0; j<image.nj(); ++j)
00235     for (unsigned i = 0; i<image.ni(); ++i)
00236       for (unsigned p = 0; p<3; ++p)
00237         prob(i,j,p) = unct[p];
00238 
00239   vcl_vector<float>& mod_hist = category_histograms_["mod"];
00240   unsigned nh = mod_hist.size();
00241   if (nh == 0) {
00242     vcl_cout << "No model category to evaluate image\n";
00243     return prob;
00244   }
00245   double thr = dist_["mod"]["mod"];
00246   //vcl_vector<vnl_vector<double> >& textons = texton_dictionary_["mod"]; -- unused!
00247   thr *= 0.25;//temporary hard coded threshold ratio
00248   int bidxv = 0;
00249   for (int j = margin; j<(nj-margin); j+=block_size_, ++bidxv) {
00250     int bidxu = 0;
00251     for (int i = margin; i<(ni-margin); i+=block_size_, ++bidxu) {
00252       float pr = 0.0f, total =0.0f;
00253       for (unsigned r = 0; r<block_size_; ++r)
00254         for (unsigned c = 0; c<block_size_; ++c) {
00255           vnl_vector<double> temp_img(dim+2), temp_exp(dim+2);
00256           for (unsigned f = 0; f<dim; ++f) {
00257             temp_exp[f]=filter_responses_.response(f)(i+c,j+r);
00258             temp_img[f]=image_resps[f](i+c,j+r);
00259           }
00260           temp_exp[dim]=laplace_(i+c,j+r); temp_exp[dim+1]=gauss_(i+c,j+r);
00261           temp_img[dim]=laplace(i+c,j+r); temp_img[dim+1]=gauss(i+c,j+r);
00262           unsigned indx_exp = this->nearest_texton_index(temp_exp);
00263           double di_exp_img = vnl_vector_ssd(temp_exp, temp_img);
00264           di_exp_img = vcl_sqrt(di_exp_img/temp_exp.size());
00265           total += mod_hist[indx_exp];
00266           if (di_exp_img<thr)
00267             pr += mod_hist[indx_exp];
00268         }
00269       pr /= total;
00270       float u = pr/(1.0f - pr);
00271       if (u>1.0f) u = 1/u;
00272       float b_good = pr - 0.5f*u;
00273       if (b_good< 0.0f) b_good = 0.0f;
00274       float b_bad = 1.0f - u - b_good;
00275       vnl_vector_fixed<float, 3> color;
00276       //colorize output according to probabilities of each category
00277       for (unsigned r = 0; r<block_size_; ++r)
00278         for (unsigned c = 0; c<block_size_; ++c) {
00279           prob(i+c,j+r,0) = b_bad;
00280           prob(i+c,j+r,1) = b_good;
00281           prob(i+c,j+r,2) = u;
00282         }
00283     }
00284       vcl_cout << '.' << vcl_flush;
00285   }
00286   vcl_cout << "\nBlock classification took " << t.real()/1000.0 << " seconds\n" << vcl_flush;
00287   return prob;
00288 }