contrib/mul/msm/tools/msm_draw_shape_modes.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \brief Tool to write eps files showing modes of model
00004 // \author Tim Cootes
00005 
00006 #include <mbl/mbl_read_props.h>
00007 #include <mbl/mbl_exception.h>
00008 #include <vul/vul_arg.h>
00009 #include <vul/vul_string.h>
00010 #include <vcl_sstream.h>
00011 #include <vcl_fstream.h>
00012 #include <vcl_string.h>
00013 #include <vcl_algorithm.h>
00014 #include <vsl/vsl_quick_file.h>
00015 
00016 #include <msm/msm_shape_model.h>
00017 #include <msm/msm_shape_instance.h>
00018 #include <msm/msm_curve.h>
00019 
00020 #include <msm/msm_add_all_loaders.h>
00021 #include <msm/utils/msm_draw_shape_to_eps.h>
00022 #include <msm/utils/msm_shape_mode_view.h>
00023 
00024 /*
00025 Parameter file format:
00026 <START FILE>
00027 //: File to load model from
00028 shape_model_path: shape_model.bfs
00029 
00030 curves_path: face_front.crvs
00031 
00032 // Maximum number of shape modes
00033 max_modes: 99
00034 
00035 //: Number of SDs to vary mode by
00036 n_sds: 3.0
00037 
00038 //: Current number of shapes per mode
00039 n_per_mode: 3
00040 
00041 //: When true, generate 2*n_per_mode frames of a movie for each mode
00042 make_movie: false
00043 
00044 //: When true, overlap all the shapes to highlight changes
00045 //  Shapes on one side of mean are drawn with dashes, on the other with dots.
00046 overlap_shapes: false
00047 
00048 //: Radius of points to display (if <0, then don't draw points)
00049 point_radius: 2
00050 
00051 //: Line width
00052 line-width: 1
00053 
00054 line_colour: black
00055 point_colour: red
00056 
00057 // Approximate width of region to display shape
00058 width: 100
00059 
00060 // Optional paths to text files containing a matrix and vector
00061 // defining a linear subspace of the parameter space.
00062 // When supplied, the tool will generate the shapes by
00063 // moving along the modes defined in the subspace.
00064 // A subspace model of the form x=mean+(PQ)b is created,
00065 // where P is the matrix of original modes, Q is the loaded matrix
00066 // defining the subspace.  so each column defines a subspace mode.
00067 // subspace_mode_path: subspace_mode_matrix.txt
00068 // subspace_var_path: subspace_var_vector.txt
00069 
00070 base_name: mode
00071 output_dir: ./
00072 
00073 <END FILE>
00074 */
00075 
00076 void print_usage()
00077 {
00078   vcl_cout << "msm_draw_shape_modes -p param_file\n"
00079            << "Tool to write eps files showing modes of model.\n"
00080            << vcl_endl;
00081 
00082   vul_arg_display_usage_and_exit();
00083 }
00084 
00085 //: Structure to hold parameters
00086 struct tool_params
00087 {
00088   //: Path to shape model
00089   vcl_string shape_model_path;
00090   vcl_string curves_path;
00091 
00092   //: Maximum number of shape modes
00093   unsigned max_modes;
00094 
00095   //: Number of SDs to vary mode by
00096   double n_sds;
00097 
00098   //: Current number of shapes per mode
00099   unsigned n_per_mode;
00100 
00101   //: When true, generate 2*n_per_mode frames of a movie for each mode
00102   bool make_movie;
00103 
00104   //: When true, overlap all the shapes to highlight changes
00105   bool overlap_shapes;
00106 
00107   //: Approximate width of region to display shape
00108   double width;
00109 
00110   //: Optional path to text file containing a mode matrix
00111   vcl_string subspace_mode_path;
00112 
00113   //: Optional path to text files containing a variance vector
00114   vcl_string subspace_var_path;
00115 
00116   //: Base name for output
00117   vcl_string base_name;
00118 
00119   //: Directory to save eps files
00120   vcl_string output_dir;
00121 
00122   //: Line colour
00123   vcl_string line_colour;
00124 
00125   //: Point colour
00126   vcl_string point_colour;
00127 
00128   //: Radius of points to display (if <0, then don't draw points)
00129   double point_radius;
00130 
00131   //: Line width
00132   double line_width;
00133 
00134   //: Parse named text file to read in data
00135   //  Throws a mbl_exception_parse_error if fails
00136   void read_from_file(const vcl_string& path);
00137 };
00138 
00139 //: Parse named text file to read in data
00140 //  Throws a mbl_exception_parse_error if fails
00141 void tool_params::read_from_file(const vcl_string& path)
00142 {
00143   vcl_ifstream ifs(path.c_str());
00144   if (!ifs)
00145   {
00146     vcl_string error_msg = "Failed to open file: "+path;
00147     throw (mbl_exception_parse_error(error_msg));
00148   }
00149 
00150   mbl_read_props_type props = mbl_read_props_ws(ifs);
00151 
00152   curves_path=props["curves_path"];
00153   n_per_mode=vul_string_atoi(props.get_optional_property("n_per_mode","3"));
00154   max_modes=vul_string_atoi(props.get_optional_property("max_modes","5"));
00155   overlap_shapes=vul_string_to_bool(props.get_optional_property("overlap_shapes","false"));
00156   make_movie=vul_string_to_bool(props.get_optional_property("make_movie","false"));
00157   n_sds=vul_string_atof(props.get_optional_property("n_sds","3.0"));
00158   point_radius=vul_string_atof(props.get_optional_property("point_radius","1.5"));
00159   line_width=vul_string_atof(props.get_optional_property("line_width","1"));
00160   width=vul_string_atof(props.get_optional_property("width","100"));
00161   line_colour=props.get_optional_property("line_colour","black");
00162   point_colour=props.get_optional_property("point_colour","red");
00163   base_name=props.get_optional_property("base_name","./");
00164   output_dir=props.get_optional_property("output_dir","./");
00165   shape_model_path=props.get_optional_property("shape_model_path",
00166                                                "shape_aam.bfs");
00167 
00168   if (make_movie) overlap_shapes=true;
00169 
00170   subspace_mode_path = props["subspace_mode_path"];
00171   if (subspace_mode_path=="-") subspace_mode_path="";
00172 
00173   subspace_var_path = props["subspace_var_path"];
00174   if (subspace_var_path=="-") subspace_var_path="";
00175 
00176   // Don't look for unused props so can use a single common parameter file.
00177 }
00178 
00179 
00180 void draw_mode(msm_shape_mode_view& mode_view,
00181                const msm_curves& curves,
00182                tool_params& params)
00183 {
00184   mode_view.compute_shapes();
00185   unsigned m = mode_view.mode();
00186   vgl_box_2d<int> win_box = mode_view.display_window();
00187 
00188   vcl_stringstream ss;
00189   ss<<params.output_dir<<'/'<<params.base_name<<"_s"<<m<<".eps";
00190   mbl_eps_writer writer(ss.str().c_str(),
00191                         win_box.width(),win_box.height());
00192 
00193   unsigned n_shapes = mode_view.points().size();
00194   for (unsigned i=0;i<n_shapes;++i)
00195   {
00196     writer.set_colour(params.point_colour);
00197     if (params.point_radius>0)
00198       msm_draw_points_to_eps(writer,mode_view.points()[i],
00199                              params.point_radius);
00200     writer.set_colour(params.line_colour);
00201     writer.set_line_width(params.line_width);
00202     if (params.overlap_shapes)
00203     {
00204       // Use dashes for one side of mean, dots for the other
00205       if (i<n_shapes/2) writer.ofs()<<"[3 2] 0 setdash"<<vcl_endl;  // Dashes
00206       else
00207       if (i==n_shapes/2) writer.ofs()<<"[1 0] 0 setdash"<<vcl_endl;  // Solid
00208       else
00209         writer.ofs()<<"[1 2] 0 setdash"<<vcl_endl;  // Dots
00210     }
00211     msm_draw_shape_to_eps(writer,mode_view.points()[i],curves);
00212   }
00213   writer.close();
00214   vcl_cout<<"Saved to "<<ss.str()<<vcl_endl;
00215 }
00216 
00217 //: Write a set of eps files defining a movie of the mode.
00218 void draw_mode_frames(msm_shape_mode_view& mode_view,
00219                       const msm_curves& curves,
00220                       tool_params& params)
00221 {
00222   mode_view.compute_shapes();
00223   unsigned m = mode_view.mode();
00224   vgl_box_2d<int> win_box = mode_view.display_window();
00225 
00226   unsigned n_shapes = mode_view.points().size();
00227   for (unsigned i=0;i<2*n_shapes-2;++i)
00228   {
00229     // f goes from 0 - (n_shapes-1), then back down to 1
00230     unsigned f=i;
00231     if (i>=n_shapes) f=2*n_shapes-2-i;
00232 
00233     vcl_stringstream ss;
00234     ss<<params.output_dir<<'/'<<params.base_name<<"_s"<<m<<'_';
00235     if (i<10) ss<<'0';
00236     ss<<i<<".eps";
00237     mbl_eps_writer writer(ss.str().c_str(),
00238                           win_box.width(),win_box.height());
00239 
00240     writer.set_colour(params.point_colour);
00241     if (params.point_radius>0)
00242       msm_draw_points_to_eps(writer,mode_view.points()[f],
00243                              params.point_radius);
00244     writer.set_colour(params.line_colour);
00245     writer.set_line_width(params.line_width);
00246     msm_draw_shape_to_eps(writer,mode_view.points()[f],curves);
00247 
00248     writer.close();
00249   }
00250   vcl_cout<<"Saved "<<2*n_shapes-2<<" frames to "
00251           <<params.base_name<<"_s"<<m<<"_XX.eps"<<vcl_endl;
00252 }
00253 
00254 //: Create new model where modes are a subspace of the original model
00255 //  Loads in a matrix Q defining modes, and vector V defining variance
00256 //  Modes of new model are given by (PQ) where P are the modes of the
00257 //  original.
00258 msm_shape_model create_subspace_model(
00259             const msm_shape_model& shape_model,
00260             const vcl_string& mode_path,
00261             const vcl_string& var_path)
00262 {
00263   // Attempt to load in modes
00264   vnl_matrix<double> Q;
00265   vcl_ifstream Qs(mode_path.c_str());
00266   if (!Qs)
00267   {
00268     vcl_cout<<"Failed to open "<<mode_path<<vcl_endl;
00269     vcl_abort();
00270   }
00271 
00272   if (!Q.read_ascii(Qs))
00273   {
00274     vcl_cerr<<"Failed to read matrix from "<<mode_path<<'\n';
00275     vcl_abort();
00276   }
00277   Qs.close();
00278 
00279   vnl_vector<double> new_var;
00280   vcl_ifstream Vs(var_path.c_str());
00281   if (!Vs)
00282   {
00283     vcl_cout<<"Failed to open "<<var_path<<vcl_endl;
00284     vcl_abort();
00285   }
00286   if (!new_var.read_ascii(Vs))
00287   {
00288     vcl_cerr<<"Failed to read vector from "<<var_path<<'\n';
00289     vcl_abort();
00290   }
00291   Vs.close();
00292 
00293 vcl_cout<<"new_var: "<<new_var<<vcl_endl;
00294 
00295   vcl_cerr<<"Number of subspace modes = "<<Q.columns()<<'\n';
00296 
00297   if (Q.columns()!=new_var.size())
00298   {
00299     vcl_cerr<<"Number of variances = "<<new_var.size()<<'\n'
00300             <<"Numbers differ."<<'\n';
00301     vcl_abort();
00302   }
00303 
00304   if (Q.rows()>shape_model.n_modes())
00305   {
00306     vcl_cerr<<"More rows in matrix than number of modes available."<<'\n';
00307     vcl_abort();
00308   }
00309 
00310   const vnl_matrix<double>& allP = shape_model.modes();
00311   vnl_matrix<double> subP = allP.extract(allP.rows(),Q.rows());
00312 
00313   vnl_matrix<double> newP = subP * Q;
00314 
00315   msm_shape_model new_model;
00316   new_model.set(shape_model.mean_points(),newP,new_var,
00317                 shape_model.default_pose(),
00318                 shape_model.aligner(),
00319                 shape_model.param_limiter());
00320 
00321   return new_model;
00322 }
00323 
00324 int main(int argc, char** argv)
00325 {
00326   vul_arg<vcl_string> param_path("-p","Parameter filename");
00327   vul_arg_parse(argc,argv);
00328 
00329   msm_add_all_loaders();
00330 
00331   if (param_path()=="")
00332   {
00333     print_usage();
00334     return 0;
00335   }
00336 
00337   tool_params params;
00338   try
00339   {
00340     params.read_from_file(param_path());
00341   }
00342   catch (mbl_exception_parse_error& e)
00343   {
00344     vcl_cerr<<"Error: "<<e.what()<<'\n';
00345     return 1;
00346   }
00347 
00348   msm_shape_model shape_model;
00349 
00350   if (!vsl_quick_file_load(shape_model,params.shape_model_path))
00351   {
00352     vcl_cerr<<"Failed to load shape model from "
00353             <<params.shape_model_path<<'\n';
00354     return 2;
00355   }
00356 
00357   vcl_cout<<"Model: "<<shape_model<<vcl_endl;
00358   vcl_cerr<<"First mode variances are ";
00359   for (unsigned i=0;i<8;++i)
00360     if (i<shape_model.n_modes())
00361       vcl_cout<<shape_model.mode_var()[i]<<' ';
00362   vcl_cout<<vcl_endl;
00363 
00364   if (params.subspace_mode_path!="")
00365     shape_model = create_subspace_model(shape_model,
00366                                         params.subspace_mode_path,
00367                                         params.subspace_var_path);
00368 
00369   msm_curves curves;
00370   if (!curves.read_text_file(params.curves_path))
00371     vcl_cerr<<"Failed to read in curves from "<<params.curves_path<<'\n';
00372 
00373   msm_shape_mode_view mode_view;
00374   mode_view.set_shape_model(shape_model);
00375 #if 0
00376   // Estimate aspect ratio
00377   vgl_box_2d<double> bounds = shape_model.mean_points().bounds();
00378   double asp=bounds.height()/(1e-3+bounds.width());
00379 
00380   int win_width;
00381   if (params.overlap_shapes)
00382     win_width=int(params.width);
00383   else
00384     win_width=int(params.width*params.n_per_mode);
00385 
00386   int win_height=int(asp*params.width);
00387   mode_view.set_display_window(vgl_box_2d<int>(0,win_width, 0,win_height));
00388 #endif // 0
00389   mode_view.set_range(params.n_sds);
00390   mode_view.set_n_per_mode(params.n_per_mode);
00391   mode_view.set_overlap_shapes(params.overlap_shapes);
00392   mode_view.set_display_width(params.width);
00393   vgl_box_2d<int> win_box = mode_view.display_window();
00394 
00395   unsigned n_modes = vcl_min(params.max_modes,shape_model.n_modes());
00396 
00397   for (unsigned m=0;m<n_modes;++m)
00398   {
00399     mode_view.set_mode(1+m);
00400 
00401     if (params.make_movie)
00402       draw_mode_frames(mode_view,curves,params);
00403     else
00404       draw_mode(mode_view,curves,params);
00405   }
00406   return 0;
00407 }