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//
12// Copyright (C) 2000, Intel Corporation, all rights reserved.
13// Third party copyrights are property of their respective owners.
14//
15// Redistribution and use in source and binary forms, with or without modification,
16// are permitted provided that the following conditions are met:
17//
18//   * Redistribution's of source code must retain the above copyright notice,
19//     this list of conditions and the following disclaimer.
20//
21//   * Redistribution's in binary form must reproduce the above copyright notice,
22//     this list of conditions and the following disclaimer in the documentation
23//     and/or other materials provided with the distribution.
24//
25//   * The name of Intel Corporation may not be used to endorse or promote products
26//     derived from this software without specific prior written permission.
27//
28// This software is provided by the copyright holders and contributors "as is" and
29// any express or implied warranties, including, but not limited to, the implied
30// warranties of merchantability and fitness for a particular purpose are disclaimed.
31// In no event shall the Intel Corporation or contributors be liable for any direct,
32// indirect, incidental, special, exemplary, or consequential damages
33// (including, but not limited to, procurement of substitute goods or services;
34// loss of use, data, or profits; or business interruption) however caused
35// and on any theory of liability, whether in contract, strict liability,
36// or tort (including negligence or otherwise) arising in any way out of
37// the use of this software, even if advised of the possibility of such damage.
38//
39//M*/
40
41#include "_ml.h"
42
43#if 0
44
45ML_IMPL int
46icvCmpIntegers (const void* a, const void* b) {return *(const int*)a - *(const int*)b;}
47
48/****************************************************************************************\
49*                    Cross-validation algorithms realizations                            *
50\****************************************************************************************/
51
52// Return pointer to trainIdx. Function DOES NOT FILL this matrix!
53ML_IMPL
54const CvMat* cvCrossValGetTrainIdxMatrix (const CvStatModel* estimateModel)
55{
56    CvMat* result = NULL;
57
58        CV_FUNCNAME ("cvCrossValGetTrainIdxMatrix");
59        __BEGIN__
60
61    if (!CV_IS_CROSSVAL(estimateModel))
62    {
63        CV_ERROR (CV_StsBadArg, "Pointer point to not CvCrossValidationModel");
64    }
65
66    result = ((CvCrossValidationModel*)estimateModel)->sampleIdxTrain;
67
68        __END__
69
70    return result;
71} // End of cvCrossValGetTrainIdxMatrix
72
73/****************************************************************************************/
74// Return pointer to checkIdx. Function DOES NOT FILL this matrix!
75ML_IMPL
76const CvMat* cvCrossValGetCheckIdxMatrix (const CvStatModel* estimateModel)
77{
78    CvMat* result = NULL;
79
80        CV_FUNCNAME ("cvCrossValGetCheckIdxMatrix");
81        __BEGIN__
82
83    if (!CV_IS_CROSSVAL (estimateModel))
84    {
85        CV_ERROR (CV_StsBadArg, "Pointer point to not CvCrossValidationModel");
86    }
87
88    result = ((CvCrossValidationModel*)estimateModel)->sampleIdxEval;
89
90        __END__
91
92    return result;
93} // End of cvCrossValGetCheckIdxMatrix
94
95/****************************************************************************************/
96// Create new Idx-matrix for next classifiers training and return code of result.
97//   Result is 0 if function can't make next step (error input or folds are finished),
98//   it is 1 if all was correct, and it is 2 if current fold wasn't' checked.
99ML_IMPL
100int cvCrossValNextStep (CvStatModel* estimateModel)
101{
102    int result = 0;
103
104        CV_FUNCNAME ("cvCrossValGetNextTrainIdx");
105        __BEGIN__
106
107    CvCrossValidationModel* crVal = (CvCrossValidationModel*) estimateModel;
108    int k, fold;
109
110    if (!CV_IS_CROSSVAL (estimateModel))
111    {
112        CV_ERROR (CV_StsBadArg, "Pointer point to not CvCrossValidationModel");
113    }
114
115    fold = ++crVal->current_fold;
116
117    if (fold >= crVal->folds_all)
118    {
119        if (fold == crVal->folds_all)
120            EXIT;
121        else
122        {
123            CV_ERROR (CV_StsInternal, "All iterations has end long ago");
124        }
125    }
126
127    k = crVal->folds[fold + 1] - crVal->folds[fold];
128    crVal->sampleIdxTrain->data.i = crVal->sampleIdxAll + crVal->folds[fold + 1];
129    crVal->sampleIdxTrain->cols = crVal->samples_all - k;
130    crVal->sampleIdxEval->data.i = crVal->sampleIdxAll + crVal->folds[fold];
131    crVal->sampleIdxEval->cols = k;
132
133    if (crVal->is_checked)
134    {
135        crVal->is_checked = 0;
136        result = 1;
137    }
138    else
139    {
140        result = 2;
141    }
142
143        __END__
144
145    return result;
146}
147
148/****************************************************************************************/
149// Do checking part of loop  of cross-validations metod.
150ML_IMPL
151void cvCrossValCheckClassifier (CvStatModel*  estimateModel,
152                          const CvStatModel*  model,
153                          const CvMat*        trainData,
154                                int           sample_t_flag,
155                          const CvMat*        trainClasses)
156{
157        CV_FUNCNAME ("cvCrossValCheckClassifier ");
158        __BEGIN__
159
160    CvCrossValidationModel* crVal = (CvCrossValidationModel*) estimateModel;
161    int  i, j, k;
162    int* data;
163    float* responses_fl;
164    int    step;
165    float* responses_result;
166    int* responses_i;
167    double te, te1;
168    double sum_c, sum_p, sum_pp, sum_cp, sum_cc, sq_err;
169
170// Check input data to correct values.
171    if (!CV_IS_CROSSVAL (estimateModel))
172    {
173        CV_ERROR (CV_StsBadArg,"First parameter point to not CvCrossValidationModel");
174    }
175    if (!CV_IS_STAT_MODEL (model))
176    {
177        CV_ERROR (CV_StsBadArg, "Second parameter point to not CvStatModel");
178    }
179    if (!CV_IS_MAT (trainData))
180    {
181        CV_ERROR (CV_StsBadArg, "Third parameter point to not CvMat");
182    }
183    if (!CV_IS_MAT (trainClasses))
184    {
185        CV_ERROR (CV_StsBadArg, "Fifth parameter point to not CvMat");
186    }
187    if (crVal->is_checked)
188    {
189        CV_ERROR (CV_StsInternal, "This iterations already was checked");
190    }
191
192// Initialize.
193    k = crVal->sampleIdxEval->cols;
194    data = crVal->sampleIdxEval->data.i;
195
196// Eval tested feature vectors.
197    CV_CALL (cvStatModelMultiPredict (model, trainData, sample_t_flag,
198                                         crVal->predict_results, NULL, crVal->sampleIdxEval));
199// Count number if correct results.
200    responses_result = crVal->predict_results->data.fl;
201    if (crVal->is_regression)
202    {
203        sum_c = sum_p = sum_pp = sum_cp = sum_cc = sq_err = 0;
204        if (CV_MAT_TYPE (trainClasses->type) == CV_32FC1)
205        {
206            responses_fl = trainClasses->data.fl;
207            step = trainClasses->rows == 1 ? 1 : trainClasses->step / sizeof(float);
208            for (i = 0; i < k; i++)
209            {
210                te = responses_result[*data];
211                te1 = responses_fl[*data * step];
212                sum_c += te1;
213                sum_p += te;
214                sum_cc += te1 * te1;
215                sum_pp += te * te;
216                sum_cp += te1 * te;
217                te -= te1;
218                sq_err += te  * te;
219
220                data++;
221            }
222        }
223        else
224        {
225            responses_i = trainClasses->data.i;
226            step = trainClasses->rows == 1 ? 1 : trainClasses->step / sizeof(int);
227            for (i = 0; i < k; i++)
228            {
229                te = responses_result[*data];
230                te1 = responses_i[*data * step];
231                sum_c += te1;
232                sum_p += te;
233                sum_cc += te1 * te1;
234                sum_pp += te * te;
235                sum_cp += te1 * te;
236                te -= te1;
237                sq_err += te  * te;
238
239                data++;
240            }
241        }
242    // Fixing new internal values of accuracy.
243        crVal->sum_correct += sum_c;
244        crVal->sum_predict += sum_p;
245        crVal->sum_cc += sum_cc;
246        crVal->sum_pp += sum_pp;
247        crVal->sum_cp += sum_cp;
248        crVal->sq_error += sq_err;
249    }
250    else
251    {
252        if (CV_MAT_TYPE (trainClasses->type) == CV_32FC1)
253        {
254            responses_fl = trainClasses->data.fl;
255            step = trainClasses->rows == 1 ? 1 : trainClasses->step / sizeof(float);
256            for (i = 0, j = 0; i < k; i++)
257            {
258                if (cvRound (responses_result[*data]) == cvRound (responses_fl[*data * step]))
259                    j++;
260                data++;
261            }
262        }
263        else
264        {
265            responses_i = trainClasses->data.i;
266            step = trainClasses->rows == 1 ? 1 : trainClasses->step / sizeof(int);
267            for (i = 0, j = 0; i < k; i++)
268            {
269                if (cvRound (responses_result[*data]) == responses_i[*data * step])
270                    j++;
271                data++;
272            }
273        }
274    // Fixing new internal values of accuracy.
275        crVal->correct_results += j;
276    }
277// Fixing that this fold already checked.
278    crVal->all_results += k;
279    crVal->is_checked = 1;
280
281        __END__
282} // End of cvCrossValCheckClassifier
283
284/****************************************************************************************/
285// Return current accuracy.
286ML_IMPL
287float cvCrossValGetResult (const CvStatModel* estimateModel,
288                                 float*       correlation)
289{
290    float result = 0;
291
292        CV_FUNCNAME ("cvCrossValGetResult");
293        __BEGIN__
294
295    double te, te1;
296    CvCrossValidationModel* crVal = (CvCrossValidationModel*)estimateModel;
297
298    if (!CV_IS_CROSSVAL (estimateModel))
299    {
300        CV_ERROR (CV_StsBadArg, "Pointer point to not CvCrossValidationModel");
301    }
302
303    if (crVal->all_results)
304    {
305        if (crVal->is_regression)
306        {
307            result = ((float)crVal->sq_error) / crVal->all_results;
308            if (correlation)
309            {
310                te = crVal->all_results * crVal->sum_cp -
311                                             crVal->sum_correct * crVal->sum_predict;
312                te *= te;
313                te1 = (crVal->all_results * crVal->sum_cc -
314                                    crVal->sum_correct * crVal->sum_correct) *
315                           (crVal->all_results * crVal->sum_pp -
316                                    crVal->sum_predict * crVal->sum_predict);
317                *correlation = (float)(te / te1);
318
319            }
320        }
321        else
322        {
323            result = ((float)crVal->correct_results) / crVal->all_results;
324        }
325    }
326
327        __END__
328
329    return result;
330}
331
332/****************************************************************************************/
333// Reset cross-validation EstimateModel to state the same as it was immidiatly after
334//   its creating.
335ML_IMPL
336void cvCrossValReset (CvStatModel* estimateModel)
337{
338        CV_FUNCNAME ("cvCrossValReset");
339        __BEGIN__
340
341    CvCrossValidationModel* crVal = (CvCrossValidationModel*)estimateModel;
342
343    if (!CV_IS_CROSSVAL (estimateModel))
344    {
345        CV_ERROR (CV_StsBadArg, "Pointer point to not CvCrossValidationModel");
346    }
347
348    crVal->current_fold = -1;
349    crVal->is_checked = 1;
350    crVal->all_results = 0;
351    crVal->correct_results = 0;
352    crVal->sq_error = 0;
353    crVal->sum_correct = 0;
354    crVal->sum_predict = 0;
355    crVal->sum_cc = 0;
356    crVal->sum_pp = 0;
357    crVal->sum_cp = 0;
358
359        __END__
360}
361
362/****************************************************************************************/
363// This function is standart CvStatModel field to release cross-validation EstimateModel.
364ML_IMPL
365void cvReleaseCrossValidationModel (CvStatModel** model)
366{
367    CvCrossValidationModel* pModel;
368
369        CV_FUNCNAME ("cvReleaseCrossValidationModel");
370        __BEGIN__
371
372    if (!model)
373    {
374        CV_ERROR (CV_StsNullPtr, "");
375    }
376
377    pModel = (CvCrossValidationModel*)*model;
378    if (!pModel)
379    {
380        return;
381    }
382    if (!CV_IS_CROSSVAL (pModel))
383    {
384        CV_ERROR (CV_StsBadArg, "");
385    }
386
387    cvFree (&pModel->sampleIdxAll);
388    cvFree (&pModel->folds);
389    cvReleaseMat (&pModel->sampleIdxEval);
390    cvReleaseMat (&pModel->sampleIdxTrain);
391    cvReleaseMat (&pModel->predict_results);
392
393    cvFree (model);
394
395        __END__
396} // End of cvReleaseCrossValidationModel.
397
398/****************************************************************************************/
399// This function create cross-validation EstimateModel.
400ML_IMPL CvStatModel*
401cvCreateCrossValidationEstimateModel(
402             int                samples_all,
403       const CvStatModelParams* estimateParams,
404       const CvMat*             sampleIdx)
405{
406    CvStatModel*            model   = NULL;
407    CvCrossValidationModel* crVal   = NULL;
408
409        CV_FUNCNAME ("cvCreateCrossValidationEstimateModel");
410        __BEGIN__
411
412    int  k_fold = 10;
413
414    int  i, j, k, s_len;
415    int  samples_selected;
416    CvRNG rng;
417    CvRNG* prng;
418    int* res_s_data;
419    int* te_s_data;
420    int* folds;
421
422    rng = cvRNG(cvGetTickCount());
423    cvRandInt (&rng); cvRandInt (&rng); cvRandInt (&rng); cvRandInt (&rng);
424// Check input parameters.
425    if (estimateParams)
426        k_fold = ((CvCrossValidationParams*)estimateParams)->k_fold;
427    if (!k_fold)
428    {
429        CV_ERROR (CV_StsBadArg, "Error in parameters of cross-validation (k_fold == 0)!");
430    }
431    if (samples_all <= 0)
432    {
433        CV_ERROR (CV_StsBadArg, "<samples_all> should be positive!");
434    }
435
436// Alloc memory and fill standart StatModel's fields.
437    CV_CALL (crVal = (CvCrossValidationModel*)cvCreateStatModel (
438                            CV_STAT_MODEL_MAGIC_VAL | CV_CROSSVAL_MAGIC_VAL,
439                            sizeof(CvCrossValidationModel),
440                            cvReleaseCrossValidationModel,
441                            NULL, NULL));
442    crVal->current_fold    = -1;
443    crVal->folds_all       = k_fold;
444    if (estimateParams && ((CvCrossValidationParams*)estimateParams)->is_regression)
445        crVal->is_regression = 1;
446    else
447        crVal->is_regression = 0;
448    if (estimateParams && ((CvCrossValidationParams*)estimateParams)->rng)
449        prng = ((CvCrossValidationParams*)estimateParams)->rng;
450    else
451        prng = &rng;
452
453    // Check and preprocess sample indices.
454    if (sampleIdx)
455    {
456        int s_step;
457        int s_type = 0;
458
459        if (!CV_IS_MAT (sampleIdx))
460            CV_ERROR (CV_StsBadArg, "Invalid sampleIdx array");
461
462        if (sampleIdx->rows != 1 && sampleIdx->cols != 1)
463            CV_ERROR (CV_StsBadSize, "sampleIdx array must be 1-dimensional");
464
465        s_len = sampleIdx->rows + sampleIdx->cols - 1;
466        s_step = sampleIdx->rows == 1 ?
467                                     1 : sampleIdx->step / CV_ELEM_SIZE(sampleIdx->type);
468
469        s_type = CV_MAT_TYPE (sampleIdx->type);
470
471        switch (s_type)
472        {
473        case CV_8UC1:
474        case CV_8SC1:
475            {
476            uchar* s_data = sampleIdx->data.ptr;
477
478            // sampleIdx is array of 1's and 0's -
479            // i.e. it is a mask of the selected samples
480            if( s_len != samples_all )
481                CV_ERROR (CV_StsUnmatchedSizes,
482       "Sample mask should contain as many elements as the total number of samples");
483
484            samples_selected = 0;
485            for (i = 0; i < s_len; i++)
486                samples_selected += s_data[i * s_step] != 0;
487
488            if (samples_selected == 0)
489                CV_ERROR (CV_StsOutOfRange, "No samples is selected!");
490            }
491            s_len = samples_selected;
492            break;
493        case CV_32SC1:
494            if (s_len > samples_all)
495                CV_ERROR (CV_StsOutOfRange,
496        "sampleIdx array may not contain more elements than the total number of samples");
497            samples_selected = s_len;
498            break;
499        default:
500            CV_ERROR (CV_StsUnsupportedFormat, "Unsupported sampleIdx array data type "
501                                               "(it should be 8uC1, 8sC1 or 32sC1)");
502        }
503
504        // Alloc additional memory for internal Idx and fill it.
505/*!!*/  CV_CALL (res_s_data = crVal->sampleIdxAll =
506                                                 (int*)cvAlloc (2 * s_len * sizeof(int)));
507
508        if (s_type < CV_32SC1)
509        {
510            uchar* s_data = sampleIdx->data.ptr;
511            for (i = 0; i < s_len; i++)
512                if (s_data[i * s_step])
513                {
514                    *res_s_data++ = i;
515                }
516            res_s_data = crVal->sampleIdxAll;
517        }
518        else
519        {
520            int* s_data = sampleIdx->data.i;
521            int out_of_order = 0;
522
523            for (i = 0; i < s_len; i++)
524            {
525                res_s_data[i] = s_data[i * s_step];
526                if (i > 0 && res_s_data[i] < res_s_data[i - 1])
527                    out_of_order = 1;
528            }
529
530            if (out_of_order)
531                qsort (res_s_data, s_len, sizeof(res_s_data[0]), icvCmpIntegers);
532
533            if (res_s_data[0] < 0 ||
534                res_s_data[s_len - 1] >= samples_all)
535                    CV_ERROR (CV_StsBadArg, "There are out-of-range sample indices");
536            for (i = 1; i < s_len; i++)
537                if (res_s_data[i] <= res_s_data[i - 1])
538                    CV_ERROR (CV_StsBadArg, "There are duplicated");
539        }
540    }
541    else // if (sampleIdx)
542    {
543        // Alloc additional memory for internal Idx and fill it.
544        s_len = samples_all;
545        CV_CALL (res_s_data = crVal->sampleIdxAll = (int*)cvAlloc (2 * s_len * sizeof(int)));
546        for (i = 0; i < s_len; i++)
547        {
548            *res_s_data++ = i;
549        }
550        res_s_data = crVal->sampleIdxAll;
551    } // if (sampleIdx) ... else
552
553// Resort internal Idx.
554    te_s_data = res_s_data + s_len;
555    for (i = s_len; i > 1; i--)
556    {
557        j = cvRandInt (prng) % i;
558        k = *(--te_s_data);
559        *te_s_data = res_s_data[j];
560        res_s_data[j] = k;
561    }
562
563// Duplicate resorted internal Idx.
564// It will be used to simplify operation of getting trainIdx.
565    te_s_data = res_s_data + s_len;
566    for (i = 0; i < s_len; i++)
567    {
568        *te_s_data++ = *res_s_data++;
569    }
570
571// Cut sampleIdxAll to parts.
572    if (k_fold > 0)
573    {
574        if (k_fold > s_len)
575        {
576            CV_ERROR (CV_StsBadArg,
577                        "Error in parameters of cross-validation ('k_fold' > #samples)!");
578        }
579        folds = crVal->folds = (int*) cvAlloc ((k_fold + 1) * sizeof (int));
580        *folds++ = 0;
581        for (i = 1; i < k_fold; i++)
582        {
583            *folds++ = cvRound (i * s_len * 1. / k_fold);
584        }
585        *folds = s_len;
586        folds = crVal->folds;
587
588        crVal->max_fold_size = (s_len - 1) / k_fold + 1;
589    }
590    else
591    {
592        k = -k_fold;
593        crVal->max_fold_size = k;
594        if (k >= s_len)
595        {
596            CV_ERROR (CV_StsBadArg,
597                      "Error in parameters of cross-validation (-'k_fold' > #samples)!");
598        }
599        crVal->folds_all = k = (s_len - 1) / k + 1;
600
601        folds = crVal->folds = (int*) cvAlloc ((k + 1) * sizeof (int));
602        for (i = 0; i < k; i++)
603        {
604            *folds++ = -i * k_fold;
605        }
606        *folds = s_len;
607        folds = crVal->folds;
608    }
609
610// Prepare other internal fields to working.
611    CV_CALL (crVal->predict_results = cvCreateMat (1, samples_all, CV_32FC1));
612    CV_CALL (crVal->sampleIdxEval = cvCreateMatHeader (1, 1, CV_32SC1));
613    CV_CALL (crVal->sampleIdxTrain = cvCreateMatHeader (1, 1, CV_32SC1));
614    crVal->sampleIdxEval->cols = 0;
615    crVal->sampleIdxTrain->cols = 0;
616    crVal->samples_all = s_len;
617    crVal->is_checked = 1;
618
619    crVal->getTrainIdxMat = cvCrossValGetTrainIdxMatrix;
620    crVal->getCheckIdxMat = cvCrossValGetCheckIdxMatrix;
621    crVal->nextStep = cvCrossValNextStep;
622    crVal->check = cvCrossValCheckClassifier;
623    crVal->getResult = cvCrossValGetResult;
624    crVal->reset = cvCrossValReset;
625
626    model = (CvStatModel*)crVal;
627
628        __END__
629
630    if (!model)
631    {
632        cvReleaseCrossValidationModel ((CvStatModel**)&crVal);
633    }
634
635    return model;
636} // End of cvCreateCrossValidationEstimateModel
637
638
639/****************************************************************************************\
640*                Extended interface with backcalls for models                            *
641\****************************************************************************************/
642ML_IMPL float
643cvCrossValidation (const CvMat*            trueData,
644                         int               tflag,
645                   const CvMat*            trueClasses,
646                         CvStatModel*     (*createClassifier) (const CvMat*,
647                                                                     int,
648                                                               const CvMat*,
649                                                               const CvClassifierTrainParams*,
650                                                               const CvMat*,
651                                                               const CvMat*,
652                                                               const CvMat*,
653                                                               const CvMat*),
654                   const CvClassifierTrainParams*    estimateParams,
655                   const CvClassifierTrainParams*    trainParams,
656                   const CvMat*            compIdx,
657                   const CvMat*            sampleIdx,
658                         CvStatModel**     pCrValModel,
659                   const CvMat*            typeMask,
660                   const CvMat*            missedMeasurementMask)
661{
662    CvCrossValidationModel* crVal = NULL;
663    float  result = 0;
664    CvStatModel* pClassifier = NULL;
665
666        CV_FUNCNAME ("cvCrossValidation");
667        __BEGIN__
668
669    const CvMat* trainDataIdx;
670    int    samples_all;
671
672// checking input data
673    if ((createClassifier) == NULL)
674    {
675        CV_ERROR (CV_StsNullPtr, "Null pointer to functiion which create classifier");
676    }
677    if (pCrValModel && *pCrValModel && !CV_IS_CROSSVAL(*pCrValModel))
678    {
679        CV_ERROR (CV_StsBadArg,
680           "<pCrValModel> point to not cross-validation model");
681    }
682
683// initialization
684    if (pCrValModel && *pCrValModel)
685    {
686        crVal = (CvCrossValidationModel*)*pCrValModel;
687        crVal->reset ((CvStatModel*)crVal);
688    }
689    else
690    {
691        samples_all = ((tflag) ? trueData->rows : trueData->cols);
692        CV_CALL (crVal = (CvCrossValidationModel*)
693           cvCreateCrossValidationEstimateModel (samples_all, estimateParams, sampleIdx));
694    }
695
696    CV_CALL (trainDataIdx = crVal->getTrainIdxMat ((CvStatModel*)crVal));
697
698// operation loop
699    for (; crVal->nextStep((CvStatModel*)crVal) != 0; )
700    {
701        CV_CALL (pClassifier = createClassifier (trueData, tflag, trueClasses,
702                    trainParams, compIdx, trainDataIdx, typeMask, missedMeasurementMask));
703        CV_CALL (crVal->check ((CvStatModel*)crVal, pClassifier,
704                                                           trueData, tflag, trueClasses));
705
706        pClassifier->release (&pClassifier);
707    }
708
709// Get result and fill output field.
710    CV_CALL (result = crVal->getResult ((CvStatModel*)crVal, 0));
711
712    if (pCrValModel && !*pCrValModel)
713        *pCrValModel = (CvStatModel*)crVal;
714
715        __END__
716
717// Free all memory that should be freed.
718    if (pClassifier)
719        pClassifier->release (&pClassifier);
720    if (crVal && (!pCrValModel || !*pCrValModel))
721        crVal->release ((CvStatModel**)&crVal);
722
723    return result;
724} // End of cvCrossValidation
725
726#endif
727
728/* End of file */
729