00001 #include "sdet_texture_classifier.h"
00002
00003 #include <brip/brip_vil_float_ops.h>
00004 #include <brip/brip_filter_bank.h>
00005 #include <sdet/sdet_k_means.h>
00006 #include <vsl/vsl_map_io.h>
00007 #include <vsl/vsl_vector_io.h>
00008 #include <vsl/vsl_binary_io.h>
00009 #include <vnl/io/vnl_io_vector.h>
00010 #include <vnl/vnl_numeric_traits.h>
00011 #include <vul/vul_timer.h>
00012 #include <vgl/vgl_polygon_scan_iterator.h>
00013 #include <vil/vil_math.h>
00014 #include <vil/vil_load.h>
00015 #include <vil/vil_save.h>
00016 #include <vil/vil_crop.h>
00017 #include <vbl/io/vbl_io_smart_ptr.h>
00018 #include <vsol/vsol_polygon_2d.h>
00019 #include <vsol/vsol_polygon_2d_sptr.h>
00020 #include <vsol/vsol_point_2d.h>
00021 #include <vsol/vsol_point_2d_sptr.h>
00022 #include <vsol/vsol_box_2d.h>
00023 #include <vsol/vsol_box_2d_sptr.h>
00024 #include <vcl_cstdlib.h>
00025 #include <vcl_iostream.h>
00026 #include <vcl_algorithm.h>
00027 #include <vcl_cassert.h>
00028
00029 static
00030 vcl_vector<vgl_polygon<double> > load_polys(vcl_string const& poly_path)
00031 {
00032 vcl_vector<vsol_spatial_object_2d_sptr> sos;
00033 vsl_b_ifstream istr(poly_path);
00034 if (!istr) {
00035 vcl_cout << "Failed to open input stream "
00036 << poly_path << vcl_endl;
00037 return vcl_vector<vgl_polygon<double> >();
00038 }
00039 vsl_b_read(istr, sos);
00040 if (!sos.size()) {
00041 vcl_cout << "no polys "
00042 << poly_path << vcl_endl;
00043 return vcl_vector<vgl_polygon<double> >();
00044 }
00045 vcl_vector<vgl_polygon<double> > vpolys;
00046 for (unsigned i = 0; i<sos.size(); ++i) {
00047 vsol_polygon_2d* poly = static_cast<vsol_polygon_2d*>(sos[i].ptr());
00048 vgl_polygon<double> vpoly; vpoly.new_sheet();
00049 unsigned nverts = poly->size();
00050 for (unsigned i = 0; i<nverts; ++i) {
00051 vsol_point_2d_sptr v = poly->vertex(i);
00052 vpoly.push_back(v->x(), v->y());
00053 }
00054 vpolys.push_back(vpoly);
00055 }
00056 return vpolys;
00057 }
00058
00059 vil_image_view<float> sdet_texture_classifier::
00060 scale_image(vil_image_resource_sptr const& resc)
00061 {
00062 vil_pixel_format fmt = resc->pixel_format();
00063 vil_image_view<float> img = brip_vil_float_ops::convert_to_float(resc);
00064 if (fmt == VIL_PIXEL_FORMAT_BYTE)
00065 vil_math_scale_values(img,1.0/255.0);
00066 if (fmt == VIL_PIXEL_FORMAT_UINT_16)
00067 vil_math_scale_values(img,1.0/2048.0);
00068 return img;
00069 }
00070
00071 static unsigned gauss_radius(float sigma, float cutoff_ratio)
00072 {
00073 double sigma_sq_inv = 1/(sigma*sigma);
00074 int r = static_cast<unsigned>(vcl_sqrt((-2.0*vcl_log(cutoff_ratio))/sigma_sq_inv)+0.5);
00075 return r;
00076 }
00077
00078
00079 void sdet_texture_classifier::init_color_map()
00080 {
00081 vcl_vector<vnl_vector_fixed<float, 3> > colors(8);
00082 colors[0][0]=0.0f; colors[0][1]=0.0f; colors[0][2]=1.0f;
00083 colors[1][0]=0.0f; colors[1][1]=1.0f; colors[1][2]=0.0f;
00084 colors[2][0]=0.0f; colors[2][1]=0.5f; colors[2][2]=0.5f;
00085 colors[3][0]=1.0f; colors[3][1]=0.0f; colors[3][2]=0.0f;
00086 colors[4][0]=0.5f; colors[4][1]=0.0f; colors[4][2]=0.5f;
00087 colors[5][0]=0.5f; colors[5][1]=0.5f; colors[5][2]=0.0f;
00088 colors[6][0]=0.0f; colors[6][1]=0.25f; colors[6][2]=0.75f;
00089 colors[7][0]=0.25f; colors[7][1]=0.25f; colors[7][2]=0.5f;
00090 unsigned i = 0;
00091 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::const_iterator it= texton_dictionary_.begin();
00092 for (; it!= texton_dictionary_.end(); ++it,++i) {
00093 color_map_[(*it).first] = colors[i];
00094 vcl_cout << (*it).first <<":(" << colors[i][0] << ' '
00095 << colors[i][1] << ' ' << colors[i][2] << ")\n" << vcl_flush;
00096 }
00097 color_map_valid_ = true;
00098 }
00099
00100 void sdet_texture_classifier::print_color_map() const
00101 {
00102 if (!color_map_valid_) return;
00103 vcl_cout << "Category Color Map\n";
00104 vcl_map< vcl_string, vnl_vector_fixed<float, 3> >::const_iterator it= color_map_.begin();
00105 for (; it!= color_map_.end(); ++it) {
00106 vnl_vector_fixed<float,3> const & colors = (*it).second;
00107 vcl_cout << (*it).first <<":(" << colors[0] << ' '
00108 << colors[1] << ' ' << colors[2] << ")\n" << vcl_flush;
00109 }
00110 }
00111
00112 sdet_texture_classifier::
00113 sdet_texture_classifier(sdet_texture_classifier_params const& params)
00114 : sdet_texture_classifier_params(params),
00115 filter_responses_(brip_filter_bank(params.n_scales_,
00116 params.scale_interval_,
00117 params.lambda0_,
00118 params.lambda1_,
00119 params.angle_interval_,
00120 params.cutoff_per_)),
00121 distances_valid_(false), inter_prob_valid_(false), color_map_valid_(false),
00122 texton_index_valid_(false), texton_weights_valid_(false)
00123 {}
00124
00125
00126 bool sdet_texture_classifier::
00127 compute_filter_bank(vil_image_view<float> const& img)
00128 {
00129 vul_timer t;
00130 filter_responses_.set_image(img);
00131 bool bright = false;
00132 bool scale_invariant = true;
00133
00134 vcl_cout<< "s = ("<< laplace_radius_<< ' ' << laplace_radius_ << ")\n"
00135 << vcl_flush;
00136
00137 laplace_ = brip_vil_float_ops::fast_extrema(img, laplace_radius_,
00138 laplace_radius_, bright, mag_,
00139 false,false, signed_response_,
00140 scale_invariant, false,
00141 cutoff_per_);
00142
00143 vcl_cout<< "s = ("<< gauss_radius_<< ' ' << gauss_radius_ << ")\n"
00144 << vcl_flush;
00145 gauss_ = brip_vil_float_ops::gaussian(img, gauss_radius_);
00146 for (unsigned j = 0; j<gauss_.nj(); ++j)
00147 for (unsigned i = 0; i<gauss_.ni(); ++i)
00148 gauss_(i,j) =gauss_(i,j)*0.03f;
00149
00150 vcl_cout << "Computed filter bank in " << t.real()/1000.0 << " secs.\n";
00151 #if 0
00152
00153 vil_save(filter_responses_.response(0), "e:/images/TextureTraining/s0.tiff");
00154 vil_save(filter_responses_.response(1), "e:/images/TextureTraining/s1.tiff");
00155 vil_save(filter_responses_.response(2), "e:/images/TextureTraining/s2.tiff");
00156 vil_save(laplace_, "e:/images/TextureTraining/laplace.tiff");
00157 vil_save(gauss_, "e:/images/TextureTraining/gauss.tiff");
00158
00159 #endif
00160 return true;
00161 }
00162
00163
00164
00165 vcl_vector<vnl_vector<double> > sdet_texture_classifier::
00166 random_centers(vcl_vector<vnl_vector<double> > const& training_data,
00167 unsigned k) const{
00168 double n = training_data.size();
00169 vcl_vector<vnl_vector<double> > rand_centers(k);
00170 for (unsigned i = 0; i<k; ++i) {
00171 unsigned index = static_cast<unsigned>((n-1)*(vcl_rand()/(RAND_MAX+1.0)));
00172 rand_centers[i] = training_data[index];
00173 }
00174 return rand_centers;
00175 }
00176
00177
00178
00179 bool sdet_texture_classifier::compute_training_data(vcl_string const& category)
00180 {
00181
00182 unsigned dim = filter_responses_.n_levels();
00183 if (!dim) {
00184 vcl_cout << "zero dimensional filter bank\n" << vcl_flush;
00185 return false;
00186 }
00187 vul_timer t;
00188
00189 unsigned maxr = this->max_filter_radius();
00190 vcl_vector<vnl_vector<double> > training_data, sampled_data;
00191
00192 int ni = filter_responses_.ni()-maxr;
00193 int nj = filter_responses_.nj()-maxr;
00194 if (ni<=0||nj<=0) {
00195 vcl_cout << "training image too small ni or nj <= " << maxr << '\n';
00196 return false;
00197 }
00198 vcl_cout << " texton dimension: " << dim + 2 << '\n' << vcl_flush;
00199 for (int j = maxr; j<nj; ++j)
00200 for (int i = maxr; i<ni; ++i) {
00201 vnl_vector<double> tx(dim+2);
00202 for (unsigned f = 0; f<dim; ++f)
00203 tx[f]=filter_responses_.response(f)(i,j);
00204 tx[dim]=laplace_(i,j); tx[dim+1]=gauss_(i,j);
00205 training_data.push_back(tx);
00206 }
00207
00208 unsigned ns = training_data.size();
00209 if (ns>n_samples_) {
00210 for (unsigned i = 0; i<n_samples_; ++i) {
00211 unsigned s = static_cast<unsigned>((n_samples_-1)*(vcl_rand()/(RAND_MAX+1.0)));
00212 sampled_data.push_back(training_data[s]);
00213 }
00214 training_data.clear();
00215 training_data = sampled_data;
00216 }
00217 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::iterator dit;
00218 dit = training_data_.find(category);
00219 if (dit == training_data_.end()) {
00220 training_data_[category]=training_data;
00221 }
00222 else {
00223 training_data_[category].insert(training_data_[category].end(),
00224 training_data.begin(),
00225 training_data.end());
00226 }
00227 vcl_cout << "Collect texture samples in texture box region" << t.real()/1000.0 << " secs.\n";
00228 return true;
00229 }
00230
00231
00232
00233
00234 bool sdet_texture_classifier::compute_training_data(vcl_string const& category,
00235 vgl_polygon<double> const& texture_region)
00236 {
00237
00238 unsigned dim = filter_responses_.n_levels();
00239 if (!dim) {
00240 vcl_cout << "zero dimensional filter bank\n" << vcl_flush;
00241 return false;
00242 }
00243 vul_timer t;
00244
00245 unsigned maxr = this->max_filter_radius();
00246 vcl_vector<vnl_vector<double> > training_data, sampled_data;
00247
00248 int ni = filter_responses_.ni()-maxr;
00249 int nj = filter_responses_.nj()-maxr;
00250 if (ni<=0||nj<=0) {
00251 vcl_cout << "training image too small ni or nj <= " << maxr << '\n';
00252 return false;
00253 }
00254 vcl_cout << " texton dimension: " << dim + 2 << '\n' << vcl_flush;
00255 vgl_polygon_scan_iterator<double> psi(texture_region);
00256 for (psi.reset(); psi.next(); ) {
00257 int j = psi.scany();
00258 for (int i = psi.startx(); i <= psi.endx(); ++i) {
00259 vnl_vector<double> tx(dim+2);
00260 for (unsigned f = 0; f<dim; ++f)
00261 tx[f]=filter_responses_.response(f)(i,j);
00262 tx[dim]=laplace_(i,j); tx[dim+1]=gauss_(i,j);
00263 training_data.push_back(tx);
00264 }
00265 }
00266
00267 unsigned ns = training_data.size();
00268 if (ns>n_samples_) {
00269 for (unsigned i = 0; i<n_samples_; ++i) {
00270 unsigned s = static_cast<unsigned>((n_samples_-1)*(vcl_rand()/(RAND_MAX+1.0)));
00271 sampled_data.push_back(training_data[s]);
00272 }
00273 training_data.clear();
00274 training_data = sampled_data;
00275 }
00276 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::iterator dit;
00277 dit = training_data_.find(category);
00278 if (dit == training_data_.end()) {
00279 training_data_[category]=training_data;
00280 }
00281 else {
00282 training_data_[category].insert(training_data_[category].end(),
00283 training_data.begin(),
00284 training_data.end());
00285 }
00286 vcl_cout << "Collect texture samples in texture polygon in " << t.real()/1000.0 << " secs.\n";
00287 return true;
00288 }
00289
00290
00291
00292
00293 bool sdet_texture_classifier::
00294 compute_training_data(vcl_string const& category,
00295 vcl_vector<vgl_polygon<double> > const& texture_regions) {
00296
00297 unsigned dim = filter_responses_.n_levels();
00298 if (!dim) {
00299 vcl_cout << "zero dimensional filter bank\n" << vcl_flush;
00300 return false;
00301 }
00302 vul_timer t;
00303 vcl_cout << " texton dimension: " << dim + 2 << '\n' << vcl_flush;
00304
00305
00306 unsigned maxr = this->max_filter_radius();
00307 vcl_vector<vnl_vector<double> > training_data, sampled_data;
00308
00309 int ni = filter_responses_.ni()-maxr;
00310 int nj = filter_responses_.nj()-maxr;
00311 if (ni<=0||nj<=0) {
00312 vcl_cout << "training image too small ni or nj <= " << maxr << '\n';
00313 return false;
00314 }
00315 vcl_vector<vgl_polygon<double> >::const_iterator pit = texture_regions.begin();
00316 for (; pit != texture_regions.end(); ++pit) {
00317 vgl_polygon_scan_iterator<double> psi(*pit, false);
00318 for (psi.reset(); psi.next(); ) {
00319 int j = psi.scany();
00320 for (int i = psi.startx(); i <= psi.endx(); ++i) {
00321 vnl_vector<double> tx(dim+2);
00322 for (unsigned f = 0; f<dim; ++f)
00323 tx[f]=filter_responses_.response(f)(i,j);
00324 double g = gauss_(i,j);
00325 tx[dim]=laplace_(i,j); tx[dim+1]=g;
00326 training_data.push_back(tx);
00327 }
00328 }
00329 }
00330
00331 unsigned ns = training_data.size();
00332 if (ns>n_samples_) {
00333 for (unsigned i = 0; i<n_samples_; ++i) {
00334 unsigned s = static_cast<unsigned>((n_samples_-1)*(vcl_rand()/(RAND_MAX+1.0)));
00335 sampled_data.push_back(training_data[s]);
00336 }
00337 training_data.clear();
00338 training_data = sampled_data;
00339 }
00340 vcl_cout<< "Is category " << category << " in dictionary?\n" << vcl_flush;
00341 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::iterator dit;
00342 dit = training_data_.find(category);
00343 if (dit == training_data_.end()) {
00344 vcl_cout << " No, so adding new map entry with " << training_data.size()
00345 << " samples\n" << vcl_flush;
00346 training_data_[category]=training_data;
00347 }
00348 else {
00349 vcl_cout << "Yes, starting with " << training_data_[category].size()
00350 << " samples\n" << vcl_flush;
00351 training_data_[category].insert(training_data_[category].end(),
00352 training_data.begin(),
00353 training_data.end());
00354 vcl_cout << "after addition, training size is "
00355 << training_data_[category].size() << '\n' << vcl_flush;
00356 }
00357 vcl_cout << "Collect texture samples in texture polygon in " << t.real()/1000.0 << " secs.\n";
00358 return true;
00359 }
00360
00361 bool sdet_texture_classifier::compute_training_data(vcl_string const& category,
00362 vcl_string const& poly_path)
00363 {
00364 vcl_vector<vgl_polygon<double> > polys;
00365 if (poly_path=="") {
00366
00367 unsigned ni = laplace_.ni(), nj = laplace_.nj();
00368 vgl_polygon<double> temp; temp.new_sheet();
00369 temp.push_back(0.0, 0.0);
00370 temp.push_back(0.0, static_cast<double>(nj));
00371 temp.push_back(static_cast<double>(ni), static_cast<double>(nj));
00372 temp.push_back(static_cast<double>(ni), 0.0);
00373 polys.push_back(temp);
00374 }
00375 else {
00376 polys = load_polys(poly_path);
00377 }
00378 return this->compute_training_data(category, polys);
00379 }
00380
00381
00382
00383 bool sdet_texture_classifier::compute_textons(vcl_string const& category)
00384 {
00385 vul_timer t;
00386
00387 vcl_vector<vnl_vector<double> >& train_data = training_data_[category];
00388 vcl_cout << "Start k means for category " << category << " with sample size "
00389 << train_data.size() << '\n' << vcl_flush;
00390 vcl_vector<vnl_vector<double> > centers = random_centers(train_data, k_);
00391
00392 unsigned converged_k = k_;
00393 t.mark();
00394 unsigned n_iter = sdet_k_means(train_data, converged_k, ¢ers);
00395 vcl_cout << "After " << n_iter << " iterations found " << converged_k
00396 << " cluster centers\n";
00397 texton_dictionary_[category]=centers;
00398 vcl_cout << "Compute k means in " << t.real()/1000.0 << " secs.\n";
00399 return true;
00400 }
00401
00402 bool sdet_texture_classifier::
00403 compute_textons(vcl_vector<vcl_string> const& image_paths,
00404 vcl_string const& category,
00405 vcl_vector<vcl_string> const& poly_paths)
00406 {
00407 if (!image_paths.size()) {
00408 vcl_cout << " no images to compute textons\n";
00409 return false;
00410 }
00411
00412 vcl_vector<vil_image_view<float> > imgs;
00413 vcl_vector<vcl_vector< vgl_polygon<double> > >polys;
00414 unsigned i = 0;
00415 for (vcl_vector<vcl_string>::const_iterator pit = image_paths.begin();
00416 pit!=image_paths.end(); ++pit, ++i) {
00417 vil_image_resource_sptr resc = vil_load_image_resource((*pit).c_str());
00418 vil_image_view<float> view = scale_image(resc);
00419 imgs.push_back(view);
00420 unsigned ni = view.ni(), nj = view.nj();
00421 if (!poly_paths.size()||poly_paths[i]=="") {
00422
00423 vgl_polygon<double> temp; temp.new_sheet();
00424 temp.push_back(0.0, 0.0);
00425 temp.push_back(0.0, static_cast<double>(nj));
00426 temp.push_back(static_cast<double>(ni), static_cast<double>(nj));
00427 temp.push_back(static_cast<double>(ni), 0.0);
00428 vcl_vector< vgl_polygon<double> > vplys;
00429 vplys.push_back(temp);
00430 polys.push_back(vplys);
00431 }
00432 else {
00433 vcl_vector<vgl_polygon<double> > vpys = load_polys(poly_paths[i]);
00434 polys.push_back(vpys);
00435 }
00436 }
00437 unsigned n = i;
00438
00439
00440 for (unsigned i = 0; i<n; ++i) {
00441 double ni = imgs[i].ni(), nj = imgs[i].nj();
00442 vsol_box_2d_sptr box = new vsol_box_2d();
00443 vcl_vector<vgl_polygon<double> >& plist = polys[i];
00444 for (vcl_vector<vgl_polygon<double> >::iterator pit = plist.begin();
00445 pit != plist.end(); ++pit) {
00446 vcl_vector<vgl_point_2d<double> > sht = (*pit)[0];
00447 for (vcl_vector<vgl_point_2d<double> >::iterator xit = sht.begin();
00448 xit != sht.end(); ++xit)
00449 box->add_point(xit->x(), xit->y());
00450 }
00451
00452 double margin = this->max_filter_radius();
00453 double xmin = box->get_min_x()-margin, xmax = box->get_max_x()+margin;
00454 if (xmin<0) xmin = 0.0; if (xmax>=ni) xmax = ni-1.0;
00455 double ymin = box->get_min_y()-margin, ymax = box->get_max_y()+margin;
00456 if (ymin<0) ymin = 0.0; if (ymax>=nj) ymax = nj-1.0;
00457 unsigned i0=static_cast<unsigned>(xmin), j0 = static_cast<unsigned>(ymin);
00458 unsigned cni = static_cast<unsigned>(xmax-xmin+1.0);
00459 unsigned cnj = static_cast<unsigned>(ymax-ymin+1.0);
00460
00461 vil_image_view<float> cview = vil_crop(imgs[i], i0, cni, j0, cnj);
00462
00463 vcl_vector<vgl_polygon<double> > cplist;
00464 for (vcl_vector<vgl_polygon<double> >::iterator pit = plist.begin();
00465 pit != plist.end(); ++pit) {
00466 vgl_polygon<double> tpoly; tpoly.new_sheet();
00467 vcl_vector<vgl_point_2d<double> > sht = (*pit)[0];
00468 for (vcl_vector<vgl_point_2d<double> >::iterator xit = sht.begin();
00469 xit != sht.end(); ++xit) {
00470 double xp = xit->x()-xmin, yp = xit->y() - ymin;
00471 tpoly.push_back(xp, yp);
00472 }
00473 cplist.push_back(tpoly);
00474 }
00475 vcl_cout << "processing image(" << cview.ni() << ' ' << cview.nj() << ")\n" << vcl_flush;
00476 this->compute_filter_bank(cview);
00477 this->compute_training_data(category, cplist);
00478 }
00479 this->compute_textons(category);
00480 return true;
00481 }
00482
00483 bool sdet_texture_classifier::save_dictionary(vcl_string const& path) const
00484 {
00485 vsl_b_ofstream os(path.c_str());
00486 if (!os) {
00487 vcl_cout << "Can't open binary stream in save_dictionary\n";
00488 return false;
00489 }
00490 vcl_cout << "Save dictionary to " << path << '\n';
00491 sdet_texture_classifier_params const * tcp_ptr =
00492 dynamic_cast<sdet_texture_classifier_params const*>(this);
00493 vsl_b_write(os, *tcp_ptr);
00494 vsl_b_write(os, texton_dictionary_);
00495 vsl_b_write(os, category_histograms_);
00496 os.close();
00497 return true;
00498 }
00499
00500 bool sdet_texture_classifier::load_dictionary(vcl_string const& path)
00501 {
00502 vsl_b_ifstream is(path.c_str());
00503 if (!is) {
00504 vcl_cout << "Can't open binary stream in load_dictionary in " << path << vcl_endl;
00505 return false;
00506 }
00507 vcl_cout << "Loading texton dictionary: " << path << '\n' << vcl_flush;
00508 texton_dictionary_.clear();
00509 texton_index_.clear();
00510 category_histograms_.clear();
00511 sdet_texture_classifier_params* tcp_ptr
00512 = dynamic_cast<sdet_texture_classifier_params*>(this);
00513 vsl_b_read(is, *tcp_ptr);
00514 vsl_b_read(is, texton_dictionary_);
00515 this->compute_distances();
00516 this->compute_texton_index();
00517 vsl_b_read(is, category_histograms_);
00518 this->compute_texton_weights();
00519 this->compute_interclass_probs();
00520 is.close();
00521 return true;
00522 }
00523
00524 void sdet_texture_classifier::print_dictionary() const
00525 {
00526 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::const_iterator it= texton_dictionary_.begin();
00527 for (; it!= texton_dictionary_.end(); ++it) {
00528 vcl_cout << " ===Category: " << (*it).first << "===\n";
00529 for (unsigned i = 0; i<(*it).second.size(); ++i) {
00530 unsigned dim = (*it).second[0].size();
00531 vcl_cout << "c[" << i << "]:(";
00532 for (unsigned f = 0; f<dim; ++f)
00533 vcl_cout << (*it).second[i][f] << ' ';
00534 vcl_cout << ")\n" << vcl_flush;
00535 }
00536 }
00537 }
00538
00539
00540
00541
00542 void sdet_texture_classifier::compute_distances()
00543 {
00544 dist_.clear();
00545 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::const_iterator jt= texton_dictionary_.begin();
00546 for (; jt!= texton_dictionary_.end(); ++jt) {
00547 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::const_iterator it= texton_dictionary_.begin();
00548 for (; it!= texton_dictionary_.end(); ++it)
00549 if ((*it).first == (*jt).first) {
00550
00551 double max_dist = 0.0;
00552 for (unsigned j = 0; j<(*it).second.size(); ++j)
00553 for (unsigned i = 0; i<(*it).second.size(); ++i) {
00554 double d = vnl_vector_ssd((*it).second[i], (*jt).second[j]);
00555
00556 d = vcl_sqrt(d/(*it).second[i].size());
00557 if (d>max_dist) max_dist = d;
00558 }
00559 dist_[(*jt).first][(*it).first]=max_dist;
00560 }
00561 else {
00562
00563 double min_dist = vnl_numeric_traits<double>::maxval;
00564 for (unsigned j = 0; j<(*jt).second.size(); ++j)
00565 for (unsigned i = 0; i<(*it).second.size(); ++i) {
00566 double d = vnl_vector_ssd((*it).second[i],(*jt).second[j]);
00567
00568 d = vcl_sqrt(d/(*it).second[i].size());
00569 if (d<min_dist) min_dist = d;
00570 }
00571 dist_[(*jt).first][(*it).first]= min_dist;
00572 }
00573 }
00574 distances_valid_ = true;
00575 }
00576
00577
00578
00579
00580
00581 void sdet_texture_classifier::compute_interclass_probs()
00582 {
00583 inter_prob_.clear();
00584 vcl_map< vcl_string, vcl_vector<float> >::const_iterator jt=
00585 category_histograms_.begin();
00586 for (; jt!= category_histograms_.end(); ++jt) {
00587 vcl_vector<float> const & histj = (*jt).second;
00588 unsigned n = histj.size();
00589 float prob_total = 0.0f;
00590 vcl_map<vcl_string, vcl_vector<float> >::const_iterator it=
00591 category_histograms_.begin();
00592 for (; it!= category_histograms_.end(); ++it) {
00593 float prob_sum = 0.0f;
00594 vcl_vector<float> const& histi = (*it).second;
00595 for (unsigned j = 0; j<n; ++j)
00596 if (histj[j]>0.0f && histi[j]>0.0f)
00597 prob_sum += texton_weights_[j]*histj[j];
00598 inter_prob_[(*jt).first][(*it).first] = prob_sum;
00599 prob_total += prob_sum;
00600 }
00601 it=category_histograms_.begin();
00602 for (; it!= category_histograms_.end(); ++it)
00603 inter_prob_[(*jt).first][(*it).first] /= prob_total;
00604 }
00605 inter_prob_valid_ = true;
00606 }
00607
00608
00609
00610
00611
00612
00613
00614 static float w(float p, float off)
00615 {
00616 float t0 = -vcl_log(off), t1 = vcl_log(1.0f+off);
00617 float res = -vcl_log(1.0f - p + off) + t1;
00618 res /= (t0 + t1);
00619 return res;
00620 }
00621
00622
00623
00624
00625 void sdet_texture_classifier::compute_texton_weights()
00626 {
00627 if (texton_index_valid_) this->compute_texton_index();
00628 unsigned n = texton_index_.size();
00629 unsigned m = category_histograms_.size();
00630 texton_weights_.resize(n);
00631 vcl_vector<vcl_vector<float> > cross_category_probs(n);
00632 for (unsigned i = 0; i<n; ++i)
00633 cross_category_probs[i] = vcl_vector<float>(m, 0.0f);
00634
00635 vcl_map<vcl_string, vcl_vector<float> >::iterator hit = category_histograms_.begin();
00636 unsigned c = 0;
00637 for (; hit!=category_histograms_.end(); ++hit, ++c) {
00638 vcl_vector<float> const& h = (*hit).second;
00639 for (unsigned i = 0; i<n; ++i)
00640 if (h[i]>0.0f)
00641 cross_category_probs[i][c] += 1.0f;
00642 }
00643
00644 for (unsigned i = 0; i<n; ++i) {
00645 float sum = 0.0f;
00646 for (unsigned c = 0; c<m; ++c)
00647 sum += cross_category_probs[i][c];
00648
00649 for (unsigned c = 0; c<m; ++c)
00650 cross_category_probs[i][c] /= sum;
00651 }
00652 for (unsigned i = 0; i<n; ++i) {
00653 float maxp = 0.0f;
00654 for (unsigned c = 0; c<m; ++c)
00655 if (cross_category_probs[i][c]>maxp)
00656 maxp = cross_category_probs[i][c];
00657 texton_weights_[i]=w(maxp, weight_offset_);
00658 }
00659 texton_weights_valid_ = true;
00660 }
00661
00662 void sdet_texture_classifier::print_distances() const
00663 {
00664
00665
00666 sdet_texture_classifier* tc = const_cast<sdet_texture_classifier*>(this);
00667 if (!distances_valid_) tc->compute_distances();
00668
00669 vcl_cout << "Category distance matrix\n";
00670 vcl_map< vcl_string, vcl_map< vcl_string, double> >::const_iterator jt = dist_.begin();
00671 for (; jt != dist_.end(); ++jt) {
00672 vcl_cout << (*jt).first << " :\n";
00673 vcl_map< vcl_string, double>::const_iterator it = (*jt).second.begin();
00674 for (; it != (*jt).second.end(); ++it)
00675 vcl_cout << (*it).first << ":(" << (*it).second << ") ";
00676 vcl_cout << '\n';
00677 }
00678 }
00679
00680 void sdet_texture_classifier::print_category_histograms() const
00681 {
00682 unsigned nt = texton_index_.size();
00683 unsigned nh = category_histograms_.size();
00684 vcl_vector<vcl_string> cat_names(nh);
00685 vcl_vector<vcl_vector<float> > hists(nt, vcl_vector<float>(nh));
00686 vcl_map<vcl_string, vcl_vector<float> >::const_iterator hit = category_histograms_.begin();
00687 unsigned j = 0;
00688 for (; hit != category_histograms_.end(); ++hit, ++j) {
00689 cat_names[j]=(*hit).first;
00690 const vcl_vector<float>& h = (*hit).second;
00691 for (unsigned i = 0; i<h.size(); ++i)
00692 hists[i][j] = h[i];
00693 }
00694 for (unsigned j = 0; j<nh; ++j)
00695 vcl_cout << cat_names[j] << ' ';
00696 vcl_cout << '\n';
00697 for (unsigned i = 0; i<nt; ++i) {
00698 for (unsigned j = 0; j<nh; ++j)
00699 vcl_cout << hists[i][j] << ' ';
00700 vcl_cout << '\n';
00701 }
00702 }
00703
00704 void sdet_texture_classifier::print_interclass_probs() const
00705 {
00706 sdet_texture_classifier* ncthis = const_cast<sdet_texture_classifier*>(this);
00707 if (!inter_prob_valid_) ncthis->compute_interclass_probs();
00708 vcl_cout << "Interclass probabilities\n";
00709 vcl_map< vcl_string, vcl_map< vcl_string, double> >::const_iterator jt = inter_prob_.begin();
00710 for (; jt != inter_prob_.end(); ++jt) {
00711 vcl_cout << (*jt).first << " :\n";
00712 vcl_map< vcl_string, double>::const_iterator it = (*jt).second.begin();
00713 for (; it != (*jt).second.end(); ++it)
00714 vcl_cout << (*it).first << ":(" << (*it).second << ") ";
00715 vcl_cout << '\n';
00716 }
00717 }
00718
00719 void sdet_texture_classifier::print_texton_weights() const
00720 {
00721 sdet_texture_classifier* ncthis = const_cast<sdet_texture_classifier*>(this);
00722 if (!texton_weights_valid_) ncthis->compute_texton_weights();
00723 vcl_cout << "Texton weights ===>\n";
00724 for (vcl_vector<float>::const_iterator wit = texton_weights_.begin();
00725 wit != texton_weights_.end(); ++wit)
00726 vcl_cout << (*wit) << '\n';
00727 }
00728
00729
00730
00731
00732 void sdet_texture_classifier::compute_texton_index()
00733 {
00734 texton_index_.clear();
00735 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::const_iterator it= texton_dictionary_.begin();
00736 for (; it!= texton_dictionary_.end(); ++it) {
00737 vcl_string const& cat = (*it).first;
00738 vcl_vector<vnl_vector<double> > const & centers = (*it).second;
00739 for (vcl_vector<vnl_vector<double> >::const_iterator cit = centers.begin();
00740 cit != centers.end(); ++cit)
00741 texton_index_.push_back(sdet_neighbor(cat, (*cit)));
00742 }
00743 }
00744
00745 unsigned sdet_texture_classifier::nearest_texton_index(vnl_vector<double> const& query)
00746 {
00747 double min_dist = vnl_numeric_traits<double>::maxval;
00748 unsigned min_index = 0;
00749 unsigned n = texton_index_.size();
00750 for (unsigned i = 0; i<n; ++i) {
00751 double d = vnl_vector_ssd(texton_index_[i].k_mean_, query);
00752 if (d<min_dist) {
00753 min_dist = d;
00754 min_index = i;
00755 }
00756 }
00757 return min_index;
00758 }
00759
00760 void sdet_texture_classifier::compute_category_histograms()
00761 {
00762
00763
00764
00765 if (!texton_index_valid_) this->compute_texton_index();
00766 unsigned n = texton_index_.size();
00767 if (!n) {
00768 vcl_cout << "no textons to compute category histograms\n";
00769 return;
00770 }
00771 else
00772 vcl_cout << "computing category histograms with " << n << " textons\n";
00773 vcl_map< vcl_string, vcl_vector<vnl_vector<double> > >::iterator dit = training_data_.begin();
00774 for (; dit != training_data_.end(); ++dit) {
00775
00776 vcl_vector<float> hist(n, 0.0f);
00777 const vcl_string& cat = (*dit).first;
00778 const vcl_vector<vnl_vector<double> >& tdata = (*dit).second;
00779 unsigned ndata = tdata.size();
00780 vcl_cout << "h[" << cat << "](" << ndata << ") "<< vcl_flush;
00781 float weight = 1.0f/static_cast<float>(ndata);
00782
00783 for (vcl_vector<vnl_vector<double> >::const_iterator vit = tdata.begin();
00784 vit != tdata.end(); ++vit)
00785 this->update_hist(*vit, weight, hist);
00786
00787 category_histograms_[cat]=hist;
00788 }
00789 vcl_cout << '\n';
00790 }
00791
00792 void sdet_texture_classifier::
00793 update_hist(vnl_vector<double> const& f, float weight, vcl_vector<float>& hist)
00794 {
00795 unsigned indx = this->nearest_texton_index(f);
00796 hist[indx]+=weight;
00797 }
00798
00799 vcl_map<vcl_string, float> sdet_texture_classifier::
00800 texture_probabilities(vcl_vector<float> const& hist)
00801 {
00802 unsigned nt = texton_index_.size();
00803 vcl_map<vcl_string, float> probs;
00804 vcl_map<vcl_string, vcl_vector<float> >::iterator hit = category_histograms_.begin();
00805
00806 for (; hit != category_histograms_.end(); ++hit) {
00807 const vcl_string& cat = (*hit).first;
00808 const vcl_vector<float>& hc = (*hit).second;
00809 float prob_sum = 0.0f;
00810 float np = 0.0f;
00811 for (unsigned i = 0; i<nt; ++i) {
00812 float w = texton_weights_[i];
00813 np += w;
00814 float vc = hc[i]*w, vh = hist[i]*w;
00815 prob_sum += (vc<=vh)?vc:vh;
00816 }
00817 prob_sum /= np;
00818 probs[cat] = prob_sum;
00819 }
00820 return probs;
00821 }
00822
00823 void sdet_texture_classifier::
00824 category_color_mix(vcl_map<vcl_string, float> & probs,
00825 vnl_vector_fixed<float, 3>& color_mix) {
00826
00827 vcl_map<vcl_string, vcl_vector<float> >::iterator hit = category_histograms_.begin();
00828 float prob_sum = 0.0f;
00829 vnl_vector_fixed<float, 3> mix(0.0f);
00830 vcl_string max_cat;
00831 for (; hit != category_histograms_.end(); ++hit) {
00832 const vcl_string& cat = (*hit).first;
00833 float p = probs[cat];
00834 prob_sum += p;
00835 mix += p*color_map_[cat];
00836 }
00837 color_mix = mix/prob_sum;
00838 }
00839
00840
00841 #if 0 //=====debug====
00842 static bool required_block(int bidxu, int bidxv, int i,
00843 int j, int block_size, int margin)
00844 {
00845 int idxu = (i-margin)/block_size, idxv = (j-margin)/block_size;
00846 return bidxu == idxu && bidxv == idxv;
00847 }
00848 #endif // 0
00849
00850 vil_image_view<float> sdet_texture_classifier::
00851 classify_image_blocks(vcl_string const& img_path)
00852 {
00853 vul_timer t;
00854 vil_image_resource_sptr resc = vil_load_image_resource(img_path.c_str());
00855 vil_image_view<float> img = scale_image(resc);
00856 vcl_cout << "Classifying categories " << img_path << "\nsize(" << img.ni()
00857 << ' ' << img.nj() << ")pixels:[" << texton_dictionary_.size()
00858 << "]categories\n" << vcl_flush;
00859 if (!color_map_valid_)
00860 this->init_color_map();
00861 if (!texton_index_valid_)
00862 this->compute_texton_index();
00863 this->compute_filter_bank(img);
00864 unsigned dim = filter_responses_.n_levels();
00865 vcl_cout << "texton dimension " << dim +2<< '\n';
00866
00867 int margin = static_cast<int>(this->max_filter_radius());
00868 vcl_cout << "filter kernel margin " << margin << '\n';
00869 int ni = static_cast<int>(img.ni());
00870 int nj = static_cast<int>(img.nj());
00871 if ((ni-margin)<=0 || (nj-margin)<=0) {
00872 vcl_cout << "Image smaller than filter margin\n";
00873 return vil_image_view<float>(0, 0);
00874 }
00875 unsigned block_area = block_size_*block_size_;
00876 float weight = 1.0f/static_cast<float>(block_area);
00877 vil_image_view<float> prob(ni, nj, 3);
00878 prob.fill(0.5f);
00879 unsigned nh = texton_index_.size();
00880 int bidxv = 0;
00881 for (int j = margin; j<(nj-margin); j+=block_size_, ++bidxv) {
00882 int bidxu = 0;
00883 for (int i = margin; i<(ni-margin); i+=block_size_, ++bidxu) {
00884 vcl_vector<float> h(nh, 0.0f);
00885 for (unsigned r = 0; r<block_size_; ++r)
00886 for (unsigned c = 0; c<block_size_; ++c) {
00887 vnl_vector<double> temp(dim+2);
00888 for (unsigned f = 0; f<dim; ++f)
00889 temp[f]=filter_responses_.response(f)(i+c,j+r);
00890 temp[dim]=laplace_(i+c,j+r); temp[dim+1]=gauss_(i+c,j+r);
00891
00892
00893 this->update_hist(temp, weight, h);
00894 }
00895
00896 vcl_map<vcl_string, float> texture_probs = this->texture_probabilities(h);
00897
00898 #if 0 //=====debug====
00899 int ii = 7518, jj = 2909;
00900 if (required_block(bidxu, bidxv, ii, jj, block_size_, margin)) {
00901 vcl_cout << "probs(" << i << ' ' << j << ")\n";
00902 float psum = 0.0;
00903 for (vcl_map<vcl_string, float>::iterator cit = texture_probs.begin();
00904 cit != texture_probs.end(); ++cit)
00905 psum += (*cit).second;
00906
00907 for (vcl_map<vcl_string, float>::iterator cit =texture_probs.begin();
00908 cit != texture_probs.end(); ++cit)
00909 vcl_cout << (*cit).first << ' ' << ((*cit).second)/psum << '\n';
00910 #ifdef DEBUG
00911 vcl_cout << " hist\n";
00912 for (unsigned i = 0; i<nh; ++i)
00913 vcl_cout << h[i]<< '\n';
00914 #endif
00915 }
00916 #endif
00917 vnl_vector_fixed<float, 3> color;
00918
00919 this->category_color_mix(texture_probs, color);
00920 for (unsigned r = 0; r<block_size_; ++r)
00921 for (unsigned c = 0; c<block_size_; ++c)
00922 for (unsigned b = 0; b<3; ++b)
00923 prob(i+c,j+r,b) = color[b];
00924 }
00925 vcl_cout << '.' << vcl_flush;
00926 }
00927 vcl_cout << "\nBlock classification took " << t.real()/1000.0 << " seconds\n" << vcl_flush;
00928 return prob;
00929 }
00930
00931 unsigned sdet_texture_classifier::max_filter_radius() const
00932 {
00933 unsigned maxr = filter_responses_.invalid_border();
00934 unsigned lapr = gauss_radius(laplace_radius_, cutoff_per_);
00935 unsigned gr = gauss_radius(gauss_radius_, cutoff_per_);
00936 if (lapr>maxr) maxr = lapr;
00937 if (gr>maxr) maxr = gr;
00938 return maxr;
00939 }
00940
00941
00942
00943
00944
00945 void vsl_b_write(vsl_b_ostream & os, sdet_texture_classifier const &tc)
00946 { }
00947 void vsl_b_read(vsl_b_istream & is, sdet_texture_classifier &tc)
00948 { }
00949 void vsl_print_summary(vcl_ostream &os, const sdet_texture_classifier &tc)
00950 { }
00951 void vsl_b_read(vsl_b_istream& is, sdet_texture_classifier* tc)
00952 { }
00953 void vsl_b_write(vsl_b_ostream& os, const sdet_texture_classifier* &tc)
00954 { }
00955 void vsl_print_summary(vcl_ostream& os, const sdet_texture_classifier* &tc)
00956 { }
00957 void vsl_b_read(vsl_b_istream& is, sdet_texture_classifier_sptr& tc)
00958 { }
00959 void vsl_b_write(vsl_b_ostream& os, const sdet_texture_classifier_sptr &tc)
00960 { }
00961 void vsl_print_summary(vcl_ostream& os, const sdet_texture_classifier_sptr &tc)
00962 { }