00001 // This is core/vgl/algo/vgl_conic_2d_regression.h 00002 #ifndef vgl_conic_2d_regression_h_ 00003 #define vgl_conic_2d_regression_h_ 00004 #ifdef VCL_NEEDS_PRAGMA_INTERFACE 00005 #pragma interface 00006 #endif 00007 //: 00008 // \file 00009 // \brief Fits a conic to a set of points using linear regression 00010 // \author J.L. Mundy 00011 // \date June 17, 2005 00012 // 00013 // Since conic fitting is rather ill-conditioned it is necessary to 00014 // normalize the point coordinates so that they have equal standard 00015 // deviations with respect to the center (mean) of the pointset. 00016 // The points are transformed to have zero mean. The resulting conic 00017 // is transformed back to the original frame for computing 00018 // the Sampson approximation to fitting error. 00019 // 00020 // The regression uses the Bookstein algorithm which constrains the 00021 // conic norm, $ |ax^2 + bxy + cy^2 + dx + ey +f|^2 $, so that 00022 // $ a^2 + 0.5*b^2 + c^2 = 2 $ 00023 // With this normalization, the resulting fit is invariant up to a similarity 00024 // transform of the pointset. The solution is formulated as an eigenvalue 00025 // problem as follows: 00026 // 00027 // The scatter matrix S is decomposed as 00028 // 00029 // $ S = \begin{array}{cc} S_{11} & S_{12} 00030 // \\ S_{21} & S_{22} \end{array}$ 00031 // note $S_{21} = S_{12}^t$ 00032 // 00033 // The conic coefficients are $v_1 = \{a,b,c\}^t$ and $v_2 = \{d,e,f\}^t$ 00034 // The Bookstein constraint is expressed by the diagonal matrix 00035 // D = Diag{1, 0.5, 1, 0, 0, 0} 00036 // The Lagrangian to be minimized is 00037 // $ L = v_1^t S_{11} v_1 + 2 v_2^t S_{21} v_1 + v_2^t S_{22} v_2 00038 // - \lambda(v_1^t D v_1 -2) $. 00039 // 00040 // Minimizing with respect to v2 gives 00041 // $ dL/dv_2 = 2 S_{21} v_1 + 2 S_{22} v_2 = 0 $ 00042 // 00043 // So, $ v_2 = -S_{22}^-1 S_{21} v_1 $. 00044 // 00045 // Substituting for v2 in L, 00046 // 00047 // $ L = v_1^t ( S_{11} - S_{12} * S_{22}^-1 * S_{21}) v_1 - \lambda(v_1^t D v_1 -2) 00048 // = v_1^t ( (S_{11} - S_{12} * S_{22}^-1 * S_{21}) - \lambda D ) v_1 - 2 \lambda $, 00049 // 00050 // $ dL/dv_1 = ( (S_{11} - S_{12} * S_{22}^-1 * S_{21}) - \lambda D ) v_1 = 0 $. 00051 // 00052 // So, $ \lambda v_1 = D^-1 (S_{11} - S_{12} * S_{22}^-1 * S_{21}) v_1 $. 00053 // 00054 // This eigenvalue problem is solved using singular value decomposition. 00055 // 00056 // \verbatim 00057 // Modifications 00058 // none 00059 // \endverbatim 00060 00061 #include <vcl_vector.h> 00062 #include <vcl_iostream.h> 00063 #include <vnl/vnl_matrix_fixed.h> 00064 #include <vgl/vgl_point_2d.h> 00065 #include <vgl/vgl_homg_point_2d.h> 00066 #include <vgl/vgl_conic.h> 00067 #include <vgl/algo/vgl_norm_trans_2d.h> 00068 00069 template <class T> 00070 class vgl_conic_2d_regression 00071 { 00072 public: 00073 00074 // Constructors/Initializers/Destructors------------------------------------- 00075 00076 vgl_conic_2d_regression(); 00077 00078 ~vgl_conic_2d_regression(){} 00079 00080 // Operations---------------------------------------------------------------- 00081 00082 00083 //: Number of regression points 00084 unsigned get_n_pts() const {return npts_;} 00085 00086 //: clear the regression data 00087 void clear_points(); 00088 00089 void add_point(vgl_point_2d<T> const& p); 00090 00091 void remove_point(vgl_point_2d<T> const& p); 00092 00093 //: get the estimated Euclidean error with respect to the fitted conic segment in the original frame 00094 T get_rms_error_est(vgl_point_2d<T> const& p) const; 00095 00096 //: get the current Euclidean fitting error in the original frame 00097 T get_rms_sampson_error() const { return sampson_error_; } //temporarily 00098 00099 //: get the current algebraic fitting error in the normalized frame 00100 T get_rms_algebraic_error() const; 00101 00102 //: the fitting method 00103 bool fit(); 00104 00105 // Data Access--------------------------------------------------------------- 00106 00107 vgl_conic<T> conic() const { return conic_; } 00108 00109 // Debug support 00110 void print_pointset(vcl_ostream& str = vcl_cout ); 00111 protected: 00112 // Internals 00113 void init(); 00114 void compute_partial_sums(); 00115 void fill_scatter_matrix(); 00116 void set_sampson_error(T a, T b, T c, T d, T e, T f); 00117 00118 // Data Members-------------------------------------------------------------- 00119 00120 //: The current set of points 00121 vcl_vector<vgl_point_2d<T> > points_; 00122 00123 //: Size of point set 00124 unsigned npts_; 00125 00126 //: The normalizing transformation 00127 vgl_norm_trans_2d<T> trans_; 00128 00129 //: The partial scatter term sums, updated with each ::add_point 00130 vcl_vector<T> partial_sums_; 00131 00132 //: The fitting matrices 00133 vnl_matrix_fixed<T,3,3> S11_, S12_, S22_; 00134 vnl_matrix_fixed<T,3,3> Dinv_; 00135 00136 //: The fitted conic in the un-normalized frame 00137 vgl_conic<T> conic_; 00138 00139 //: The algebraic fitting cost in the normalized frame 00140 T cost_; 00141 00142 //: The Sampson approximation to Euclidean distance in the normalized frame 00143 T sampson_error_; 00144 00145 //: Normalized points 00146 vcl_vector<vgl_homg_point_2d<T> > hnorm_points_; 00147 }; 00148 00149 #define VGL_CONIC_2D_REGRESSION_INSTANTIATE(T) extern "please include vgl/algo/vgl_conic_2d_regression.txx first" 00150 00151 #endif // vgl_conic_2d_regression_h_