/home/oan/prosjekt/gotools/segmentation/gpl_distro/lsseg_1.0_gpl/src/EigValComp3x3.C

Go to the documentation of this file.
00001 //===========================================================================
00002 // The Level-Set Segmentation Library (LSSEG)
00003 //
00004 //
00005 // Copyright (C) 2000-2005 SINTEF ICT, Applied Mathematics, Norway.
00006 //
00007 // This program is free software; you can redistribute it and/or          
00008 // modify it under the terms of the GNU General Public License            
00009 // as published by the Free Software Foundation version 2 of the License. 
00010 //
00011 // This program is distributed in the hope that it will be useful,        
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of         
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
00014 // GNU General Public License for more details.                           
00015 //
00016 // You should have received a copy of the GNU General Public License      
00017 // along with this program; if not, write to the Free Software            
00018 // Foundation, Inc.,                                                      
00019 // 59 Temple Place - Suite 330,                                           
00020 // Boston, MA  02111-1307, USA.                                           
00021 //
00022 // Contact information: e-mail: tor.dokken@sintef.no                      
00023 // SINTEF ICT, Department of Applied Mathematics,                         
00024 // P.O. Box 124 Blindern,                                                 
00025 // 0314 Oslo, Norway.                                                     
00026 // 
00027 //
00028 // Other licenses are also available for this software, notably licenses
00029 // for:
00030 // - Building commercial software.                                        
00031 // - Building software whose source code you wish to keep private.        
00032 //
00033 //===========================================================================
00034 //===========================================================================
00035 //                                                                           
00036 // File: EigValComp3x3.C                                                     
00037 //                                                                           
00038 // Created: Wed May  3 14:55:25 2006                                         
00039 //                                                                           
00040 // Author: Odd A. Andersen <Odd.Andersen@sintef.no>
00041 //                                                                           
00042 // Revision: $Id: EigValComp3x3.C,v 1.7 2006/11/22 15:23:00 oan Exp $
00043 //                                                                           
00044 // Description:
00047 //                                                                           
00048 //===========================================================================
00049 
00050 #include <assert.h>
00051 #include <math.h>
00052 #include "EigValComp3x3.h"
00053 #include <iostream>
00054 #include <limits>
00055 using namespace std;
00056 
00057 namespace { // anonymous namespace
00058     const double EPS = numeric_limits<double>::epsilon();
00059     const double PI = 3.1415926535897932384;
00060     const double THIRD = double(1)/3;
00061     const double TW7TH = double(1)/27;
00062 
00063     inline void xprod(const double* const u, const double* const v, double* res)
00064     {
00065         res[0] = u[1] * v[2] - u[2] * v[1];
00066         res[1] = u[2] * v[0] - u[0] * v[2];
00067         res[2] = u[0] * v[1] - u[1] * v[0];
00068     }
00069 
00070     inline double norm2(const double* v) {return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];}
00071 
00072     // do not call for a zero vector
00073     inline void normalize(double* v) 
00074     {
00075         double invnorm = double(1) / sqrt(norm2(v));
00076         for (int i = 0; i < 3; ++i) {
00077             v[i] *= invnorm;
00078         }
00079     }
00080 
00081     inline void find_non_collinear(const double* const v, double* res)
00082     {
00083         // heuristic for finding a unit vector that we know is not parallel to v
00084         int min_ix = fabs(v[0]) < fabs(v[1]) ? 0 : 1;
00085         min_ix = fabs(v[min_ix]) < fabs(v[2]) ? min_ix : 2;
00086 
00087         for (int i = 0; i < 3; ++i) {
00088             res[i] = (i == min_ix) ? 1 : 0;
00089         }
00090 
00091     }
00092 
00093     // only call if dimension of kernel is known to be 1 (not 0, 2 or 3)
00094     void find_kernel(const double* const col1, 
00095                      const double* const col2, 
00096                      const double* const col3, 
00097                      double* ker);
00098 
00099 inline void givens(const double a, const double b, double& c, double& s) 
00100 {
00101     if (b == 0) {
00102         c = 1;
00103         s = 0;
00104     } else {
00105         if (fabs(b) > fabs(a)) {
00106             const double tau = -a / b;
00107             s = 1 / sqrt(1 + tau * tau);
00108             c = tau * s;
00109         } else {
00110             const double tau = -b / a;
00111             c = 1 / sqrt(1 + tau * tau);
00112             s = tau * c;
00113         }
00114     }
00115 }
00116 
00117 void apply_givens(const double c, const double s, 
00118                   double& alpha1, double& alpha2, double& alpha3,
00119                   double& beta, double& delta, double& corner)
00120 {
00121     const double tmp1 = c*alpha1 - s*beta;
00122     const double tmp2 = c*beta - s*alpha2;
00123     const double tmp3 = s*alpha1 + c*beta;
00124     const double tmp4 = s*beta + c*alpha2;
00125     const double ctemp = corner;
00126     
00127     corner = -s * delta + c * ctemp;
00128     delta = c * delta + s * ctemp;
00129     
00130     alpha1 = c * tmp1 - s * tmp2;
00131     alpha2 = s * tmp3 + c * tmp4;
00132     beta = s * tmp1 + c * tmp2;
00133 }
00134 
00135 void apply_givens(const double c, const double s,
00136                   double& alpha1, double& alpha2, double& beta)
00137 {
00138     const double tmp1 = c*alpha1 - s*beta;
00139     const double tmp2 = c*beta - s*alpha2;
00140     const double tmp3 = s*alpha1 + c*beta;
00141     const double tmp4 = s*beta + c*alpha2;
00142 
00143     alpha1 = c * tmp1 - s * tmp2;
00144     alpha2 = s * tmp3 + c * tmp4;
00145     beta = s * tmp1 + c * tmp2;
00146 }
00147 
00148 void post_mul_givens(const double c, const double s, double* u, double* v)
00149 {
00150     const double u0 = u[0];
00151     const double u1 = u[1];
00152     const double u2 = u[2];
00153     const double v0 = v[0];
00154     const double v1 = v[1];
00155     const double v2 = v[2];
00156 
00157     u[0] = c * u0 - s * v0;
00158     u[1] = c * u1 - s * v1;
00159     u[2] = c * u2 - s * v2;
00160     v[0] = s * u0 + c * v0;
00161     v[1] = s * u1 + c * v1;
00162     v[2] = s * u2 + c * v2;
00163 }
00164 
00165 void construct_eigvecs(double alpha1, double alpha2, double alpha3,
00166                        double beta, double gamma, double delta,
00167                        double& lambda1, 
00168                        double& lambda2, 
00169                        double& lambda3,
00170                        double* v1, double* v2, double* v3);
00171 
00172 }; // end anonymous namespace 
00173 
00174 namespace lsseg {
00175 
00176 //===========================================================================
00177 void analytic_eigvals(double alpha1, double alpha2, double alpha3,
00178                       double beta, double gamma, double delta,
00179                       double& lambda1, double& lambda2, double& lambda3)
00180 //===========================================================================
00181 {
00182     // computing coefficients of characteristic polynomial, a_2, a_1 and a_0
00183     const double bgd = beta * gamma * delta;
00184     const double beta2 = beta * beta;
00185     const double gamma2 = gamma * gamma;
00186     const double delta2 = delta * delta;
00187    
00188     const double a_2 = -(alpha1 + alpha2 + alpha3);  // coef. of x^2
00189     const double a_1 = 
00190         -1*(beta2 + gamma2 + delta2 - alpha1*alpha2 - alpha2*alpha3 - alpha1*alpha3);  // coef. of x
00191     const double a_0 = 
00192         -1*(alpha1*alpha2*alpha3 - alpha1*delta2 - alpha2*gamma2 - alpha3*beta2 + 2*bgd);  // coef. of 1
00193 
00194     
00195     //cout << a_0 << " " << a_1 << " " <<a_2 << endl;
00196     // computing the three real roots.  Formula is taken from 
00197     // http://mathworld.wolfram.com/CubicFormula.html
00198 
00199     const double a_2_sq = a_2 * a_2;
00200     const double a_2_cub = a_2_sq * a_2;
00201 
00202     const double p = a_1 - (a_2_sq) * THIRD;
00203 
00204     //cout << "p: " << p << endl;
00205     const double q = -a_0 + (9 * a_1 * a_2 - 2 * a_2_cub) * TW7TH;
00206 
00207     const double fac = fabs(p) * THIRD;
00208     
00209     double C = (fac > 0) ? 0.5 * q * pow(double(1)/fac, 1.5) : 1;
00210     //cout << "C: " << C << endl;
00211     
00212     // according to theory, |C| should be less or equal to one (since our symmetric matrix
00213     // guarantee us no complex roots).  To make sure numerical
00214     // rounding errors do not cause pain in the acos function below, we assure this here:
00215 
00216     const double acos_C = (fabs(C) <= 1) ? acos(C) : C > 0 ? 0 : PI;
00217     const double y_1 = cos (THIRD * acos_C);
00218     const double y_2 = cos (THIRD * (acos_C + 2 * PI));
00219     const double y_3 = cos (THIRD * (acos_C + 4 * PI));
00220 
00221     //cout << "y1, y2, y3: " << y_1 << " " << y_2 << " " << y_3 << endl;
00222 
00223     const double tmp1 = 2 * sqrt(fac);
00224     const double tmp2 = THIRD * a_2;
00225 
00226     lambda1 = tmp1 * y_1 - tmp2;
00227     lambda2 = tmp1 * y_2 - tmp2;
00228     lambda3 = tmp1 * y_3 - tmp2;
00229 }
00230 
00231 //===========================================================================
00232 void analytic_eigsys(double alpha1, double alpha2, double alpha3,
00233                      double beta, double gamma, double delta,
00234                      double& lambda1, double& lambda2, double& lambda3,
00235                      double* v1, double* v2, double* v3)
00236 //===========================================================================
00237 {
00238     // computing eigenvalues (analytically)
00239     analytic_eigvals(alpha1, alpha2, alpha3, beta, gamma, delta, lambda1, lambda2, lambda3);
00240     construct_eigvecs(alpha1, alpha2, alpha3, beta, gamma, delta, lambda1, lambda2, lambda3,
00241                       v1, v2, v3);
00242 }
00243 
00244 //===========================================================================
00245 void numeric_eigsys(double alpha1, double alpha2, double alpha3,
00246                     double beta, double gamma, double delta,
00247                     double& lambda1, double& lambda2, double& lambda3,
00248                     double* u, double* v, double* w)
00249 //===========================================================================
00250 {
00251     const bool compute_eigvecs = u && v && w;
00252 
00253     // reduce to tridiagonal form ("get rid of gamma") by using Householder
00254     bool apply_tridiagonalization = fabs(gamma) > EPS * (fabs(alpha1) + fabs(alpha2) + fabs(alpha3)) * THIRD;
00255     double root_sigma, u_1, u_2, H_inv,p_1, p_2, p_3, K, q_1, q_2, q_3;
00256     if (apply_tridiagonalization) {
00257         root_sigma = sqrt(gamma*gamma + delta*delta);
00258         u_1 = gamma; // @@
00259         u_2 = (delta > 0) ? delta + root_sigma : delta - root_sigma;
00260         // u_3 is 0 and we do not take it into consideration
00261         H_inv = double(2) / (u_1 * u_1 + u_2 * u_2);
00262         p_1 = H_inv * (alpha1 * u_1 + beta * u_2);
00263         p_2 = H_inv * (beta * u_1 + alpha2 * u_2);
00264         p_3 = H_inv * (gamma * u_1 + delta * u_2);
00265         K = 0.5 * H_inv * (u_1 * p_1 + u_2 * p_2);
00266         q_1 = p_1 - K * u_1;
00267         q_2 = p_2 - K * u_2;
00268         q_3 = p_3;
00269         
00270         // A_tridiag = A - q*u' - u*q'
00271         alpha1 -= 2 * (q_1 * u_1);
00272         alpha2 -= 2 * (q_2 * u_2);
00273         // alpha3 not modified since u_3 = 0
00274         beta -= (q_1 * u_2 + u_1 * q_2);
00275         gamma -= q_3 * u_1;  // (u_3 is 0, so omit term q_1 * u_3...
00276         delta -= q_3 * u_2;  // idem
00277         
00278         //cout << alpha1 << " " << alpha2 << " " << alpha3 << endl;
00279         //cout << beta << " " << gamma << " " << delta << endl;
00280     } else {
00281         gamma = 0; // from now, we consider gamma identically zero
00282     }
00283 
00284     double corner = 0;
00285     bool beta_zero = fabs(beta) <= EPS * (fabs(alpha1) + fabs(alpha2));
00286     bool delta_zero = fabs(delta) <= EPS * fabs(alpha3);
00287     double d, sign_d, mu, c1, s1, c2, s2;
00288     if (compute_eigvecs) {
00289         u[0] = 1; u[1] = 0; u[2] = 0;
00290         v[0] = 0; v[1] = 1; v[2] = 0;
00291         w[0] = 0; w[1] = 0; w[2] = 1;
00292     }
00293 
00294     while(true) {
00295         if (!beta_zero && !delta_zero) { // neither beta nor delta are zero
00296 
00297             d = (alpha2 - alpha3) * 0.5;
00298             sign_d = d > 0 ? 1 : -1;
00299             mu = alpha3 - delta * delta / (d + sign_d * sqrt(d*d + delta*delta));
00300             
00301             givens(alpha1-mu, beta, c1, s1);
00302             apply_givens(c1,s1,alpha1, alpha2, alpha3, beta, delta, corner);
00303             beta_zero = fabs(beta) <= EPS * (fabs(alpha1) + fabs(alpha2));
00304             
00305             givens(beta, corner, c2, s2);
00306             apply_givens(c2, s2, alpha2, alpha3, alpha1, delta, corner, beta);
00307             delta_zero = fabs(delta) <= EPS * (fabs(alpha3));
00308 
00309             if (compute_eigvecs) {
00310                 post_mul_givens(c1, s1, u, v);
00311                 post_mul_givens(c2, s2, v, w);
00312             }
00313 
00314         } else if (!beta_zero) { // delta is zero, but not beta
00315 
00316             d = (alpha1 - alpha2) * 0.5;
00317             sign_d = d > 0 ? 1 : -1;
00318             mu = alpha2 - beta * beta / (d + sign_d * sqrt(d*d + beta*beta));
00319             givens(alpha1 - mu, beta, c1, s1);
00320             apply_givens(c1, s1, alpha1, alpha2, beta);
00321             beta_zero = fabs(beta) <= EPS * (fabs(alpha1) + fabs(alpha2));
00322             if (compute_eigvecs) {
00323                 post_mul_givens(c1, s1, u, v);
00324             }
00325             
00326         } else if (!delta_zero) { // beta is zero, but not delta
00327 
00328             d = (alpha2 - alpha3) * 0.5;
00329             sign_d = d > 0 ? 1 : -1;
00330             mu = alpha3 - delta * delta / (d + sign_d * sqrt(d*d + delta*delta));
00331             
00332             givens(alpha2 - mu, delta, c2, s2);
00333             apply_givens(c2, s2, alpha2, alpha3, delta);
00334             delta_zero = fabs(delta) <= EPS * (fabs(alpha3));
00335             if (compute_eigvecs) {
00336                 post_mul_givens(c2, s2, v, w);
00337             }
00338         } else { // both delta and beta are zero
00339             break;
00340         }
00341     }
00342     lambda1 = alpha1;
00343     lambda2 = alpha2;
00344     lambda3 = alpha3;
00345 
00346     // transforming eigenvectors for the tridiagonal matrix to the eigenvectors
00347     // for the original matrix
00348     if (compute_eigvecs && apply_tridiagonalization) {
00349         const double fac_u = H_inv * (u[0] * u_1 + u[1] * u_2);
00350         u[0] -= fac_u * u_1;
00351         u[1] -= fac_u * u_2;
00352 
00353         const double fac_v = H_inv * (v[0] * u_1 + v[1] * u_2);
00354         v[0] -= fac_v * u_1;
00355         v[1] -= fac_v * u_2;
00356 
00357         const double fac_w = H_inv * (w[0] * u_1 + w[1] * u_2);
00358         w[0] -= fac_w * u_1;
00359         w[1] -= fac_w * u_2;
00360     }
00361 }
00362 
00363 }; // end namespace lsseg
00364 
00365 namespace {
00366 
00367 double tempvec1[3];
00368 double tempvec2[3];
00369 double tempvec3[3];
00370 
00371 // only call if dimension of kernel is known to be 1 (not 0, 2 or 3)
00372 void find_kernel(const double* const col1, 
00373                  const double* const col2, 
00374                  const double* const col3, 
00375                  double* ker)
00376 {
00377     xprod(col1, col2, tempvec1);
00378     xprod(col2, col3, tempvec2);
00379     xprod(col1, col3, tempvec3);
00380 
00381     double n1 = norm2(tempvec1);
00382     double n2 = norm2(tempvec2);
00383     double n3 = norm2(tempvec3);
00384 
00385     if (n1 > n2) {
00386         if (n1 > n3) {
00387             // n1 is biggest
00388             n1 = double(1) / sqrt(n1);
00389             for (int i = 0; i < 3; ker[i] = tempvec1[i++] * n1);
00390         } else {
00391             // n3 is biggest
00392             n3 = double(1) / sqrt(n3);
00393             for (int i = 0; i < 3; ker[i] = tempvec3[i++] * n3);
00394         }
00395     } else {
00396         if (n2 > n3) {
00397             // n2 is biggest
00398             n2 = double(1) / sqrt(n2);
00399             for (int i = 0; i < 3; ker[i] = tempvec2[i++] * n2);
00400         } else {
00401             // n3 is biggest
00402             n3 = double(1) / sqrt(n3);
00403             for (int i = 0; i < 3; ker[i] = tempvec3[i++] * n3);
00404         }
00405     }
00406 }
00407 
00408 //===========================================================================
00409 void construct_eigvecs(double alpha1, double alpha2, double alpha3,
00410                        double beta, double gamma, double delta,
00411                        double& lambda1, 
00412                        double& lambda2, 
00413                        double& lambda3,
00414                        double* v1, double* v2, double* v3)
00415 //===========================================================================
00416 {
00417     // sorting eigenvals
00418     if (fabs(lambda3) > fabs(lambda2)) swap(lambda3, lambda2);
00419     if (fabs(lambda2) > fabs(lambda1)) {
00420         swap(lambda2, lambda1);
00421         if (fabs(lambda3) > fabs(lambda2)) swap(lambda3, lambda2);
00422     }
00423     // we have now fabs(lambda1) >= fabs(lambda2) >= fabs(lambda3)
00424 
00425     // constructing eigenvectors
00426     double col1[3], col2[3], col3[3];
00427     double tmp[3];
00428     const double numeric_threshold =  EPS * (fabs(alpha1) + fabs(alpha2) + fabs(alpha3));
00429     
00430     //if (fabs(lambda1 - lambda2) > EPS * (fabs(lambda1) + fabs(lambda2)) * 0.5) {
00431     if (fabs(lambda1 - lambda2) > numeric_threshold) {
00432 
00433         col1[0] = alpha1-lambda1; col1[1] = beta;            col1[2] = gamma;
00434         col2[0] = beta;           col2[1] = alpha2-lambda1 ; col2[2] = delta;
00435         col3[0] = gamma;          col3[1] = delta;           col3[2] = alpha3-lambda1;
00436         find_kernel(col1, col2, col3, v1);
00437 
00438         if (fabs(lambda2 - lambda3) > numeric_threshold) {
00439 
00440             // lambda1 > lambda2 > lambda3
00441             col1[0] = alpha1-lambda2; col2[1] = alpha2-lambda2 ; col3[2] = alpha3-lambda2;
00442             find_kernel(col1, col2, col3, v2);
00443 
00444             col1[0] = alpha1-lambda3; col2[1] = alpha2-lambda3 ; col3[2] = alpha3-lambda3;
00445             find_kernel(col1, col2, col3, v3);
00446 
00447         } else {
00448             //lambda1 > lambda2 == lambda3
00449 
00450             find_non_collinear(v1, tmp);
00451             
00452             // finding vector of space perpendicular to v1
00453             xprod(v1, tmp, v2);
00454             normalize(v2);
00455             xprod(v1, v2, v3);
00456             normalize(v3);
00457         }
00458     } else if (fabs(lambda1 - lambda3) > numeric_threshold) {
00459         // lambda1 == lambda2 > lambda3
00460 
00461         col1[0] = alpha1-lambda3; col1[1] = beta;            col1[2] = gamma;
00462         col2[0] = beta;           col2[1] = alpha2-lambda3 ; col2[2] = delta;
00463         col3[0] = gamma;          col3[1] = delta;           col3[2] = alpha3-lambda3;
00464         find_kernel(col1, col2, col3, v3);
00465 
00466         find_non_collinear(v3, tmp);
00467         
00468         // finding vector of space perpendicular to v3
00469         xprod(v3, tmp, v1);
00470         normalize(v1);
00471         xprod(v3, v1, v2);
00472         normalize(v2);
00473 
00474     } else {
00475         // lambda1 == lambda2 == lambda3
00476         v1[0] = 1; v1[1] = 0; v1[2] = 0;
00477         v2[0] = 0; v2[1] = 1; v2[2] = 0;
00478         v3[0] = 0; v3[1] = 0; v3[2] = 1;
00479     }
00480 }
00481 
00482 };

Generated on Tue Nov 28 18:35:47 2006 for lsseg by  doxygen 1.4.7