Apply maemo2 patch
[opencv] / ml / src / mlcnn.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 //
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 /****************************************************************************************\
45 *                         Auxilary functions declarations                                *
46 \****************************************************************************************/
47 /*---------------------- functions for the CNN classifier ------------------------------*/
48 static float icvCNNModelPredict(
49         const CvStatModel* cnn_model,
50         const CvMat* image,
51         CvMat* probs CV_DEFAULT(0) );
52
53 static void icvCNNModelUpdate(
54         CvStatModel* cnn_model, const CvMat* images, int tflag,
55         const CvMat* responses, const CvStatModelParams* params,
56         const CvMat* CV_DEFAULT(0), const CvMat* sample_idx CV_DEFAULT(0),
57         const CvMat* CV_DEFAULT(0), const CvMat* CV_DEFAULT(0));
58
59 static void icvCNNModelRelease( CvStatModel** cnn_model );
60
61 static void icvTrainCNNetwork( CvCNNetwork* network,
62                                const float** images,
63                                const CvMat* responses,
64                                const CvMat* etalons,
65                                int grad_estim_type,
66                                int max_iter,
67                                int start_iter );
68
69 /*------------------------- functions for the CNN network ------------------------------*/
70 static void icvCNNetworkAddLayer( CvCNNetwork* network, CvCNNLayer* layer );
71 static void icvCNNetworkRelease( CvCNNetwork** network );
72
73 /* In all layer functions we denote input by X and output by Y, where
74    X and Y are column-vectors, so that
75    length(X)==<n_input_planes>*<input_height>*<input_width>,
76    length(Y)==<n_output_planes>*<output_height>*<output_width>.
77 */
78 /*------------------------ functions for convolutional layer ---------------------------*/
79 static void icvCNNConvolutionRelease( CvCNNLayer** p_layer );
80
81 static void icvCNNConvolutionForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y );
82
83 static void icvCNNConvolutionBackward( CvCNNLayer*  layer, int t,
84     const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX );
85
86 /*------------------------ functions for sub-sampling layer ----------------------------*/
87 static void icvCNNSubSamplingRelease( CvCNNLayer** p_layer );
88
89 static void icvCNNSubSamplingForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y );
90
91 static void icvCNNSubSamplingBackward( CvCNNLayer*  layer, int t,
92     const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX );
93
94 /*------------------------ functions for full connected layer --------------------------*/
95 static void icvCNNFullConnectRelease( CvCNNLayer** p_layer );
96
97 static void icvCNNFullConnectForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y );
98
99 static void icvCNNFullConnectBackward( CvCNNLayer* layer, int,
100     const CvMat*, const CvMat* dE_dY, CvMat* dE_dX );
101
102 /****************************************************************************************\
103 *                             Functions implementations                                  *
104 \****************************************************************************************/
105
106 #define ICV_CHECK_CNN_NETWORK(network)                                                  \
107 {                                                                                       \
108     CvCNNLayer* first_layer, *layer, *last_layer;                                       \
109     int n_layers, i;                                                                    \
110     if( !network )                                                                      \
111         CV_ERROR( CV_StsNullPtr,                                                        \
112         "Null <network> pointer. Network must be created by user." );                   \
113     n_layers = network->n_layers;                                                       \
114     first_layer = last_layer = network->layers;                                         \
115     for( i = 0, layer = first_layer; i < n_layers && layer; i++ )                       \
116     {                                                                                   \
117         if( !ICV_IS_CNN_LAYER(layer) )                                                  \
118             CV_ERROR( CV_StsNullPtr, "Invalid network" );                               \
119         last_layer = layer;                                                             \
120         layer = layer->next_layer;                                                      \
121     }                                                                                   \
122                                                                                         \
123     if( i == 0 || i != n_layers || first_layer->prev_layer || layer )                   \
124         CV_ERROR( CV_StsNullPtr, "Invalid network" );                                   \
125                                                                                         \
126     if( first_layer->n_input_planes != 1 )                                              \
127         CV_ERROR( CV_StsBadArg, "First layer must contain only one input plane" );      \
128                                                                                         \
129     if( img_size != first_layer->input_height*first_layer->input_width )                \
130         CV_ERROR( CV_StsBadArg, "Invalid input sizes of the first layer" );             \
131                                                                                         \
132     if( params->etalons->cols != last_layer->n_output_planes*                           \
133         last_layer->output_height*last_layer->output_width )                            \
134         CV_ERROR( CV_StsBadArg, "Invalid output sizes of the last layer" );             \
135 }
136
137 #define ICV_CHECK_CNN_MODEL_PARAMS(params)                                              \
138 {                                                                                       \
139     if( !params )                                                                       \
140         CV_ERROR( CV_StsNullPtr, "Null <params> pointer" );                             \
141                                                                                         \
142     if( !ICV_IS_MAT_OF_TYPE(params->etalons, CV_32FC1) )                                \
143         CV_ERROR( CV_StsBadArg, "<etalons> must be CV_32FC1 type" );                    \
144     if( params->etalons->rows != cnn_model->cls_labels->cols )                          \
145         CV_ERROR( CV_StsBadArg, "Invalid <etalons> size" );                             \
146                                                                                         \
147     if( params->grad_estim_type != CV_CNN_GRAD_ESTIM_RANDOM &&                          \
148         params->grad_estim_type != CV_CNN_GRAD_ESTIM_BY_WORST_IMG )                     \
149         CV_ERROR( CV_StsBadArg, "Invalid <grad_estim_type>" );                          \
150                                                                                         \
151     if( params->start_iter < 0 )                                                        \
152         CV_ERROR( CV_StsBadArg, "Parameter <start_iter> must be positive or zero" );    \
153                                                                                         \
154     if( params->max_iter < 1 )                                                \
155         params->max_iter = 1;                                                 \
156 }
157
158 /****************************************************************************************\
159 *                              Classifier functions                                      *
160 \****************************************************************************************/
161 ML_IMPL CvStatModel*
162 cvTrainCNNClassifier( const CvMat* _train_data, int tflag,
163             const CvMat* _responses,
164             const CvStatModelParams* _params,
165             const CvMat*, const CvMat* _sample_idx, const CvMat*, const CvMat* )
166 {
167     CvCNNStatModel* cnn_model    = 0;
168     const float** out_train_data = 0;
169     CvMat* responses             = 0;
170
171     CV_FUNCNAME("cvTrainCNNClassifier");
172     __BEGIN__;
173
174     int n_images;
175     int img_size;
176     CvCNNStatModelParams* params = (CvCNNStatModelParams*)_params;
177
178     CV_CALL(cnn_model = (CvCNNStatModel*)cvCreateStatModel(
179         CV_STAT_MODEL_MAGIC_VAL|CV_CNN_MAGIC_VAL, sizeof(CvCNNStatModel),
180         icvCNNModelRelease, icvCNNModelPredict, icvCNNModelUpdate ));
181
182     CV_CALL(cvPrepareTrainData( "cvTrainCNNClassifier",
183         _train_data, tflag, _responses, CV_VAR_CATEGORICAL,
184         0, _sample_idx, false, &out_train_data,
185         &n_images, &img_size, &img_size, &responses,
186         &cnn_model->cls_labels, 0 ));
187
188     ICV_CHECK_CNN_MODEL_PARAMS(params);
189     ICV_CHECK_CNN_NETWORK(params->network);
190
191     cnn_model->network = params->network;
192     CV_CALL(cnn_model->etalons = (CvMat*)cvClone( params->etalons ));
193
194     CV_CALL( icvTrainCNNetwork( cnn_model->network, out_train_data, responses,
195         cnn_model->etalons, params->grad_estim_type, params->max_iter,
196         params->start_iter ));
197
198     __END__;
199
200     if( cvGetErrStatus() < 0 && cnn_model )
201     {
202         cnn_model->release( (CvStatModel**)&cnn_model );
203     }
204     cvFree( &out_train_data );
205     cvReleaseMat( &responses );
206
207     return (CvStatModel*)cnn_model;
208 }
209
210 /****************************************************************************************/
211 static void icvTrainCNNetwork( CvCNNetwork* network,
212                                const float** images,
213                                const CvMat* responses,
214                                const CvMat* etalons,
215                                int grad_estim_type,
216                                int max_iter,
217                                int start_iter )
218 {
219     CvMat** X     = 0;
220     CvMat** dE_dX = 0;
221     const int n_layers = network->n_layers;
222     int k;
223
224     CV_FUNCNAME("icvTrainCNNetwork");
225     __BEGIN__;
226
227     CvCNNLayer* first_layer = network->layers;
228     const int img_height = first_layer->input_height;
229     const int img_width  = first_layer->input_width;
230     const int img_size   = img_width*img_height;
231     const int n_images   = responses->cols;
232     CvMat image = cvMat( 1, img_size, CV_32FC1 );
233     CvCNNLayer* layer;
234     int n;
235     CvRNG rng = cvRNG(-1);
236
237     CV_CALL(X = (CvMat**)cvAlloc( (n_layers+1)*sizeof(CvMat*) ));
238     CV_CALL(dE_dX = (CvMat**)cvAlloc( (n_layers+1)*sizeof(CvMat*) ));
239     memset( X, 0, (n_layers+1)*sizeof(CvMat*) );
240     memset( dE_dX, 0, (n_layers+1)*sizeof(CvMat*) );
241
242     CV_CALL(X[0] = cvCreateMat( img_height*img_width,1,CV_32FC1 ));
243     CV_CALL(dE_dX[0] = cvCreateMat( 1, X[0]->rows, CV_32FC1 ));
244     for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer )
245     {
246         CV_CALL(X[k+1] = cvCreateMat( layer->n_output_planes*layer->output_height*
247             layer->output_width, 1, CV_32FC1 ));
248         CV_CALL(dE_dX[k+1] = cvCreateMat( 1, X[k+1]->rows, CV_32FC1 ));
249     }
250
251     for( n = 1; n <= max_iter; n++ )
252     {
253         float loss, max_loss = 0;
254         int i;
255         int worst_img_idx = -1;
256         int* right_etal_idx = responses->data.i;
257         CvMat etalon;
258
259         // Find the worst image (which produces the greatest loss) or use the random image
260         if( grad_estim_type == CV_CNN_GRAD_ESTIM_BY_WORST_IMG )
261         {
262             for( i = 0; i < n_images; i++, right_etal_idx++ )
263             {
264                 image.data.fl = (float*)images[i];
265                 cvTranspose( &image, X[0] );
266                 
267                 for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer )
268                     CV_CALL(layer->forward( layer, X[k], X[k+1] ));
269                 
270                 cvTranspose( X[n_layers], dE_dX[n_layers] );
271                 cvGetRow( etalons, &etalon, *right_etal_idx );
272                 loss = (float)cvNorm( dE_dX[n_layers], &etalon );
273                 if( loss > max_loss )
274                 {
275                     max_loss = loss;
276                     worst_img_idx = i;
277                 }
278             }
279         }
280         else
281             worst_img_idx = cvRandInt(&rng) % n_images;
282
283         // Train network on the worst image
284         // 1) Compute the network output on the <image>
285         image.data.fl = (float*)images[worst_img_idx];
286         CV_CALL(cvTranspose( &image, X[0] ));
287
288         for( k = 0, layer = first_layer; k < n_layers - 1; k++, layer = layer->next_layer )
289             CV_CALL(layer->forward( layer, X[k], X[k+1] ));
290         CV_CALL(layer->forward( layer, X[k], X[k+1] ));
291
292         // 2) Compute the gradient
293         cvTranspose( X[n_layers], dE_dX[n_layers] );
294         cvGetRow( etalons, &etalon, responses->data.i[worst_img_idx] );
295         cvSub( dE_dX[n_layers], &etalon, dE_dX[n_layers] );
296
297         // 3) Update weights by the gradient descent
298         for( k = n_layers; k > 0; k--, layer = layer->prev_layer )
299             CV_CALL(layer->backward( layer, n + start_iter, X[k-1], dE_dX[k], dE_dX[k-1] ));
300     }
301
302     __END__;
303
304     for( k = 0; k <= n_layers; k++ )
305     {
306         cvReleaseMat( &X[k] );
307         cvReleaseMat( &dE_dX[k] );
308     }
309     cvFree( &X );
310     cvFree( &dE_dX );
311 }
312
313 /****************************************************************************************/
314 static float icvCNNModelPredict( const CvStatModel* model,
315                                  const CvMat* _image,
316                                  CvMat* probs )
317 {
318     CvMat** X       = 0;
319     float* img_data = 0;
320     int n_layers = 0;
321     int best_etal_idx = -1;
322     int k;
323
324     CV_FUNCNAME("icvCNNModelPredict");
325     __BEGIN__;
326
327     CvCNNStatModel* cnn_model = (CvCNNStatModel*)model;
328     CvCNNLayer* first_layer, *layer = 0;
329     int img_height, img_width, img_size;
330     int nclasses, i;
331     float loss, min_loss = FLT_MAX;
332     float* probs_data;
333     CvMat etalon, image;
334
335     if( !CV_IS_CNN(model) )
336         CV_ERROR( CV_StsBadArg, "Invalid model" );
337
338     nclasses = cnn_model->cls_labels->cols;
339     n_layers = cnn_model->network->n_layers;
340     first_layer   = cnn_model->network->layers;
341     img_height = first_layer->input_height;
342     img_width  = first_layer->input_width;
343     img_size   = img_height*img_width;
344
345     cvPreparePredictData( _image, img_size, 0, nclasses, probs, &img_data );
346
347     CV_CALL(X = (CvMat**)cvAlloc( (n_layers+1)*sizeof(CvMat*) ));
348     memset( X, 0, (n_layers+1)*sizeof(CvMat*) );
349
350     CV_CALL(X[0] = cvCreateMat( img_size,1,CV_32FC1 ));
351     for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer )
352     {
353         CV_CALL(X[k+1] = cvCreateMat( layer->n_output_planes*layer->output_height*
354             layer->output_width, 1, CV_32FC1 ));
355     }
356
357     image = cvMat( 1, img_size, CV_32FC1, img_data );
358     cvTranspose( &image, X[0] );
359     for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer )
360         CV_CALL(layer->forward( layer, X[k], X[k+1] ));
361
362     probs_data = probs ? probs->data.fl : 0;
363     etalon = cvMat( cnn_model->etalons->cols, 1, CV_32FC1, cnn_model->etalons->data.fl );
364     for( i = 0; i < nclasses; i++, etalon.data.fl += cnn_model->etalons->cols )
365     {
366         loss = (float)cvNorm( X[n_layers], &etalon );
367         if( loss < min_loss )
368         {
369             min_loss = loss;
370             best_etal_idx = i;
371         }
372         if( probs )
373             *probs_data++ = -loss;
374     }
375
376     if( probs )
377     {
378         cvExp( probs, probs );
379         CvScalar sum = cvSum( probs );
380         cvConvertScale( probs, probs, 1./sum.val[0] );
381     }
382
383     __END__;
384
385     for( k = 0; k <= n_layers; k++ )
386         cvReleaseMat( &X[k] );
387     cvFree( &X );
388     if( img_data != _image->data.fl )
389         cvFree( &img_data );
390
391     return ((float) ((CvCNNStatModel*)model)->cls_labels->data.i[best_etal_idx]);
392 }
393
394 /****************************************************************************************/
395 static void icvCNNModelUpdate(
396         CvStatModel* _cnn_model, const CvMat* _train_data, int tflag,
397         const CvMat* _responses, const CvStatModelParams* _params,
398         const CvMat*, const CvMat* _sample_idx,
399         const CvMat*, const CvMat* )
400 {
401     const float** out_train_data = 0;
402     CvMat* responses             = 0;
403     CvMat* cls_labels            = 0;
404
405     CV_FUNCNAME("icvCNNModelUpdate");
406     __BEGIN__;
407
408     int n_images, img_size, i;
409     CvCNNStatModelParams* params = (CvCNNStatModelParams*)_params;
410     CvCNNStatModel* cnn_model = (CvCNNStatModel*)_cnn_model;
411
412     if( !CV_IS_CNN(cnn_model) )
413         CV_ERROR( CV_StsBadArg, "Invalid model" );
414
415     CV_CALL(cvPrepareTrainData( "cvTrainCNNClassifier",
416         _train_data, tflag, _responses, CV_VAR_CATEGORICAL,
417         0, _sample_idx, false, &out_train_data,
418         &n_images, &img_size, &img_size, &responses,
419         &cls_labels, 0, 0 ));
420
421     ICV_CHECK_CNN_MODEL_PARAMS(params);
422
423     // Number of classes must be the same as when classifiers was created
424     if( !CV_ARE_SIZES_EQ(cls_labels, cnn_model->cls_labels) )
425         CV_ERROR( CV_StsBadArg, "Number of classes must be left unchanged" );
426     for( i = 0; i < cls_labels->cols; i++ )
427     {
428         if( cls_labels->data.i[i] != cnn_model->cls_labels->data.i[i] )
429             CV_ERROR( CV_StsBadArg, "Number of classes must be left unchanged" );
430     }
431
432     CV_CALL( icvTrainCNNetwork( cnn_model->network, out_train_data, responses,
433         cnn_model->etalons, params->grad_estim_type, params->max_iter,
434         params->start_iter ));
435
436     __END__;
437
438     cvFree( &out_train_data );
439     cvReleaseMat( &responses );
440 }
441
442 /****************************************************************************************/
443 static void icvCNNModelRelease( CvStatModel** cnn_model )
444 {
445     CV_FUNCNAME("icvCNNModelRelease");
446     __BEGIN__;
447
448     CvCNNStatModel* cnn;
449     if( !cnn_model )
450         CV_ERROR( CV_StsNullPtr, "Null double pointer" );
451
452     cnn = *(CvCNNStatModel**)cnn_model;
453
454     cvReleaseMat( &cnn->cls_labels );
455     cvReleaseMat( &cnn->etalons );
456     cnn->network->release( &cnn->network );
457
458     cvFree( &cnn );
459
460     __END__;
461
462 }
463
464 /****************************************************************************************\
465 *                                 Network functions                                      *
466 \****************************************************************************************/
467 ML_IMPL CvCNNetwork* cvCreateCNNetwork( CvCNNLayer* first_layer )
468 {
469     CvCNNetwork* network = 0;    
470     
471     CV_FUNCNAME( "cvCreateCNNetwork" );
472     __BEGIN__;
473
474     if( !ICV_IS_CNN_LAYER(first_layer) )
475         CV_ERROR( CV_StsBadArg, "Invalid layer" );
476
477     CV_CALL(network = (CvCNNetwork*)cvAlloc( sizeof(CvCNNetwork) ));
478     memset( network, 0, sizeof(CvCNNetwork) );
479
480     network->layers    = first_layer;
481     network->n_layers  = 1;
482     network->release   = icvCNNetworkRelease;
483     network->add_layer = icvCNNetworkAddLayer;
484
485     __END__;
486
487     if( cvGetErrStatus() < 0 && network )
488         cvFree( &network );
489
490     return network;
491
492 }
493
494 /****************************************************************************************/
495 static void icvCNNetworkAddLayer( CvCNNetwork* network, CvCNNLayer* layer )
496 {
497     CV_FUNCNAME( "icvCNNetworkAddLayer" );
498     __BEGIN__;
499
500     CvCNNLayer* prev_layer;
501
502     if( network == NULL )
503         CV_ERROR( CV_StsNullPtr, "Null <network> pointer" );
504
505     prev_layer = network->layers;
506     while( prev_layer->next_layer )
507         prev_layer = prev_layer->next_layer;
508
509     if( ICV_IS_CNN_FULLCONNECT_LAYER(layer) )
510     {
511         if( layer->n_input_planes != prev_layer->output_width*prev_layer->output_height*
512             prev_layer->n_output_planes )
513             CV_ERROR( CV_StsBadArg, "Unmatched size of the new layer" );
514         if( layer->input_height != 1 || layer->output_height != 1 ||
515             layer->input_width != 1  || layer->output_width != 1 )
516             CV_ERROR( CV_StsBadArg, "Invalid size of the new layer" );
517     }
518     else if( ICV_IS_CNN_CONVOLUTION_LAYER(layer) || ICV_IS_CNN_SUBSAMPLING_LAYER(layer) )
519     {
520         if( prev_layer->n_output_planes != layer->n_input_planes ||
521         prev_layer->output_height   != layer->input_height ||
522         prev_layer->output_width    != layer->input_width )
523         CV_ERROR( CV_StsBadArg, "Unmatched size of the new layer" );
524     }
525     else
526         CV_ERROR( CV_StsBadArg, "Invalid layer" );
527
528     layer->prev_layer = prev_layer;
529     prev_layer->next_layer = layer;
530     network->n_layers++;
531
532     __END__;
533 }
534
535 /****************************************************************************************/
536 static void icvCNNetworkRelease( CvCNNetwork** network_pptr )
537 {
538     CV_FUNCNAME( "icvReleaseCNNetwork" );
539     __BEGIN__;
540
541     CvCNNetwork* network = 0;
542     CvCNNLayer* layer = 0, *next_layer = 0;
543     int k;
544
545     if( network_pptr == NULL )
546         CV_ERROR( CV_StsBadArg, "Null double pointer" );
547     if( *network_pptr == NULL )
548         return;
549
550     network = *network_pptr;
551     layer = network->layers;
552     if( layer == NULL )
553         CV_ERROR( CV_StsBadArg, "CNN is empty (does not contain any layer)" );
554
555     // k is the number of the layer to be deleted
556     for( k = 0; k < network->n_layers && layer; k++ )
557     {
558         next_layer = layer->next_layer;
559         layer->release( &layer );
560         layer = next_layer;
561     }
562
563     if( k != network->n_layers || layer)
564         CV_ERROR( CV_StsBadArg, "Invalid network" );
565
566     cvFree( &network );
567
568     __END__;
569 }
570
571 /****************************************************************************************\
572 *                                  Layer functions                                       *
573 \****************************************************************************************/
574 static CvCNNLayer* icvCreateCNNLayer( int layer_type, int header_size,
575     int n_input_planes, int input_height, int input_width,
576     int n_output_planes, int output_height, int output_width,
577     float init_learn_rate, int learn_rate_decrease_type,
578     CvCNNLayerRelease release, CvCNNLayerForward forward, CvCNNLayerBackward backward )
579 {
580     CvCNNLayer* layer = 0;
581
582     CV_FUNCNAME("icvCreateCNNLayer");
583     __BEGIN__;
584
585     CV_ASSERT( release && forward && backward )
586     CV_ASSERT( header_size >= sizeof(CvCNNLayer) )
587
588     if( n_input_planes < 1 || n_output_planes < 1 ||
589         input_height   < 1 || input_width < 1 ||
590         output_height  < 1 || output_width < 1 ||
591         input_height < output_height ||
592         input_width  < output_width )
593         CV_ERROR( CV_StsBadArg, "Incorrect input or output parameters" );
594     if( init_learn_rate < FLT_EPSILON )
595         CV_ERROR( CV_StsBadArg, "Initial learning rate must be positive" );
596     if( learn_rate_decrease_type != CV_CNN_LEARN_RATE_DECREASE_HYPERBOLICALLY &&
597         learn_rate_decrease_type != CV_CNN_LEARN_RATE_DECREASE_SQRT_INV &&
598         learn_rate_decrease_type != CV_CNN_LEARN_RATE_DECREASE_LOG_INV )
599         CV_ERROR( CV_StsBadArg, "Invalid type of learning rate dynamics" );
600
601     CV_CALL(layer = (CvCNNLayer*)cvAlloc( header_size ));
602     memset( layer, 0, header_size );
603
604     layer->flags = ICV_CNN_LAYER|layer_type;
605     CV_ASSERT( ICV_IS_CNN_LAYER(layer) )
606
607     layer->n_input_planes = n_input_planes;
608     layer->input_height   = input_height;
609     layer->input_width    = input_width;
610
611     layer->n_output_planes = n_output_planes;
612     layer->output_height   = output_height;
613     layer->output_width    = output_width;
614
615     layer->init_learn_rate = init_learn_rate;
616     layer->learn_rate_decrease_type = learn_rate_decrease_type;
617
618     layer->release  = release;
619     layer->forward  = forward;
620     layer->backward = backward;
621
622     __END__;
623
624     if( cvGetErrStatus() < 0 && layer)
625         cvFree( &layer );
626
627     return layer;
628 }
629
630 /****************************************************************************************/
631 ML_IMPL CvCNNLayer* cvCreateCNNConvolutionLayer(
632     int n_input_planes, int input_height, int input_width,
633     int n_output_planes, int K,
634     float init_learn_rate, int learn_rate_decrease_type,
635     CvMat* connect_mask, CvMat* weights )
636
637 {
638     CvCNNConvolutionLayer* layer = 0;
639
640     CV_FUNCNAME("cvCreateCNNConvolutionLayer");
641     __BEGIN__;
642
643     const int output_height = input_height - K + 1;
644     const int output_width = input_width - K + 1;
645
646     if( K < 1 || init_learn_rate <= 0 )
647         CV_ERROR( CV_StsBadArg, "Incorrect parameters" );
648
649     CV_CALL(layer = (CvCNNConvolutionLayer*)icvCreateCNNLayer( ICV_CNN_CONVOLUTION_LAYER,
650         sizeof(CvCNNConvolutionLayer), n_input_planes, input_height, input_width,
651         n_output_planes, output_height, output_width,
652         init_learn_rate, learn_rate_decrease_type,
653         icvCNNConvolutionRelease, icvCNNConvolutionForward, icvCNNConvolutionBackward ));
654
655     layer->K = K;
656     CV_CALL(layer->weights = cvCreateMat( n_output_planes, K*K+1, CV_32FC1 ));
657     CV_CALL(layer->connect_mask = cvCreateMat( n_output_planes, n_input_planes, CV_8UC1));
658
659     if( weights )
660     {
661         if( !ICV_IS_MAT_OF_TYPE( weights, CV_32FC1 ) )
662             CV_ERROR( CV_StsBadSize, "Type of initial weights matrix must be CV_32FC1" );
663         if( !CV_ARE_SIZES_EQ( weights, layer->weights ) )
664             CV_ERROR( CV_StsBadSize, "Invalid size of initial weights matrix" );
665         CV_CALL(cvCopy( weights, layer->weights ));
666     }
667     else
668     {
669         CvRNG rng = cvRNG( 0xFFFFFFFF );
670         cvRandArr( &rng, layer->weights, CV_RAND_UNI, cvRealScalar(-1), cvRealScalar(1) );
671     }
672  
673     if( connect_mask )
674     {
675         if( !ICV_IS_MAT_OF_TYPE( connect_mask, CV_8UC1 ) )
676             CV_ERROR( CV_StsBadSize, "Type of connection matrix must be CV_32FC1" );
677         if( !CV_ARE_SIZES_EQ( connect_mask, layer->connect_mask ) )
678             CV_ERROR( CV_StsBadSize, "Invalid size of connection matrix" );
679         CV_CALL(cvCopy( connect_mask, layer->connect_mask ));
680     }
681     else
682         CV_CALL(cvSet( layer->connect_mask, cvRealScalar(1) ));
683
684     __END__;
685
686     if( cvGetErrStatus() < 0 && layer )
687     {
688         cvReleaseMat( &layer->weights );
689         cvReleaseMat( &layer->connect_mask );
690         cvFree( &layer );
691     }
692     
693     return (CvCNNLayer*)layer;
694 }
695
696 /****************************************************************************************/
697 ML_IMPL CvCNNLayer* cvCreateCNNSubSamplingLayer(
698     int n_input_planes, int input_height, int input_width,
699     int sub_samp_scale, float a, float s,
700     float init_learn_rate, int learn_rate_decrease_type, CvMat* weights )
701
702 {
703     CvCNNSubSamplingLayer* layer = 0;
704
705     CV_FUNCNAME("cvCreateCNNSubSamplingLayer");
706     __BEGIN__;
707
708     const int output_height   = input_height/sub_samp_scale;
709     const int output_width    = input_width/sub_samp_scale;
710     const int n_output_planes = n_input_planes;
711
712     if( sub_samp_scale < 1 || a <= 0 || s <= 0)
713         CV_ERROR( CV_StsBadArg, "Incorrect parameters" );
714
715     CV_CALL(layer = (CvCNNSubSamplingLayer*)icvCreateCNNLayer( ICV_CNN_SUBSAMPLING_LAYER,
716         sizeof(CvCNNSubSamplingLayer), n_input_planes, input_height, input_width,
717         n_output_planes, output_height, output_width,
718         init_learn_rate, learn_rate_decrease_type,
719         icvCNNSubSamplingRelease, icvCNNSubSamplingForward, icvCNNSubSamplingBackward ));
720
721     layer->sub_samp_scale  = sub_samp_scale;
722     layer->a               = a;
723     layer->s               = s;
724
725     CV_CALL(layer->sumX =
726         cvCreateMat( n_output_planes*output_width*output_height, 1, CV_32FC1 ));
727     CV_CALL(layer->exp2ssumWX =
728         cvCreateMat( n_output_planes*output_width*output_height, 1, CV_32FC1 ));
729
730     cvZero( layer->sumX );
731     cvZero( layer->exp2ssumWX );
732
733     CV_CALL(layer->weights = cvCreateMat( n_output_planes, 2, CV_32FC1 ));
734     if( weights )
735     {
736         if( !ICV_IS_MAT_OF_TYPE( weights, CV_32FC1 ) )
737             CV_ERROR( CV_StsBadSize, "Type of initial weights matrix must be CV_32FC1" );
738         if( !CV_ARE_SIZES_EQ( weights, layer->weights ) )
739             CV_ERROR( CV_StsBadSize, "Invalid size of initial weights matrix" );
740         CV_CALL(cvCopy( weights, layer->weights ));
741     }
742     else
743     {
744         CvRNG rng = cvRNG( 0xFFFFFFFF );
745         cvRandArr( &rng, layer->weights, CV_RAND_UNI, cvRealScalar(-1), cvRealScalar(1) );
746     }
747
748     __END__;
749
750     if( cvGetErrStatus() < 0 && layer )
751     {
752         cvReleaseMat( &layer->exp2ssumWX );
753         cvFree( &layer );
754     }
755
756     return (CvCNNLayer*)layer;
757 }
758
759 /****************************************************************************************/
760 ML_IMPL CvCNNLayer* cvCreateCNNFullConnectLayer( 
761     int n_inputs, int n_outputs, float a, float s,
762     float init_learn_rate, int learn_rate_decrease_type, CvMat* weights )
763 {
764     CvCNNFullConnectLayer* layer = 0;
765
766     CV_FUNCNAME("cvCreateCNNFullConnectLayer");
767     __BEGIN__;
768
769     if( a <= 0 || s <= 0 || init_learn_rate <= 0)
770         CV_ERROR( CV_StsBadArg, "Incorrect parameters" );
771
772     CV_CALL(layer = (CvCNNFullConnectLayer*)icvCreateCNNLayer( ICV_CNN_FULLCONNECT_LAYER,
773         sizeof(CvCNNFullConnectLayer), n_inputs, 1, 1, n_outputs, 1, 1,
774         init_learn_rate, learn_rate_decrease_type,
775         icvCNNFullConnectRelease, icvCNNFullConnectForward, icvCNNFullConnectBackward ));
776
777     layer->a = a;
778     layer->s = s;
779
780     CV_CALL(layer->exp2ssumWX = cvCreateMat( n_outputs, 1, CV_32FC1 ));
781     cvZero( layer->exp2ssumWX );
782
783     CV_CALL(layer->weights = cvCreateMat( n_outputs, n_inputs+1, CV_32FC1 ));
784     if( weights )
785     {
786         if( !ICV_IS_MAT_OF_TYPE( weights, CV_32FC1 ) )
787             CV_ERROR( CV_StsBadSize, "Type of initial weights matrix must be CV_32FC1" );
788         if( !CV_ARE_SIZES_EQ( weights, layer->weights ) )
789             CV_ERROR( CV_StsBadSize, "Invalid size of initial weights matrix" );
790         CV_CALL(cvCopy( weights, layer->weights ));
791     }
792     else
793     {
794         CvRNG rng = cvRNG( 0xFFFFFFFF );
795         cvRandArr( &rng, layer->weights, CV_RAND_UNI, cvRealScalar(-1), cvRealScalar(1) );
796     }
797
798     __END__;
799
800     if( cvGetErrStatus() < 0 && layer )
801     {
802         cvReleaseMat( &layer->exp2ssumWX );
803         cvReleaseMat( &layer->weights );
804         cvFree( &layer );
805     }
806
807     return (CvCNNLayer*)layer;
808 }
809
810
811 /****************************************************************************************\
812 *                           Layer FORWARD functions                                      *
813 \****************************************************************************************/
814 static void icvCNNConvolutionForward( CvCNNLayer* _layer,
815                                       const CvMat* X,
816                                       CvMat* Y )
817 {
818     CV_FUNCNAME("icvCNNConvolutionForward");
819
820     if( !ICV_IS_CNN_CONVOLUTION_LAYER(_layer) )
821         CV_ERROR( CV_StsBadArg, "Invalid layer" );
822
823     {__BEGIN__;
824
825     const CvCNNConvolutionLayer* layer = (CvCNNConvolutionLayer*) _layer;
826
827     const int K = layer->K;
828     const int n_weights_for_Yplane = K*K + 1;
829
830     const int nXplanes = layer->n_input_planes;
831     const int Xheight  = layer->input_height;
832     const int Xwidth   = layer->input_width ;
833     const int Xsize    = Xwidth*Xheight;
834
835     const int nYplanes = layer->n_output_planes;
836     const int Yheight  = layer->output_height;
837     const int Ywidth   = layer->output_width;
838     const int Ysize    = Ywidth*Yheight;
839
840     int xx, yy, ni, no, kx, ky;
841     float *Yplane = 0, *Xplane = 0, *w = 0;
842     uchar* connect_mask_data = 0;
843
844     CV_ASSERT( X->rows == nXplanes*Xsize && X->cols == 1 );
845     CV_ASSERT( Y->rows == nYplanes*Ysize && Y->cols == 1 );
846
847     cvSetZero( Y );
848
849     Yplane = Y->data.fl;
850     connect_mask_data = layer->connect_mask->data.ptr;
851     w = layer->weights->data.fl;
852     for( no = 0; no < nYplanes; no++, Yplane += Ysize, w += n_weights_for_Yplane )
853     {
854         Xplane = X->data.fl;
855         for( ni = 0; ni < nXplanes; ni++, Xplane += Xsize, connect_mask_data++ )
856         {
857             if( *connect_mask_data )
858             {
859                 float* Yelem = Yplane;
860
861                 // Xheight-K+1 == Yheight && Xwidth-K+1 == Ywidth
862                 for( yy = 0; yy < Xheight-K+1; yy++ )
863                 {
864                     for( xx = 0; xx < Xwidth-K+1; xx++, Yelem++ )
865                     {
866                         float* templ = Xplane+yy*Xwidth+xx;
867                         float WX = 0;
868                         for( ky = 0; ky < K; ky++, templ += Xwidth-K )
869                         {
870                             for( kx = 0; kx < K; kx++, templ++ )
871                             {
872                                 WX += *templ*w[ky*K+kx];
873                             }
874                         }
875                         *Yelem += WX + w[K*K];
876                     }
877                 }
878             }
879         }
880     }
881     }__END__;
882 }
883
884 /****************************************************************************************/
885 static void icvCNNSubSamplingForward( CvCNNLayer* _layer,
886                                       const CvMat* X,
887                                       CvMat* Y )
888 {
889     CV_FUNCNAME("icvCNNSubSamplingForward");
890
891     if( !ICV_IS_CNN_SUBSAMPLING_LAYER(_layer) )
892         CV_ERROR( CV_StsBadArg, "Invalid layer" );
893
894     {__BEGIN__;
895
896     const CvCNNSubSamplingLayer* layer = (CvCNNSubSamplingLayer*) _layer;
897
898     const int sub_sampl_scale = layer->sub_samp_scale;
899     const int nplanes = layer->n_input_planes;
900
901     const int Xheight = layer->input_height;
902     const int Xwidth  = layer->input_width ;
903     const int Xsize   = Xwidth*Xheight;
904
905     const int Yheight = layer->output_height;
906     const int Ywidth  = layer->output_width;
907     const int Ysize   = Ywidth*Yheight;
908
909     int xx, yy, ni, kx, ky;
910     float* sumX_data = 0, *w = 0;
911     CvMat sumX_sub_col, exp2ssumWX_sub_col;
912
913     CV_ASSERT(X->rows == nplanes*Xsize && X->cols == 1);
914     CV_ASSERT(layer->exp2ssumWX->cols == 1 && layer->exp2ssumWX->rows == nplanes*Ysize);
915
916     // update inner variable layer->exp2ssumWX, which will be used in back-progation
917     cvZero( layer->sumX );
918     cvZero( layer->exp2ssumWX );
919
920     for( ky = 0; ky < sub_sampl_scale; ky++ )
921         for( kx = 0; kx < sub_sampl_scale; kx++ )
922         {
923             float* Xplane = X->data.fl;
924             sumX_data = layer->sumX->data.fl;
925             for( ni = 0; ni < nplanes; ni++, Xplane += Xsize )
926             {
927                 for( yy = 0; yy < Yheight; yy++ )
928                     for( xx = 0; xx < Ywidth; xx++, sumX_data++ )
929                         *sumX_data += Xplane[((yy+ky)*Xwidth+(xx+kx))];
930             }
931         }                
932
933     w = layer->weights->data.fl;
934     cvGetRows( layer->sumX, &sumX_sub_col, 0, Ysize );
935     cvGetRows( layer->exp2ssumWX, &exp2ssumWX_sub_col, 0, Ysize );
936     for( ni = 0; ni < nplanes; ni++, w += 2 )
937     {
938         CV_CALL(cvConvertScale( &sumX_sub_col, &exp2ssumWX_sub_col, w[0], w[1] ));
939         sumX_sub_col.data.fl += Ysize;
940         exp2ssumWX_sub_col.data.fl += Ysize;
941     }
942
943     CV_CALL(cvScale( layer->exp2ssumWX, layer->exp2ssumWX, 2.0*layer->s ));
944     CV_CALL(cvExp( layer->exp2ssumWX, layer->exp2ssumWX ));
945     CV_CALL(cvMinS( layer->exp2ssumWX, FLT_MAX, layer->exp2ssumWX ));
946 //#ifdef _DEBUG
947     {
948         float* exp2ssumWX_data = layer->exp2ssumWX->data.fl;
949         for( ni = 0; ni < layer->exp2ssumWX->rows; ni++, exp2ssumWX_data++ )
950         {
951             if( *exp2ssumWX_data == FLT_MAX )
952                 cvSetErrStatus( 1 );
953         }
954     }
955 //#endif
956     // compute the output variable Y == ( a - 2a/(layer->exp2ssumWX + 1))
957     CV_CALL(cvAddS( layer->exp2ssumWX, cvRealScalar(1), Y ));
958     CV_CALL(cvDiv( 0, Y, Y, -2.0*layer->a ));
959     CV_CALL(cvAddS( Y, cvRealScalar(layer->a), Y ));
960
961     }__END__;
962 }
963
964 /****************************************************************************************/
965 static void icvCNNFullConnectForward( CvCNNLayer* _layer, const CvMat* X, CvMat* Y )
966 {
967     CV_FUNCNAME("icvCNNFullConnectForward");
968
969     if( !ICV_IS_CNN_FULLCONNECT_LAYER(_layer) )
970         CV_ERROR( CV_StsBadArg, "Invalid layer" );
971
972     {__BEGIN__;
973
974     const CvCNNFullConnectLayer* layer = (CvCNNFullConnectLayer*)_layer;
975     CvMat* weights = layer->weights;
976     CvMat sub_weights, bias;
977
978     CV_ASSERT(X->cols == 1 && X->rows == layer->n_input_planes);
979     CV_ASSERT(Y->cols == 1 && Y->rows == layer->n_output_planes);
980
981     CV_CALL(cvGetSubRect( weights, &sub_weights,
982                           cvRect(0, 0, weights->cols-1, weights->rows )));
983     CV_CALL(cvGetCol( weights, &bias, weights->cols-1));
984
985     // update inner variable layer->exp2ssumWX, which will be used in Back-Propagation
986     CV_CALL(cvGEMM( &sub_weights, X, 2*layer->s, &bias, 2*layer->s, layer->exp2ssumWX ));
987     CV_CALL(cvExp( layer->exp2ssumWX, layer->exp2ssumWX ));
988     CV_CALL(cvMinS( layer->exp2ssumWX, FLT_MAX, layer->exp2ssumWX ));
989 //#ifdef _DEBUG
990     {
991         float* exp2ssumWX_data = layer->exp2ssumWX->data.fl;
992         int i;
993         for( i = 0; i < layer->exp2ssumWX->rows; i++, exp2ssumWX_data++ )
994         {
995             if( *exp2ssumWX_data == FLT_MAX )
996                 cvSetErrStatus( 1 );
997         }
998     }
999 //#endif
1000     // compute the output variable Y == ( a - 2a/(layer->exp2ssumWX + 1))
1001     CV_CALL(cvAddS( layer->exp2ssumWX, cvRealScalar(1), Y ));
1002     CV_CALL(cvDiv( 0, Y, Y, -2.0*layer->a ));
1003     CV_CALL(cvAddS( Y, cvRealScalar(layer->a), Y ));
1004
1005     }__END__;
1006 }
1007
1008 /****************************************************************************************\
1009 *                           Layer BACKWARD functions                                     *
1010 \****************************************************************************************/
1011
1012 /* <dE_dY>, <dE_dX> should be row-vectors.
1013    Function computes partial derivatives <dE_dX>
1014    of the loss function with respect to the planes components
1015    of the previous layer (X).
1016    It is a basic function for back propagation method.
1017    Input parameter <dE_dY> is the partial derivative of the
1018    loss function with respect to the planes components
1019    of the current layer. */
1020 static void icvCNNConvolutionBackward(
1021     CvCNNLayer* _layer, int t, const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX )
1022 {
1023     CvMat* dY_dX = 0;
1024     CvMat* dY_dW = 0;
1025     CvMat* dE_dW = 0;
1026
1027     CV_FUNCNAME("icvCNNConvolutionBackward");
1028
1029     if( !ICV_IS_CNN_CONVOLUTION_LAYER(_layer) )
1030         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1031
1032     {__BEGIN__;
1033
1034     const CvCNNConvolutionLayer* layer = (CvCNNConvolutionLayer*) _layer;
1035
1036     const int K = layer->K;
1037
1038     const int n_X_planes     = layer->n_input_planes;
1039     const int X_plane_height = layer->input_height;
1040     const int X_plane_width  = layer->input_width;
1041     const int X_plane_size   = X_plane_height*X_plane_width;
1042
1043     const int n_Y_planes     = layer->n_output_planes;
1044     const int Y_plane_height = layer->output_height;
1045     const int Y_plane_width  = layer->output_width;
1046     const int Y_plane_size   = Y_plane_height*Y_plane_width;
1047
1048     int no, ni, yy, xx, ky, kx;
1049     int X_idx = 0, Y_idx = 0;
1050
1051     float *X_plane = 0, *w = 0;
1052
1053     CvMat* weights = layer->weights;
1054
1055     CV_ASSERT( t >= 1 );
1056     CV_ASSERT( n_Y_planes == weights->rows );
1057
1058     dY_dX = cvCreateMat( n_Y_planes*Y_plane_size, X->rows, CV_32FC1 );
1059     dY_dW = cvCreateMat( dY_dX->rows, weights->cols*weights->rows, CV_32FC1 );
1060     dE_dW = cvCreateMat( 1, dY_dW->cols, CV_32FC1 );
1061
1062     cvZero( dY_dX );
1063     cvZero( dY_dW );
1064
1065     // compute gradient of the loss function with respect to X and W
1066     for( no = 0; no < n_Y_planes; no++, Y_idx += Y_plane_size )
1067     {
1068         w = weights->data.fl + no*(K*K+1);
1069         X_idx = 0;
1070         X_plane = X->data.fl;
1071         for( ni = 0; ni < n_X_planes; ni++, X_plane += X_plane_size )
1072         {
1073             if( layer->connect_mask->data.ptr[ni*n_Y_planes+no] )
1074             {
1075                 for( yy = 0; yy < X_plane_height - K + 1; yy++ )
1076                 {
1077                     for( xx = 0; xx < X_plane_width - K + 1; xx++ )
1078                     {
1079                         for( ky = 0; ky < K; ky++ )
1080                         {
1081                             for( kx = 0; kx < K; kx++ )
1082                             {
1083                                 CV_MAT_ELEM(*dY_dX, float, Y_idx+yy*Y_plane_width+xx,
1084                                     X_idx+(yy+ky)*X_plane_width+(xx+kx)) = w[ky*K+kx];
1085
1086                                 // dY_dWi, i=1,...,K*K
1087                                 CV_MAT_ELEM(*dY_dW, float, Y_idx+yy*Y_plane_width+xx,
1088                                     no*(K*K+1)+ky*K+kx) +=
1089                                     X_plane[(yy+ky)*X_plane_width+(xx+kx)];
1090                             }
1091                         }
1092                         // dY_dW(K*K+1)==1 because W(K*K+1) is bias
1093                         CV_MAT_ELEM(*dY_dW, float, Y_idx+yy*Y_plane_width+xx,
1094                             no*(K*K+1)+K*K) += 1;
1095                     }
1096                 }
1097             }
1098             X_idx += X_plane_size;
1099         }
1100     }
1101
1102     CV_CALL(cvMatMul( dE_dY, dY_dW, dE_dW ));
1103     CV_CALL(cvMatMul( dE_dY, dY_dX, dE_dX ));
1104
1105     // update weights
1106     {
1107         CvMat dE_dW_mat;
1108         float eta;
1109         if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_LOG_INV )
1110             eta = -layer->init_learn_rate/logf(1+(float)t);
1111         else if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_SQRT_INV )
1112             eta = -layer->init_learn_rate/sqrtf((float)t);
1113         else
1114             eta = -layer->init_learn_rate/(float)t;
1115         cvReshape( dE_dW, &dE_dW_mat, 0, weights->rows );
1116         cvScaleAdd( &dE_dW_mat, cvRealScalar(eta), weights, weights );
1117     }
1118
1119     }__END__;
1120
1121     cvReleaseMat( &dY_dX );
1122     cvReleaseMat( &dY_dW );
1123     cvReleaseMat( &dE_dW );
1124 }
1125
1126 /****************************************************************************************/
1127 static void icvCNNSubSamplingBackward(
1128     CvCNNLayer* _layer, int t, const CvMat*, const CvMat* dE_dY, CvMat* dE_dX )
1129 {
1130     // derivative of activation function
1131     CvMat* dY_dX_elems = 0; // elements of matrix dY_dX
1132     CvMat* dY_dW_elems = 0; // elements of matrix dY_dW
1133     CvMat* dE_dW = 0;
1134
1135     CV_FUNCNAME("icvCNNSubSamplingBackward");
1136
1137     if( !ICV_IS_CNN_SUBSAMPLING_LAYER(_layer) )
1138         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1139
1140     {__BEGIN__;
1141
1142     const CvCNNSubSamplingLayer* layer = (CvCNNSubSamplingLayer*) _layer;
1143
1144     const int Xwidth  = layer->input_width;
1145     const int Ywidth  = layer->output_width;
1146     const int Yheight = layer->output_height;
1147     const int Ysize   = Ywidth * Yheight;
1148     const int scale   = layer->sub_samp_scale;
1149     const int k_max   = layer->n_output_planes * Yheight;
1150
1151     int k, i, j, m;
1152     float* dY_dX_current_elem = 0, *dE_dX_start = 0, *dE_dW_data = 0, *w = 0;
1153     CvMat dy_dw0, dy_dw1;
1154     CvMat activ_func_der, sumX_row;
1155     CvMat dE_dY_sub_row, dY_dX_sub_col, dy_dw0_sub_row, dy_dw1_sub_row;
1156
1157     CV_CALL(dY_dX_elems = cvCreateMat( layer->sumX->rows, 1, CV_32FC1 ));
1158     CV_CALL(dY_dW_elems = cvCreateMat( 2, layer->sumX->rows, CV_32FC1 ));
1159     CV_CALL(dE_dW = cvCreateMat( 1, 2*layer->n_output_planes, CV_32FC1 ));
1160
1161     // compute derivative of activ.func.
1162     // ==<dY_dX_elems> = 4as*(layer->exp2ssumWX)/(layer->exp2ssumWX + 1)^2
1163     CV_CALL(cvAddS( layer->exp2ssumWX, cvRealScalar(1), dY_dX_elems ));
1164     CV_CALL(cvPow( dY_dX_elems, dY_dX_elems, -2.0 ));
1165     CV_CALL(cvMul( dY_dX_elems, layer->exp2ssumWX, dY_dX_elems, 4.0*layer->a*layer->s ));
1166
1167     // compute <dE_dW>
1168     // a) compute <dY_dW_elems>
1169     cvReshape( dY_dX_elems, &activ_func_der, 0, 1 );
1170     cvGetRow( dY_dW_elems, &dy_dw0, 0 );
1171     cvGetRow( dY_dW_elems, &dy_dw1, 1 );
1172     CV_CALL(cvCopy( &activ_func_der, &dy_dw0 ));
1173     CV_CALL(cvCopy( &activ_func_der, &dy_dw1 ));
1174
1175     cvReshape( layer->sumX, &sumX_row, 0, 1 );
1176     cvMul( &dy_dw0, &sumX_row, &dy_dw0 );
1177
1178     // b) compute <dE_dW> = <dE_dY>*<dY_dW_elems>
1179     cvGetCols( dE_dY, &dE_dY_sub_row, 0, Ysize );
1180     cvGetCols( &dy_dw0, &dy_dw0_sub_row, 0, Ysize );
1181     cvGetCols( &dy_dw1, &dy_dw1_sub_row, 0, Ysize );
1182     dE_dW_data = dE_dW->data.fl;
1183     for( i = 0; i < layer->n_output_planes; i++ )
1184     {
1185         *dE_dW_data++ = (float)cvDotProduct( &dE_dY_sub_row, &dy_dw0_sub_row );
1186         *dE_dW_data++ = (float)cvDotProduct( &dE_dY_sub_row, &dy_dw1_sub_row );
1187
1188         dE_dY_sub_row.data.fl += Ysize;
1189         dy_dw0_sub_row.data.fl += Ysize;
1190         dy_dw1_sub_row.data.fl += Ysize;
1191     }
1192
1193     // compute <dY_dX> = layer->weights*<dY_dX>
1194     w = layer->weights->data.fl;
1195     cvGetRows( dY_dX_elems, &dY_dX_sub_col, 0, Ysize );
1196     for( i = 0; i < layer->n_input_planes; i++, w++, dY_dX_sub_col.data.fl += Ysize )
1197         CV_CALL(cvConvertScale( &dY_dX_sub_col, &dY_dX_sub_col, (float)*w ));
1198
1199     // compute <dE_dX>
1200     CV_CALL(cvReshape( dY_dX_elems, dY_dX_elems, 0, 1 ));
1201     CV_CALL(cvMul( dY_dX_elems, dE_dY, dY_dX_elems ));
1202
1203     dY_dX_current_elem = dY_dX_elems->data.fl;
1204     dE_dX_start = dE_dX->data.fl;
1205     for( k = 0; k < k_max; k++ )
1206     {
1207         for( i = 0; i < Ywidth; i++, dY_dX_current_elem++ )
1208         {
1209             float* dE_dX_current_elem = dE_dX_start;
1210             for( j = 0; j < scale; j++, dE_dX_current_elem += Xwidth - scale )
1211             {
1212                 for( m = 0; m < scale; m++, dE_dX_current_elem++ )
1213                     *dE_dX_current_elem = *dY_dX_current_elem;
1214             }
1215             dE_dX_start += scale;
1216         }
1217         dE_dX_start += Xwidth * (scale - 1);
1218     }
1219
1220     // update weights
1221     {
1222         CvMat dE_dW_mat, *weights = layer->weights;
1223         float eta;
1224         if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_LOG_INV )
1225             eta = -layer->init_learn_rate/logf(1+(float)t);
1226         else if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_SQRT_INV )
1227             eta = -layer->init_learn_rate/sqrtf((float)t);
1228         else
1229             eta = -layer->init_learn_rate/(float)t;
1230         cvReshape( dE_dW, &dE_dW_mat, 0, weights->rows );
1231         cvScaleAdd( &dE_dW_mat, cvRealScalar(eta), weights, weights );
1232     }
1233
1234     }__END__;
1235
1236     cvReleaseMat( &dY_dX_elems );
1237     cvReleaseMat( &dY_dW_elems );
1238     cvReleaseMat( &dE_dW );
1239 }
1240
1241 /****************************************************************************************/
1242 /* <dE_dY>, <dE_dX> should be row-vectors.
1243    Function computes partial derivatives <dE_dX>, <dE_dW>
1244    of the loss function with respect to the planes components
1245    of the previous layer (X) and the weights of the current layer (W)
1246    and updates weights od the current layer by using <dE_dW>.
1247    It is a basic function for back propagation method.
1248    Input parameter <dE_dY> is the partial derivative of the
1249    loss function with respect to the planes components
1250    of the current layer. */
1251 static void icvCNNFullConnectBackward( CvCNNLayer* _layer,
1252                                     int t,
1253                                     const CvMat* X,
1254                                     const CvMat* dE_dY,
1255                                     CvMat* dE_dX )
1256 {
1257     CvMat* dE_dY_activ_func_der = 0;
1258     CvMat* dE_dW = 0;
1259     
1260     CV_FUNCNAME( "icvCNNFullConnectBackward" );
1261
1262     if( !ICV_IS_CNN_FULLCONNECT_LAYER(_layer) )
1263         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1264
1265     {__BEGIN__;
1266
1267     const CvCNNFullConnectLayer* layer = (CvCNNFullConnectLayer*)_layer;
1268     const int n_outputs = layer->n_output_planes;
1269     const int n_inputs  = layer->n_input_planes;
1270
1271     int i;
1272     float* dE_dY_activ_func_der_data;
1273     CvMat* weights = layer->weights;
1274     CvMat sub_weights, Xtemplate, Xrow, exp2ssumWXrow;
1275
1276     CV_ASSERT(X->cols == 1 && X->rows == n_inputs);
1277     CV_ASSERT(dE_dY->rows == 1 && dE_dY->cols == n_outputs );
1278     CV_ASSERT(dE_dX->rows == 1 && dE_dX->cols == n_inputs );
1279     
1280     // we violate the convetion about vector's orientation because
1281     // here is more convenient to make this parameter a row-vector 
1282     CV_CALL(dE_dY_activ_func_der = cvCreateMat( 1, n_outputs, CV_32FC1 ));
1283     CV_CALL(dE_dW = cvCreateMat( 1, weights->rows*weights->cols, CV_32FC1 ));
1284     
1285     // 1) compute gradients dE_dX and dE_dW
1286     // activ_func_der == 4as*(layer->exp2ssumWX)/(layer->exp2ssumWX + 1)^2
1287     CV_CALL(cvReshape( layer->exp2ssumWX, &exp2ssumWXrow, 0, layer->exp2ssumWX->cols ));
1288     CV_CALL(cvAddS( &exp2ssumWXrow, cvRealScalar(1), dE_dY_activ_func_der ));
1289     CV_CALL(cvPow( dE_dY_activ_func_der, dE_dY_activ_func_der, -2.0 ));
1290     CV_CALL(cvMul( dE_dY_activ_func_der, &exp2ssumWXrow, dE_dY_activ_func_der,
1291                    4.0*layer->a*layer->s ));
1292     CV_CALL(cvMul( dE_dY, dE_dY_activ_func_der, dE_dY_activ_func_der ));
1293
1294     // sub_weights = d(W*(X|1))/dX
1295     CV_CALL(cvGetSubRect( weights, &sub_weights,
1296         cvRect(0, 0, weights->cols-1, weights->rows) ));
1297     CV_CALL(cvMatMul( dE_dY_activ_func_der, &sub_weights, dE_dX ));
1298
1299     cvReshape( X, &Xrow, 0, 1 );
1300     dE_dY_activ_func_der_data = dE_dY_activ_func_der->data.fl;
1301     Xtemplate = cvMat( 1, n_inputs, CV_32FC1, dE_dW->data.fl );
1302     for( i = 0; i < n_outputs; i++, Xtemplate.data.fl += n_inputs + 1 )
1303     {
1304         CV_CALL(cvConvertScale( &Xrow, &Xtemplate, *dE_dY_activ_func_der_data ));
1305         Xtemplate.data.fl[n_inputs] = *dE_dY_activ_func_der_data++;
1306     }
1307
1308     // 2) update weights
1309     {
1310         CvMat dE_dW_mat;
1311         float eta;
1312         if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_LOG_INV )
1313             eta = -layer->init_learn_rate/logf(1+(float)t);
1314         else if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_SQRT_INV )
1315             eta = -layer->init_learn_rate/sqrtf((float)t);
1316         else
1317             eta = -layer->init_learn_rate/(float)t;
1318         cvReshape( dE_dW, &dE_dW_mat, 0, n_outputs );
1319         cvScaleAdd( &dE_dW_mat, cvRealScalar(eta), weights, weights );
1320     }
1321
1322     }__END__;
1323
1324     cvReleaseMat( &dE_dY_activ_func_der );
1325     cvReleaseMat( &dE_dW );
1326 }
1327
1328 /****************************************************************************************\
1329 *                           Layer RELEASE functions                                      *
1330 \****************************************************************************************/
1331 static void icvCNNConvolutionRelease( CvCNNLayer** p_layer )
1332 {
1333     CV_FUNCNAME("icvCNNConvolutionRelease");
1334     __BEGIN__;
1335
1336     CvCNNConvolutionLayer* layer = 0;
1337
1338     if( !p_layer )
1339         CV_ERROR( CV_StsNullPtr, "Null double pointer" );
1340
1341     layer = *(CvCNNConvolutionLayer**)p_layer;
1342
1343     if( !layer )
1344         return;
1345     if( !ICV_IS_CNN_CONVOLUTION_LAYER(layer) )
1346         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1347
1348     cvReleaseMat( &layer->weights );
1349     cvReleaseMat( &layer->connect_mask );
1350     cvFree( p_layer );
1351
1352     __END__;
1353 }
1354
1355 /****************************************************************************************/
1356 static void icvCNNSubSamplingRelease( CvCNNLayer** p_layer )
1357 {
1358     CV_FUNCNAME("icvCNNSubSamplingRelease");
1359     __BEGIN__;
1360
1361     CvCNNSubSamplingLayer* layer = 0;
1362     
1363     if( !p_layer )
1364         CV_ERROR( CV_StsNullPtr, "Null double pointer" );
1365
1366     layer = *(CvCNNSubSamplingLayer**)p_layer;
1367
1368     if( !layer )
1369         return;
1370     if( !ICV_IS_CNN_SUBSAMPLING_LAYER(layer) )
1371         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1372
1373     cvReleaseMat( &layer->exp2ssumWX );
1374     cvReleaseMat( &layer->weights );
1375     cvFree( p_layer );
1376
1377     __END__;
1378 }
1379
1380 /****************************************************************************************/
1381 static void icvCNNFullConnectRelease( CvCNNLayer** p_layer )
1382 {
1383     CV_FUNCNAME("icvCNNFullConnectRelease");
1384     __BEGIN__;
1385
1386     CvCNNFullConnectLayer* layer = 0;
1387     
1388     if( !p_layer )
1389         CV_ERROR( CV_StsNullPtr, "Null double pointer" );
1390
1391     layer = *(CvCNNFullConnectLayer**)p_layer;
1392
1393     if( !layer )
1394         return;
1395     if( !ICV_IS_CNN_FULLCONNECT_LAYER(layer) )
1396         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1397
1398     cvReleaseMat( &layer->exp2ssumWX );
1399     cvReleaseMat( &layer->weights );
1400     cvFree( p_layer );
1401
1402     __END__;
1403 }
1404
1405 /****************************************************************************************\
1406 *                              Read/Write CNN classifier                                 *
1407 \****************************************************************************************/
1408 static int icvIsCNNModel( const void* ptr )
1409 {
1410     return CV_IS_CNN(ptr);
1411 }
1412
1413 /****************************************************************************************/
1414 static void icvReleaseCNNModel( void** ptr )
1415 {
1416     CV_FUNCNAME("icvReleaseCNNModel");
1417     __BEGIN__;
1418
1419     if( !ptr )
1420         CV_ERROR( CV_StsNullPtr, "NULL double pointer" );
1421     CV_ASSERT(CV_IS_CNN(*ptr));
1422
1423     icvCNNModelRelease( (CvStatModel**)ptr );
1424
1425     __END__;
1426 }
1427
1428 /****************************************************************************************/
1429 static CvCNNLayer* icvReadCNNLayer( CvFileStorage* fs, CvFileNode* node )
1430 {
1431     CvCNNLayer* layer = 0;
1432     CvMat* weights    = 0;
1433     CvMat* connect_mask = 0;
1434
1435     CV_FUNCNAME("icvReadCNNLayer");
1436     __BEGIN__;
1437
1438     int n_input_planes, input_height, input_width;
1439     int n_output_planes, output_height, output_width;
1440     int learn_type, layer_type;
1441     float init_learn_rate;
1442
1443     CV_CALL(n_input_planes  = cvReadIntByName( fs, node, "n_input_planes",  -1 ));
1444     CV_CALL(input_height    = cvReadIntByName( fs, node, "input_height",    -1 ));
1445     CV_CALL(input_width     = cvReadIntByName( fs, node, "input_width",     -1 ));
1446     CV_CALL(n_output_planes = cvReadIntByName( fs, node, "n_output_planes", -1 ));
1447     CV_CALL(output_height   = cvReadIntByName( fs, node, "output_height",   -1 ));
1448     CV_CALL(output_width    = cvReadIntByName( fs, node, "output_width",    -1 ));
1449     CV_CALL(layer_type      = cvReadIntByName( fs, node, "layer_type",      -1 ));
1450
1451     CV_CALL(init_learn_rate = (float)cvReadRealByName( fs, node, "init_learn_rate", -1 ));
1452     CV_CALL(learn_type = cvReadIntByName( fs, node, "learn_rate_decrease_type", -1 ));
1453     CV_CALL(weights    = (CvMat*)cvReadByName( fs, node, "weights" ));
1454
1455     if( n_input_planes < 0  || input_height < 0  || input_width < 0 ||
1456         n_output_planes < 0 || output_height < 0 || output_width < 0 ||
1457         init_learn_rate < 0 || learn_type < 0 || layer_type < 0 || !weights )
1458         CV_ERROR( CV_StsParseError, "" );
1459
1460     if( layer_type == ICV_CNN_CONVOLUTION_LAYER )
1461     {
1462         const int K = input_height - output_height + 1;
1463         if( K <= 0 || K != input_width - output_width + 1 )
1464             CV_ERROR( CV_StsBadArg, "Invalid <K>" );
1465
1466         CV_CALL(connect_mask = (CvMat*)cvReadByName( fs, node, "connect_mask" ));
1467         if( !connect_mask )
1468             CV_ERROR( CV_StsParseError, "Missing <connect mask>" );
1469
1470         CV_CALL(layer = cvCreateCNNConvolutionLayer( 
1471             n_input_planes, input_height, input_width, n_output_planes, K,
1472             init_learn_rate, learn_type, connect_mask, weights ));
1473     }
1474     else if( layer_type == ICV_CNN_SUBSAMPLING_LAYER )
1475     {
1476         float a, s;
1477         const int sub_samp_scale = input_height/output_height;
1478
1479         if( sub_samp_scale <= 0 || sub_samp_scale != input_width/output_width )
1480             CV_ERROR( CV_StsBadArg, "Invalid <sub_samp_scale>" );
1481
1482         CV_CALL(a = (float)cvReadRealByName( fs, node, "a", -1 ));
1483         CV_CALL(s = (float)cvReadRealByName( fs, node, "s", -1 ));
1484         if( a  < 0 || s  < 0 )
1485             CV_ERROR( CV_StsParseError, "Missing <a> or <s>" );
1486
1487         CV_CALL(layer = cvCreateCNNSubSamplingLayer(
1488             n_input_planes, input_height, input_width, sub_samp_scale,
1489             a, s, init_learn_rate, learn_type, weights ));
1490     }
1491     else if( layer_type == ICV_CNN_FULLCONNECT_LAYER )
1492     {
1493         float a, s;
1494         CV_CALL(a = (float)cvReadRealByName( fs, node, "a", -1 ));
1495         CV_CALL(s = (float)cvReadRealByName( fs, node, "s", -1 ));
1496         if( a  < 0 || s  < 0 )
1497             CV_ERROR( CV_StsParseError, "" );
1498         if( input_height != 1  || input_width != 1 ||
1499             output_height != 1 || output_width != 1 )
1500             CV_ERROR( CV_StsBadArg, "" );
1501
1502         CV_CALL(layer = cvCreateCNNFullConnectLayer( n_input_planes, n_output_planes,
1503             a, s, init_learn_rate, learn_type, weights ));
1504     }
1505     else
1506         CV_ERROR( CV_StsBadArg, "Invalid <layer_type>" );
1507
1508     __END__;
1509
1510     if( cvGetErrStatus() < 0 && layer )
1511         layer->release( &layer );
1512
1513     cvReleaseMat( &weights );
1514     cvReleaseMat( &connect_mask );
1515
1516     return layer;
1517 }
1518
1519 /****************************************************************************************/
1520 static void icvWriteCNNLayer( CvFileStorage* fs, CvCNNLayer* layer )
1521 {
1522     CV_FUNCNAME ("icvWriteCNNLayer");
1523     __BEGIN__;
1524
1525     if( !ICV_IS_CNN_LAYER(layer) )
1526         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1527
1528     CV_CALL( cvStartWriteStruct( fs, NULL, CV_NODE_MAP, "opencv-ml-cnn-layer" ));
1529
1530     CV_CALL(cvWriteInt( fs, "n_input_planes",  layer->n_input_planes ));
1531     CV_CALL(cvWriteInt( fs, "input_height",    layer->input_height ));
1532     CV_CALL(cvWriteInt( fs, "input_width",     layer->input_width ));
1533     CV_CALL(cvWriteInt( fs, "n_output_planes", layer->n_output_planes ));
1534     CV_CALL(cvWriteInt( fs, "output_height",   layer->output_height ));
1535     CV_CALL(cvWriteInt( fs, "output_width",    layer->output_width ));
1536     CV_CALL(cvWriteInt( fs, "learn_rate_decrease_type", layer->learn_rate_decrease_type));
1537     CV_CALL(cvWriteReal( fs, "init_learn_rate", layer->init_learn_rate ));
1538     CV_CALL(cvWrite( fs, "weights", layer->weights ));
1539
1540     if( ICV_IS_CNN_CONVOLUTION_LAYER( layer ))
1541     {
1542         CvCNNConvolutionLayer* l = (CvCNNConvolutionLayer*)layer;
1543         CV_CALL(cvWriteInt( fs, "layer_type", ICV_CNN_CONVOLUTION_LAYER ));
1544         CV_CALL(cvWrite( fs, "connect_mask", l->connect_mask ));
1545     }
1546     else if( ICV_IS_CNN_SUBSAMPLING_LAYER( layer ) )
1547     {
1548         CvCNNSubSamplingLayer* l = (CvCNNSubSamplingLayer*)layer;
1549         CV_CALL(cvWriteInt( fs, "layer_type", ICV_CNN_SUBSAMPLING_LAYER ));
1550         CV_CALL(cvWriteReal( fs, "a", l->a ));
1551         CV_CALL(cvWriteReal( fs, "s", l->s ));
1552     }
1553     else if( ICV_IS_CNN_FULLCONNECT_LAYER( layer ) )
1554     {
1555         CvCNNFullConnectLayer* l = (CvCNNFullConnectLayer*)layer;
1556         CV_CALL(cvWriteInt( fs, "layer_type", ICV_CNN_FULLCONNECT_LAYER ));
1557         CV_CALL(cvWriteReal( fs, "a", l->a ));
1558         CV_CALL(cvWriteReal( fs, "s", l->s ));
1559     }
1560     else
1561         CV_ERROR( CV_StsBadArg, "Invalid layer" );
1562
1563     CV_CALL( cvEndWriteStruct( fs )); //"opencv-ml-cnn-layer"
1564
1565     __END__;
1566 }
1567
1568 /****************************************************************************************/
1569 static void* icvReadCNNModel( CvFileStorage* fs, CvFileNode* root_node )
1570 {
1571     CvCNNStatModel* cnn = 0;
1572     CvCNNLayer* layer = 0;
1573
1574     CV_FUNCNAME("icvReadCNNModel");
1575     __BEGIN__;
1576
1577     CvFileNode* node;
1578     CvSeq* seq;
1579     CvSeqReader reader;
1580     int i;
1581
1582     CV_CALL(cnn = (CvCNNStatModel*)cvCreateStatModel(
1583         CV_STAT_MODEL_MAGIC_VAL|CV_CNN_MAGIC_VAL, sizeof(CvCNNStatModel),
1584         icvCNNModelRelease, icvCNNModelPredict, icvCNNModelUpdate ));
1585
1586     CV_CALL(cnn->etalons = (CvMat*)cvReadByName( fs, root_node, "etalons" ));
1587     CV_CALL(cnn->cls_labels = (CvMat*)cvReadByName( fs, root_node, "cls_labels" ));
1588
1589     if( !cnn->etalons || !cnn->cls_labels )
1590         CV_ERROR( CV_StsParseError, "No <etalons> or <cls_labels> in CNN model" );
1591
1592     CV_CALL( node = cvGetFileNodeByName( fs, root_node, "network" ));
1593     seq = node->data.seq;
1594     if( !CV_NODE_IS_SEQ(node->tag) )
1595         CV_ERROR( CV_StsBadArg, "" );
1596
1597     CV_CALL( cvStartReadSeq( seq, &reader, 0 ));
1598     CV_CALL(layer = icvReadCNNLayer( fs, (CvFileNode*)reader.ptr ));
1599     CV_CALL(cnn->network = cvCreateCNNetwork( layer ));
1600
1601     for( i = 1; i < seq->total; i++ )
1602     {
1603         CV_NEXT_SEQ_ELEM( seq->elem_size, reader );
1604         CV_CALL(layer = icvReadCNNLayer( fs, (CvFileNode*)reader.ptr ));
1605         CV_CALL(cnn->network->add_layer( cnn->network, layer ));
1606     }
1607
1608     __END__;
1609
1610     if( cvGetErrStatus() < 0 )
1611     {
1612         if( cnn ) cnn->release( (CvStatModel**)&cnn );
1613         if( layer ) layer->release( &layer );
1614     }
1615     return (void*)cnn;
1616 }
1617
1618 /****************************************************************************************/
1619 static void
1620 icvWriteCNNModel( CvFileStorage* fs, const char* name,
1621                   const void* struct_ptr, CvAttrList )
1622                                    
1623 {
1624     CV_FUNCNAME ("icvWriteCNNModel");
1625     __BEGIN__;
1626
1627     CvCNNStatModel* cnn = (CvCNNStatModel*)struct_ptr;
1628     int n_layers, i;
1629     CvCNNLayer* layer;
1630
1631     if( !CV_IS_CNN(cnn) )
1632         CV_ERROR( CV_StsBadArg, "Invalid pointer" );
1633
1634     n_layers = cnn->network->n_layers;
1635     
1636     CV_CALL( cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_ML_CNN ));
1637
1638     CV_CALL(cvWrite( fs, "etalons", cnn->etalons ));
1639     CV_CALL(cvWrite( fs, "cls_labels", cnn->cls_labels ));
1640     
1641     CV_CALL( cvStartWriteStruct( fs, "network", CV_NODE_SEQ ));
1642
1643     layer = cnn->network->layers;
1644     for( i = 0; i < n_layers && layer; i++, layer = layer->next_layer )
1645         CV_CALL(icvWriteCNNLayer( fs, layer ));
1646     if( i < n_layers || layer )
1647         CV_ERROR( CV_StsBadArg, "Invalid network" );
1648
1649     CV_CALL( cvEndWriteStruct( fs )); //"network"
1650     CV_CALL( cvEndWriteStruct( fs )); //"opencv-ml-cnn"
1651
1652     __END__;
1653 }
1654
1655 static int icvRegisterCNNStatModelType()
1656 {
1657     CvTypeInfo info;
1658
1659     info.header_size = sizeof( info );
1660     info.is_instance = icvIsCNNModel;
1661     info.release = icvReleaseCNNModel;
1662     info.read = icvReadCNNModel;
1663     info.write = icvWriteCNNModel;
1664     info.clone = NULL;
1665     info.type_name = CV_TYPE_NAME_ML_CNN;
1666     cvRegisterType( &info );
1667
1668     return 1;
1669 } // End of icvRegisterCNNStatModelType
1670
1671 static int cnn = icvRegisterCNNStatModelType();
1672
1673 #endif
1674
1675 // End of file