Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of gr-peach-opencv-project-sd-card by
lda.cpp
00001 /* 00002 * Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>. 00003 * Released to public domain under terms of the BSD Simplified license. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * * Redistributions of source code must retain the above copyright 00008 * notice, this list of conditions and the following disclaimer. 00009 * * Redistributions in binary form must reproduce the above copyright 00010 * notice, this list of conditions and the following disclaimer in the 00011 * documentation and/or other materials provided with the distribution. 00012 * * Neither the name of the organization nor the names of its contributors 00013 * may be used to endorse or promote products derived from this software 00014 * without specific prior written permission. 00015 * 00016 * See <http://www.opensource.org/licenses/bsd-license> 00017 */ 00018 00019 #include "precomp.hpp" 00020 #include <iostream> 00021 #include <map> 00022 #include <set> 00023 00024 namespace cv 00025 { 00026 00027 // Removes duplicate elements in a given vector. 00028 template<typename _Tp> 00029 inline std::vector<_Tp> remove_dups(const std::vector<_Tp>& src) { 00030 typedef typename std::set<_Tp>::const_iterator constSetIterator; 00031 typedef typename std::vector<_Tp>::const_iterator constVecIterator; 00032 std::set<_Tp> set_elems; 00033 for (constVecIterator it = src.begin(); it != src.end(); ++it) 00034 set_elems.insert(*it); 00035 std::vector<_Tp> elems; 00036 for (constSetIterator it = set_elems.begin(); it != set_elems.end(); ++it) 00037 elems.push_back(*it); 00038 return elems; 00039 } 00040 00041 static Mat argsort(InputArray _src, bool ascending=true) 00042 { 00043 Mat src = _src.getMat(); 00044 if (src.rows != 1 && src.cols != 1) { 00045 String error_message = "Wrong shape of input matrix! Expected a matrix with one row or column."; 00046 CV_Error(Error::StsBadArg, error_message); 00047 } 00048 int flags = SORT_EVERY_ROW | (ascending ? SORT_ASCENDING : SORT_DESCENDING); 00049 Mat sorted_indices; 00050 sortIdx(src.reshape(1,1),sorted_indices,flags); 00051 return sorted_indices; 00052 } 00053 00054 static Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double beta=0) { 00055 // make sure the input data is a vector of matrices or vector of vector 00056 if(src.kind() != _InputArray::STD_VECTOR_MAT && src.kind() != _InputArray::STD_VECTOR_VECTOR) { 00057 String error_message = "The data is expected as InputArray::STD_VECTOR_MAT (a std::vector<Mat>) or _InputArray::STD_VECTOR_VECTOR (a std::vector< std::vector<...> >)."; 00058 CV_Error(Error::StsBadArg, error_message); 00059 } 00060 // number of samples 00061 size_t n = src.total(); 00062 // return empty matrix if no matrices given 00063 if(n == 0) 00064 return Mat(); 00065 // dimensionality of (reshaped) samples 00066 size_t d = src.getMat(0).total(); 00067 // create data matrix 00068 Mat data((int)n, (int)d, rtype); 00069 // now copy data 00070 for(int i = 0; i < (int)n; i++) { 00071 // make sure data can be reshaped, throw exception if not! 00072 if(src.getMat(i).total() != d) { 00073 String error_message = format("Wrong number of elements in matrix #%d! Expected %d was %d.", i, (int)d, (int)src.getMat(i).total()); 00074 CV_Error(Error::StsBadArg, error_message); 00075 } 00076 // get a hold of the current row 00077 Mat xi = data.row(i); 00078 // make reshape happy by cloning for non-continuous matrices 00079 if(src.getMat(i).isContinuous()) { 00080 src.getMat(i).reshape(1, 1).convertTo(xi, rtype, alpha, beta); 00081 } else { 00082 src.getMat(i).clone().reshape(1, 1).convertTo(xi, rtype, alpha, beta); 00083 } 00084 } 00085 return data; 00086 } 00087 00088 static void sortMatrixColumnsByIndices(InputArray _src, InputArray _indices, OutputArray _dst) { 00089 if(_indices.getMat().type() != CV_32SC1) { 00090 CV_Error(Error::StsUnsupportedFormat, "cv::sortColumnsByIndices only works on integer indices!"); 00091 } 00092 Mat src = _src.getMat(); 00093 std::vector<int> indices = _indices.getMat(); 00094 _dst.create(src.rows, src.cols, src.type()); 00095 Mat dst = _dst.getMat(); 00096 for(size_t idx = 0; idx < indices.size(); idx++) { 00097 Mat originalCol = src.col(indices[idx]); 00098 Mat sortedCol = dst.col((int)idx); 00099 originalCol.copyTo(sortedCol); 00100 } 00101 } 00102 00103 static Mat sortMatrixColumnsByIndices(InputArray src, InputArray indices) { 00104 Mat dst; 00105 sortMatrixColumnsByIndices(src, indices, dst); 00106 return dst; 00107 } 00108 00109 00110 template<typename _Tp> static bool 00111 isSymmetric_(InputArray src) { 00112 Mat _src = src.getMat(); 00113 if(_src.cols != _src.rows) 00114 return false; 00115 for (int i = 0; i < _src.rows; i++) { 00116 for (int j = 0; j < _src.cols; j++) { 00117 _Tp a = _src.at<_Tp> (i, j); 00118 _Tp b = _src.at<_Tp> (j, i); 00119 if (a != b) { 00120 return false; 00121 } 00122 } 00123 } 00124 return true; 00125 } 00126 00127 template<typename _Tp> static bool 00128 isSymmetric_(InputArray src, double eps) { 00129 Mat _src = src.getMat(); 00130 if(_src.cols != _src.rows) 00131 return false; 00132 for (int i = 0; i < _src.rows; i++) { 00133 for (int j = 0; j < _src.cols; j++) { 00134 _Tp a = _src.at<_Tp> (i, j); 00135 _Tp b = _src.at<_Tp> (j, i); 00136 if (std::abs(a - b) > eps) { 00137 return false; 00138 } 00139 } 00140 } 00141 return true; 00142 } 00143 00144 static bool isSymmetric(InputArray src, double eps=1e-16) 00145 { 00146 Mat m = src.getMat(); 00147 switch (m.type()) { 00148 case CV_8SC1: return isSymmetric_<char>(m); break; 00149 case CV_8UC1: 00150 return isSymmetric_<unsigned char>(m); break; 00151 case CV_16SC1: 00152 return isSymmetric_<short>(m); break; 00153 case CV_16UC1: 00154 return isSymmetric_<unsigned short>(m); break; 00155 case CV_32SC1: 00156 return isSymmetric_<int>(m); break; 00157 case CV_32FC1: 00158 return isSymmetric_<float>(m, eps); break; 00159 case CV_64FC1: 00160 return isSymmetric_<double>(m, eps); break; 00161 default: 00162 break; 00163 } 00164 return false; 00165 } 00166 00167 00168 //------------------------------------------------------------------------------ 00169 // cv::subspaceProject 00170 //------------------------------------------------------------------------------ 00171 Mat LDA::subspaceProject(InputArray _W, InputArray _mean, InputArray _src) { 00172 // get data matrices 00173 Mat W = _W.getMat(); 00174 Mat mean = _mean.getMat(); 00175 Mat src = _src.getMat(); 00176 // get number of samples and dimension 00177 int n = src.rows; 00178 int d = src.cols; 00179 // make sure the data has the correct shape 00180 if(W.rows != d) { 00181 String error_message = format("Wrong shapes for given matrices. Was size(src) = (%d,%d), size(W) = (%d,%d).", src.rows, src.cols, W.rows, W.cols); 00182 CV_Error(Error::StsBadArg, error_message); 00183 } 00184 // make sure mean is correct if not empty 00185 if(!mean.empty() && (mean.total() != (size_t) d)) { 00186 String error_message = format("Wrong mean shape for the given data matrix. Expected %d, but was %d.", d, mean.total()); 00187 CV_Error(Error::StsBadArg, error_message); 00188 } 00189 // create temporary matrices 00190 Mat X, Y; 00191 // make sure you operate on correct type 00192 src.convertTo(X, W.type()); 00193 // safe to do, because of above assertion 00194 if(!mean.empty()) { 00195 for(int i=0; i<n; i++) { 00196 Mat r_i = X.row(i); 00197 subtract(r_i, mean.reshape(1,1), r_i); 00198 } 00199 } 00200 // finally calculate projection as Y = (X-mean)*W 00201 gemm(X, W, 1.0, Mat(), 0.0, Y); 00202 return Y; 00203 } 00204 00205 //------------------------------------------------------------------------------ 00206 // cv::subspaceReconstruct 00207 //------------------------------------------------------------------------------ 00208 Mat LDA::subspaceReconstruct(InputArray _W, InputArray _mean, InputArray _src) 00209 { 00210 // get data matrices 00211 Mat W = _W.getMat(); 00212 Mat mean = _mean.getMat(); 00213 Mat src = _src.getMat(); 00214 // get number of samples and dimension 00215 int n = src.rows; 00216 int d = src.cols; 00217 // make sure the data has the correct shape 00218 if(W.cols != d) { 00219 String error_message = format("Wrong shapes for given matrices. Was size(src) = (%d,%d), size(W) = (%d,%d).", src.rows, src.cols, W.rows, W.cols); 00220 CV_Error(Error::StsBadArg, error_message); 00221 } 00222 // make sure mean is correct if not empty 00223 if(!mean.empty() && (mean.total() != (size_t) W.rows)) { 00224 String error_message = format("Wrong mean shape for the given eigenvector matrix. Expected %d, but was %d.", W.cols, mean.total()); 00225 CV_Error(Error::StsBadArg, error_message); 00226 } 00227 // initialize temporary matrices 00228 Mat X, Y; 00229 // copy data & make sure we are using the correct type 00230 src.convertTo(Y, W.type()); 00231 // calculate the reconstruction 00232 gemm(Y, W, 1.0, Mat(), 0.0, X, GEMM_2_T); 00233 // safe to do because of above assertion 00234 if(!mean.empty()) { 00235 for(int i=0; i<n; i++) { 00236 Mat r_i = X.row(i); 00237 add(r_i, mean.reshape(1,1), r_i); 00238 } 00239 } 00240 return X; 00241 } 00242 00243 00244 class EigenvalueDecomposition { 00245 private: 00246 00247 // Holds the data dimension. 00248 int n; 00249 00250 // Stores real/imag part of a complex division. 00251 double cdivr, cdivi; 00252 00253 // Pointer to internal memory. 00254 double *d, *e, *ort; 00255 double **V, **H; 00256 00257 // Holds the computed eigenvalues. 00258 Mat _eigenvalues; 00259 00260 // Holds the computed eigenvectors. 00261 Mat _eigenvectors; 00262 00263 // Allocates memory. 00264 template<typename _Tp> 00265 _Tp *alloc_1d(int m) { 00266 return new _Tp[m]; 00267 } 00268 00269 // Allocates memory. 00270 template<typename _Tp> 00271 _Tp *alloc_1d(int m, _Tp val) { 00272 _Tp *arr = alloc_1d<_Tp> (m); 00273 for (int i = 0; i < m; i++) 00274 arr[i] = val; 00275 return arr; 00276 } 00277 00278 // Allocates memory. 00279 template<typename _Tp> 00280 _Tp **alloc_2d(int m, int _n) { 00281 _Tp **arr = new _Tp*[m]; 00282 for (int i = 0; i < m; i++) 00283 arr[i] = new _Tp[_n]; 00284 return arr; 00285 } 00286 00287 // Allocates memory. 00288 template<typename _Tp> 00289 _Tp **alloc_2d(int m, int _n, _Tp val) { 00290 _Tp **arr = alloc_2d<_Tp> (m, _n); 00291 for (int i = 0; i < m; i++) { 00292 for (int j = 0; j < _n; j++) { 00293 arr[i][j] = val; 00294 } 00295 } 00296 return arr; 00297 } 00298 00299 void cdiv(double xr, double xi, double yr, double yi) { 00300 double r, dv; 00301 if (std::abs(yr) > std::abs(yi)) { 00302 r = yi / yr; 00303 dv = yr + r * yi; 00304 cdivr = (xr + r * xi) / dv; 00305 cdivi = (xi - r * xr) / dv; 00306 } else { 00307 r = yr / yi; 00308 dv = yi + r * yr; 00309 cdivr = (r * xr + xi) / dv; 00310 cdivi = (r * xi - xr) / dv; 00311 } 00312 } 00313 00314 // Nonsymmetric reduction from Hessenberg to real Schur form. 00315 00316 void hqr2() { 00317 00318 // This is derived from the Algol procedure hqr2, 00319 // by Martin and Wilkinson, Handbook for Auto. Comp., 00320 // Vol.ii-Linear Algebra, and the corresponding 00321 // Fortran subroutine in EISPACK. 00322 00323 // Initialize 00324 int nn = this->n; 00325 int n1 = nn - 1; 00326 int low = 0; 00327 int high = nn - 1; 00328 double eps = std::pow(2.0, -52.0); 00329 double exshift = 0.0; 00330 double p = 0, q = 0, r = 0, s = 0, z = 0, t, w, x, y; 00331 00332 // Store roots isolated by balanc and compute matrix norm 00333 00334 double norm = 0.0; 00335 for (int i = 0; i < nn; i++) { 00336 if (i < low || i > high) { 00337 d[i] = H[i][i]; 00338 e[i] = 0.0; 00339 } 00340 for (int j = std::max(i - 1, 0); j < nn; j++) { 00341 norm = norm + std::abs(H[i][j]); 00342 } 00343 } 00344 00345 // Outer loop over eigenvalue index 00346 int iter = 0; 00347 while (n1 >= low) { 00348 00349 // Look for single small sub-diagonal element 00350 int l = n1; 00351 while (l > low) { 00352 s = std::abs(H[l - 1][l - 1]) + std::abs(H[l][l]); 00353 if (s == 0.0) { 00354 s = norm; 00355 } 00356 if (std::abs(H[l][l - 1]) < eps * s) { 00357 break; 00358 } 00359 l--; 00360 } 00361 00362 // Check for convergence 00363 // One root found 00364 00365 if (l == n1) { 00366 H[n1][n1] = H[n1][n1] + exshift; 00367 d[n1] = H[n1][n1]; 00368 e[n1] = 0.0; 00369 n1--; 00370 iter = 0; 00371 00372 // Two roots found 00373 00374 } else if (l == n1 - 1) { 00375 w = H[n1][n1 - 1] * H[n1 - 1][n1]; 00376 p = (H[n1 - 1][n1 - 1] - H[n1][n1]) / 2.0; 00377 q = p * p + w; 00378 z = std::sqrt(std::abs(q)); 00379 H[n1][n1] = H[n1][n1] + exshift; 00380 H[n1 - 1][n1 - 1] = H[n1 - 1][n1 - 1] + exshift; 00381 x = H[n1][n1]; 00382 00383 // Real pair 00384 00385 if (q >= 0) { 00386 if (p >= 0) { 00387 z = p + z; 00388 } else { 00389 z = p - z; 00390 } 00391 d[n1 - 1] = x + z; 00392 d[n1] = d[n1 - 1]; 00393 if (z != 0.0) { 00394 d[n1] = x - w / z; 00395 } 00396 e[n1 - 1] = 0.0; 00397 e[n1] = 0.0; 00398 x = H[n1][n1 - 1]; 00399 s = std::abs(x) + std::abs(z); 00400 p = x / s; 00401 q = z / s; 00402 r = std::sqrt(p * p + q * q); 00403 p = p / r; 00404 q = q / r; 00405 00406 // Row modification 00407 00408 for (int j = n1 - 1; j < nn; j++) { 00409 z = H[n1 - 1][j]; 00410 H[n1 - 1][j] = q * z + p * H[n1][j]; 00411 H[n1][j] = q * H[n1][j] - p * z; 00412 } 00413 00414 // Column modification 00415 00416 for (int i = 0; i <= n1; i++) { 00417 z = H[i][n1 - 1]; 00418 H[i][n1 - 1] = q * z + p * H[i][n1]; 00419 H[i][n1] = q * H[i][n1] - p * z; 00420 } 00421 00422 // Accumulate transformations 00423 00424 for (int i = low; i <= high; i++) { 00425 z = V[i][n1 - 1]; 00426 V[i][n1 - 1] = q * z + p * V[i][n1]; 00427 V[i][n1] = q * V[i][n1] - p * z; 00428 } 00429 00430 // Complex pair 00431 00432 } else { 00433 d[n1 - 1] = x + p; 00434 d[n1] = x + p; 00435 e[n1 - 1] = z; 00436 e[n1] = -z; 00437 } 00438 n1 = n1 - 2; 00439 iter = 0; 00440 00441 // No convergence yet 00442 00443 } else { 00444 00445 // Form shift 00446 00447 x = H[n1][n1]; 00448 y = 0.0; 00449 w = 0.0; 00450 if (l < n1) { 00451 y = H[n1 - 1][n1 - 1]; 00452 w = H[n1][n1 - 1] * H[n1 - 1][n1]; 00453 } 00454 00455 // Wilkinson's original ad hoc shift 00456 00457 if (iter == 10) { 00458 exshift += x; 00459 for (int i = low; i <= n1; i++) { 00460 H[i][i] -= x; 00461 } 00462 s = std::abs(H[n1][n1 - 1]) + std::abs(H[n1 - 1][n1 - 2]); 00463 x = y = 0.75 * s; 00464 w = -0.4375 * s * s; 00465 } 00466 00467 // MATLAB's new ad hoc shift 00468 00469 if (iter == 30) { 00470 s = (y - x) / 2.0; 00471 s = s * s + w; 00472 if (s > 0) { 00473 s = std::sqrt(s); 00474 if (y < x) { 00475 s = -s; 00476 } 00477 s = x - w / ((y - x) / 2.0 + s); 00478 for (int i = low; i <= n1; i++) { 00479 H[i][i] -= s; 00480 } 00481 exshift += s; 00482 x = y = w = 0.964; 00483 } 00484 } 00485 00486 iter = iter + 1; // (Could check iteration count here.) 00487 00488 // Look for two consecutive small sub-diagonal elements 00489 int m = n1 - 2; 00490 while (m >= l) { 00491 z = H[m][m]; 00492 r = x - z; 00493 s = y - z; 00494 p = (r * s - w) / H[m + 1][m] + H[m][m + 1]; 00495 q = H[m + 1][m + 1] - z - r - s; 00496 r = H[m + 2][m + 1]; 00497 s = std::abs(p) + std::abs(q) + std::abs(r); 00498 p = p / s; 00499 q = q / s; 00500 r = r / s; 00501 if (m == l) { 00502 break; 00503 } 00504 if (std::abs(H[m][m - 1]) * (std::abs(q) + std::abs(r)) < eps * (std::abs(p) 00505 * (std::abs(H[m - 1][m - 1]) + std::abs(z) + std::abs( 00506 H[m + 1][m + 1])))) { 00507 break; 00508 } 00509 m--; 00510 } 00511 00512 for (int i = m + 2; i <= n1; i++) { 00513 H[i][i - 2] = 0.0; 00514 if (i > m + 2) { 00515 H[i][i - 3] = 0.0; 00516 } 00517 } 00518 00519 // Double QR step involving rows l:n and columns m:n 00520 00521 for (int k = m; k <= n1 - 1; k++) { 00522 bool notlast = (k != n1 - 1); 00523 if (k != m) { 00524 p = H[k][k - 1]; 00525 q = H[k + 1][k - 1]; 00526 r = (notlast ? H[k + 2][k - 1] : 0.0); 00527 x = std::abs(p) + std::abs(q) + std::abs(r); 00528 if (x != 0.0) { 00529 p = p / x; 00530 q = q / x; 00531 r = r / x; 00532 } 00533 } 00534 if (x == 0.0) { 00535 break; 00536 } 00537 s = std::sqrt(p * p + q * q + r * r); 00538 if (p < 0) { 00539 s = -s; 00540 } 00541 if (s != 0) { 00542 if (k != m) { 00543 H[k][k - 1] = -s * x; 00544 } else if (l != m) { 00545 H[k][k - 1] = -H[k][k - 1]; 00546 } 00547 p = p + s; 00548 x = p / s; 00549 y = q / s; 00550 z = r / s; 00551 q = q / p; 00552 r = r / p; 00553 00554 // Row modification 00555 00556 for (int j = k; j < nn; j++) { 00557 p = H[k][j] + q * H[k + 1][j]; 00558 if (notlast) { 00559 p = p + r * H[k + 2][j]; 00560 H[k + 2][j] = H[k + 2][j] - p * z; 00561 } 00562 H[k][j] = H[k][j] - p * x; 00563 H[k + 1][j] = H[k + 1][j] - p * y; 00564 } 00565 00566 // Column modification 00567 00568 for (int i = 0; i <= std::min(n1, k + 3); i++) { 00569 p = x * H[i][k] + y * H[i][k + 1]; 00570 if (notlast) { 00571 p = p + z * H[i][k + 2]; 00572 H[i][k + 2] = H[i][k + 2] - p * r; 00573 } 00574 H[i][k] = H[i][k] - p; 00575 H[i][k + 1] = H[i][k + 1] - p * q; 00576 } 00577 00578 // Accumulate transformations 00579 00580 for (int i = low; i <= high; i++) { 00581 p = x * V[i][k] + y * V[i][k + 1]; 00582 if (notlast) { 00583 p = p + z * V[i][k + 2]; 00584 V[i][k + 2] = V[i][k + 2] - p * r; 00585 } 00586 V[i][k] = V[i][k] - p; 00587 V[i][k + 1] = V[i][k + 1] - p * q; 00588 } 00589 } // (s != 0) 00590 } // k loop 00591 } // check convergence 00592 } // while (n1 >= low) 00593 00594 // Backsubstitute to find vectors of upper triangular form 00595 00596 if (norm == 0.0) { 00597 return; 00598 } 00599 00600 for (n1 = nn - 1; n1 >= 0; n1--) { 00601 p = d[n1]; 00602 q = e[n1]; 00603 00604 // Real vector 00605 00606 if (q == 0) { 00607 int l = n1; 00608 H[n1][n1] = 1.0; 00609 for (int i = n1 - 1; i >= 0; i--) { 00610 w = H[i][i] - p; 00611 r = 0.0; 00612 for (int j = l; j <= n1; j++) { 00613 r = r + H[i][j] * H[j][n1]; 00614 } 00615 if (e[i] < 0.0) { 00616 z = w; 00617 s = r; 00618 } else { 00619 l = i; 00620 if (e[i] == 0.0) { 00621 if (w != 0.0) { 00622 H[i][n1] = -r / w; 00623 } else { 00624 H[i][n1] = -r / (eps * norm); 00625 } 00626 00627 // Solve real equations 00628 00629 } else { 00630 x = H[i][i + 1]; 00631 y = H[i + 1][i]; 00632 q = (d[i] - p) * (d[i] - p) + e[i] * e[i]; 00633 t = (x * s - z * r) / q; 00634 H[i][n1] = t; 00635 if (std::abs(x) > std::abs(z)) { 00636 H[i + 1][n1] = (-r - w * t) / x; 00637 } else { 00638 H[i + 1][n1] = (-s - y * t) / z; 00639 } 00640 } 00641 00642 // Overflow control 00643 00644 t = std::abs(H[i][n1]); 00645 if ((eps * t) * t > 1) { 00646 for (int j = i; j <= n1; j++) { 00647 H[j][n1] = H[j][n1] / t; 00648 } 00649 } 00650 } 00651 } 00652 // Complex vector 00653 } else if (q < 0) { 00654 int l = n1 - 1; 00655 00656 // Last vector component imaginary so matrix is triangular 00657 00658 if (std::abs(H[n1][n1 - 1]) > std::abs(H[n1 - 1][n1])) { 00659 H[n1 - 1][n1 - 1] = q / H[n1][n1 - 1]; 00660 H[n1 - 1][n1] = -(H[n1][n1] - p) / H[n1][n1 - 1]; 00661 } else { 00662 cdiv(0.0, -H[n1 - 1][n1], H[n1 - 1][n1 - 1] - p, q); 00663 H[n1 - 1][n1 - 1] = cdivr; 00664 H[n1 - 1][n1] = cdivi; 00665 } 00666 H[n1][n1 - 1] = 0.0; 00667 H[n1][n1] = 1.0; 00668 for (int i = n1 - 2; i >= 0; i--) { 00669 double ra, sa, vr, vi; 00670 ra = 0.0; 00671 sa = 0.0; 00672 for (int j = l; j <= n1; j++) { 00673 ra = ra + H[i][j] * H[j][n1 - 1]; 00674 sa = sa + H[i][j] * H[j][n1]; 00675 } 00676 w = H[i][i] - p; 00677 00678 if (e[i] < 0.0) { 00679 z = w; 00680 r = ra; 00681 s = sa; 00682 } else { 00683 l = i; 00684 if (e[i] == 0) { 00685 cdiv(-ra, -sa, w, q); 00686 H[i][n1 - 1] = cdivr; 00687 H[i][n1] = cdivi; 00688 } else { 00689 00690 // Solve complex equations 00691 00692 x = H[i][i + 1]; 00693 y = H[i + 1][i]; 00694 vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q; 00695 vi = (d[i] - p) * 2.0 * q; 00696 if (vr == 0.0 && vi == 0.0) { 00697 vr = eps * norm * (std::abs(w) + std::abs(q) + std::abs(x) 00698 + std::abs(y) + std::abs(z)); 00699 } 00700 cdiv(x * r - z * ra + q * sa, 00701 x * s - z * sa - q * ra, vr, vi); 00702 H[i][n1 - 1] = cdivr; 00703 H[i][n1] = cdivi; 00704 if (std::abs(x) > (std::abs(z) + std::abs(q))) { 00705 H[i + 1][n1 - 1] = (-ra - w * H[i][n1 - 1] + q 00706 * H[i][n1]) / x; 00707 H[i + 1][n1] = (-sa - w * H[i][n1] - q * H[i][n1 00708 - 1]) / x; 00709 } else { 00710 cdiv(-r - y * H[i][n1 - 1], -s - y * H[i][n1], z, 00711 q); 00712 H[i + 1][n1 - 1] = cdivr; 00713 H[i + 1][n1] = cdivi; 00714 } 00715 } 00716 00717 // Overflow control 00718 00719 t = std::max(std::abs(H[i][n1 - 1]), std::abs(H[i][n1])); 00720 if ((eps * t) * t > 1) { 00721 for (int j = i; j <= n1; j++) { 00722 H[j][n1 - 1] = H[j][n1 - 1] / t; 00723 H[j][n1] = H[j][n1] / t; 00724 } 00725 } 00726 } 00727 } 00728 } 00729 } 00730 00731 // Vectors of isolated roots 00732 00733 for (int i = 0; i < nn; i++) { 00734 if (i < low || i > high) { 00735 for (int j = i; j < nn; j++) { 00736 V[i][j] = H[i][j]; 00737 } 00738 } 00739 } 00740 00741 // Back transformation to get eigenvectors of original matrix 00742 00743 for (int j = nn - 1; j >= low; j--) { 00744 for (int i = low; i <= high; i++) { 00745 z = 0.0; 00746 for (int k = low; k <= std::min(j, high); k++) { 00747 z = z + V[i][k] * H[k][j]; 00748 } 00749 V[i][j] = z; 00750 } 00751 } 00752 } 00753 00754 // Nonsymmetric reduction to Hessenberg form. 00755 void orthes() { 00756 // This is derived from the Algol procedures orthes and ortran, 00757 // by Martin and Wilkinson, Handbook for Auto. Comp., 00758 // Vol.ii-Linear Algebra, and the corresponding 00759 // Fortran subroutines in EISPACK. 00760 int low = 0; 00761 int high = n - 1; 00762 00763 for (int m = low + 1; m <= high - 1; m++) { 00764 00765 // Scale column. 00766 00767 double scale = 0.0; 00768 for (int i = m; i <= high; i++) { 00769 scale = scale + std::abs(H[i][m - 1]); 00770 } 00771 if (scale != 0.0) { 00772 00773 // Compute Householder transformation. 00774 00775 double h = 0.0; 00776 for (int i = high; i >= m; i--) { 00777 ort[i] = H[i][m - 1] / scale; 00778 h += ort[i] * ort[i]; 00779 } 00780 double g = std::sqrt(h); 00781 if (ort[m] > 0) { 00782 g = -g; 00783 } 00784 h = h - ort[m] * g; 00785 ort[m] = ort[m] - g; 00786 00787 // Apply Householder similarity transformation 00788 // H = (I-u*u'/h)*H*(I-u*u')/h) 00789 00790 for (int j = m; j < n; j++) { 00791 double f = 0.0; 00792 for (int i = high; i >= m; i--) { 00793 f += ort[i] * H[i][j]; 00794 } 00795 f = f / h; 00796 for (int i = m; i <= high; i++) { 00797 H[i][j] -= f * ort[i]; 00798 } 00799 } 00800 00801 for (int i = 0; i <= high; i++) { 00802 double f = 0.0; 00803 for (int j = high; j >= m; j--) { 00804 f += ort[j] * H[i][j]; 00805 } 00806 f = f / h; 00807 for (int j = m; j <= high; j++) { 00808 H[i][j] -= f * ort[j]; 00809 } 00810 } 00811 ort[m] = scale * ort[m]; 00812 H[m][m - 1] = scale * g; 00813 } 00814 } 00815 00816 // Accumulate transformations (Algol's ortran). 00817 00818 for (int i = 0; i < n; i++) { 00819 for (int j = 0; j < n; j++) { 00820 V[i][j] = (i == j ? 1.0 : 0.0); 00821 } 00822 } 00823 00824 for (int m = high - 1; m >= low + 1; m--) { 00825 if (H[m][m - 1] != 0.0) { 00826 for (int i = m + 1; i <= high; i++) { 00827 ort[i] = H[i][m - 1]; 00828 } 00829 for (int j = m; j <= high; j++) { 00830 double g = 0.0; 00831 for (int i = m; i <= high; i++) { 00832 g += ort[i] * V[i][j]; 00833 } 00834 // Double division avoids possible underflow 00835 g = (g / ort[m]) / H[m][m - 1]; 00836 for (int i = m; i <= high; i++) { 00837 V[i][j] += g * ort[i]; 00838 } 00839 } 00840 } 00841 } 00842 } 00843 00844 // Releases all internal working memory. 00845 void release() { 00846 // releases the working data 00847 delete[] d; 00848 delete[] e; 00849 delete[] ort; 00850 for (int i = 0; i < n; i++) { 00851 delete[] H[i]; 00852 delete[] V[i]; 00853 } 00854 delete[] H; 00855 delete[] V; 00856 } 00857 00858 // Computes the Eigenvalue Decomposition for a matrix given in H. 00859 void compute() { 00860 // Allocate memory for the working data. 00861 V = alloc_2d<double> (n, n, 0.0); 00862 d = alloc_1d<double> (n); 00863 e = alloc_1d<double> (n); 00864 ort = alloc_1d<double> (n); 00865 // Reduce to Hessenberg form. 00866 orthes(); 00867 // Reduce Hessenberg to real Schur form. 00868 hqr2(); 00869 // Copy eigenvalues to OpenCV Matrix. 00870 _eigenvalues.create(1, n, CV_64FC1); 00871 for (int i = 0; i < n; i++) { 00872 _eigenvalues.at<double> (0, i) = d[i]; 00873 } 00874 // Copy eigenvectors to OpenCV Matrix. 00875 _eigenvectors.create(n, n, CV_64FC1); 00876 for (int i = 0; i < n; i++) 00877 for (int j = 0; j < n; j++) 00878 _eigenvectors.at<double> (i, j) = V[i][j]; 00879 // Deallocate the memory by releasing all internal working data. 00880 release(); 00881 } 00882 00883 public: 00884 EigenvalueDecomposition() 00885 : n(0) { } 00886 00887 // Initializes & computes the Eigenvalue Decomposition for a general matrix 00888 // given in src. This function is a port of the EigenvalueSolver in JAMA, 00889 // which has been released to public domain by The MathWorks and the 00890 // National Institute of Standards and Technology (NIST). 00891 EigenvalueDecomposition(InputArray src) { 00892 compute(src); 00893 } 00894 00895 // This function computes the Eigenvalue Decomposition for a general matrix 00896 // given in src. This function is a port of the EigenvalueSolver in JAMA, 00897 // which has been released to public domain by The MathWorks and the 00898 // National Institute of Standards and Technology (NIST). 00899 void compute(InputArray src) 00900 { 00901 if(isSymmetric(src)) { 00902 // Fall back to OpenCV for a symmetric matrix! 00903 cv::eigen(src, _eigenvalues, _eigenvectors); 00904 } else { 00905 Mat tmp; 00906 // Convert the given input matrix to double. Is there any way to 00907 // prevent allocating the temporary memory? Only used for copying 00908 // into working memory and deallocated after. 00909 src.getMat().convertTo(tmp, CV_64FC1); 00910 // Get dimension of the matrix. 00911 this->n = tmp.cols; 00912 // Allocate the matrix data to work on. 00913 this->H = alloc_2d<double> (n, n); 00914 // Now safely copy the data. 00915 for (int i = 0; i < tmp.rows; i++) { 00916 for (int j = 0; j < tmp.cols; j++) { 00917 this->H[i][j] = tmp.at<double>(i, j); 00918 } 00919 } 00920 // Deallocates the temporary matrix before computing. 00921 tmp.release(); 00922 // Performs the eigenvalue decomposition of H. 00923 compute(); 00924 } 00925 } 00926 00927 ~EigenvalueDecomposition() {} 00928 00929 // Returns the eigenvalues of the Eigenvalue Decomposition. 00930 Mat eigenvalues() { return _eigenvalues; } 00931 // Returns the eigenvectors of the Eigenvalue Decomposition. 00932 Mat eigenvectors() { return _eigenvectors; } 00933 }; 00934 00935 00936 //------------------------------------------------------------------------------ 00937 // Linear Discriminant Analysis implementation 00938 //------------------------------------------------------------------------------ 00939 00940 LDA::LDA(int num_components) : _dataAsRow(true), _num_components(num_components) { } 00941 00942 LDA::LDA(InputArrayOfArrays src, InputArray labels, int num_components) : _dataAsRow(true), _num_components(num_components) 00943 { 00944 this->compute(src, labels); //! compute eigenvectors and eigenvalues 00945 } 00946 00947 LDA::~LDA() {} 00948 00949 void LDA::save(const String& filename) const 00950 { 00951 FileStorage fs(filename, FileStorage::WRITE); 00952 if (!fs.isOpened()) { 00953 CV_Error(Error::StsError, "File can't be opened for writing!"); 00954 } 00955 this->save(fs); 00956 fs.release(); 00957 } 00958 00959 // Deserializes this object from a given filename. 00960 void LDA::load(const String& filename) { 00961 FileStorage fs(filename, FileStorage::READ); 00962 if (!fs.isOpened()) 00963 CV_Error(Error::StsError, "File can't be opened for writing!"); 00964 this->load(fs); 00965 fs.release(); 00966 } 00967 00968 // Serializes this object to a given FileStorage. 00969 void LDA::save(cv::FileStorage& fs) const { 00970 /*the debug 00971 // write matrices 00972 fs << "num_components" << _num_components; 00973 fs << "eigenvalues" << _eigenvalues; 00974 fs << "eigenvectors" << _eigenvectors;*/ 00975 } 00976 00977 // Deserializes this object from a given FileStorage. 00978 void LDA::load(const FileStorage& fs) { 00979 //read matrices 00980 fs["num_components"] >> _num_components; 00981 fs["eigenvalues"] >> _eigenvalues; 00982 fs["eigenvectors"] >> _eigenvectors; 00983 } 00984 00985 void LDA::lda (InputArrayOfArrays _src, InputArray _lbls) { 00986 // get data 00987 Mat src = _src.getMat(); 00988 std::vector<int> labels; 00989 // safely copy the labels 00990 { 00991 Mat tmp = _lbls.getMat(); 00992 for(unsigned int i = 0; i < tmp.total(); i++) { 00993 labels.push_back(tmp.at<int>(i)); 00994 } 00995 } 00996 // turn into row sampled matrix 00997 Mat data; 00998 // ensure working matrix is double precision 00999 src.convertTo(data, CV_64FC1); 01000 // maps the labels, so they're ascending: [0,1,...,C] 01001 std::vector<int> mapped_labels(labels.size()); 01002 std::vector<int> num2label = remove_dups(labels); 01003 std::map<int, int> label2num; 01004 for (int i = 0; i < (int)num2label.size(); i++) 01005 label2num[num2label[i]] = i; 01006 for (size_t i = 0; i < labels.size(); i++) 01007 mapped_labels[i] = label2num[labels[i]]; 01008 // get sample size, dimension 01009 int N = data.rows; 01010 int D = data.cols; 01011 // number of unique labels 01012 int C = (int)num2label.size(); 01013 // we can't do a LDA on one class, what do you 01014 // want to separate from each other then? 01015 if(C == 1) { 01016 String error_message = "At least two classes are needed to perform a LDA. Reason: Only one class was given!"; 01017 CV_Error(Error::StsBadArg, error_message); 01018 } 01019 // throw error if less labels, than samples 01020 if (labels.size() != static_cast<size_t>(N)) { 01021 String error_message = format("The number of samples must equal the number of labels. Given %d labels, %d samples. ", labels.size(), N); 01022 CV_Error(Error::StsBadArg, error_message); 01023 } 01024 // warn if within-classes scatter matrix becomes singular 01025 if (N < D) { 01026 std::cout << "Warning: Less observations than feature dimension given!" 01027 << "Computation will probably fail." 01028 << std::endl; 01029 } 01030 // clip number of components to be a valid number 01031 if ((_num_components <= 0) || (_num_components > (C - 1))) { 01032 _num_components = (C - 1); 01033 } 01034 // holds the mean over all classes 01035 Mat meanTotal = Mat::zeros(1, D, data.type()); 01036 // holds the mean for each class 01037 std::vector<Mat> meanClass(C); 01038 std::vector<int> numClass(C); 01039 // initialize 01040 for (int i = 0; i < C; i++) { 01041 numClass[i] = 0; 01042 meanClass[i] = Mat::zeros(1, D, data.type()); //! Dx1 image vector 01043 } 01044 // calculate sums 01045 for (int i = 0; i < N; i++) { 01046 Mat instance = data.row(i); 01047 int classIdx = mapped_labels[i]; 01048 add(meanTotal, instance, meanTotal); 01049 add(meanClass[classIdx], instance, meanClass[classIdx]); 01050 numClass[classIdx]++; 01051 } 01052 // calculate total mean 01053 meanTotal.convertTo(meanTotal, meanTotal.type(), 1.0 / static_cast<double> (N)); 01054 // calculate class means 01055 for (int i = 0; i < C; i++) { 01056 meanClass[i].convertTo(meanClass[i], meanClass[i].type(), 1.0 / static_cast<double> (numClass[i])); 01057 } 01058 // subtract class means 01059 for (int i = 0; i < N; i++) { 01060 int classIdx = mapped_labels[i]; 01061 Mat instance = data.row(i); 01062 subtract(instance, meanClass[classIdx], instance); 01063 } 01064 // calculate within-classes scatter 01065 Mat Sw = Mat::zeros(D, D, data.type()); 01066 mulTransposed(data, Sw, true); 01067 // calculate between-classes scatter 01068 Mat Sb = Mat::zeros(D, D, data.type()); 01069 for (int i = 0; i < C; i++) { 01070 Mat tmp; 01071 subtract(meanClass[i], meanTotal, tmp); 01072 mulTransposed(tmp, tmp, true); 01073 add(Sb, tmp, Sb); 01074 } 01075 // invert Sw 01076 Mat Swi = Sw.inv(); 01077 // M = inv(Sw)*Sb 01078 Mat M; 01079 gemm(Swi, Sb, 1.0, Mat(), 0.0, M); 01080 EigenvalueDecomposition es(M); 01081 _eigenvalues = es.eigenvalues(); 01082 _eigenvectors = es.eigenvectors(); 01083 // reshape eigenvalues, so they are stored by column 01084 _eigenvalues = _eigenvalues.reshape(1, 1); 01085 // get sorted indices descending by their eigenvalue 01086 std::vector<int> sorted_indices = argsort(_eigenvalues, false); 01087 // now sort eigenvalues and eigenvectors accordingly 01088 _eigenvalues = sortMatrixColumnsByIndices(_eigenvalues, sorted_indices); 01089 _eigenvectors = sortMatrixColumnsByIndices(_eigenvectors, sorted_indices); 01090 // and now take only the num_components and we're out! 01091 _eigenvalues = Mat(_eigenvalues, Range::all(), Range(0, _num_components)); 01092 _eigenvectors = Mat(_eigenvectors, Range::all(), Range(0, _num_components)); 01093 } 01094 01095 void LDA::compute(InputArrayOfArrays _src, InputArray _lbls) { 01096 switch(_src.kind()) { 01097 case _InputArray::STD_VECTOR_MAT: 01098 lda (asRowMatrix(_src, CV_64FC1), _lbls); 01099 break; 01100 case _InputArray::MAT: 01101 lda (_src.getMat(), _lbls); 01102 break; 01103 default: 01104 String error_message= format("InputArray Datatype %d is not supported.", _src.kind()); 01105 CV_Error(Error::StsBadArg, error_message); 01106 break; 01107 } 01108 } 01109 01110 // Projects one or more row aligned samples into the LDA subspace. 01111 Mat LDA::project(InputArray src) { 01112 return subspaceProject(_eigenvectors, Mat(), src); 01113 } 01114 01115 // Reconstructs projections from the LDA subspace from one or more row aligned samples. 01116 Mat LDA::reconstruct(InputArray src) { 01117 return subspaceReconstruct(_eigenvectors, Mat(), src); 01118 } 01119 01120 } 01121
Generated on Tue Jul 12 2022 14:47:14 by
