]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/src/cv/cvhaar.cpp
loading/saving haar classifier fixed
[opencv.git] / opencv / src / cv / cvhaar.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 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 /* Haar features calculation */
43
44 #include "_cv.h"
45 #include <stdio.h>
46
47 /* these settings affect the quality of detection: change with care */
48 #define CV_ADJUST_FEATURES 1
49 #define CV_ADJUST_WEIGHTS  0
50
51 typedef int sumtype;
52 typedef double sqsumtype;
53
54 typedef struct CvHidHaarFeature
55 {
56     struct
57     {
58         sumtype *p0, *p1, *p2, *p3;
59         float weight;
60     }
61     rect[CV_HAAR_FEATURE_MAX];
62 }
63 CvHidHaarFeature;
64
65
66 typedef struct CvHidHaarTreeNode
67 {
68     CvHidHaarFeature feature;
69     float threshold;
70     int left;
71     int right;
72 }
73 CvHidHaarTreeNode;
74
75
76 typedef struct CvHidHaarClassifier
77 {
78     int count;
79     //CvHaarFeature* orig_feature;
80     CvHidHaarTreeNode* node;
81     float* alpha;
82 }
83 CvHidHaarClassifier;
84
85
86 typedef struct CvHidHaarStageClassifier
87 {
88     int  count;
89     float threshold;
90     CvHidHaarClassifier* classifier;
91     int two_rects;
92     
93     struct CvHidHaarStageClassifier* next;
94     struct CvHidHaarStageClassifier* child;
95     struct CvHidHaarStageClassifier* parent;
96 }
97 CvHidHaarStageClassifier;
98
99
100 struct CvHidHaarClassifierCascade
101 {
102     int  count;
103     int  is_stump_based;
104     int  has_tilted_features;
105     int  is_tree;
106     double inv_window_area;
107     CvMat sum, sqsum, tilted;
108     CvHidHaarStageClassifier* stage_classifier;
109     sqsumtype *pq0, *pq1, *pq2, *pq3;
110     sumtype *p0, *p1, *p2, *p3;
111 };
112
113
114 static CvHaarClassifierCascade*
115 icvCreateHaarClassifierCascade( int stage_count )
116 {
117     CvHaarClassifierCascade* cascade = 0;
118     
119     CV_FUNCNAME( "icvCreateHaarClassifierCascade" );
120
121     __BEGIN__;
122
123     int block_size = sizeof(*cascade) + stage_count*sizeof(*cascade->stage_classifier);
124
125     if( stage_count <= 0 )
126         CV_ERROR( CV_StsOutOfRange, "Number of stages should be positive" );
127
128     CV_CALL( cascade = (CvHaarClassifierCascade*)cvAlloc( block_size ));
129     memset( cascade, 0, block_size );
130
131     cascade->stage_classifier = (CvHaarStageClassifier*)(cascade + 1);
132     cascade->flags = CV_HAAR_MAGIC_VAL;
133     cascade->count = stage_count;
134
135     __END__;
136
137     return cascade;
138 }
139
140 /* create more efficient internal representation of haar classifier cascade */
141 static CvHidHaarClassifierCascade*
142 icvCreateHidHaarClassifierCascade( CvHaarClassifierCascade* cascade )
143 {
144     CvHidHaarClassifierCascade* out = 0;
145
146     CV_FUNCNAME( "icvCreateHidHaarClassifierCascade" );
147
148     __BEGIN__;
149
150     int i, j, k, l;
151     int datasize;
152     int total_classifiers = 0;
153     int total_nodes = 0;
154     char errorstr[100];
155     CvHidHaarClassifier* haar_classifier_ptr;
156     CvHidHaarTreeNode* haar_node_ptr;
157     CvSize orig_window_size;
158     int has_tilted_features = 0;
159
160     if( !CV_IS_HAAR_CLASSIFIER(cascade) )
161         CV_ERROR( !cascade ? CV_StsNullPtr : CV_StsBadArg, "Invalid classifier pointer" );
162
163     if( cascade->hid_cascade )
164         CV_ERROR( CV_StsError, "hid_cascade has been already created" );
165
166     if( !cascade->stage_classifier )
167         CV_ERROR( CV_StsNullPtr, "" );
168
169     if( cascade->count <= 0 )
170         CV_ERROR( CV_StsOutOfRange, "Negative number of cascade stages" );
171
172     orig_window_size = cascade->orig_window_size;
173     
174     /* check input structure correctness and calculate total memory size needed for
175        internal representation of the classifier cascade */
176     for( i = 0; i < cascade->count; i++ )
177     {
178         CvHaarStageClassifier* stage_classifier = cascade->stage_classifier + i;
179
180         if( !stage_classifier->classifier ||
181             stage_classifier->count <= 0 )
182         {
183             sprintf( errorstr, "header of the stage classifier #%d is invalid "
184                      "(has null pointers or non-positive classfier count)", i );
185             CV_ERROR( CV_StsError, errorstr );
186         }
187
188         total_classifiers += stage_classifier->count;
189
190         for( j = 0; j < stage_classifier->count; j++ )
191         {
192             CvHaarClassifier* classifier = stage_classifier->classifier + j;
193
194             total_nodes += classifier->count;
195             for( l = 0; l < classifier->count; l++ )
196             {
197                 for( k = 0; k < CV_HAAR_FEATURE_MAX; k++ )
198                 {
199                     if( classifier->haar_feature[l].rect[k].weight )
200                     {
201                         CvRect r = classifier->haar_feature[l].rect[k].r;
202                         int tilted = classifier->haar_feature[l].tilted;
203                         has_tilted_features |= tilted != 0;
204                         if( r.width < 0 || r.height < 0 || r.y < 0 ||
205                             r.x + r.width > orig_window_size.width
206                             ||
207                             (!tilted &&
208                             (r.x < 0 || r.y + r.height > orig_window_size.height))
209                             ||
210                             (tilted && (r.x - r.height < 0 ||
211                             r.y + r.width + r.height > orig_window_size.height)))
212                         {
213                             sprintf( errorstr, "rectangle #%d of the classifier #%d of "
214                                      "the stage classifier #%d is not inside "
215                                      "the reference (original) cascade window", k, j, i );
216                             CV_ERROR( CV_StsNullPtr, errorstr );
217                         }
218                     }
219                 }
220             }
221         }
222     }
223
224     // this is an upper boundary for the whole hidden cascade size
225     datasize = sizeof(CvHidHaarClassifierCascade) +
226                sizeof(CvHidHaarStageClassifier)*cascade->count +
227                sizeof(CvHidHaarClassifier) * total_classifiers +
228                sizeof(CvHidHaarTreeNode) * total_nodes +
229                sizeof(void*)*(total_nodes + total_classifiers);
230
231     CV_CALL( out = (CvHidHaarClassifierCascade*)cvAlloc( datasize ));
232     memset( out, 0, sizeof(*out) );
233
234     /* init header */
235     out->count = cascade->count;
236     out->stage_classifier = (CvHidHaarStageClassifier*)(out + 1);
237     haar_classifier_ptr = (CvHidHaarClassifier*)(out->stage_classifier + cascade->count);
238     haar_node_ptr = (CvHidHaarTreeNode*)(haar_classifier_ptr + total_classifiers);
239
240     out->is_stump_based = 1;
241     out->has_tilted_features = has_tilted_features;
242     out->is_tree = 0;
243
244     /* initialize internal representation */
245     for( i = 0; i < cascade->count; i++ )
246     {
247         CvHaarStageClassifier* stage_classifier = cascade->stage_classifier + i;
248         CvHidHaarStageClassifier* hid_stage_classifier = out->stage_classifier + i;
249
250         hid_stage_classifier->count = stage_classifier->count;
251         hid_stage_classifier->threshold = stage_classifier->threshold;
252         hid_stage_classifier->classifier = haar_classifier_ptr;
253         hid_stage_classifier->two_rects = 1;
254         haar_classifier_ptr += stage_classifier->count;
255
256         hid_stage_classifier->parent = (stage_classifier->parent == -1)
257             ? NULL : out->stage_classifier + stage_classifier->parent;
258         hid_stage_classifier->next = (stage_classifier->next == -1)
259             ? NULL : out->stage_classifier + stage_classifier->next;
260         hid_stage_classifier->child = (stage_classifier->child == -1)
261             ? NULL : out->stage_classifier + stage_classifier->child;
262         
263         out->is_tree |= hid_stage_classifier->next != NULL;
264
265         for( j = 0; j < stage_classifier->count; j++ )
266         {
267             CvHaarClassifier* classifier = stage_classifier->classifier + j;
268             CvHidHaarClassifier* hid_classifier = hid_stage_classifier->classifier + j;
269             int node_count = classifier->count;
270             float* alpha_ptr = (float*)(haar_node_ptr + node_count);
271
272             hid_classifier->count = node_count;
273             hid_classifier->node = haar_node_ptr;
274             hid_classifier->alpha = alpha_ptr;
275             
276             for( l = 0; l < node_count; l++ )
277             {
278                 CvHidHaarTreeNode* node = hid_classifier->node + l;
279                 CvHaarFeature* feature = classifier->haar_feature + l;
280                 memset( node, -1, sizeof(*node) );
281                 node->threshold = classifier->threshold[l];
282                 node->left = classifier->left[l];
283                 node->right = classifier->right[l];
284
285                 if( feature->rect[2].weight == 0 ||
286                     feature->rect[2].r.width == 0 ||
287                     feature->rect[2].r.height == 0 )
288                     memset( &(node->feature.rect[2]), 0, sizeof(node->feature.rect[2]) );
289                 else
290                     hid_stage_classifier->two_rects = 0;
291             }
292
293             memcpy( alpha_ptr, classifier->alpha, (node_count+1)*sizeof(alpha_ptr[0]));
294             haar_node_ptr =
295                 (CvHidHaarTreeNode*)cvAlignPtr(alpha_ptr+node_count+1, sizeof(void*));
296
297             out->is_stump_based &= node_count == 1;
298         }
299     }
300
301     cascade->hid_cascade = out;
302     assert( (char*)haar_node_ptr - (char*)out <= datasize );
303
304     __END__;
305
306     if( cvGetErrStatus() < 0 )
307         cvFree( (void**)&out );
308
309     return out;
310 }
311
312
313 #define sum_elem_ptr(sum,row,col)  \
314     ((sumtype*)CV_MAT_ELEM_PTR_FAST((sum),(row),(col),sizeof(sumtype)))
315
316 #define sqsum_elem_ptr(sqsum,row,col)  \
317     ((sqsumtype*)CV_MAT_ELEM_PTR_FAST((sqsum),(row),(col),sizeof(sqsumtype)))
318
319 #define calc_sum(rect,offset) \
320     ((rect).p0[offset] - (rect).p1[offset] - (rect).p2[offset] + (rect).p3[offset])
321
322
323 CV_IMPL void
324 cvSetImagesForHaarClassifierCascade( CvHaarClassifierCascade* _cascade,
325                                      const CvArr* _sum,
326                                      const CvArr* _sqsum,
327                                      const CvArr* _tilted_sum,
328                                      double scale )
329 {
330     CV_FUNCNAME("cvSetImagesForHaarClassifierCascade");
331
332     __BEGIN__;
333
334     CvMat sum_stub, *sum = (CvMat*)_sum;
335     CvMat sqsum_stub, *sqsum = (CvMat*)_sqsum;
336     CvMat tilted_stub, *tilted = (CvMat*)_tilted_sum;
337     CvHidHaarClassifierCascade* cascade;
338     int coi0 = 0, coi1 = 0;
339     int i, j, k, l;
340     CvRect equ_rect;
341     double weight_scale;
342
343     if( !CV_IS_HAAR_CLASSIFIER(_cascade) )
344         CV_ERROR( !_cascade ? CV_StsNullPtr : CV_StsBadArg, "Invalid classifier pointer" );
345
346     if( scale <= 0 )
347         CV_ERROR( CV_StsOutOfRange, "Scale must be positive" );
348
349     CV_CALL( sum = cvGetMat( sum, &sum_stub, &coi0 ));
350     CV_CALL( sqsum = cvGetMat( sqsum, &sqsum_stub, &coi1 ));
351
352     if( coi0 || coi1 )
353         CV_ERROR( CV_BadCOI, "COI is not supported" );
354
355     if( !CV_ARE_SIZES_EQ( sum, sqsum ))
356         CV_ERROR( CV_StsUnmatchedSizes, "All integral images must have the same size" );
357
358     if( CV_MAT_TYPE(sqsum->type) != CV_64FC1 ||
359         CV_MAT_TYPE(sum->type) != CV_32SC1 )
360         CV_ERROR( CV_StsUnsupportedFormat,
361         "Only (32s, 64f, 32s) combination of (sum,sqsum,tilted_sum) formats is allowed" );
362
363     if( !_cascade->hid_cascade )
364         CV_CALL( icvCreateHidHaarClassifierCascade(_cascade) );
365
366     cascade = _cascade->hid_cascade;
367
368     if( cascade->has_tilted_features )
369     {
370         CV_CALL( tilted = cvGetMat( tilted, &tilted_stub, &coi1 ));
371
372         if( CV_MAT_TYPE(tilted->type) != CV_32SC1 )
373             CV_ERROR( CV_StsUnsupportedFormat,
374             "Only (32s, 64f, 32s) combination of (sum,sqsum,tilted_sum) formats is allowed" );
375
376         if( sum->step != tilted->step )
377             CV_ERROR( CV_StsUnmatchedSizes,
378             "Sum and tilted_sum must have the same stride (step, widthStep)" );
379
380         if( !CV_ARE_SIZES_EQ( sum, tilted ))
381             CV_ERROR( CV_StsUnmatchedSizes, "All integral images must have the same size" );
382         cascade->tilted = *tilted;
383     }
384     
385     _cascade->scale = scale;
386     _cascade->real_window_size.width = cvRound( _cascade->orig_window_size.width * scale );
387     _cascade->real_window_size.height = cvRound( _cascade->orig_window_size.height * scale );
388
389     cascade->sum = *sum;
390     cascade->sqsum = *sqsum;
391     
392     equ_rect.x = equ_rect.y = cvRound(scale);
393     equ_rect.width = cvRound((_cascade->orig_window_size.width-2)*scale);
394     equ_rect.height = cvRound((_cascade->orig_window_size.height-2)*scale);
395     weight_scale = 1./(equ_rect.width*equ_rect.height);
396     cascade->inv_window_area = weight_scale;
397
398     cascade->p0 = sum_elem_ptr(*sum, equ_rect.y, equ_rect.x);
399     cascade->p1 = sum_elem_ptr(*sum, equ_rect.y, equ_rect.x + equ_rect.width );
400     cascade->p2 = sum_elem_ptr(*sum, equ_rect.y + equ_rect.height, equ_rect.x );
401     cascade->p3 = sum_elem_ptr(*sum, equ_rect.y + equ_rect.height,
402                                      equ_rect.x + equ_rect.width );
403
404     cascade->pq0 = sqsum_elem_ptr(*sqsum, equ_rect.y, equ_rect.x);
405     cascade->pq1 = sqsum_elem_ptr(*sqsum, equ_rect.y, equ_rect.x + equ_rect.width );
406     cascade->pq2 = sqsum_elem_ptr(*sqsum, equ_rect.y + equ_rect.height, equ_rect.x );
407     cascade->pq3 = sqsum_elem_ptr(*sqsum, equ_rect.y + equ_rect.height,
408                                           equ_rect.x + equ_rect.width );
409
410     /* init pointers in haar features according to real window size and
411        given image pointers */
412     for( i = 0; i < _cascade->count; i++ )
413     {
414         for( j = 0; j < cascade->stage_classifier[i].count; j++ )
415         {
416             for( l = 0; l < cascade->stage_classifier[i].classifier[j].count; l++ )
417             {
418                 CvHaarFeature* feature = 
419                     &_cascade->stage_classifier[i].classifier[j].haar_feature[l];
420                 /* CvHidHaarClassifier* classifier =
421                     cascade->stage_classifier[i].classifier + j; */
422                 CvHidHaarFeature* hidfeature = 
423                     &cascade->stage_classifier[i].classifier[j].node[l].feature;
424                 double sum0 = 0, area0 = 0;
425                 CvRect r[3];
426 #if CV_ADJUST_FEATURES
427                 int base_w = -1, base_h = -1;
428                 int new_base_w = 0, new_base_h = 0;
429                 int kx, ky;
430                 int flagx = 0, flagy = 0;
431                 int x0 = 0, y0 = 0;
432 #endif
433                 int nr;
434
435                 /* align blocks */
436                 for( k = 0; k < CV_HAAR_FEATURE_MAX; k++ )
437                 {
438                     if( !hidfeature->rect[k].p0 )
439                         break;
440 #if CV_ADJUST_FEATURES
441                     r[k] = feature->rect[k].r;
442                     base_w = (int)CV_IMIN( (unsigned)base_w, (unsigned)(r[k].width-1) );
443                     base_w = (int)CV_IMIN( (unsigned)base_w, (unsigned)(r[k].x - r[0].x-1) );
444                     base_h = (int)CV_IMIN( (unsigned)base_h, (unsigned)(r[k].height-1) );
445                     base_h = (int)CV_IMIN( (unsigned)base_h, (unsigned)(r[k].y - r[0].y-1) );
446 #endif
447                 }
448
449                 base_w += 1;
450                 base_h += 1;
451                 nr = k;
452
453 #if CV_ADJUST_FEATURES
454                 kx = r[0].width / base_w;
455                 ky = r[0].height / base_h;
456
457                 if( kx <= 0 )
458                 {
459                     flagx = 1;
460                     new_base_w = cvRound( r[0].width * scale ) / kx;
461                     x0 = cvRound( r[0].x * scale );
462                 }
463
464                 if( ky <= 0 )
465                 {
466                     flagy = 1;
467                     new_base_h = cvRound( r[0].height * scale ) / ky;
468                     y0 = cvRound( r[0].y * scale );
469                 }
470 #endif
471             
472                 for( k = 0; k < nr; k++ )
473                 {
474                     CvRect tr;
475                     double correctionRatio;
476                 
477 #if CV_ADJUST_FEATURES
478                     if( flagx )
479                     {
480                         tr.x = (r[k].x - r[0].x) * new_base_w / base_w + x0;
481                         tr.width = r[k].width * new_base_w / base_w;
482                     }
483                     else
484 #endif
485                     {
486                         tr.x = cvRound( r[k].x * scale );
487                         tr.width = cvRound( r[k].width * scale );
488                     }
489
490 #if CV_ADJUST_FEATURES
491                     if( flagy )
492                     {
493                         tr.y = (r[k].y - r[0].y) * new_base_h / base_h + y0;
494                         tr.height = r[k].height * new_base_h / base_h;
495                     }
496                     else
497 #endif
498                     {
499                         tr.y = cvRound( r[k].y * scale );
500                         tr.height = cvRound( r[k].height * scale );
501                     }
502
503 #if CV_ADJUST_WEIGHTS
504                     {
505                     // RAINER START
506                     const float orig_feature_size =  (float)(feature->rect[k].r.width)*feature->rect[k].r.height; 
507                     const float orig_norm_size = (float)(cascade->orig_window_size.width)*(cascade->orig_window_size.height);
508                     const float feature_size = float(tr.width*tr.height);
509                     //const float normSize    = float(equ_rect.width*equ_rect.height);
510                     float targetRatio = orig_feature_size / origNormSize;
511                     //float isRatio = featureSize / normSize;
512                     //correctionRatio = targetRatio / isRatio / normSize;
513                     correctionRatio = targetRatio / featureSize;
514                     // RAINER END
515                     }
516 #else
517                     correctionRatio = weight_scale * (!feature->tilted ? 1 : 0.5);
518 #endif
519
520                     if( !feature->tilted )
521                     {
522                         hidfeature->rect[k].p0 = sum_elem_ptr(*sum, tr.y, tr.x);
523                         hidfeature->rect[k].p1 = sum_elem_ptr(*sum, tr.y, tr.x + tr.width);
524                         hidfeature->rect[k].p2 = sum_elem_ptr(*sum, tr.y + tr.height, tr.x);
525                         hidfeature->rect[k].p3 = sum_elem_ptr(*sum, tr.y + tr.height, tr.x + tr.width);
526                     }
527                     else
528                     {
529                         hidfeature->rect[k].p2 = sum_elem_ptr(*tilted, tr.y + tr.width, tr.x + tr.width);
530                         hidfeature->rect[k].p3 = sum_elem_ptr(*tilted, tr.y + tr.width + tr.height,
531                                                               tr.x + tr.width - tr.height);
532                         hidfeature->rect[k].p0 = sum_elem_ptr(*tilted, tr.y, tr.x);
533                         hidfeature->rect[k].p1 = sum_elem_ptr(*tilted, tr.y + tr.height, tr.x - tr.height);
534                     }
535
536                     hidfeature->rect[k].weight = (float)(feature->rect[k].weight * correctionRatio);
537
538                     if( k == 0 )
539                         area0 = tr.width * tr.height;
540                     else
541                         sum0 += hidfeature->rect[k].weight * tr.width * tr.height;
542                 }
543
544                 hidfeature->rect[0].weight = (float)(-sum0/area0);
545             } /* l */
546         } /* j */
547     }
548
549     __END__;
550 }
551
552
553 CV_INLINE
554 double icvEvalHidHaarClassifier( CvHidHaarClassifier* classifier,
555                                  double variance_norm_factor,
556                                  size_t p_offset )
557 {
558     int idx = 0;
559     do 
560     {
561         CvHidHaarTreeNode* node = classifier->node + idx;
562         double t = node->threshold * variance_norm_factor;
563
564         double sum = calc_sum(node->feature.rect[0],p_offset) * node->feature.rect[0].weight;
565         sum += calc_sum(node->feature.rect[1],p_offset) * node->feature.rect[1].weight;
566
567         if( node->feature.rect[2].p0 )
568             sum += calc_sum(node->feature.rect[2],p_offset) * node->feature.rect[2].weight;
569
570         idx = sum < t ? node->left : node->right;
571     }
572     while( idx > 0 );
573     return classifier->alpha[-idx];
574 }
575
576
577 CV_IMPL int
578 cvRunHaarClassifierCascade( CvHaarClassifierCascade* _cascade,
579                             CvPoint pt, int start_stage )
580 {
581     int result = -1;
582     CV_FUNCNAME("cvRunHaarClassifierCascade");
583
584     __BEGIN__;
585
586     int p_offset, pq_offset;
587     int i, j;
588     double mean, variance_norm_factor;
589     CvHidHaarClassifierCascade* cascade;
590
591     if( !CV_IS_HAAR_CLASSIFIER(_cascade) )
592         CV_ERROR( !_cascade ? CV_StsNullPtr : CV_StsBadArg, "Invalid cascade pointer" );
593
594     cascade = _cascade->hid_cascade;
595     if( !cascade )
596         CV_ERROR( CV_StsNullPtr, "Hidden cascade has not been created.\n"
597             "Use cvSetImagesForHaarClassifierCascade" );
598
599     if( pt.x < 0 || pt.y < 0 ||
600         pt.x + _cascade->real_window_size.width >= cascade->sum.width-2 ||
601         pt.y + _cascade->real_window_size.height >= cascade->sum.height-2 )
602         EXIT;
603
604     p_offset = pt.y * (cascade->sum.step/sizeof(sumtype)) + pt.x;
605     pq_offset = pt.y * (cascade->sqsum.step/sizeof(sqsumtype)) + pt.x;
606     mean = calc_sum(*cascade,p_offset)*cascade->inv_window_area;
607     variance_norm_factor = cascade->pq0[pq_offset] - cascade->pq1[pq_offset] -
608                            cascade->pq2[pq_offset] + cascade->pq3[pq_offset];
609     variance_norm_factor = variance_norm_factor*cascade->inv_window_area - mean*mean;
610     if( variance_norm_factor >= 0. )
611         variance_norm_factor = sqrt(variance_norm_factor);
612     else
613         variance_norm_factor = 1.;
614
615     if( cascade->is_tree )
616     {
617         CvHidHaarStageClassifier* ptr;
618         assert( start_stage == 0 );
619
620         result = 1;
621         ptr = cascade->stage_classifier;
622
623         while( ptr )
624         {
625             double stage_sum = 0;
626
627             for( j = 0; j < ptr->count; j++ )
628             {
629                 stage_sum += icvEvalHidHaarClassifier( ptr->classifier + j,
630                     variance_norm_factor, p_offset );
631             }
632
633             if( stage_sum >= ptr->threshold - 0.0001 )
634             {
635                 ptr = ptr->child;
636             }
637             else
638             {
639                 while( ptr && ptr->next == NULL ) ptr = ptr->parent;
640                 if( ptr == NULL )
641                 {
642                     result = 0;
643                     EXIT;
644                 }
645                 ptr = ptr->next;
646             }
647         }
648     }
649     else if( cascade->is_stump_based )
650     {
651         for( i = start_stage; i < cascade->count; i++ )
652         {
653             double stage_sum = 0;
654
655             if( cascade->stage_classifier[i].two_rects )
656             {
657                 for( j = 0; j < cascade->stage_classifier[i].count; j++ )
658                 {
659                     CvHidHaarClassifier* classifier = cascade->stage_classifier[i].classifier + j;
660                     CvHidHaarTreeNode* node = classifier->node;
661                     double sum, t = node->threshold*variance_norm_factor, a, b;
662
663                     sum = calc_sum(node->feature.rect[0],p_offset) * node->feature.rect[0].weight;
664                     sum += calc_sum(node->feature.rect[1],p_offset) * node->feature.rect[1].weight;
665
666                     a = classifier->alpha[0];
667                     b = classifier->alpha[1];
668                     stage_sum += sum < t ? a : b;
669                 }
670             }
671             else
672             {
673                 for( j = 0; j < cascade->stage_classifier[i].count; j++ )
674                 {
675                     CvHidHaarClassifier* classifier = cascade->stage_classifier[i].classifier + j;
676                     CvHidHaarTreeNode* node = classifier->node;
677                     double sum, t = node->threshold*variance_norm_factor, a, b;
678
679                     sum = calc_sum(node->feature.rect[0],p_offset) * node->feature.rect[0].weight;
680                     sum += calc_sum(node->feature.rect[1],p_offset) * node->feature.rect[1].weight;
681
682                     if( node->feature.rect[2].p0 )
683                         sum += calc_sum(node->feature.rect[2],p_offset) * node->feature.rect[2].weight;
684
685                     a = classifier->alpha[0];
686                     b = classifier->alpha[1];
687                     stage_sum += sum < t ? a : b;
688                 }
689             }
690
691             if( stage_sum < cascade->stage_classifier[i].threshold - 0.0001 )
692             {
693                 result = -i;
694                 EXIT;
695             }
696         }
697     }
698     else
699     {
700         for( i = start_stage; i < cascade->count; i++ )
701         {
702             double stage_sum = 0;
703
704             for( j = 0; j < cascade->stage_classifier[i].count; j++ )
705             {
706                 stage_sum += icvEvalHidHaarClassifier(
707                     cascade->stage_classifier[i].classifier + j,
708                     variance_norm_factor, p_offset );
709             }
710
711             if( stage_sum < cascade->stage_classifier[i].threshold - 0.0001 )
712             {
713                 result = -i;
714                 EXIT;
715             }
716         }
717     }
718
719     result = 1;
720
721     __END__;
722
723     return result;
724 }
725
726
727 static int is_equal( const void* _r1, const void* _r2, void* )
728 {
729     const CvRect* r1 = (const CvRect*)_r1;
730     const CvRect* r2 = (const CvRect*)_r2;
731     int distance = cvRound(r1->width*0.2);
732
733     return r2->x <= r1->x + distance &&
734            r2->x >= r1->x - distance &&
735            r2->y <= r1->y + distance &&
736            r2->y >= r1->y - distance &&
737            r2->width <= cvRound( r1->width * 1.2 ) &&
738            cvRound( r2->width * 1.2 ) >= r1->width;
739 }
740
741
742 CV_IMPL CvSeq*
743 cvHaarDetectObjects( const CvArr* _img,
744                      CvHaarClassifierCascade* cascade,
745                      CvMemStorage* storage, double scale_factor,
746                      int min_neighbors, int flags, CvSize min_size )
747 {
748     int split_stage = 2;
749     CvMat stub, *img = (CvMat*)_img;
750     CvMat *temp = 0, *sum = 0, *tilted = 0, *sqsum = 0, *sumcanny = 0;
751     CvSeq* seq = 0;
752     CvSeq* seq2 = 0;
753     CvSeq* idx_seq = 0;
754     CvSeq* result_seq = 0;
755     CvMemStorage* temp_storage = 0;
756     CvAvgComp* comps = 0;
757     
758     CV_FUNCNAME( "cvHaarDetectObjects" );
759
760     __BEGIN__;
761
762     double factor;
763     int i, npass = 2, coi;
764     int do_canny_pruning = flags & CV_HAAR_DO_CANNY_PRUNING;
765
766     if( !CV_IS_HAAR_CLASSIFIER(cascade) )
767         CV_ERROR( !cascade ? CV_StsNullPtr : CV_StsBadArg, "Invalid classifier cascade" );
768
769     if( !storage )
770         CV_ERROR( CV_StsNullPtr, "Null storage pointer" );
771
772     CV_CALL( img = cvGetMat( img, &stub, &coi ));
773     if( coi )
774         CV_ERROR( CV_BadCOI, "COI is not supported" );
775
776     if( CV_MAT_DEPTH(img->type) != CV_8U )
777         CV_ERROR( CV_StsUnsupportedFormat, "Only 8-bit images are supported" );
778
779     temp = cvCreateMat( img->rows, img->cols, CV_8UC1 );
780     sum = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );
781     sqsum = cvCreateMat( img->rows + 1, img->cols + 1, CV_64FC1 );
782     temp_storage = cvCreateChildMemStorage( storage );
783
784     if( !cascade->hid_cascade )
785         CV_CALL( icvCreateHidHaarClassifierCascade(cascade) );
786
787     if( cascade->hid_cascade->has_tilted_features )
788         tilted = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );
789
790     seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvRect), temp_storage );
791     seq2 = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), temp_storage );
792     result_seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), storage );
793
794     if( min_neighbors == 0 )
795         seq = result_seq;
796
797     if( CV_MAT_CN(img->type) > 1 )
798     {
799         cvCvtColor( img, temp, CV_BGR2GRAY );
800         img = temp;
801     }
802     
803     cvIntegral( img, sum, sqsum, tilted );
804     
805     if( do_canny_pruning )
806     {
807         sumcanny = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );
808         cvCanny( img, temp, 0, 50, 3 );
809         cvIntegral( temp, sumcanny );
810     }
811     
812     if( (unsigned)split_stage >= (unsigned)cascade->count ||
813         cascade->hid_cascade->is_tree )
814     {
815         split_stage = cascade->count;
816         npass = 1;
817     }
818
819     for( factor = 1; factor*cascade->orig_window_size.width < img->cols - 10 &&
820                      factor*cascade->orig_window_size.height < img->rows - 10;
821          factor *= scale_factor )
822     {
823         const double ystep = MAX( 2, factor );
824         CvSize win_size = { cvRound( cascade->orig_window_size.width * factor ),
825                             cvRound( cascade->orig_window_size.height * factor )};
826         CvRect equ_rect = { 0, 0, 0, 0 };
827         int *p0 = 0, *p1 = 0, *p2 = 0, *p3 = 0;
828         int *pq0 = 0, *pq1 = 0, *pq2 = 0, *pq3 = 0;
829         int pass, stage_offset = 0;
830         int stop_height = cvRound((img->rows - win_size.height) / ystep);
831
832         if( win_size.width < min_size.width || win_size.height < min_size.height )
833             continue;
834
835         cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor );
836         cvZero( temp );
837
838         if( do_canny_pruning )
839         {
840             equ_rect.x = cvRound(win_size.width*0.3);
841             equ_rect.y = cvRound(win_size.height*0.3);
842             equ_rect.width = cvRound(win_size.width*0.7);
843             equ_rect.height = cvRound(win_size.height*0.7);
844
845             p0 = (int*)(sumcanny->data.ptr + equ_rect.y*sumcanny->step) + equ_rect.x;
846             p1 = (int*)(sumcanny->data.ptr + equ_rect.y*sumcanny->step)
847                         + equ_rect.x + equ_rect.width;
848             p2 = (int*)(sumcanny->data.ptr + (equ_rect.y + equ_rect.height)*sumcanny->step) + equ_rect.x;
849             p3 = (int*)(sumcanny->data.ptr + (equ_rect.y + equ_rect.height)*sumcanny->step)
850                         + equ_rect.x + equ_rect.width;
851
852             pq0 = (int*)(sum->data.ptr + equ_rect.y*sum->step) + equ_rect.x;
853             pq1 = (int*)(sum->data.ptr + equ_rect.y*sum->step)
854                         + equ_rect.x + equ_rect.width;
855             pq2 = (int*)(sum->data.ptr + (equ_rect.y + equ_rect.height)*sum->step) + equ_rect.x;
856             pq3 = (int*)(sum->data.ptr + (equ_rect.y + equ_rect.height)*sum->step)
857                         + equ_rect.x + equ_rect.width;
858         }
859
860         cascade->hid_cascade->count = split_stage;
861
862         for( pass = 0; pass < npass; pass++ )
863         {
864 #ifdef _OPENMP
865     #pragma omp parallel for shared(hid_cascade, stop_height, seq, ystep, temp, size, win_size, pass, npass, sum, p0, p1, p2 ,p3, pq0, pq1, pq2, pq3, stage_offset) 
866 #endif // _OPENMP
867
868             for( int _iy = 0; _iy < stop_height; _iy++ )
869             {
870                 int iy = cvRound(_iy*ystep);
871                 int _ix, _xstep = 1;
872                 int stop_width = cvRound((img->cols - win_size.width) / ystep);
873                 uchar* mask_row = temp->data.ptr + temp->step * iy;
874
875                 for( _ix = 0; _ix < stop_width; _ix += _xstep )
876                 {
877                     int ix = cvRound(_ix*ystep); // it really should be ystep
878                     
879                     if( pass == 0 )
880                     {
881                         int result;
882                         _xstep = 2;
883
884                         if( do_canny_pruning )
885                         {
886                             int offset;
887                             int s, sq;
888                         
889                             offset = iy*(sum->step/sizeof(p0[0])) + ix;
890                             s = p0[offset] - p1[offset] - p2[offset] + p3[offset];
891                             sq = pq0[offset] - pq1[offset] - pq2[offset] + pq3[offset];
892                             if( s < 100 || sq < 20 )
893                                 continue;
894                         }
895
896                         result = cvRunHaarClassifierCascade( cascade, cvPoint(ix,iy), 0 );
897 #ifdef _OPENMP
898 #pragma omp critical
899 #endif
900                         if( result > 0 )
901                         {
902                             if( pass < npass - 1 )
903                                 mask_row[ix] = 1;
904                             else
905                             {
906                                 CvRect rect = cvRect(ix,iy,win_size.width,win_size.height);
907                                 cvSeqPush( seq, &rect );
908                             }
909                         }
910                         if( result < 0 )
911                             _xstep = 1;
912                     }
913                     else if( mask_row[ix] )
914                     {
915                         int result = cvRunHaarClassifierCascade( cascade, cvPoint(ix,iy),
916                                                                  stage_offset );
917 #ifdef _OPENMP
918 #pragma omp critical
919 #endif
920                         if( result > 0 )
921                         {
922                             if( pass == npass - 1 )
923                             {
924                                 CvRect rect = cvRect(ix,iy,win_size.width,win_size.height);
925                                 cvSeqPush( seq, &rect );
926                             }
927                         }
928                         else
929                             mask_row[ix] = 0;
930                     }
931                 }
932             }
933             stage_offset = cascade->hid_cascade->count;
934             cascade->hid_cascade->count = cascade->count;
935         }
936     }
937
938     if( min_neighbors != 0 )
939     {
940         // group retrieved rectangles in order to filter out noise 
941         int ncomp = cvSeqPartition( seq, 0, &idx_seq, is_equal, 0 );
942         CV_CALL( comps = (CvAvgComp*)cvAlloc( (ncomp+1)*sizeof(comps[0])));
943         memset( comps, 0, (ncomp+1)*sizeof(comps[0]));
944
945         // count number of neighbors
946         for( i = 0; i < seq->total; i++ )
947         {
948             CvRect r1 = *(CvRect*)cvGetSeqElem( seq, i );
949             int idx = *(int*)cvGetSeqElem( idx_seq, i );
950             assert( (unsigned)idx < (unsigned)ncomp );
951
952             comps[idx].neighbors++;
953              
954             comps[idx].rect.x += r1.x;
955             comps[idx].rect.y += r1.y;
956             comps[idx].rect.width += r1.width;
957             comps[idx].rect.height += r1.height;
958         }
959
960         // calculate average bounding box
961         for( i = 0; i < ncomp; i++ )
962         {
963             int n = comps[i].neighbors;
964             if( n >= min_neighbors )
965             {
966                 CvAvgComp comp;
967                 comp.rect.x = (comps[i].rect.x*2 + n)/(2*n);
968                 comp.rect.y = (comps[i].rect.y*2 + n)/(2*n);
969                 comp.rect.width = (comps[i].rect.width*2 + n)/(2*n);
970                 comp.rect.height = (comps[i].rect.height*2 + n)/(2*n);
971                 comp.neighbors = comps[i].neighbors;
972
973                 cvSeqPush( seq2, &comp );
974             }
975         }
976
977         // filter out small face rectangles inside large face rectangles
978         for( i = 0; i < seq2->total; i++ )
979         {
980             CvAvgComp r1 = *(CvAvgComp*)cvGetSeqElem( seq2, i );
981             int j, flag = 1;
982
983             for( j = 0; j < seq2->total; j++ )
984             {
985                 CvAvgComp r2 = *(CvAvgComp*)cvGetSeqElem( seq2, j );
986                 int distance = cvRound( r2.rect.width * 0.2 );
987             
988                 if( i != j &&
989                     r1.rect.x >= r2.rect.x - distance &&
990                     r1.rect.y >= r2.rect.y - distance &&
991                     r1.rect.x + r1.rect.width <= r2.rect.x + r2.rect.width + distance &&
992                     r1.rect.y + r1.rect.height <= r2.rect.y + r2.rect.height + distance &&
993                     (r2.neighbors > MAX( 3, r1.neighbors ) || r1.neighbors < 3) )
994                 {
995                     flag = 0;
996                     break;
997                 }
998             }
999
1000             if( flag )
1001             {
1002                 cvSeqPush( result_seq, &r1 );
1003                 /* cvSeqPush( result_seq, &r1.rect ); */
1004             }
1005         }
1006     }
1007
1008     __END__;
1009
1010     cvReleaseMemStorage( &temp_storage );
1011     cvReleaseMat( &sum );
1012     cvReleaseMat( &sqsum );
1013     cvReleaseMat( &tilted );
1014     cvReleaseMat( &temp );
1015     cvReleaseMat( &sumcanny );
1016     cvFree( (void**)&comps );
1017
1018     return result_seq;
1019 }
1020
1021
1022 static CvHaarClassifierCascade*
1023 icvLoadCascadeCART( const char** input_cascade, int n, CvSize orig_window_size )
1024 {
1025     int i;
1026     CvHaarClassifierCascade* cascade = icvCreateHaarClassifierCascade(n);
1027     cascade->orig_window_size = orig_window_size;
1028
1029     for( i = 0; i < n; i++ )
1030     {
1031         int j, count, l;
1032         float threshold = 0;
1033         const char* stage = input_cascade[i];
1034         int dl = 0;
1035
1036         /* tree links */
1037         int parent = -1;
1038         int next = -1;
1039
1040         sscanf( stage, "%d%n", &count, &dl );
1041         stage += dl;
1042         
1043         assert( count > 0 );
1044         cascade->stage_classifier[i].count = count;
1045         cascade->stage_classifier[i].classifier =
1046             (CvHaarClassifier*)cvAlloc( count*sizeof(cascade->stage_classifier[i].classifier[0]));
1047
1048         for( j = 0; j < count; j++ )
1049         {
1050             CvHaarClassifier* classifier = cascade->stage_classifier[i].classifier + j;
1051             int k, rects = 0;
1052             char str[100];
1053             
1054             sscanf( stage, "%d%n", &classifier->count, &dl );
1055             stage += dl;
1056
1057             classifier->haar_feature = (CvHaarFeature*) cvAlloc( 
1058                 classifier->count * ( sizeof( *classifier->haar_feature ) +
1059                                       sizeof( *classifier->threshold ) +
1060                                       sizeof( *classifier->left ) +
1061                                       sizeof( *classifier->right ) ) +
1062                 (classifier->count + 1) * sizeof( *classifier->alpha ) );
1063             classifier->threshold = (float*) (classifier->haar_feature+classifier->count);
1064             classifier->left = (int*) (classifier->threshold + classifier->count);
1065             classifier->right = (int*) (classifier->left + classifier->count);
1066             classifier->alpha = (float*) (classifier->right + classifier->count);
1067             
1068             for( l = 0; l < classifier->count; l++ )
1069             {
1070                 sscanf( stage, "%d%n", &rects, &dl );
1071                 stage += dl;
1072
1073                 assert( rects >= 2 && rects <= CV_HAAR_FEATURE_MAX );
1074
1075                 for( k = 0; k < rects; k++ )
1076                 {
1077                     CvRect r;
1078                     int band = 0;
1079                     sscanf( stage, "%d%d%d%d%d%f%n",
1080                             &r.x, &r.y, &r.width, &r.height, &band,
1081                             &(classifier->haar_feature[l].rect[k].weight), &dl );
1082                     stage += dl;
1083                     classifier->haar_feature[l].rect[k].r = r;
1084                 }
1085                 sscanf( stage, "%s%n", str, &dl );
1086                 stage += dl;
1087             
1088                 classifier->haar_feature[l].tilted = strncmp( str, "tilted", 6 ) == 0;
1089             
1090                 for( k = rects; k < CV_HAAR_FEATURE_MAX; k++ )
1091                 {
1092                     memset( classifier->haar_feature[l].rect + k, 0,
1093                             sizeof(classifier->haar_feature[l].rect[k]) );
1094                 }
1095                 
1096                 sscanf( stage, "%f%d%d%n", &(classifier->threshold[l]), 
1097                                        &(classifier->left[l]),
1098                                        &(classifier->right[l]), &dl );
1099                 stage += dl;
1100             }
1101             for( l = 0; l <= classifier->count; l++ )
1102             {
1103                 sscanf( stage, "%f%n", &(classifier->alpha[l]), &dl );
1104                 stage += dl;
1105             }
1106         }
1107        
1108         sscanf( stage, "%f%n", &threshold, &dl );
1109         stage += dl;
1110
1111         cascade->stage_classifier[i].threshold = threshold;
1112
1113         /* load tree links */
1114         if( sscanf( stage, "%d%d%n", &parent, &next, &dl ) != 2 )
1115         {
1116             parent = i - 1;
1117             next = -1;
1118         }
1119         stage += dl;
1120
1121         cascade->stage_classifier[i].parent = parent;
1122         cascade->stage_classifier[i].next = next;
1123         cascade->stage_classifier[i].child = -1;
1124
1125         if( parent != -1 && cascade->stage_classifier[parent].child == -1 )
1126         {
1127             cascade->stage_classifier[parent].child = i;
1128         }
1129     }
1130
1131     return cascade;
1132 }
1133
1134 #ifndef _MAX_PATH
1135 #define _MAX_PATH 1024
1136 #endif
1137
1138 CV_IMPL CvHaarClassifierCascade*
1139 cvLoadHaarClassifierCascade( const char* directory, CvSize orig_window_size )
1140 {
1141     const char** input_cascade = 0; 
1142     CvHaarClassifierCascade *cascade = 0;
1143
1144     CV_FUNCNAME( "cvLoadHaarClassifierCascade" );
1145
1146     __BEGIN__;
1147
1148     int i, n;
1149     const char* slash;
1150     char name[_MAX_PATH];
1151     int size = 0;
1152     char* ptr = 0;
1153
1154     if( !directory )
1155         CV_ERROR( CV_StsNullPtr, "Null path is passed" );
1156
1157     n = strlen(directory)-1;
1158     slash = directory[n] == '\\' || directory[n] == '/' ? "" : "/";
1159
1160     /* try to read the classifier from directory */
1161     for( n = 0; ; n++ )
1162     {
1163         sprintf( name, "%s%s%d/AdaBoostCARTHaarClassifier.txt", directory, slash, n );
1164         FILE* f = fopen( name, "rb" );
1165         if( !f )
1166             break;
1167         fseek( f, 0, SEEK_END );
1168         size += ftell( f ) + 1;
1169         fclose(f);
1170     }
1171
1172     if( n == 0 && slash[0] )
1173     {
1174         CV_CALL( cascade = (CvHaarClassifierCascade*)cvLoad( directory ));
1175         EXIT;
1176     }
1177     else if( n == 0 )
1178         CV_ERROR( CV_StsBadArg, "Invalid path" );
1179     
1180     size += (n+1)*sizeof(char*);
1181     CV_CALL( input_cascade = (const char**)cvAlloc( size ));
1182     ptr = (char*)(input_cascade + n + 1);
1183
1184     for( i = 0; i < n; i++ )
1185     {
1186         sprintf( name, "%s/%d/AdaBoostCARTHaarClassifier.txt", directory, i );
1187         FILE* f = fopen( name, "rb" );
1188         if( !f )
1189             CV_ERROR( CV_StsError, "" );
1190         fseek( f, 0, SEEK_END );
1191         size = ftell( f );
1192         fseek( f, 0, SEEK_SET );
1193         fread( ptr, 1, size, f );
1194         fclose(f);
1195         input_cascade[i] = ptr;
1196         ptr += size;
1197         *ptr++ = '\0';
1198     }
1199
1200     input_cascade[n] = 0;
1201     cascade = icvLoadCascadeCART( input_cascade, n, orig_window_size );
1202
1203     __END__;
1204
1205     if( input_cascade )
1206         cvFree( (void**)&input_cascade );
1207
1208     if( cvGetErrStatus() < 0 )
1209         cvReleaseHaarClassifierCascade( &cascade );
1210
1211     return cascade;
1212 }
1213
1214
1215 CV_IMPL void
1216 cvReleaseHaarClassifierCascade( CvHaarClassifierCascade** cascade )
1217 {
1218     if( cascade && *cascade )
1219     {
1220         int i;
1221         int j;
1222
1223         for( i = 0; i < cascade[0]->count; i++ )
1224         {
1225             for( j = 0; j < cascade[0]->stage_classifier[i].count; j++ )
1226             {
1227                 cvFree( (void**)
1228                     &(cascade[0]->stage_classifier[i].classifier[j].haar_feature) );
1229             }
1230             cvFree( (void**) &(cascade[0]->stage_classifier[i].classifier) );
1231         }
1232         cvFree( (void**)&(cascade[0]->hid_cascade) );
1233         cvFree( (void**)cascade );
1234     }
1235 }
1236
1237
1238 /****************************************************************************************\
1239 *                                  Persistence functions                                 *
1240 \****************************************************************************************/
1241
1242 /* field names */
1243
1244 #define ICV_HAAR_SIZE_NAME            "size"
1245 #define ICV_HAAR_STAGES_NAME          "stages"
1246 #define ICV_HAAR_TREES_NAME             "trees"
1247 #define ICV_HAAR_FEATURE_NAME             "feature"
1248 #define ICV_HAAR_RECTS_NAME                 "rects"
1249 #define ICV_HAAR_TILTED_NAME                "tilted"
1250 #define ICV_HAAR_THRESHOLD_NAME           "threshold"
1251 #define ICV_HAAR_LEFT_NODE_NAME           "left_node"
1252 #define ICV_HAAR_LEFT_VAL_NAME            "left_val"
1253 #define ICV_HAAR_RIGHT_NODE_NAME          "right_node"
1254 #define ICV_HAAR_RIGHT_VAL_NAME           "right_val"
1255 #define ICV_HAAR_STAGE_THRESHOLD_NAME   "stage_threshold"
1256 #define ICV_HAAR_PARENT_NAME            "parent"
1257 #define ICV_HAAR_NEXT_NAME              "next"
1258
1259 static int
1260 icvIsHaarClassifier( const void* struct_ptr )
1261 {
1262     return CV_IS_HAAR_CLASSIFIER( struct_ptr );
1263 }
1264
1265 static void*
1266 icvReadHaarClassifier( CvFileStorage* fs, CvFileNode* node )
1267 {
1268     CvHaarClassifierCascade* cascade = NULL;
1269
1270     CV_FUNCNAME( "cvReadHaarClassifier" );
1271
1272     __BEGIN__;
1273
1274     char buf[256];
1275     CvFileNode* seq_fn = NULL; /* sequence */
1276     CvFileNode* fn = NULL;
1277     CvFileNode* stages_fn = NULL;
1278     CvSeqReader stages_reader;
1279     int n;
1280     int i, j, k, l;
1281     int parent, next;
1282
1283     CV_CALL( stages_fn = cvGetFileNodeByName( fs, node, ICV_HAAR_STAGES_NAME ) );
1284     if( !stages_fn || !CV_NODE_IS_SEQ( stages_fn->tag) )
1285         CV_ERROR( CV_StsError, "Invalid stages node" );
1286
1287     n = stages_fn->data.seq->total;
1288     CV_CALL( cascade = icvCreateHaarClassifierCascade(n) );
1289
1290     /* read size */
1291     CV_CALL( seq_fn = cvGetFileNodeByName( fs, node, ICV_HAAR_SIZE_NAME ) );
1292     if( !seq_fn || !CV_NODE_IS_SEQ( seq_fn->tag ) || seq_fn->data.seq->total != 2 )
1293         CV_ERROR( CV_StsError, "size node is not a valid sequence." );
1294     CV_CALL( fn = (CvFileNode*) cvGetSeqElem( seq_fn->data.seq, 0 ) );
1295     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i <= 0 )
1296         CV_ERROR( CV_StsError, "Invalid size node: width must be positive integer" );
1297     cascade->orig_window_size.width = fn->data.i;
1298     CV_CALL( fn = (CvFileNode*) cvGetSeqElem( seq_fn->data.seq, 1 ) );
1299     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i <= 0 )
1300         CV_ERROR( CV_StsError, "Invalid size node: height must be positive integer" );
1301     cascade->orig_window_size.height = fn->data.i;
1302
1303     CV_CALL( cvStartReadSeq( stages_fn->data.seq, &stages_reader ) );
1304     for( i = 0; i < n; ++i )
1305     {
1306         CvFileNode* stage_fn;
1307         CvFileNode* trees_fn;
1308         CvSeqReader trees_reader;
1309
1310         stage_fn = (CvFileNode*) stages_reader.ptr;
1311         if( !CV_NODE_IS_MAP( stage_fn->tag ) )
1312         {
1313             sprintf( buf, "Invalid stage %d", i );
1314             CV_ERROR( CV_StsError, buf );
1315         }
1316
1317         CV_CALL( trees_fn = cvGetFileNodeByName( fs, stage_fn, ICV_HAAR_TREES_NAME ) );
1318         if( !trees_fn || !CV_NODE_IS_SEQ( trees_fn->tag )
1319             || trees_fn->data.seq->total <= 0 )
1320         {
1321             sprintf( buf, "Trees node is not a valid sequence. (stage %d)", i );
1322             CV_ERROR( CV_StsError, buf );
1323         }
1324
1325         CV_CALL( cascade->stage_classifier[i].classifier =
1326             (CvHaarClassifier*) cvAlloc( trees_fn->data.seq->total
1327                 * sizeof( cascade->stage_classifier[i].classifier[0] ) ) );
1328         for( j = 0; j < trees_fn->data.seq->total; ++j )
1329         {
1330             cascade->stage_classifier[i].classifier[j].haar_feature = NULL;
1331         }
1332         cascade->stage_classifier[i].count = trees_fn->data.seq->total;
1333
1334         CV_CALL( cvStartReadSeq( trees_fn->data.seq, &trees_reader ) );
1335         for( j = 0; j < trees_fn->data.seq->total; ++j )
1336         {
1337             CvFileNode* tree_fn;
1338             CvSeqReader tree_reader;
1339             CvHaarClassifier* classifier;
1340             int last_idx;
1341
1342             classifier = &cascade->stage_classifier[i].classifier[j];
1343             tree_fn = (CvFileNode*) trees_reader.ptr;
1344             if( !CV_NODE_IS_SEQ( tree_fn->tag ) || tree_fn->data.seq->total <= 0 )
1345             {
1346                 sprintf( buf, "Tree node is not a valid sequence."
1347                          " (stage %d, tree %d)", i, j );
1348                 CV_ERROR( CV_StsError, buf );
1349             }
1350
1351             classifier->count = tree_fn->data.seq->total;
1352             CV_CALL( classifier->haar_feature = (CvHaarFeature*) cvAlloc( 
1353                 classifier->count * ( sizeof( *classifier->haar_feature ) +
1354                                       sizeof( *classifier->threshold ) +
1355                                       sizeof( *classifier->left ) +
1356                                       sizeof( *classifier->right ) ) +
1357                 (classifier->count + 1) * sizeof( *classifier->alpha ) ) );
1358             classifier->threshold = (float*) (classifier->haar_feature+classifier->count);
1359             classifier->left = (int*) (classifier->threshold + classifier->count);
1360             classifier->right = (int*) (classifier->left + classifier->count);
1361             classifier->alpha = (float*) (classifier->right + classifier->count);
1362
1363             CV_CALL( cvStartReadSeq( tree_fn->data.seq, &tree_reader ) );
1364             for( k = 0, last_idx = 0; k < tree_fn->data.seq->total; ++k )
1365             {
1366                 CvFileNode* node_fn;
1367                 CvFileNode* feature_fn;
1368                 CvFileNode* rects_fn;
1369                 CvSeqReader rects_reader;
1370
1371                 node_fn = (CvFileNode*) tree_reader.ptr;
1372                 if( !CV_NODE_IS_MAP( node_fn->tag ) )
1373                 {
1374                     sprintf( buf, "Tree node %d is not a valid map. (stage %d, tree %d)",
1375                              k, i, j );
1376                     CV_ERROR( CV_StsError, buf );
1377                 }
1378                 CV_CALL( feature_fn = cvGetFileNodeByName( fs, node_fn,
1379                     ICV_HAAR_FEATURE_NAME ) );
1380                 if( !feature_fn || !CV_NODE_IS_MAP( feature_fn->tag ) )
1381                 {
1382                     sprintf( buf, "Feature node is not a valid map. "
1383                              "(stage %d, tree %d, node %d)", i, j, k );
1384                     CV_ERROR( CV_StsError, buf );
1385                 }
1386                 CV_CALL( rects_fn = cvGetFileNodeByName( fs, feature_fn,
1387                     ICV_HAAR_RECTS_NAME ) );
1388                 if( !rects_fn || !CV_NODE_IS_SEQ( rects_fn->tag )
1389                     || rects_fn->data.seq->total < 1
1390                     || rects_fn->data.seq->total > CV_HAAR_FEATURE_MAX )
1391                 {
1392                     sprintf( buf, "Rects node is not a valid sequence. "
1393                              "(stage %d, tree %d, node %d)", i, j, k );
1394                     CV_ERROR( CV_StsError, buf );
1395                 }
1396                 CV_CALL( cvStartReadSeq( rects_fn->data.seq, &rects_reader ) );
1397                 for( l = 0; l < rects_fn->data.seq->total; ++l )
1398                 {
1399                     CvFileNode* rect_fn;
1400                     CvRect r;
1401
1402                     rect_fn = (CvFileNode*) rects_reader.ptr;
1403                     if( !CV_NODE_IS_SEQ( rect_fn->tag ) || rect_fn->data.seq->total != 5 )
1404                     {
1405                         sprintf( buf, "Rect %d is not a valid sequence. "
1406                                  "(stage %d, tree %d, node %d)", l, i, j, k );
1407                         CV_ERROR( CV_StsError, buf );
1408                     }
1409                     
1410                     fn = CV_SEQ_ELEM( rect_fn->data.seq, CvFileNode, 0 );
1411                     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i < 0 )
1412                     {
1413                         sprintf( buf, "x coordinate must be non-negative integer. "
1414                                  "(stage %d, tree %d, node %d, rect %d)", i, j, k, l );
1415                         CV_ERROR( CV_StsError, buf );
1416                     }
1417                     r.x = fn->data.i;
1418                     fn = CV_SEQ_ELEM( rect_fn->data.seq, CvFileNode, 1 );
1419                     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i < 0 )
1420                     {
1421                         sprintf( buf, "y coordinate must be non-negative integer. "
1422                                  "(stage %d, tree %d, node %d, rect %d)", i, j, k, l );
1423                         CV_ERROR( CV_StsError, buf );
1424                     }
1425                     r.y = fn->data.i;
1426                     fn = CV_SEQ_ELEM( rect_fn->data.seq, CvFileNode, 2 );
1427                     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i <= 0
1428                         || r.x + fn->data.i > cascade->orig_window_size.width )
1429                     {
1430                         sprintf( buf, "width must be positive integer and "
1431                                  "(x + width) must not exceed window width. "
1432                                  "(stage %d, tree %d, node %d, rect %d)", i, j, k, l );
1433                         CV_ERROR( CV_StsError, buf );
1434                     }
1435                     r.width = fn->data.i;
1436                     fn = CV_SEQ_ELEM( rect_fn->data.seq, CvFileNode, 3 );
1437                     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i <= 0
1438                         || r.y + fn->data.i > cascade->orig_window_size.height )
1439                     {
1440                         sprintf( buf, "height must be positive integer and "
1441                                  "(y + height) must not exceed window height. "
1442                                  "(stage %d, tree %d, node %d, rect %d)", i, j, k, l );
1443                         CV_ERROR( CV_StsError, buf );
1444                     }
1445                     r.height = fn->data.i;
1446                     fn = CV_SEQ_ELEM( rect_fn->data.seq, CvFileNode, 4 );
1447                     if( !CV_NODE_IS_REAL( fn->tag ) )
1448                     {
1449                         sprintf( buf, "weight must be real number. "
1450                                  "(stage %d, tree %d, node %d, rect %d)", i, j, k, l );
1451                         CV_ERROR( CV_StsError, buf );
1452                     }
1453
1454                     classifier->haar_feature[k].rect[l].weight = (float) fn->data.f;
1455                     classifier->haar_feature[k].rect[l].r = r;
1456
1457                     CV_NEXT_SEQ_ELEM( sizeof( *rect_fn ), rects_reader );
1458                 } /* for each rect */
1459                 for( l = rects_fn->data.seq->total; l < CV_HAAR_FEATURE_MAX; ++l )
1460                 {
1461                     classifier->haar_feature[k].rect[l].weight = 0;
1462                     classifier->haar_feature[k].rect[l].r = cvRect( 0, 0, 0, 0 );
1463                 }
1464
1465                 CV_CALL( fn = cvGetFileNodeByName( fs, feature_fn, ICV_HAAR_TILTED_NAME));
1466                 if( !fn || !CV_NODE_IS_INT( fn->tag ) )
1467                 {
1468                     sprintf( buf, "tilted must be 0 or 1. "
1469                              "(stage %d, tree %d, node %d)", i, j, k );
1470                     CV_ERROR( CV_StsError, buf );
1471                 }
1472                 classifier->haar_feature[k].tilted = ( fn->data.i != 0 );
1473                 CV_CALL( fn = cvGetFileNodeByName( fs, node_fn, ICV_HAAR_THRESHOLD_NAME));
1474                 if( !fn || !CV_NODE_IS_REAL( fn->tag ) )
1475                 {
1476                     sprintf( buf, "threshold must be real number. "
1477                              "(stage %d, tree %d, node %d)", i, j, k );
1478                     CV_ERROR( CV_StsError, buf );
1479                 }
1480                 classifier->threshold[k] = (float) fn->data.f;
1481                 CV_CALL( fn = cvGetFileNodeByName( fs, node_fn, ICV_HAAR_LEFT_NODE_NAME));
1482                 if( fn )
1483                 {
1484                     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i <= k
1485                         || fn->data.i >= tree_fn->data.seq->total )
1486                     {
1487                         sprintf( buf, "left node must be valid node number. "
1488                                  "(stage %d, tree %d, node %d)", i, j, k );
1489                         CV_ERROR( CV_StsError, buf );
1490                     }
1491                     /* left node */
1492                     classifier->left[k] = fn->data.i;
1493                 }
1494                 else
1495                 {
1496                     CV_CALL( fn = cvGetFileNodeByName( fs, node_fn,
1497                         ICV_HAAR_LEFT_VAL_NAME ) );
1498                     if( !fn )
1499                     {
1500                         sprintf( buf, "left node or left value must be specified. "
1501                                  "(stage %d, tree %d, node %d)", i, j, k );
1502                         CV_ERROR( CV_StsError, buf );
1503                     }
1504                     if( !CV_NODE_IS_REAL( fn->tag ) )
1505                     {
1506                         sprintf( buf, "left value must be real number. "
1507                                  "(stage %d, tree %d, node %d)", i, j, k );
1508                         CV_ERROR( CV_StsError, buf );
1509                     }
1510                     /* left value */
1511                     if( last_idx >= classifier->count + 1 )
1512                     {
1513                         sprintf( buf, "Tree structure is broken: too many values. "
1514                                  "(stage %d, tree %d, node %d)", i, j, k );
1515                         CV_ERROR( CV_StsError, buf );
1516                     }
1517                     classifier->left[k] = -last_idx;
1518                     classifier->alpha[last_idx++] = (float) fn->data.f;
1519                 }
1520                 CV_CALL( fn = cvGetFileNodeByName( fs, node_fn,ICV_HAAR_RIGHT_NODE_NAME));
1521                 if( fn )
1522                 {
1523                     if( !CV_NODE_IS_INT( fn->tag ) || fn->data.i <= k
1524                         || fn->data.i >= tree_fn->data.seq->total )
1525                     {
1526                         sprintf( buf, "right node must be valid node number. "
1527                                  "(stage %d, tree %d, node %d)", i, j, k );
1528                         CV_ERROR( CV_StsError, buf );
1529                     }
1530                     /* right node */
1531                     classifier->right[k] = fn->data.i;
1532                 }
1533                 else
1534                 {
1535                     CV_CALL( fn = cvGetFileNodeByName( fs, node_fn,
1536                         ICV_HAAR_RIGHT_VAL_NAME ) );
1537                     if( !fn )
1538                     {
1539                         sprintf( buf, "right node or right value must be specified. "
1540                                  "(stage %d, tree %d, node %d)", i, j, k );
1541                         CV_ERROR( CV_StsError, buf );
1542                     }
1543                     if( !CV_NODE_IS_REAL( fn->tag ) )
1544                     {
1545                         sprintf( buf, "right value must be real number. "
1546                                  "(stage %d, tree %d, node %d)", i, j, k );
1547                         CV_ERROR( CV_StsError, buf );
1548                     }
1549                     /* right value */
1550                     if( last_idx >= classifier->count + 1 )
1551                     {
1552                         sprintf( buf, "Tree structure is broken: too many values. "
1553                                  "(stage %d, tree %d, node %d)", i, j, k );
1554                         CV_ERROR( CV_StsError, buf );
1555                     }
1556                     classifier->right[k] = -last_idx;
1557                     classifier->alpha[last_idx++] = (float) fn->data.f;
1558                 }
1559
1560                 CV_NEXT_SEQ_ELEM( sizeof( *node_fn ), tree_reader );
1561             } /* for each node */
1562             if( last_idx != classifier->count + 1 )
1563             {
1564                 sprintf( buf, "Tree structure is broken: too few values. "
1565                          "(stage %d, tree %d)", i, j );
1566                 CV_ERROR( CV_StsError, buf );
1567             }
1568
1569             CV_NEXT_SEQ_ELEM( sizeof( *tree_fn ), trees_reader );
1570         } /* for each tree */
1571
1572         CV_CALL( fn = cvGetFileNodeByName( fs, stage_fn, ICV_HAAR_STAGE_THRESHOLD_NAME));
1573         if( !fn || !CV_NODE_IS_REAL( fn->tag ) )
1574         {
1575             sprintf( buf, "stage threshold must be real number. (stage %d)", i );
1576             CV_ERROR( CV_StsError, buf );
1577         }
1578         cascade->stage_classifier[i].threshold = (float) fn->data.f;
1579
1580         parent = i - 1;
1581         next = -1;
1582
1583         CV_CALL( fn = cvGetFileNodeByName( fs, stage_fn, ICV_HAAR_PARENT_NAME ) );
1584         if( !fn || !CV_NODE_IS_INT( fn->tag )
1585             || fn->data.i < -1 || fn->data.i >= cascade->count )
1586         {
1587             sprintf( buf, "parent must be integer number. (stage %d)", i );
1588             CV_ERROR( CV_StsError, buf );
1589         }
1590         parent = fn->data.i;
1591         CV_CALL( fn = cvGetFileNodeByName( fs, stage_fn, ICV_HAAR_NEXT_NAME ) );
1592         if( !fn || !CV_NODE_IS_INT( fn->tag )
1593             || fn->data.i < -1 || fn->data.i >= cascade->count )
1594         {
1595             sprintf( buf, "next must be integer number. (stage %d)", i );
1596             CV_ERROR( CV_StsError, buf );
1597         }
1598         next = fn->data.i;
1599
1600         cascade->stage_classifier[i].parent = parent;
1601         cascade->stage_classifier[i].next = next;
1602         cascade->stage_classifier[i].child = -1;
1603
1604         if( parent != -1 && cascade->stage_classifier[parent].child == -1 )
1605         {
1606             cascade->stage_classifier[parent].child = i;
1607         }
1608         
1609         CV_NEXT_SEQ_ELEM( sizeof( *stage_fn ), stages_reader );
1610     } /* for each stage */
1611
1612     __END__;
1613
1614     if( cvGetErrStatus() < 0 )
1615     {
1616         cvReleaseHaarClassifierCascade( &cascade );
1617         cascade = NULL;
1618     }
1619
1620     return cascade;
1621 }
1622
1623 static void
1624 icvWriteHaarClassifier( CvFileStorage* fs, const char* name, const void* struct_ptr,
1625                         CvAttrList attributes )
1626 {
1627     CV_FUNCNAME( "cvWriteHaarClassifier" );
1628
1629     __BEGIN__;
1630
1631     int i, j, k, l;
1632     char buf[256];
1633     const CvHaarClassifierCascade* cascade = (const CvHaarClassifierCascade*) struct_ptr;
1634
1635     /* TODO: parameters check */
1636
1637     CV_CALL( cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_HAAR, attributes ) );
1638     
1639     CV_CALL( cvStartWriteStruct( fs, ICV_HAAR_SIZE_NAME, CV_NODE_SEQ | CV_NODE_FLOW ) );
1640     CV_CALL( cvWriteInt( fs, NULL, cascade->orig_window_size.width ) );
1641     CV_CALL( cvWriteInt( fs, NULL, cascade->orig_window_size.height ) );
1642     CV_CALL( cvEndWriteStruct( fs ) ); /* size */
1643     
1644     CV_CALL( cvStartWriteStruct( fs, ICV_HAAR_STAGES_NAME, CV_NODE_SEQ ) );    
1645     for( i = 0; i < cascade->count; ++i )
1646     {
1647         CV_CALL( cvStartWriteStruct( fs, NULL, CV_NODE_MAP ) );
1648         sprintf( buf, "stage %d", i );
1649         CV_CALL( cvWriteComment( fs, buf, 1 ) );
1650         
1651         CV_CALL( cvStartWriteStruct( fs, ICV_HAAR_TREES_NAME, CV_NODE_SEQ ) );
1652
1653         for( j = 0; j < cascade->stage_classifier[i].count; ++j )
1654         {
1655             CvHaarClassifier* tree = &cascade->stage_classifier[i].classifier[j];
1656
1657             CV_CALL( cvStartWriteStruct( fs, NULL, CV_NODE_SEQ ) );
1658             sprintf( buf, "tree %d", j );
1659             CV_CALL( cvWriteComment( fs, buf, 1 ) );
1660
1661             for( k = 0; k < tree->count; ++k )
1662             {
1663                 CvHaarFeature* feature = &tree->haar_feature[k];
1664
1665                 CV_CALL( cvStartWriteStruct( fs, NULL, CV_NODE_MAP ) );
1666                 if( k )
1667                 {
1668                     sprintf( buf, "node %d", k );
1669                 }
1670                 else
1671                 {
1672                     sprintf( buf, "root node" );
1673                 }
1674                 CV_CALL( cvWriteComment( fs, buf, 1 ) );
1675
1676                 CV_CALL( cvStartWriteStruct( fs, ICV_HAAR_FEATURE_NAME, CV_NODE_MAP ) );
1677                 
1678                 CV_CALL( cvStartWriteStruct( fs, ICV_HAAR_RECTS_NAME, CV_NODE_SEQ ) );
1679                 for( l = 0; l < CV_HAAR_FEATURE_MAX && feature->rect[l].weight != 0; ++l )
1680                 {
1681                     CV_CALL( cvStartWriteStruct( fs, NULL, CV_NODE_SEQ | CV_NODE_FLOW ) );
1682                     CV_CALL( cvWriteInt(  fs, NULL, feature->rect[l].r.x ) );
1683                     CV_CALL( cvWriteInt(  fs, NULL, feature->rect[l].r.y ) );
1684                     CV_CALL( cvWriteInt(  fs, NULL, feature->rect[l].r.width ) );
1685                     CV_CALL( cvWriteInt(  fs, NULL, feature->rect[l].r.height ) );
1686                     CV_CALL( cvWriteReal( fs, NULL, feature->rect[l].weight ) );
1687                     CV_CALL( cvEndWriteStruct( fs ) ); /* rect */
1688                 }
1689                 CV_CALL( cvEndWriteStruct( fs ) ); /* rects */
1690                 CV_CALL( cvWriteInt( fs, ICV_HAAR_TILTED_NAME, feature->tilted ) );
1691                 CV_CALL( cvEndWriteStruct( fs ) ); /* feature */
1692                 
1693                 CV_CALL( cvWriteReal( fs, ICV_HAAR_THRESHOLD_NAME, tree->threshold[k]) );
1694
1695                 if( tree->left[k] > 0 )
1696                 {
1697                     CV_CALL( cvWriteInt( fs, ICV_HAAR_LEFT_NODE_NAME, tree->left[k] ) );
1698                 }
1699                 else
1700                 {
1701                     CV_CALL( cvWriteReal( fs, ICV_HAAR_LEFT_VAL_NAME,
1702                         tree->alpha[-tree->left[k]] ) );
1703                 }
1704
1705                 if( tree->right[k] > 0 )
1706                 {
1707                     CV_CALL( cvWriteInt( fs, ICV_HAAR_RIGHT_NODE_NAME, tree->right[k] ) );
1708                 }
1709                 else
1710                 {
1711                     CV_CALL( cvWriteReal( fs, ICV_HAAR_RIGHT_VAL_NAME,
1712                         tree->alpha[-tree->right[k]] ) );
1713                 }
1714
1715                 CV_CALL( cvEndWriteStruct( fs ) ); /* split */
1716             }
1717
1718             CV_CALL( cvEndWriteStruct( fs ) ); /* tree */
1719         }
1720
1721         CV_CALL( cvEndWriteStruct( fs ) ); /* trees */
1722
1723         CV_CALL( cvWriteReal( fs, ICV_HAAR_STAGE_THRESHOLD_NAME,
1724                               cascade->stage_classifier[i].threshold) );
1725
1726         CV_CALL( cvWriteInt( fs, ICV_HAAR_PARENT_NAME,
1727                               cascade->stage_classifier[i].parent ) );
1728         CV_CALL( cvWriteInt( fs, ICV_HAAR_NEXT_NAME,
1729                               cascade->stage_classifier[i].next ) );
1730
1731         CV_CALL( cvEndWriteStruct( fs ) ); /* stage */
1732     } /* for each stage */
1733     
1734     CV_CALL( cvEndWriteStruct( fs ) ); /* stages */
1735     CV_CALL( cvEndWriteStruct( fs ) ); /* root */
1736
1737     __END__;
1738 }
1739
1740 static void*
1741 icvCloneHaarClassifier( const void* struct_ptr )
1742 {
1743     CvHaarClassifierCascade* cascade = NULL;
1744
1745     CV_FUNCNAME( "cvCloneHaarClassifier" );
1746
1747     __BEGIN__;
1748
1749     int i, j, k, n;
1750     const CvHaarClassifierCascade* cascade_src =
1751         (const CvHaarClassifierCascade*) struct_ptr;
1752
1753     n = cascade_src->count;
1754     CV_CALL( cascade = icvCreateHaarClassifierCascade(n) );
1755     cascade->orig_window_size = cascade_src->orig_window_size;
1756
1757     for( i = 0; i < n; ++i )
1758     {
1759         cascade->stage_classifier[i].parent = cascade_src->stage_classifier[i].parent;
1760         cascade->stage_classifier[i].next = cascade_src->stage_classifier[i].next;
1761         cascade->stage_classifier[i].child = cascade_src->stage_classifier[i].child;
1762         cascade->stage_classifier[i].threshold = cascade_src->stage_classifier[i].threshold;
1763
1764         cascade->stage_classifier[i].count = 0;
1765         CV_CALL( cascade->stage_classifier[i].classifier =
1766             (CvHaarClassifier*) cvAlloc( cascade_src->stage_classifier[i].count
1767                 * sizeof( cascade->stage_classifier[i].classifier[0] ) ) );
1768         
1769         cascade->stage_classifier[i].count = cascade_src->stage_classifier[i].count;
1770
1771         for( j = 0; j < cascade->stage_classifier[i].count; ++j )
1772         {
1773             cascade->stage_classifier[i].classifier[j].haar_feature = NULL;
1774         }
1775
1776         for( j = 0; j < cascade->stage_classifier[i].count; ++j )
1777         {
1778             const CvHaarClassifier* classifier_src = 
1779                 &cascade_src->stage_classifier[i].classifier[j];
1780             CvHaarClassifier* classifier = 
1781                 &cascade->stage_classifier[i].classifier[j];
1782
1783             classifier->count = classifier_src->count;
1784             CV_CALL( classifier->haar_feature = (CvHaarFeature*) cvAlloc( 
1785                 classifier->count * ( sizeof( *classifier->haar_feature ) +
1786                                       sizeof( *classifier->threshold ) +
1787                                       sizeof( *classifier->left ) +
1788                                       sizeof( *classifier->right ) ) +
1789                 (classifier->count + 1) * sizeof( *classifier->alpha ) ) );
1790             classifier->threshold = (float*) (classifier->haar_feature+classifier->count);
1791             classifier->left = (int*) (classifier->threshold + classifier->count);
1792             classifier->right = (int*) (classifier->left + classifier->count);
1793             classifier->alpha = (float*) (classifier->right + classifier->count);
1794             for( k = 0; k < classifier->count; ++k )
1795             {
1796                 classifier->haar_feature[k] = classifier_src->haar_feature[k];
1797                 classifier->threshold[k] = classifier_src->threshold[k];
1798                 classifier->left[k] = classifier_src->left[k];
1799                 classifier->right[k] = classifier_src->right[k];
1800                 classifier->alpha[k] = classifier_src->alpha[k];
1801             }
1802             classifier->alpha[classifier->count] = 
1803                 classifier_src->alpha[classifier->count];
1804         }
1805     }
1806
1807     __END__;
1808
1809     return cascade;
1810 }
1811
1812 static int
1813 icvRegisterHaarClassifierType()
1814 {
1815     CV_FUNCNAME( "icvRegisterHaarClassifierType" );
1816
1817     __BEGIN__;
1818
1819     CvTypeInfo info;
1820
1821     info.header_size = sizeof( info );
1822     info.is_instance = icvIsHaarClassifier;
1823     info.release = (CvReleaseFunc) cvReleaseHaarClassifierCascade;
1824     info.read = icvReadHaarClassifier;
1825     info.write = icvWriteHaarClassifier;
1826     info.clone = icvCloneHaarClassifier;
1827     info.type_name = CV_TYPE_NAME_HAAR;
1828     CV_CALL( cvRegisterType( &info ) );
1829
1830     __END__;
1831
1832     return 1;
1833 }
1834
1835 static int icv_register_haar_classifier_type = icvRegisterHaarClassifierType();
1836
1837 /* End of file. */