Update to 2.0.0 tree from current Fremantle build
[opencv] / src / cvaux / cvoneway.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 /*
44  *  one_way_descriptor.cpp
45  *  
46  *
47  *  Created by Victor  Eruhimov on 4/19/09.
48  *  Copyright 2009 Argus Corp. All rights reserved.
49  *
50  */
51
52 #include "_cvaux.h"
53 #include "highgui.h"
54
55 namespace cv
56 {
57
58 static const float pi = (float)CV_PI;
59     
60 static void readPCAFeatures(const char* filename, CvMat** avg, CvMat** eigenvectors);
61 static void eigenvector2image(CvMat* eigenvector, IplImage* img);
62
63 inline CvRect resize_rect(CvRect rect, float alpha)
64 {
65         return cvRect(rect.x + cvRound((float)(0.5*(1 - alpha)*rect.width)), rect.y + cvRound((float)(0.5*(1 - alpha)*rect.height)), 
66                                   cvRound(rect.width*alpha), cvRound(rect.height*alpha));
67 }
68
69 inline CvRect fit_rect_roi_fixedsize(CvRect rect, CvRect roi)
70 {
71         CvRect fit = rect;
72         fit.x = MAX(fit.x, roi.x);
73         fit.y = MAX(fit.y, roi.y);
74     fit.x = MIN(fit.x, roi.x + roi.width - fit.width - 1);
75     fit.y = MIN(fit.y, roi.y + roi.height - fit.height - 1);
76         return(fit);
77 }
78
79 inline CvRect fit_rect_fixedsize(CvRect rect, IplImage* img)
80 {
81         CvRect roi = cvGetImageROI(img);
82         return fit_rect_roi_fixedsize(rect, roi);
83 }
84
85
86 // AffinePose: defines a parameterized affine transformation of an image patch. 
87 // An image patch is rotated on angle phi (in degrees), then scaled lambda1 times
88 // along horizontal and lambda2 times along vertical direction, and then rotated again 
89 // on angle (theta - phi).
90 class AffinePose
91 {
92 public:
93     float phi;
94     float theta;
95     float lambda1;
96     float lambda2;
97 };
98
99 // AffineTransformPatch: generates an affine transformed image patch. 
100 // - src: source image (roi is supported)
101 // - dst: output image. ROI of dst image should be 2 times smaller than ROI of src. 
102 // - pose: parameters of an affine transformation
103 void AffineTransformPatch(IplImage* src, IplImage* dst, AffinePose pose);
104
105 // GenerateAffineTransformFromPose: generates an affine transformation matrix from AffinePose instance
106 // - size: the size of image patch
107 // - pose: affine transformation
108 // - transform: 2x3 transformation matrix
109 void GenerateAffineTransformFromPose(Size size, AffinePose pose, CvMat* transform);
110
111 // Generates a random affine pose
112 AffinePose GenRandomAffinePose();
113
114 const static int num_mean_components = 500;
115 const static float noise_intensity = 0.15f;
116
117
118 CvMat* ConvertImageToMatrix(IplImage* patch)
119 {
120     CvRect roi = cvGetImageROI(patch);
121     CvMat* mat = cvCreateMat(1, roi.width*roi.height, CV_32FC1);
122     
123     if(patch->depth == 32)
124     {
125         for(int y = 0; y < roi.height; y++)
126         {
127             for(int x = 0; x < roi.width; x++)
128             {
129                 mat->data.fl[y*roi.width + x] = *((float*)(patch->imageData + (y + roi.y)*patch->widthStep) + x + roi.x);
130             }
131         }
132     }
133     else if(patch->depth == 8)
134     {
135         for(int y = 0; y < roi.height; y++)
136         {
137             for(int x = 0; x < roi.width; x++)
138             {
139                 mat->data.fl[y*roi.width + x] = (float)(unsigned char)patch->imageData[(y + roi.y)*patch->widthStep + x + roi.x];
140             }
141         }
142     }
143     else
144     {
145         printf("Image depth %d is not supported\n", patch->depth);
146         return 0;
147     }
148     
149     return mat;
150 }
151
152 const OneWayDescriptor* OneWayDescriptorBase::GetDescriptor(int desc_idx) const
153 {
154     return &m_descriptors[desc_idx];
155 }
156
157 OneWayDescriptorBase::OneWayDescriptorBase(Size patch_size, int pose_count, const char* train_path, 
158                                                const char* pca_config, const char* pca_hr_config, 
159                                                const char* pca_desc_config, int pyr_levels, 
160                                                int pca_dim_high, int pca_dim_low) : m_pca_dim_high(pca_dim_high), m_pca_dim_low(pca_dim_low)
161 {
162     m_patch_size = patch_size;
163     m_pose_count = pose_count;
164     m_pyr_levels = pyr_levels;
165     m_poses = 0;
166     m_transforms = 0;
167     
168     m_pca_avg = 0;
169     m_pca_eigenvectors = 0;
170     m_pca_hr_avg = 0;
171     m_pca_hr_eigenvectors = 0;
172     m_pca_descriptors = 0;
173     
174     m_descriptors = 0;
175     
176     if(train_path == 0 || strlen(train_path) == 0)
177     {
178         // skip pca loading
179         return;
180     }
181     char pca_config_filename[1024];
182     sprintf(pca_config_filename, "%s/%s", train_path, pca_config);
183     readPCAFeatures(pca_config_filename, &m_pca_avg, &m_pca_eigenvectors);
184     if(pca_hr_config && strlen(pca_hr_config) > 0)
185     {
186         char pca_hr_config_filename[1024];
187         sprintf(pca_hr_config_filename, "%s/%s", train_path, pca_hr_config);
188         readPCAFeatures(pca_hr_config_filename, &m_pca_hr_avg, &m_pca_hr_eigenvectors);
189     }
190     
191     m_pca_descriptors = new OneWayDescriptor[m_pca_dim_high + 1];
192     if(pca_desc_config && strlen(pca_desc_config) > 0)
193 //    if(0)
194     {
195         char pca_desc_config_filename[1024];
196         sprintf(pca_desc_config_filename, "%s/%s", train_path, pca_desc_config);
197         LoadPCADescriptors(pca_desc_config_filename);
198     }
199     else
200     {
201         printf("Initializing the descriptors...\n");
202         InitializePoseTransforms();
203         CreatePCADescriptors();
204         SavePCADescriptors("pca_descriptors.yml");
205     }
206 //    SavePCADescriptors("./pca_descriptors.yml");
207     
208 }
209
210 OneWayDescriptorBase::~OneWayDescriptorBase()
211 {
212     cvReleaseMat(&m_pca_avg);
213     cvReleaseMat(&m_pca_eigenvectors);
214     
215     if(m_pca_hr_eigenvectors)
216     {
217         delete[] m_pca_descriptors;
218         cvReleaseMat(&m_pca_hr_avg);
219         cvReleaseMat(&m_pca_hr_eigenvectors);
220     }
221     
222     
223     delete []m_descriptors;
224     
225     if(!m_transforms)
226     {
227         delete []m_poses;
228     }
229     
230     for(int i = 0; i < m_pose_count; i++)
231     {
232         cvReleaseMat(&m_transforms[i]);
233     }
234     delete []m_transforms;
235 }
236
237 void OneWayDescriptorBase::InitializePoses()
238 {
239     m_poses = new AffinePose[m_pose_count];
240     for(int i = 0; i < m_pose_count; i++)
241     {
242         m_poses[i] = GenRandomAffinePose();
243     }
244 }
245
246 void OneWayDescriptorBase::InitializeTransformsFromPoses()
247 {
248     m_transforms = new CvMat*[m_pose_count];
249     for(int i = 0; i < m_pose_count; i++)
250     {
251         m_transforms[i] = cvCreateMat(2, 3, CV_32FC1);
252         GenerateAffineTransformFromPose(cvSize(m_patch_size.width*2, m_patch_size.height*2), m_poses[i], m_transforms[i]);
253     }        
254 }
255
256 void OneWayDescriptorBase::InitializePoseTransforms()
257 {
258     InitializePoses();
259     InitializeTransformsFromPoses();
260 }
261
262 void OneWayDescriptorBase::InitializeDescriptor(int desc_idx, IplImage* train_image, const char* feature_label)
263 {
264     m_descriptors[desc_idx].SetPCADimHigh(m_pca_dim_high);
265     m_descriptors[desc_idx].SetPCADimLow(m_pca_dim_low);
266     
267     if(!m_pca_hr_eigenvectors)
268     {
269         m_descriptors[desc_idx].Initialize(m_pose_count, train_image, feature_label);
270     }
271     else
272     {
273         m_descriptors[desc_idx].InitializeFast(m_pose_count, train_image, feature_label, 
274                                       m_pca_hr_avg, m_pca_hr_eigenvectors, m_pca_descriptors);
275     }
276     
277     if(m_pca_avg)
278     {
279         m_descriptors[desc_idx].InitializePCACoeffs(m_pca_avg, m_pca_eigenvectors);    
280     }
281 }
282
283 void OneWayDescriptorBase::FindDescriptor(IplImage* src, Point2f pt, int& desc_idx, int& pose_idx, float& distance) const
284 {
285     CvRect roi = cvRect(cvRound(pt.x - m_patch_size.width/4),
286         cvRound(pt.y - m_patch_size.height/4),
287         m_patch_size.width/2, m_patch_size.height/2);
288     cvSetImageROI(src, roi);
289     
290     FindDescriptor(src, desc_idx, pose_idx, distance);
291     
292     cvResetImageROI(src);
293 }
294
295 void OneWayDescriptorBase::FindDescriptor(IplImage* patch, int& desc_idx, int& pose_idx, float& distance) const
296 {
297 #if 1
298     findOneWayDescriptor(m_train_feature_count, m_descriptors, patch, desc_idx, pose_idx, distance, m_pca_avg, m_pca_eigenvectors);
299 #else
300     const float scale_min = 0.8f;
301     const float scale_max = 1.2f;
302     const float scale_step = 1.1f;
303     float scale = 1.0f;
304     findOneWayDescriptor(m_train_feature_count, m_descriptors, patch, 
305                          scale_min, scale_max, scale_step, desc_idx, pose_idx, distance, scale, 
306                          m_pca_avg, m_pca_eigenvectors);
307 #endif
308 }
309
310 void OneWayDescriptorBase::SetPCAHigh(CvMat* avg, CvMat* eigenvectors)
311 {
312     m_pca_hr_avg = cvCloneMat(avg);
313     m_pca_hr_eigenvectors = cvCloneMat(eigenvectors);
314 }
315
316 void OneWayDescriptorBase::SetPCALow(CvMat* avg, CvMat* eigenvectors)
317 {
318     m_pca_avg = cvCloneMat(avg);
319     m_pca_eigenvectors = cvCloneMat(eigenvectors);
320 }
321
322 void OneWayDescriptorBase::AllocatePCADescriptors()
323 {
324     m_pca_descriptors = new OneWayDescriptor[m_pca_dim_high + 1];
325     for(int i = 0; i < m_pca_dim_high + 1; i++)
326     {
327         m_pca_descriptors[i].SetPCADimHigh(m_pca_dim_high);
328         m_pca_descriptors[i].SetPCADimLow(m_pca_dim_low);
329     }
330 }
331
332 void OneWayDescriptorBase::CreatePCADescriptors()
333 {
334     if(m_pca_descriptors == 0)
335     {
336         AllocatePCADescriptors();
337     }
338     IplImage* frontal = cvCreateImage(m_patch_size, IPL_DEPTH_32F, 1);
339     
340     eigenvector2image(m_pca_hr_avg, frontal);
341     m_pca_descriptors[0].SetTransforms(m_poses, m_transforms);
342     m_pca_descriptors[0].Initialize(m_pose_count, frontal, "", 0);
343     
344     for(int j = 0; j < m_pca_dim_high; j++)
345     {
346         CvMat eigenvector;
347         cvGetSubRect(m_pca_hr_eigenvectors, &eigenvector, cvRect(0, j, m_pca_hr_eigenvectors->cols, 1));
348         eigenvector2image(&eigenvector, frontal);
349         
350         m_pca_descriptors[j + 1].SetTransforms(m_poses, m_transforms);
351         m_pca_descriptors[j + 1].Initialize(m_pose_count, frontal, "", 0);
352         
353         printf("Created descriptor for PCA component %d\n", j);
354     }
355     
356     cvReleaseImage(&frontal);
357 }
358
359
360 int OneWayDescriptorBase::LoadPCADescriptors(const char* filename)
361 {
362     CvMemStorage* storage = cvCreateMemStorage();
363     CvFileStorage* fs = cvOpenFileStorage(filename, storage, CV_STORAGE_READ);
364     
365     // read affine poses
366     CvFileNode* node = cvGetFileNodeByName(fs, 0, "affine poses");
367     if(node != 0)
368     {
369         CvMat* poses = (CvMat*)cvRead(fs, node);
370         if(poses->rows != m_pose_count)
371         {
372             printf("Inconsistency in the number of poses between the class instance and the file! Exiting...\n");
373             cvReleaseMat(&poses);
374             cvReleaseFileStorage(&fs);
375             cvReleaseMemStorage(&storage);
376         }
377         
378         if(m_poses)
379         {
380             delete m_poses;
381         }
382         m_poses = new AffinePose[m_pose_count];
383         for(int i = 0; i < m_pose_count; i++)
384         {
385             m_poses[i].phi = (float)cvmGet(poses, i, 0);
386             m_poses[i].theta = (float)cvmGet(poses, i, 1);
387             m_poses[i].lambda1 = (float)cvmGet(poses, i, 2);
388             m_poses[i].lambda2 = (float)cvmGet(poses, i, 3);
389         }
390         cvReleaseMat(&poses);
391         
392         // now initialize pose transforms
393         InitializeTransformsFromPoses();
394     }
395     
396     node = cvGetFileNodeByName(fs, 0, "pca components number");
397     if(node != 0)
398     {
399         
400         m_pca_dim_high = cvReadInt(node);
401         if(m_pca_descriptors)
402         {
403             delete []m_pca_descriptors;
404         }
405         AllocatePCADescriptors();
406         for(int i = 0; i < m_pca_dim_high + 1; i++)
407         {
408             m_pca_descriptors[i].Allocate(m_pose_count, m_patch_size, 1);
409             m_pca_descriptors[i].SetTransforms(m_poses, m_transforms);
410             char buf[1024];
411             sprintf(buf, "descriptor for pca component %d", i);
412             m_pca_descriptors[i].ReadByName(fs, 0, buf);
413         }
414     }
415     cvReleaseFileStorage(&fs);
416     cvReleaseMemStorage(&storage);
417     
418     return 1;
419 }
420
421 void OneWayDescriptorBase::SavePCADescriptors(const char* filename)
422 {
423     CvMemStorage* storage = cvCreateMemStorage();
424     CvFileStorage* fs = cvOpenFileStorage(filename, storage, CV_STORAGE_WRITE);
425     
426     cvWriteInt(fs, "pca components number", m_pca_dim_high);
427     cvWriteComment(fs, "The first component is the average vector, so the total number of components is <pca components number> + 1", 0);
428     cvWriteInt(fs, "patch width", m_patch_size.width);
429     cvWriteInt(fs, "patch height", m_patch_size.height);
430     
431     // pack the affine transforms into a single CvMat and write them
432     CvMat* poses = cvCreateMat(m_pose_count, 4, CV_32FC1);
433     for(int i = 0; i < m_pose_count; i++)
434     {
435         cvmSet(poses, i, 0, m_poses[i].phi);
436         cvmSet(poses, i, 1, m_poses[i].theta);
437         cvmSet(poses, i, 2, m_poses[i].lambda1);
438         cvmSet(poses, i, 3, m_poses[i].lambda2);
439     }
440     cvWrite(fs, "affine poses", poses);
441     cvReleaseMat(&poses);
442     
443     for(int i = 0; i < m_pca_dim_high + 1; i++)
444     {
445         char buf[1024];
446         sprintf(buf, "descriptor for pca component %d", i);
447         m_pca_descriptors[i].Write(fs, buf);
448     }
449     
450     cvReleaseMemStorage(&storage);
451     cvReleaseFileStorage(&fs);
452 }
453
454 void OneWayDescriptorBase::Allocate(int train_feature_count)
455 {
456     m_train_feature_count = train_feature_count;
457     m_descriptors = new OneWayDescriptor[m_train_feature_count];
458     for(int i = 0; i < m_train_feature_count; i++)
459     {
460         m_descriptors[i].SetPCADimHigh(m_pca_dim_high);
461         m_descriptors[i].SetPCADimLow(m_pca_dim_low);
462     }
463 }
464
465 void OneWayDescriptorBase::InitializeDescriptors(IplImage* train_image, const vector<KeyPoint>& features, 
466                                                  const char* feature_label, int desc_start_idx)
467 {
468     for(int i = 0; i < (int)features.size(); i++)
469     {
470         Point center = features[i].pt;
471         
472         CvRect roi = cvRect(center.x - m_patch_size.width/2, center.y - m_patch_size.height/2, m_patch_size.width, m_patch_size.height);
473         cvResetImageROI(train_image);
474         roi = fit_rect_fixedsize(roi, train_image);
475         cvSetImageROI(train_image, roi);
476 //        roi = cvGetImageROI(train_image);
477         if(roi.width != m_patch_size.width || roi.height != m_patch_size.height)
478         {
479             continue;
480         }
481         
482         InitializeDescriptor(desc_start_idx + i, train_image, feature_label);
483         
484 //        printf("Completed feature %d\n", i);
485         
486     }
487     cvResetImageROI(train_image);
488 }
489
490 void OneWayDescriptorBase::CreateDescriptorsFromImage(IplImage* src, const vector<KeyPoint>& features)
491 {
492     m_train_feature_count = (int)features.size();
493     
494     m_descriptors = new OneWayDescriptor[m_train_feature_count];
495     
496     InitializeDescriptors(src, features);
497 }
498
499 void OneWayDescriptorObject::Allocate(int train_feature_count, int object_feature_count)
500 {
501     OneWayDescriptorBase::Allocate(train_feature_count);
502     
503     m_object_feature_count = object_feature_count;
504     m_part_id = new int[m_object_feature_count];
505 }
506
507
508 void OneWayDescriptorObject::InitializeObjectDescriptors(IplImage* train_image, const vector<KeyPoint>& features, 
509                                                          const char* feature_label, int desc_start_idx, float scale)
510 {
511     InitializeDescriptors(train_image, features, feature_label, desc_start_idx);
512     
513     for(int i = 0; i < (int)features.size(); i++)
514     {
515         Point center = features[i].pt;
516         
517         if(m_part_id)
518         {
519             // remember descriptor part id
520             Point center_scaled = cvPoint(cvRound(center.x*scale), cvRound(center.y*scale));
521             m_part_id[i + desc_start_idx] = MatchPointToPart(center_scaled);
522         }
523     }
524     cvResetImageROI(train_image);
525 }
526
527 int OneWayDescriptorObject::IsDescriptorObject(int desc_idx) const
528 {
529     return desc_idx < m_object_feature_count ? 1 : 0;
530 }
531
532 int OneWayDescriptorObject::MatchPointToPart(Point pt) const
533 {
534     int idx = -1;
535     const float max_dist = 100.f;
536     for(int i = 0; i < (int)m_train_features.size(); i++)
537     {
538         Point2f t(m_train_features[i].pt);
539         Point2f delta(pt.x - t.x, pt.y - t.y);
540         if(delta.dot(delta) < max_dist)
541         {
542             idx = i;
543             break;
544         }
545     }
546     
547     return idx;
548 }
549
550 int OneWayDescriptorObject::GetDescriptorPart(int desc_idx) const
551 {
552     //    return MatchPointToPart(GetDescriptor(desc_idx)->GetCenter());
553     return m_part_id[desc_idx];
554 }
555
556 OneWayDescriptorObject::OneWayDescriptorObject(Size patch_size, int pose_count, const char* train_path, 
557                                                const char* pca_config, const char* pca_hr_config, const char* pca_desc_config, int pyr_levels) :
558 OneWayDescriptorBase(patch_size, pose_count, train_path, pca_config, pca_hr_config, pca_desc_config, pyr_levels)
559 {
560     m_part_id = 0;
561 }
562
563 OneWayDescriptorObject::~OneWayDescriptorObject()
564 {
565     delete m_part_id;
566 }
567
568 vector<KeyPoint> OneWayDescriptorObject::_GetTrainFeatures() const
569 {
570     vector<KeyPoint> features;
571     for(size_t i = 0; i < m_train_features.size(); i++)
572     {
573         features.push_back(m_train_features[i]);
574     }
575     
576     return features;
577 }
578
579 static void eigenvector2image(CvMat* eigenvector, IplImage* img)
580 {
581     CvRect roi = cvGetImageROI(img);
582     if(img->depth == 32)
583     {
584         for(int y = 0; y < roi.height; y++)
585         {
586             for(int x = 0; x < roi.width; x++)
587             {
588                 float val = (float)cvmGet(eigenvector, 0, roi.width*y + x);
589                 *((float*)(img->imageData + (roi.y + y)*img->widthStep) + roi.x + x) = val;
590             }
591         }
592     }
593     else
594     {
595         for(int y = 0; y < roi.height; y++)
596         {
597             for(int x = 0; x < roi.width; x++)
598             {
599                 float val = (float)cvmGet(eigenvector, 0, roi.width*y + x);
600                 img->imageData[(roi.y + y)*img->widthStep + roi.x + x] = (unsigned char)val;
601             }
602         }
603     }
604 }
605
606 static void readPCAFeatures(const char* filename, CvMat** avg, CvMat** eigenvectors)
607 {
608     CvMemStorage* storage = cvCreateMemStorage();
609     CvFileStorage* fs = cvOpenFileStorage(filename, storage, CV_STORAGE_READ);
610     if(!fs)
611     {
612         printf("Cannot open file %s! Exiting!", filename);
613         cvReleaseMemStorage(&storage);
614     }
615     
616     CvFileNode* node = cvGetFileNodeByName(fs, 0, "avg");
617     CvMat* _avg = (CvMat*)cvRead(fs, node);
618     node = cvGetFileNodeByName(fs, 0, "eigenvectors");
619     CvMat* _eigenvectors = (CvMat*)cvRead(fs, node);
620     
621     *avg = cvCloneMat(_avg);
622     *eigenvectors = cvCloneMat(_eigenvectors);
623     
624     cvReleaseFileStorage(&fs);
625     cvReleaseMemStorage(&storage);
626 }
627
628 static inline Point rect_center(CvRect rect)
629 {
630         return cvPoint(rect.x + rect.width/2, rect.y + rect.height/2);
631 }
632
633 void homography_transform(IplImage* frontal, IplImage* result, CvMat* homography)
634 {
635     cvWarpPerspective(frontal, result, homography);
636 }
637
638 AffinePose perturbate_pose(AffinePose pose, float noise)
639 {
640     // perturbate the matrix
641     float noise_mult_factor = 1 + (0.5f - float(rand())/RAND_MAX)*noise;
642     float noise_add_factor = noise_mult_factor - 1;
643     
644     AffinePose pose_pert = pose;
645     pose_pert.phi += noise_add_factor;
646     pose_pert.theta += noise_mult_factor;
647     pose_pert.lambda1 *= noise_mult_factor;
648     pose_pert.lambda2 *= noise_mult_factor;
649
650     return pose_pert;
651 }
652
653 void generate_mean_patch(IplImage* frontal, IplImage* result, AffinePose pose, int pose_count, float noise)
654 {
655     IplImage* sum = cvCreateImage(cvSize(result->width, result->height), IPL_DEPTH_32F, 1);
656     IplImage* workspace = cvCloneImage(result);
657     IplImage* workspace_float = cvCloneImage(sum);
658     
659     cvSetZero(sum);
660     for(int i = 0; i < pose_count; i++)
661     {
662         AffinePose pose_pert = perturbate_pose(pose, noise);
663         
664         AffineTransformPatch(frontal, workspace, pose_pert);
665         cvConvertScale(workspace, workspace_float);
666         cvAdd(sum, workspace_float, sum);
667     }
668     
669     cvConvertScale(sum, result, 1.0f/pose_count);
670             
671     cvReleaseImage(&workspace);
672     cvReleaseImage(&sum);
673     cvReleaseImage(&workspace_float);
674 }
675
676 void generate_mean_patch_fast(IplImage* /*frontal*/, IplImage* /*result*/, AffinePose /*pose*/, 
677                     CvMat* /*pca_hr_avg*/, CvMat* pca_hr_eigenvectors,
678                     const OneWayDescriptor* /*pca_descriptors*/)
679 {
680     for(int i = 0; i < pca_hr_eigenvectors->cols; i++)
681     {
682     }
683 }
684
685
686 OneWayDescriptor::OneWayDescriptor()
687 {
688     m_pose_count = 0;
689     m_samples = 0;
690     m_pca_coeffs = 0;
691     m_affine_poses = 0;
692     m_transforms = 0;
693 }
694
695 OneWayDescriptor::~OneWayDescriptor()
696 {
697     if(m_pose_count)
698     {
699         for(int i = 0; i < m_pose_count; i++)
700         {
701             cvReleaseImage(&m_samples[i]);
702             cvReleaseMat(&m_pca_coeffs[i]);
703         }
704         delete []m_samples;
705         delete []m_pca_coeffs;
706         
707         if(!m_transforms)
708         {
709             delete []m_affine_poses;
710         }
711     }
712 }
713
714 void OneWayDescriptor::Allocate(int pose_count, Size size, int nChannels)
715 {
716     m_pose_count = pose_count;
717     m_samples = new IplImage* [m_pose_count];
718     m_pca_coeffs = new CvMat* [m_pose_count];
719     m_patch_size = cvSize(size.width/2, size.height/2);
720     
721     if(!m_transforms)
722     {
723         m_affine_poses = new AffinePose[m_pose_count];
724     }
725     
726     int length = m_pca_dim_low;//roi.width*roi.height;
727     for(int i = 0; i < m_pose_count; i++)
728     {
729         m_samples[i] = cvCreateImage(cvSize(size.width/2, size.height/2), IPL_DEPTH_32F, nChannels);
730         m_pca_coeffs[i] = cvCreateMat(1, length, CV_32FC1);
731     }
732 }
733
734 void cvmSet2DPoint(CvMat* matrix, int row, int col, CvPoint2D32f point)
735 {
736     cvmSet(matrix, row, col, point.x);
737     cvmSet(matrix, row, col + 1, point.y);
738 }
739
740 void cvmSet3DPoint(CvMat* matrix, int row, int col, CvPoint3D32f point)
741 {
742     cvmSet(matrix, row, col, point.x);
743     cvmSet(matrix, row, col + 1, point.y);
744     cvmSet(matrix, row, col + 2, point.z);
745 }
746
747 AffinePose GenRandomAffinePose()
748 {
749     const float scale_min = 0.8f;
750     const float scale_max = 1.2f;
751     AffinePose pose;
752     pose.theta = float(rand())/RAND_MAX*120 - 60;
753     pose.phi = float(rand())/RAND_MAX*360;
754     pose.lambda1 = scale_min + float(rand())/RAND_MAX*(scale_max - scale_min);
755     pose.lambda2 = scale_min + float(rand())/RAND_MAX*(scale_max - scale_min);
756     
757     return pose;
758 }
759
760 void GenerateAffineTransformFromPose(Size size, AffinePose pose, CvMat* transform)
761 {
762     CvMat* temp = cvCreateMat(3, 3, CV_32FC1);
763     CvMat* final = cvCreateMat(3, 3, CV_32FC1);
764     cvmSet(temp, 2, 0, 0.0f);
765     cvmSet(temp, 2, 1, 0.0f);
766     cvmSet(temp, 2, 2, 1.0f);
767
768     CvMat rotation;
769     cvGetSubRect(temp, &rotation, cvRect(0, 0, 3, 2));
770     
771     cv2DRotationMatrix(cvPoint2D32f(size.width/2, size.height/2), pose.phi, 1.0, &rotation);
772     cvCopy(temp, final);
773
774     cvmSet(temp, 0, 0, pose.lambda1);
775     cvmSet(temp, 0, 1, 0.0f);
776     cvmSet(temp, 1, 0, 0.0f);
777     cvmSet(temp, 1, 1, pose.lambda2);
778     cvmSet(temp, 0, 2, size.width/2*(1 - pose.lambda1));
779     cvmSet(temp, 1, 2, size.height/2*(1 - pose.lambda2));
780     cvMatMul(temp, final, final);
781     
782     cv2DRotationMatrix(cvPoint2D32f(size.width/2, size.height/2), pose.theta - pose.phi, 1.0, &rotation);
783     cvMatMul(temp, final, final);
784
785     cvGetSubRect(final, &rotation, cvRect(0, 0, 3, 2));
786     cvCopy(&rotation, transform);   
787     
788     cvReleaseMat(&temp);
789     cvReleaseMat(&final);
790 }
791
792 void AffineTransformPatch(IplImage* src, IplImage* dst, AffinePose pose)
793 {
794     CvRect src_large_roi = cvGetImageROI(src);
795     
796     IplImage* temp = cvCreateImage(cvSize(src_large_roi.width, src_large_roi.height), IPL_DEPTH_32F, src->nChannels);
797     cvSetZero(temp);
798     IplImage* temp2 = cvCloneImage(temp);
799     CvMat* rotation_phi = cvCreateMat(2, 3, CV_32FC1);
800     
801     Size new_size = cvSize(cvRound(temp->width*pose.lambda1), cvRound(temp->height*pose.lambda2));
802     IplImage* temp3 = cvCreateImage(new_size, IPL_DEPTH_32F, src->nChannels);
803
804     cvConvertScale(src, temp);
805     cvResetImageROI(temp);
806     
807     
808     cv2DRotationMatrix(cvPoint2D32f(temp->width/2, temp->height/2), pose.phi, 1.0, rotation_phi);
809     cvWarpAffine(temp, temp2, rotation_phi);
810     
811     cvSetZero(temp);
812     
813     cvResize(temp2, temp3);
814     
815     cv2DRotationMatrix(cvPoint2D32f(temp3->width/2, temp3->height/2), pose.theta - pose.phi, 1.0, rotation_phi);
816     cvWarpAffine(temp3, temp, rotation_phi);
817     
818     cvSetImageROI(temp, cvRect(temp->width/2 - src_large_roi.width/4, temp->height/2 - src_large_roi.height/4, 
819                                src_large_roi.width/2, src_large_roi.height/2));
820     cvConvertScale(temp, dst);   
821     cvReleaseMat(&rotation_phi);
822     
823     cvReleaseImage(&temp3);
824     cvReleaseImage(&temp2);
825     cvReleaseImage(&temp);
826 }
827
828 void OneWayDescriptor::GenerateSamples(int pose_count, IplImage* frontal, int norm)
829 {
830 /*    if(m_transforms)
831     {
832         GenerateSamplesWithTransforms(pose_count, frontal);
833         return;
834     }
835 */    
836     CvRect roi = cvGetImageROI(frontal);
837     IplImage* patch_8u = cvCreateImage(cvSize(roi.width/2, roi.height/2), frontal->depth, frontal->nChannels);
838     for(int i = 0; i < pose_count; i++)
839     {
840         if(!m_transforms)
841         {
842             m_affine_poses[i] = GenRandomAffinePose();
843         }
844         //AffineTransformPatch(frontal, patch_8u, m_affine_poses[i]);
845         generate_mean_patch(frontal, patch_8u, m_affine_poses[i], num_mean_components, noise_intensity);
846
847         float scale = 1.0f;
848         if(norm)
849         {
850             float sum = (float)cvSum(patch_8u).val[0];
851             scale = 1.f/sum;
852         }
853         cvConvertScale(patch_8u, m_samples[i], scale);
854         
855 #if 0
856         double maxval;
857         cvMinMaxLoc(m_samples[i], 0, &maxval);
858         IplImage* test = cvCreateImage(cvSize(roi.width/2, roi.height/2), IPL_DEPTH_8U, 1);
859         cvConvertScale(m_samples[i], test, 255.0/maxval);
860         cvNamedWindow("1", 1);
861         cvShowImage("1", test);
862         cvWaitKey(0);
863 #endif
864     }
865     cvReleaseImage(&patch_8u);
866 }
867
868 void OneWayDescriptor::GenerateSamplesFast(IplImage* frontal, CvMat* pca_hr_avg, 
869                                              CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors)
870 {
871     CvMat* pca_coeffs = cvCreateMat(1, pca_hr_eigenvectors->cols, CV_32FC1);
872     double maxval;
873     cvMinMaxLoc(frontal, 0, &maxval);
874     CvMat* frontal_data = ConvertImageToMatrix(frontal);
875     
876     float sum = (float)cvSum(frontal_data).val[0];
877     cvConvertScale(frontal_data, frontal_data, 1.0f/sum);
878     cvProjectPCA(frontal_data, pca_hr_avg, pca_hr_eigenvectors, pca_coeffs);
879     for(int i = 0; i < m_pose_count; i++)
880     {
881         cvSetZero(m_samples[i]);
882         for(int j = 0; j < m_pca_dim_high; j++)
883         {
884             float coeff = (float)cvmGet(pca_coeffs, 0, j);
885             IplImage* patch = pca_descriptors[j + 1].GetPatch(i);
886             cvAddWeighted(m_samples[i], 1.0, patch, coeff, 0, m_samples[i]);
887             
888 #if 0
889             printf("coeff%d = %f\n", j, coeff);
890             IplImage* test = cvCreateImage(cvSize(12, 12), IPL_DEPTH_8U, 1);
891             double maxval;
892             cvMinMaxLoc(patch, 0, &maxval);
893             cvConvertScale(patch, test, 255.0/maxval);
894             cvNamedWindow("1", 1);
895             cvShowImage("1", test);
896             cvWaitKey(0);
897 #endif
898         }
899         
900         cvAdd(pca_descriptors[0].GetPatch(i), m_samples[i], m_samples[i]);
901         float sum = (float)cvSum(m_samples[i]).val[0];
902         cvConvertScale(m_samples[i], m_samples[i], 1.0/sum);
903         
904 #if 0
905         IplImage* test = cvCreateImage(cvSize(12, 12), IPL_DEPTH_8U, 1);
906 /*        IplImage* temp1 = cvCreateImage(cvSize(12, 12), IPL_DEPTH_32F, 1);
907         eigenvector2image(pca_hr_avg, temp1);
908         IplImage* test = cvCreateImage(cvSize(12, 12), IPL_DEPTH_8U, 1);
909         cvAdd(m_samples[i], temp1, temp1);
910         cvMinMaxLoc(temp1, 0, &maxval);
911         cvConvertScale(temp1, test, 255.0/maxval);*/
912         cvMinMaxLoc(m_samples[i], 0, &maxval);
913         cvConvertScale(m_samples[i], test, 255.0/maxval);
914
915         cvNamedWindow("1", 1);
916         cvShowImage("1", frontal);
917         cvNamedWindow("2", 1);
918         cvShowImage("2", test);
919         cvWaitKey(0);
920 #endif
921     }
922     
923     cvReleaseMat(&pca_coeffs);
924     cvReleaseMat(&frontal_data);
925 }
926
927 void OneWayDescriptor::SetTransforms(AffinePose* poses, CvMat** transforms)
928 {
929     if(m_affine_poses)
930     {
931         delete []m_affine_poses;
932     }
933     
934     m_affine_poses = poses;
935     m_transforms = transforms;
936 }
937
938 void OneWayDescriptor::Initialize(int pose_count, IplImage* frontal, const char* feature_name, int norm)
939 {
940     m_feature_name = String(feature_name);
941     CvRect roi = cvGetImageROI(frontal);
942     m_center = rect_center(roi);
943     
944     Allocate(pose_count, cvSize(roi.width, roi.height), frontal->nChannels);
945     
946     GenerateSamples(pose_count, frontal, norm);
947 }
948
949 void OneWayDescriptor::InitializeFast(int pose_count, IplImage* frontal, const char* feature_name, 
950                     CvMat* pca_hr_avg, CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors)
951 {
952     if(pca_hr_avg == 0)
953     {
954         Initialize(pose_count, frontal, feature_name, 1);
955         return;
956     }
957     m_feature_name = String(feature_name);
958     CvRect roi = cvGetImageROI(frontal);
959     m_center = rect_center(roi);
960     
961     Allocate(pose_count, cvSize(roi.width, roi.height), frontal->nChannels);
962     
963     GenerateSamplesFast(frontal, pca_hr_avg, pca_hr_eigenvectors, pca_descriptors);
964 }
965
966 void OneWayDescriptor::InitializePCACoeffs(CvMat* avg, CvMat* eigenvectors)
967 {
968     for(int i = 0; i < m_pose_count; i++)
969     {
970         ProjectPCASample(m_samples[i], avg, eigenvectors, m_pca_coeffs[i]);
971     }
972 }
973
974 void OneWayDescriptor::ProjectPCASample(IplImage* patch, CvMat* avg, CvMat* eigenvectors, CvMat* pca_coeffs) const
975 {
976     CvMat* patch_mat = ConvertImageToMatrix(patch);
977 //    CvMat eigenvectorsr;
978 //    cvGetSubRect(eigenvectors, &eigenvectorsr, cvRect(0, 0, eigenvectors->cols, pca_coeffs->cols));
979     CvMat* temp = cvCreateMat(1, eigenvectors->cols, CV_32FC1);
980     cvProjectPCA(patch_mat, avg, eigenvectors, temp);
981     CvMat temp1;
982     cvGetSubRect(temp, &temp1, cvRect(0, 0, pca_coeffs->cols, 1));
983     cvCopy(&temp1, pca_coeffs);
984     
985     cvReleaseMat(&temp);
986     cvReleaseMat(&patch_mat);
987 }
988
989 void OneWayDescriptor::EstimatePosePCA(IplImage* patch, int& pose_idx, float& distance, CvMat* avg, CvMat* eigenvectors) const
990 {
991     if(avg == 0)
992     {
993         // do not use pca
994         EstimatePose(patch, pose_idx, distance);
995         return;
996     }
997     
998     CvRect roi = cvGetImageROI(patch);
999     CvMat* pca_coeffs = cvCreateMat(1, m_pca_dim_low, CV_32FC1);
1000     
1001     IplImage* patch_32f = cvCreateImage(cvSize(roi.width, roi.height), IPL_DEPTH_32F, 1);
1002     float sum = (float)cvSum(patch).val[0];
1003     cvConvertScale(patch, patch_32f, 1.0f/sum);
1004  
1005     ProjectPCASample(patch_32f, avg, eigenvectors, pca_coeffs);
1006
1007     distance = 1e10;
1008     pose_idx = -1;
1009     
1010     for(int i = 0; i < m_pose_count; i++)
1011     {
1012         float dist = (float)cvNorm(m_pca_coeffs[i], pca_coeffs);
1013         if(dist < distance)
1014         {
1015             distance = dist;
1016             pose_idx = i;
1017         }
1018     }
1019     
1020     cvReleaseMat(&pca_coeffs);
1021     cvReleaseImage(&patch_32f);
1022 }
1023
1024 void OneWayDescriptor::EstimatePose(IplImage* patch, int& pose_idx, float& distance) const
1025 {
1026     distance = 1e10;
1027     pose_idx = -1;
1028     
1029     CvRect roi = cvGetImageROI(patch);
1030     IplImage* patch_32f = cvCreateImage(cvSize(roi.width, roi.height), IPL_DEPTH_32F, patch->nChannels);
1031     float sum = (float)cvSum(patch).val[0];
1032     cvConvertScale(patch, patch_32f, 1/sum);
1033     
1034     for(int i = 0; i < m_pose_count; i++)
1035     {
1036         if(m_samples[i]->width != patch_32f->width || m_samples[i]->height != patch_32f->height)
1037         {
1038             continue;
1039         }
1040         float dist = (float)cvNorm(m_samples[i], patch_32f);
1041         if(dist < distance)
1042         {
1043             distance = dist;
1044             pose_idx = i;
1045         }
1046         
1047 #if 0
1048         IplImage* img1 = cvCreateImage(cvSize(roi.width, roi.height), IPL_DEPTH_8U, 1);
1049         IplImage* img2 = cvCreateImage(cvSize(roi.width, roi.height), IPL_DEPTH_8U, 1);
1050         double maxval;
1051         cvMinMaxLoc(m_samples[i], 0, &maxval);
1052         cvConvertScale(m_samples[i], img1, 255.0/maxval);
1053         cvMinMaxLoc(patch_32f, 0, &maxval);
1054         cvConvertScale(patch_32f, img2, 255.0/maxval);
1055         
1056         cvNamedWindow("1", 1);
1057         cvShowImage("1", img1);
1058         cvNamedWindow("2", 1);
1059         cvShowImage("2", img2);
1060         printf("Distance = %f\n", dist);
1061         cvWaitKey(0);
1062 #endif
1063     }
1064     
1065     cvReleaseImage(&patch_32f);
1066 }
1067
1068 void OneWayDescriptor::Save(const char* path)
1069 {
1070     for(int i = 0; i < m_pose_count; i++)
1071     {
1072         char buf[1024];
1073         sprintf(buf, "%s/patch_%04d.jpg", path, i);
1074         IplImage* patch = cvCreateImage(cvSize(m_samples[i]->width, m_samples[i]->height), IPL_DEPTH_8U, m_samples[i]->nChannels);
1075         
1076         double maxval;
1077         cvMinMaxLoc(m_samples[i], 0, &maxval);
1078         cvConvertScale(m_samples[i], patch, 255/maxval);
1079         
1080         cvSaveImage(buf, patch);
1081         
1082         cvReleaseImage(&patch);
1083     }
1084 }
1085
1086 void OneWayDescriptor::Write(CvFileStorage* fs, const char* name)
1087 {
1088     CvMat* mat = cvCreateMat(m_pose_count, m_samples[0]->width*m_samples[0]->height, CV_32FC1);
1089     
1090     // prepare data to write as a single matrix
1091     for(int i = 0; i < m_pose_count; i++)
1092     {
1093         for(int y = 0; y < m_samples[i]->height; y++)
1094         {
1095             for(int x = 0; x < m_samples[i]->width; x++)
1096             {
1097                 float val = *((float*)(m_samples[i]->imageData + m_samples[i]->widthStep*y) + x);
1098                 cvmSet(mat, i, y*m_samples[i]->width + x, val);
1099             }
1100         }
1101     }
1102     
1103     cvWrite(fs, name, mat);
1104     
1105     cvReleaseMat(&mat);
1106 }
1107
1108 int OneWayDescriptor::ReadByName(CvFileStorage* fs, CvFileNode* parent, const char* name)
1109 {
1110     CvMat* mat = (CvMat*)cvReadByName(fs, parent, name);
1111     if(!mat)
1112     {
1113         return 0;
1114     }
1115     
1116
1117     for(int i = 0; i < m_pose_count; i++)
1118     {
1119         for(int y = 0; y < m_samples[i]->height; y++)
1120         {
1121             for(int x = 0; x < m_samples[i]->width; x++)
1122             {
1123                 float val = (float)cvmGet(mat, i, y*m_samples[i]->width + x);
1124                 *((float*)(m_samples[i]->imageData + y*m_samples[i]->widthStep) + x) = val;
1125             }
1126         }
1127     }
1128             
1129     cvReleaseMat(&mat);
1130     return 1;
1131 }
1132
1133 IplImage* OneWayDescriptor::GetPatch(int index)
1134 {
1135     return m_samples[index];
1136 }
1137
1138 AffinePose OneWayDescriptor::GetPose(int index) const
1139 {
1140     return m_affine_poses[index];
1141 }
1142
1143 void findOneWayDescriptor(int desc_count, const OneWayDescriptor* descriptors,
1144                           IplImage* patch, int& desc_idx, int& pose_idx, float& distance, 
1145                           CvMat* avg, CvMat* eigenvectors)
1146 {
1147     desc_idx = -1;
1148     pose_idx = -1;
1149     distance = 1e10;
1150     for(int i = 0; i < desc_count; i++)
1151     {
1152         int _pose_idx = -1;
1153         float _distance = 0;
1154
1155 #if 0
1156         descriptors[i].EstimatePose(patch, _pose_idx, _distance);
1157 #else
1158         descriptors[i].EstimatePosePCA(patch, _pose_idx, _distance, avg, eigenvectors);
1159 #endif
1160         
1161         if(_distance < distance)
1162         {
1163             desc_idx = i;
1164             pose_idx = _pose_idx;
1165             distance = _distance;
1166         }
1167     }
1168 }
1169
1170 void findOneWayDescriptor(int desc_count, const OneWayDescriptor* descriptors, IplImage* patch, 
1171                           float scale_min, float scale_max, float scale_step,
1172                           int& desc_idx, int& pose_idx, float& distance, float& scale, 
1173                           CvMat* avg, CvMat* eigenvectors)
1174 {
1175     Size patch_size = descriptors[0].GetPatchSize();
1176     IplImage* input_patch = cvCreateImage(patch_size, IPL_DEPTH_8U, 1);
1177     CvRect roi = cvGetImageROI(patch);
1178     int _desc_idx, _pose_idx;
1179     float _distance;
1180     distance = 1e10;
1181     for(float cur_scale = scale_min; cur_scale < scale_max; cur_scale *= scale_step)
1182     {
1183         CvRect roi_scaled = resize_rect(roi, cur_scale);
1184         cvSetImageROI(patch, roi_scaled);
1185         cvResize(patch, input_patch);
1186         
1187 #if 0
1188         if(roi.x > 244 && roi.y < 200)
1189         {
1190             cvNamedWindow("1", 1);
1191             cvShowImage("1", input_patch);
1192             cvWaitKey(0);
1193         }
1194 #endif
1195         
1196         findOneWayDescriptor(desc_count, descriptors, input_patch, _desc_idx, _pose_idx, _distance, avg, eigenvectors);
1197         if(_distance < distance)
1198         {
1199             distance = _distance;
1200             desc_idx = _desc_idx;
1201             _pose_idx = pose_idx;
1202             scale = cur_scale;
1203         }
1204     }
1205     cvSetImageROI(patch, roi);
1206     
1207     cvReleaseImage(&input_patch);
1208 }
1209
1210 const char* OneWayDescriptor::GetFeatureName() const
1211 {
1212     return m_feature_name.c_str();
1213 }
1214
1215 Point OneWayDescriptor::GetCenter() const
1216 {
1217     return m_center;
1218 }
1219
1220 }
1221