nasil122002
nasil122002

Reputation: 145

Dimension reduction with PCA in OpenCV

I have a 3xN Mat data, which is saved in yaml file and looks like:

%YAML:1.0

data1: !!opencv-matrix
rows: 50
 cols: 3
 dt: d
 data: [ 7.1709999084472656e+01, -2.5729999542236328e+01,
   -1.4074000549316406e+02, 7.1680000305175781e+01,
   -2.5729999542236328e+01, -1.4075000000000000e+02,
   7.1639999389648438e+01, -2.5729999542236328e+01,
   -1.4075000000000000e+02, 7.1680000305175781e+01,
   -2.5729999542236328e+01, -1.4075000000000000e+02, ...

I want to reduce the dimension of my 3D data to 1D or rather 2D and after that visualize it on a QwtPlotCurve. In order to do that, I have implemented pca function under opencv, but have no idea how to get the calculated x and y coordinates from pca result:

int numOfComponents= 100;
PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, numOfComponents);

Mat mean= pca.mean.clone();
Mat eigenvalues= pca.eigenvalues.clone();
Mat eigenvectors= pca.eigenvectors.clone();

Upvotes: 3

Views: 4102

Answers (1)

Saeed Masoomi
Saeed Masoomi

Reputation: 1834

Here's an example of a 2D data set.

x=[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2, 1, 1.5, 1.1];
y=[2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9];

We can write these arrays in OpenCV with the following code.

float X_array[]={2.5,0.5,2.2,1.9,3.1,2.3,2,1,1.5,1.1};
float Y_array[]={2.4,0.7,2.9,2.2,3.0,2.7,1.6,1.1,1.6,0.9};

cv::Mat x(10,1,CV_32F,X_array);   //Copy X_array to Mat (PCA need Mat form)
cv::Mat y(10,1,CV_32F,Y_array);   //Copy Y_array to Mat

Next, we will combine x and y into a unified cv::Mat data. Because the whole data must be in one place for PCA function to work, we have to combine our data. (If your data is in 2D format, such as as an image, then you can simply convert the 2D to 1D signals and combine them)

x.col(0).copyTo(data.col(0));  //copy x into first column of data
y.col(0).copyTo(data.col(1));  //copy y into second column of data

the data after the last code will look like the following:

data=
    [2.5, 2.4;
     0.5, 0.7;
     2.2, 2.9;
     1.9, 2.2;
     3.1, 3;
     2.3, 2.7;
     2,   1.6;
     1,   1.1;
     1.5, 1.6;
     1.1, 0.9]

With cv::PCA, we can calculate the eigenvalues and eigenvectors of the 2D signal.

cv::PCA pca(data,                 //Input Array Data
            Mat(),                //Mean of input array, if you don't want to pass it   simply put Mat()
            CV_PCA_DATA_AS_ROW,   //int flag
            2);                   // number of components that you want to retain(keep)
           


Mat mean=pca.mean;                // get mean of Data in Mat form
Mat eigenvalue=pca.eigenvalues;
Mat eigenvectors=pca.eigenvectors;

our eigenValue and eigenvectors will be as the below:

EigenValue=
         [1.155625;
          0.044175029]


EigenVectors=
        [0.67787337, 0.73517865;
         0.73517865, -0.67787337]

As you can see in the eigenValue, the first-row value is 1.55 and is much bigger than 0.044. So in eigenvectors, the first row is most important than the second row and if you retain the correspond row in EigenVectors, you can have almost whole data in 1D (Simply you have compressed the data, but your 2D pattern available in new 1D data)

How we can Extract final Data??

To extract final data you can multiply the eigenVector by the original data and get new data, for example, if I want to convert my data to 1D, I can use the below code

Mat final=eigenvectors.row(0)*data.t(); //firts_row_in_eigenVector * tranpose(data)

In your example, if you want to convert 3D to 2D then set the dimension to retain 2, and if you want to convert to 1D then set this argument to 1 like the below

1D

int numOfComponents= 1;
PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, numOfComponents);

2

int numOfComponents= 2;
PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, numOfComponents);

Upvotes: 13

Related Questions