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