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 ------------------------------*/ 48static float icvCNNModelPredict( 49 const CvStatModel* cnn_model, 50 const CvMat* image, 51 CvMat* probs CV_DEFAULT(0) ); 52 53static 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 59static void icvCNNModelRelease( CvStatModel** cnn_model ); 60 61static 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 ------------------------------*/ 70static void icvCNNetworkAddLayer( CvCNNetwork* network, CvCNNLayer* layer ); 71static 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 ---------------------------*/ 79static void icvCNNConvolutionRelease( CvCNNLayer** p_layer ); 80 81static void icvCNNConvolutionForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y ); 82 83static void icvCNNConvolutionBackward( CvCNNLayer* layer, int t, 84 const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX ); 85 86/*------------------------ functions for sub-sampling layer ----------------------------*/ 87static void icvCNNSubSamplingRelease( CvCNNLayer** p_layer ); 88 89static void icvCNNSubSamplingForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y ); 90 91static void icvCNNSubSamplingBackward( CvCNNLayer* layer, int t, 92 const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX ); 93 94/*------------------------ functions for full connected layer --------------------------*/ 95static void icvCNNFullConnectRelease( CvCNNLayer** p_layer ); 96 97static void icvCNNFullConnectForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y ); 98 99static 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\****************************************************************************************/ 161ML_IMPL CvStatModel* 162cvTrainCNNClassifier( 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/****************************************************************************************/ 211static 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/****************************************************************************************/ 314static 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/****************************************************************************************/ 395static 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/****************************************************************************************/ 443static 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\****************************************************************************************/ 467ML_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/****************************************************************************************/ 495static 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/****************************************************************************************/ 536static 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\****************************************************************************************/ 574static 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/****************************************************************************************/ 631ML_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/****************************************************************************************/ 697ML_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/****************************************************************************************/ 760ML_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\****************************************************************************************/ 814static 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/****************************************************************************************/ 885static 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/****************************************************************************************/ 965static 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. */ 1020static 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/****************************************************************************************/ 1127static 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. */ 1251static 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\****************************************************************************************/ 1331static 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/****************************************************************************************/ 1356static 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/****************************************************************************************/ 1381static 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\****************************************************************************************/ 1408static int icvIsCNNModel( const void* ptr ) 1409{ 1410 return CV_IS_CNN(ptr); 1411} 1412 1413/****************************************************************************************/ 1414static 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/****************************************************************************************/ 1429static 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/****************************************************************************************/ 1520static 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/****************************************************************************************/ 1569static 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/****************************************************************************************/ 1619static void 1620icvWriteCNNModel( 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 1655static 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 1671static int cnn = icvRegisterCNNStatModelType(); 1672 1673#endif 1674 1675// End of file 1676