Libav
|
00001 /* 00002 * principal component analysis (PCA) 00003 * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00027 #include "common.h" 00028 #include "pca.h" 00029 00030 typedef struct PCA{ 00031 int count; 00032 int n; 00033 double *covariance; 00034 double *mean; 00035 }PCA; 00036 00037 PCA *ff_pca_init(int n){ 00038 PCA *pca; 00039 if(n<=0) 00040 return NULL; 00041 00042 pca= av_mallocz(sizeof(PCA)); 00043 pca->n= n; 00044 pca->count=0; 00045 pca->covariance= av_mallocz(sizeof(double)*n*n); 00046 pca->mean= av_mallocz(sizeof(double)*n); 00047 00048 return pca; 00049 } 00050 00051 void ff_pca_free(PCA *pca){ 00052 av_freep(&pca->covariance); 00053 av_freep(&pca->mean); 00054 av_free(pca); 00055 } 00056 00057 void ff_pca_add(PCA *pca, double *v){ 00058 int i, j; 00059 const int n= pca->n; 00060 00061 for(i=0; i<n; i++){ 00062 pca->mean[i] += v[i]; 00063 for(j=i; j<n; j++) 00064 pca->covariance[j + i*n] += v[i]*v[j]; 00065 } 00066 pca->count++; 00067 } 00068 00069 int ff_pca(PCA *pca, double *eigenvector, double *eigenvalue){ 00070 int i, j, pass; 00071 int k=0; 00072 const int n= pca->n; 00073 double z[n]; 00074 00075 memset(eigenvector, 0, sizeof(double)*n*n); 00076 00077 for(j=0; j<n; j++){ 00078 pca->mean[j] /= pca->count; 00079 eigenvector[j + j*n] = 1.0; 00080 for(i=0; i<=j; i++){ 00081 pca->covariance[j + i*n] /= pca->count; 00082 pca->covariance[j + i*n] -= pca->mean[i] * pca->mean[j]; 00083 pca->covariance[i + j*n] = pca->covariance[j + i*n]; 00084 } 00085 eigenvalue[j]= pca->covariance[j + j*n]; 00086 z[j]= 0; 00087 } 00088 00089 for(pass=0; pass < 50; pass++){ 00090 double sum=0; 00091 00092 for(i=0; i<n; i++) 00093 for(j=i+1; j<n; j++) 00094 sum += fabs(pca->covariance[j + i*n]); 00095 00096 if(sum == 0){ 00097 for(i=0; i<n; i++){ 00098 double maxvalue= -1; 00099 for(j=i; j<n; j++){ 00100 if(eigenvalue[j] > maxvalue){ 00101 maxvalue= eigenvalue[j]; 00102 k= j; 00103 } 00104 } 00105 eigenvalue[k]= eigenvalue[i]; 00106 eigenvalue[i]= maxvalue; 00107 for(j=0; j<n; j++){ 00108 double tmp= eigenvector[k + j*n]; 00109 eigenvector[k + j*n]= eigenvector[i + j*n]; 00110 eigenvector[i + j*n]= tmp; 00111 } 00112 } 00113 return pass; 00114 } 00115 00116 for(i=0; i<n; i++){ 00117 for(j=i+1; j<n; j++){ 00118 double covar= pca->covariance[j + i*n]; 00119 double t,c,s,tau,theta, h; 00120 00121 if(pass < 3 && fabs(covar) < sum / (5*n*n)) //FIXME why pass < 3 00122 continue; 00123 if(fabs(covar) == 0.0) //FIXME should not be needed 00124 continue; 00125 if(pass >=3 && fabs((eigenvalue[j]+z[j])/covar) > (1LL<<32) && fabs((eigenvalue[i]+z[i])/covar) > (1LL<<32)){ 00126 pca->covariance[j + i*n]=0.0; 00127 continue; 00128 } 00129 00130 h= (eigenvalue[j]+z[j]) - (eigenvalue[i]+z[i]); 00131 theta=0.5*h/covar; 00132 t=1.0/(fabs(theta)+sqrt(1.0+theta*theta)); 00133 if(theta < 0.0) t = -t; 00134 00135 c=1.0/sqrt(1+t*t); 00136 s=t*c; 00137 tau=s/(1.0+c); 00138 z[i] -= t*covar; 00139 z[j] += t*covar; 00140 00141 #define ROTATE(a,i,j,k,l) {\ 00142 double g=a[j + i*n];\ 00143 double h=a[l + k*n];\ 00144 a[j + i*n]=g-s*(h+g*tau);\ 00145 a[l + k*n]=h+s*(g-h*tau); } 00146 for(k=0; k<n; k++) { 00147 if(k!=i && k!=j){ 00148 ROTATE(pca->covariance,FFMIN(k,i),FFMAX(k,i),FFMIN(k,j),FFMAX(k,j)) 00149 } 00150 ROTATE(eigenvector,k,i,k,j) 00151 } 00152 pca->covariance[j + i*n]=0.0; 00153 } 00154 } 00155 for (i=0; i<n; i++) { 00156 eigenvalue[i] += z[i]; 00157 z[i]=0.0; 00158 } 00159 } 00160 00161 return -1; 00162 } 00163 00164 #ifdef TEST 00165 00166 #undef printf 00167 #include <stdio.h> 00168 #include <stdlib.h> 00169 #include "lfg.h" 00170 00171 int main(void){ 00172 PCA *pca; 00173 int i, j, k; 00174 #define LEN 8 00175 double eigenvector[LEN*LEN]; 00176 double eigenvalue[LEN]; 00177 AVLFG prng; 00178 00179 av_lfg_init(&prng, 1); 00180 00181 pca= ff_pca_init(LEN); 00182 00183 for(i=0; i<9000000; i++){ 00184 double v[2*LEN+100]; 00185 double sum=0; 00186 int pos = av_lfg_get(&prng) % LEN; 00187 int v2 = av_lfg_get(&prng) % 101 - 50; 00188 v[0] = av_lfg_get(&prng) % 101 - 50; 00189 for(j=1; j<8; j++){ 00190 if(j<=pos) v[j]= v[0]; 00191 else v[j]= v2; 00192 sum += v[j]; 00193 } 00194 /* for(j=0; j<LEN; j++){ 00195 v[j] -= v[pos]; 00196 }*/ 00197 // sum += av_lfg_get(&prng) % 10; 00198 /* for(j=0; j<LEN; j++){ 00199 v[j] -= sum/LEN; 00200 }*/ 00201 // lbt1(v+100,v+100,LEN); 00202 ff_pca_add(pca, v); 00203 } 00204 00205 00206 ff_pca(pca, eigenvector, eigenvalue); 00207 for(i=0; i<LEN; i++){ 00208 pca->count= 1; 00209 pca->mean[i]= 0; 00210 00211 // (0.5^|x|)^2 = 0.5^2|x| = 0.25^|x| 00212 00213 00214 // pca.covariance[i + i*LEN]= pow(0.5, fabs 00215 for(j=i; j<LEN; j++){ 00216 printf("%f ", pca->covariance[i + j*LEN]); 00217 } 00218 printf("\n"); 00219 } 00220 00221 #if 1 00222 for(i=0; i<LEN; i++){ 00223 double v[LEN]; 00224 double error=0; 00225 memset(v, 0, sizeof(v)); 00226 for(j=0; j<LEN; j++){ 00227 for(k=0; k<LEN; k++){ 00228 v[j] += pca->covariance[FFMIN(k,j) + FFMAX(k,j)*LEN] * eigenvector[i + k*LEN]; 00229 } 00230 v[j] /= eigenvalue[i]; 00231 error += fabs(v[j] - eigenvector[i + j*LEN]); 00232 } 00233 printf("%f ", error); 00234 } 00235 printf("\n"); 00236 #endif 00237 for(i=0; i<LEN; i++){ 00238 for(j=0; j<LEN; j++){ 00239 printf("%9.6f ", eigenvector[i + j*LEN]); 00240 } 00241 printf(" %9.1f %f\n", eigenvalue[i], eigenvalue[i]/eigenvalue[0]); 00242 } 00243 00244 return 0; 00245 } 00246 #endif