Cognotics Home  
Search Cognotics:
.
Home > OpenCV Resources > Seeing With OpenCV > Part 5 Pages:   Prev   1   2   3   4   5   Next
.
 
Seeing With OpenCV, Part 5: Implementing Eigenface (Continued)
 
This article originally appeared in SERVO Magazine, May 2007. Reprinted by permission of T & L Publications, Inc.
 

Finding the PCA Subspace

The code to find the PCA subspace is in Figure 6. It calls the built-in OpenCV function for doing PCA, cvCalcEigenObjects(), at lines 27-36. The remainder of doPCA() creates the output variables that will hold the PCA results when cvCalcEigenObjects() returns.

At line 8, the number of eigenvalues is set to one less than the number of training images. (As explained last month, this is the maximum number of eigenvalues we can find.)

. Figure 6 .
.
Figure 6. (Click for larger view) doPCA()finds the PCA subspace using OpenCV's cvCalcEigenObjects() function.
.

Lines 11-15 create the global image array eigenVectArr. When cvCalcEigenObjects() returns, each image in this array will hold one eigenvector, one "eigenface," in other words. Note that these are floating-point images, with data depth = IPL_DEPTH_32F.

At line 18, another matrix is created - eigenValMat. This matrix will hold the eigenvalues. The eigenvalues are floating-point numbers, and we only need one channel for this, so the matrix type is CV_32FC1. That gives us a one-channel matrix, with 32-bit, floating-point data values.

To do PCA, the dataset must first be "centered." For our face images, this means finding the average image - an image in which each pixel contains the average value for that pixel across all face images in the training set. The dataset is centered by subtracting the average face's pixel values from each training image.

You don't have to do that yourself. It happens inside cvCalcEigenObjects(). But you do need to hold onto the average image, because you'll need it later to project the data. So you'll need to allocate memory for the average image. The code for doing that is at line 21. Note that, like the eigenvectors, this is a floating-point image.

The last step, before calling cvCalcEigenObjects() is to prepare a data structure called CvTermCriteria. The fields in this structure specify termination criteria for iterative algorithms such as PCA. You can read more about CvTermCriteria options in the CXCORE documentation. Here, we can simply tell it to compute each eigenvalue, then stop, since that's all we need. The code for that is at line 24.

Now that all the output variables are ready, we call cvCalcEigenObjects() to compute the PCA subspace for the training faces. The last parameter, eigenValMat->data.fl, is the pointer to the data values in eigenValMat. Here, we use the data.fl field, not data.i, since this matrix variable holds floating-point data.

Projecting the training faces

Now that you've found a subspace using PCA, you can convert the training images to points in this subspace. As explained last month, this step is called "projecting" the training image. The OpenCV function for this step is called cvEigenDecomposite() (Figure 4, line 22).

The OpenCV function names are, unfortunately, confusing. Not only is the projection function oddly named, but there's also a function named "EigenProjection" that doesn't project image data onto the subspace. In fact, it does the opposite. It restores (uncompresses) projected data, turning it back into the original image. The correct name for doing that is Reconstruction, not Projection!

You'll need a place to put the projected training images. Line 19, in Figure 4, creates a matrix for that purpose. The for loop, at lines 20-29, calls cvEigenDecomposite() once for each training image.

Saving the learned face model

The small bit of extra effort to use OpenCV's CvMat datatype really pays off when it comes time to save the training data! Figure 7 shows the complete code for saving all the data for your learned face representation as an XML file using OpenCV's built-in persistence functions.

Figure 7

Figure 7. (Click for larger view) storeTrainingData()saves all the data for the learned face representation as an XML file using OpenCV's built-in persistence functions.

At line 7, the call to cvOpenFileStorage() opens an XML file named facedata.xml. The last parameter to this function controls the access mode. Here, it's CV_STORAGE_WRITE, which means to create (or overwrite) that file and open it for writing.

To write basic C-language data - integers, floating-point values, and strings - in XML format, you can use functions cvWrite<datatype>(). For example, the call to cvWriteInt(), at line 10, writes the number of eigenvalues as <nEigens>2</nEigens>.

The really nice thing about using OpenCV's persistence functions is that it's just as easy to save complex datatypes, such as an image or matrix. Lines 12 15 add three matrices and an image to the same XML file. The built-in persistence functions save, not only the row and column data, but all the header information as well. Here's the XML that line 13 generates:

<eigenValMat type_id="opencv-matrix">
 <rows>1</rows>
 <cols>2</cols>
 <dt>f</dt>
 <data>
 14279064. 9614034.</data></eigenValMat>

The second parameter to the cvWrite() functions is a string. The string can be anything you like, but to ensure uniqueness, and for clarity's sake, it's usually a good idea to make it the same as your variable name.

When you've finished writing data, close the file and release the file storage as in line 24.

CONTINUED   1   2   3   4   5   Next

.
.
 
Source Code for this Article
 
 
Related Resources:

Download OpenCV
Official OpenCV usergroup
OpenCV Wiki

 
Articles in this series:

Part 1: Introduction to OpenCV

Part 2: Finding Faces in Images

Part 3: Follow that Face!

Part 4: Face Recognition With Eigenface

Part 5: Implementing Eigenface
 
.
bottom margin
Home | OpenCV Resources | Seeing With OpenCV