| /*M/////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
| // |
| // By downloading, copying, installing or using the software you agree to this license. |
| // If you do not agree to this license, do not download, install, |
| // copy or use the software. |
| // |
| // |
| // Intel License Agreement |
| // |
| // Copyright (C) 2000, Intel Corporation, all rights reserved. |
| // Third party copyrights are property of their respective owners. |
| // |
| // Redistribution and use in source and binary forms, with or without modification, |
| // are permitted provided that the following conditions are met: |
| // |
| // * Redistribution's of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // * Redistribution's in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // * The name of Intel Corporation may not be used to endorse or promote products |
| // derived from this software without specific prior written permission. |
| // |
| // This software is provided by the copyright holders and contributors "as is" and |
| // any express or implied warranties, including, but not limited to, the implied |
| // warranties of merchantability and fitness for a particular purpose are disclaimed. |
| // In no event shall the Intel Corporation or contributors be liable for any direct, |
| // indirect, incidental, special, exemplary, or consequential damages |
| // (including, but not limited to, procurement of substitute goods or services; |
| // loss of use, data, or profits; or business interruption) however caused |
| // and on any theory of liability, whether in contract, strict liability, |
| // or tort (including negligence or otherwise) arising in any way out of |
| // the use of this software, even if advised of the possibility of such damage. |
| // |
| //M*/ |
| |
| #include "_ml.h" |
| |
| #if 0 |
| /****************************************************************************************\ |
| * Auxilary functions declarations * |
| \****************************************************************************************/ |
| /*---------------------- functions for the CNN classifier ------------------------------*/ |
| static float icvCNNModelPredict( |
| const CvStatModel* cnn_model, |
| const CvMat* image, |
| CvMat* probs CV_DEFAULT(0) ); |
| |
| static void icvCNNModelUpdate( |
| CvStatModel* cnn_model, const CvMat* images, int tflag, |
| const CvMat* responses, const CvStatModelParams* params, |
| const CvMat* CV_DEFAULT(0), const CvMat* sample_idx CV_DEFAULT(0), |
| const CvMat* CV_DEFAULT(0), const CvMat* CV_DEFAULT(0)); |
| |
| static void icvCNNModelRelease( CvStatModel** cnn_model ); |
| |
| static void icvTrainCNNetwork( CvCNNetwork* network, |
| const float** images, |
| const CvMat* responses, |
| const CvMat* etalons, |
| int grad_estim_type, |
| int max_iter, |
| int start_iter ); |
| |
| /*------------------------- functions for the CNN network ------------------------------*/ |
| static void icvCNNetworkAddLayer( CvCNNetwork* network, CvCNNLayer* layer ); |
| static void icvCNNetworkRelease( CvCNNetwork** network ); |
| |
| /* In all layer functions we denote input by X and output by Y, where |
| X and Y are column-vectors, so that |
| length(X)==<n_input_planes>*<input_height>*<input_width>, |
| length(Y)==<n_output_planes>*<output_height>*<output_width>. |
| */ |
| /*------------------------ functions for convolutional layer ---------------------------*/ |
| static void icvCNNConvolutionRelease( CvCNNLayer** p_layer ); |
| |
| static void icvCNNConvolutionForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y ); |
| |
| static void icvCNNConvolutionBackward( CvCNNLayer* layer, int t, |
| const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX ); |
| |
| /*------------------------ functions for sub-sampling layer ----------------------------*/ |
| static void icvCNNSubSamplingRelease( CvCNNLayer** p_layer ); |
| |
| static void icvCNNSubSamplingForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y ); |
| |
| static void icvCNNSubSamplingBackward( CvCNNLayer* layer, int t, |
| const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX ); |
| |
| /*------------------------ functions for full connected layer --------------------------*/ |
| static void icvCNNFullConnectRelease( CvCNNLayer** p_layer ); |
| |
| static void icvCNNFullConnectForward( CvCNNLayer* layer, const CvMat* X, CvMat* Y ); |
| |
| static void icvCNNFullConnectBackward( CvCNNLayer* layer, int, |
| const CvMat*, const CvMat* dE_dY, CvMat* dE_dX ); |
| |
| /****************************************************************************************\ |
| * Functions implementations * |
| \****************************************************************************************/ |
| |
| #define ICV_CHECK_CNN_NETWORK(network) \ |
| { \ |
| CvCNNLayer* first_layer, *layer, *last_layer; \ |
| int n_layers, i; \ |
| if( !network ) \ |
| CV_ERROR( CV_StsNullPtr, \ |
| "Null <network> pointer. Network must be created by user." ); \ |
| n_layers = network->n_layers; \ |
| first_layer = last_layer = network->layers; \ |
| for( i = 0, layer = first_layer; i < n_layers && layer; i++ ) \ |
| { \ |
| if( !ICV_IS_CNN_LAYER(layer) ) \ |
| CV_ERROR( CV_StsNullPtr, "Invalid network" ); \ |
| last_layer = layer; \ |
| layer = layer->next_layer; \ |
| } \ |
| \ |
| if( i == 0 || i != n_layers || first_layer->prev_layer || layer ) \ |
| CV_ERROR( CV_StsNullPtr, "Invalid network" ); \ |
| \ |
| if( first_layer->n_input_planes != 1 ) \ |
| CV_ERROR( CV_StsBadArg, "First layer must contain only one input plane" ); \ |
| \ |
| if( img_size != first_layer->input_height*first_layer->input_width ) \ |
| CV_ERROR( CV_StsBadArg, "Invalid input sizes of the first layer" ); \ |
| \ |
| if( params->etalons->cols != last_layer->n_output_planes* \ |
| last_layer->output_height*last_layer->output_width ) \ |
| CV_ERROR( CV_StsBadArg, "Invalid output sizes of the last layer" ); \ |
| } |
| |
| #define ICV_CHECK_CNN_MODEL_PARAMS(params) \ |
| { \ |
| if( !params ) \ |
| CV_ERROR( CV_StsNullPtr, "Null <params> pointer" ); \ |
| \ |
| if( !ICV_IS_MAT_OF_TYPE(params->etalons, CV_32FC1) ) \ |
| CV_ERROR( CV_StsBadArg, "<etalons> must be CV_32FC1 type" ); \ |
| if( params->etalons->rows != cnn_model->cls_labels->cols ) \ |
| CV_ERROR( CV_StsBadArg, "Invalid <etalons> size" ); \ |
| \ |
| if( params->grad_estim_type != CV_CNN_GRAD_ESTIM_RANDOM && \ |
| params->grad_estim_type != CV_CNN_GRAD_ESTIM_BY_WORST_IMG ) \ |
| CV_ERROR( CV_StsBadArg, "Invalid <grad_estim_type>" ); \ |
| \ |
| if( params->start_iter < 0 ) \ |
| CV_ERROR( CV_StsBadArg, "Parameter <start_iter> must be positive or zero" ); \ |
| \ |
| if( params->max_iter < 1 ) \ |
| params->max_iter = 1; \ |
| } |
| |
| /****************************************************************************************\ |
| * Classifier functions * |
| \****************************************************************************************/ |
| ML_IMPL CvStatModel* |
| cvTrainCNNClassifier( const CvMat* _train_data, int tflag, |
| const CvMat* _responses, |
| const CvStatModelParams* _params, |
| const CvMat*, const CvMat* _sample_idx, const CvMat*, const CvMat* ) |
| { |
| CvCNNStatModel* cnn_model = 0; |
| const float** out_train_data = 0; |
| CvMat* responses = 0; |
| |
| CV_FUNCNAME("cvTrainCNNClassifier"); |
| __BEGIN__; |
| |
| int n_images; |
| int img_size; |
| CvCNNStatModelParams* params = (CvCNNStatModelParams*)_params; |
| |
| CV_CALL(cnn_model = (CvCNNStatModel*)cvCreateStatModel( |
| CV_STAT_MODEL_MAGIC_VAL|CV_CNN_MAGIC_VAL, sizeof(CvCNNStatModel), |
| icvCNNModelRelease, icvCNNModelPredict, icvCNNModelUpdate )); |
| |
| CV_CALL(cvPrepareTrainData( "cvTrainCNNClassifier", |
| _train_data, tflag, _responses, CV_VAR_CATEGORICAL, |
| 0, _sample_idx, false, &out_train_data, |
| &n_images, &img_size, &img_size, &responses, |
| &cnn_model->cls_labels, 0 )); |
| |
| ICV_CHECK_CNN_MODEL_PARAMS(params); |
| ICV_CHECK_CNN_NETWORK(params->network); |
| |
| cnn_model->network = params->network; |
| CV_CALL(cnn_model->etalons = (CvMat*)cvClone( params->etalons )); |
| |
| CV_CALL( icvTrainCNNetwork( cnn_model->network, out_train_data, responses, |
| cnn_model->etalons, params->grad_estim_type, params->max_iter, |
| params->start_iter )); |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 && cnn_model ) |
| { |
| cnn_model->release( (CvStatModel**)&cnn_model ); |
| } |
| cvFree( &out_train_data ); |
| cvReleaseMat( &responses ); |
| |
| return (CvStatModel*)cnn_model; |
| } |
| |
| /****************************************************************************************/ |
| static void icvTrainCNNetwork( CvCNNetwork* network, |
| const float** images, |
| const CvMat* responses, |
| const CvMat* etalons, |
| int grad_estim_type, |
| int max_iter, |
| int start_iter ) |
| { |
| CvMat** X = 0; |
| CvMat** dE_dX = 0; |
| const int n_layers = network->n_layers; |
| int k; |
| |
| CV_FUNCNAME("icvTrainCNNetwork"); |
| __BEGIN__; |
| |
| CvCNNLayer* first_layer = network->layers; |
| const int img_height = first_layer->input_height; |
| const int img_width = first_layer->input_width; |
| const int img_size = img_width*img_height; |
| const int n_images = responses->cols; |
| CvMat image = cvMat( 1, img_size, CV_32FC1 ); |
| CvCNNLayer* layer; |
| int n; |
| CvRNG rng = cvRNG(-1); |
| |
| CV_CALL(X = (CvMat**)cvAlloc( (n_layers+1)*sizeof(CvMat*) )); |
| CV_CALL(dE_dX = (CvMat**)cvAlloc( (n_layers+1)*sizeof(CvMat*) )); |
| memset( X, 0, (n_layers+1)*sizeof(CvMat*) ); |
| memset( dE_dX, 0, (n_layers+1)*sizeof(CvMat*) ); |
| |
| CV_CALL(X[0] = cvCreateMat( img_height*img_width,1,CV_32FC1 )); |
| CV_CALL(dE_dX[0] = cvCreateMat( 1, X[0]->rows, CV_32FC1 )); |
| for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer ) |
| { |
| CV_CALL(X[k+1] = cvCreateMat( layer->n_output_planes*layer->output_height* |
| layer->output_width, 1, CV_32FC1 )); |
| CV_CALL(dE_dX[k+1] = cvCreateMat( 1, X[k+1]->rows, CV_32FC1 )); |
| } |
| |
| for( n = 1; n <= max_iter; n++ ) |
| { |
| float loss, max_loss = 0; |
| int i; |
| int worst_img_idx = -1; |
| int* right_etal_idx = responses->data.i; |
| CvMat etalon; |
| |
| // Find the worst image (which produces the greatest loss) or use the random image |
| if( grad_estim_type == CV_CNN_GRAD_ESTIM_BY_WORST_IMG ) |
| { |
| for( i = 0; i < n_images; i++, right_etal_idx++ ) |
| { |
| image.data.fl = (float*)images[i]; |
| cvTranspose( &image, X[0] ); |
| |
| for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer ) |
| CV_CALL(layer->forward( layer, X[k], X[k+1] )); |
| |
| cvTranspose( X[n_layers], dE_dX[n_layers] ); |
| cvGetRow( etalons, &etalon, *right_etal_idx ); |
| loss = (float)cvNorm( dE_dX[n_layers], &etalon ); |
| if( loss > max_loss ) |
| { |
| max_loss = loss; |
| worst_img_idx = i; |
| } |
| } |
| } |
| else |
| worst_img_idx = cvRandInt(&rng) % n_images; |
| |
| // Train network on the worst image |
| // 1) Compute the network output on the <image> |
| image.data.fl = (float*)images[worst_img_idx]; |
| CV_CALL(cvTranspose( &image, X[0] )); |
| |
| for( k = 0, layer = first_layer; k < n_layers - 1; k++, layer = layer->next_layer ) |
| CV_CALL(layer->forward( layer, X[k], X[k+1] )); |
| CV_CALL(layer->forward( layer, X[k], X[k+1] )); |
| |
| // 2) Compute the gradient |
| cvTranspose( X[n_layers], dE_dX[n_layers] ); |
| cvGetRow( etalons, &etalon, responses->data.i[worst_img_idx] ); |
| cvSub( dE_dX[n_layers], &etalon, dE_dX[n_layers] ); |
| |
| // 3) Update weights by the gradient descent |
| for( k = n_layers; k > 0; k--, layer = layer->prev_layer ) |
| CV_CALL(layer->backward( layer, n + start_iter, X[k-1], dE_dX[k], dE_dX[k-1] )); |
| } |
| |
| __END__; |
| |
| for( k = 0; k <= n_layers; k++ ) |
| { |
| cvReleaseMat( &X[k] ); |
| cvReleaseMat( &dE_dX[k] ); |
| } |
| cvFree( &X ); |
| cvFree( &dE_dX ); |
| } |
| |
| /****************************************************************************************/ |
| static float icvCNNModelPredict( const CvStatModel* model, |
| const CvMat* _image, |
| CvMat* probs ) |
| { |
| CvMat** X = 0; |
| float* img_data = 0; |
| int n_layers = 0; |
| int best_etal_idx = -1; |
| int k; |
| |
| CV_FUNCNAME("icvCNNModelPredict"); |
| __BEGIN__; |
| |
| CvCNNStatModel* cnn_model = (CvCNNStatModel*)model; |
| CvCNNLayer* first_layer, *layer = 0; |
| int img_height, img_width, img_size; |
| int nclasses, i; |
| float loss, min_loss = FLT_MAX; |
| float* probs_data; |
| CvMat etalon, image; |
| |
| if( !CV_IS_CNN(model) ) |
| CV_ERROR( CV_StsBadArg, "Invalid model" ); |
| |
| nclasses = cnn_model->cls_labels->cols; |
| n_layers = cnn_model->network->n_layers; |
| first_layer = cnn_model->network->layers; |
| img_height = first_layer->input_height; |
| img_width = first_layer->input_width; |
| img_size = img_height*img_width; |
| |
| cvPreparePredictData( _image, img_size, 0, nclasses, probs, &img_data ); |
| |
| CV_CALL(X = (CvMat**)cvAlloc( (n_layers+1)*sizeof(CvMat*) )); |
| memset( X, 0, (n_layers+1)*sizeof(CvMat*) ); |
| |
| CV_CALL(X[0] = cvCreateMat( img_size,1,CV_32FC1 )); |
| for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer ) |
| { |
| CV_CALL(X[k+1] = cvCreateMat( layer->n_output_planes*layer->output_height* |
| layer->output_width, 1, CV_32FC1 )); |
| } |
| |
| image = cvMat( 1, img_size, CV_32FC1, img_data ); |
| cvTranspose( &image, X[0] ); |
| for( k = 0, layer = first_layer; k < n_layers; k++, layer = layer->next_layer ) |
| CV_CALL(layer->forward( layer, X[k], X[k+1] )); |
| |
| probs_data = probs ? probs->data.fl : 0; |
| etalon = cvMat( cnn_model->etalons->cols, 1, CV_32FC1, cnn_model->etalons->data.fl ); |
| for( i = 0; i < nclasses; i++, etalon.data.fl += cnn_model->etalons->cols ) |
| { |
| loss = (float)cvNorm( X[n_layers], &etalon ); |
| if( loss < min_loss ) |
| { |
| min_loss = loss; |
| best_etal_idx = i; |
| } |
| if( probs ) |
| *probs_data++ = -loss; |
| } |
| |
| if( probs ) |
| { |
| cvExp( probs, probs ); |
| CvScalar sum = cvSum( probs ); |
| cvConvertScale( probs, probs, 1./sum.val[0] ); |
| } |
| |
| __END__; |
| |
| for( k = 0; k <= n_layers; k++ ) |
| cvReleaseMat( &X[k] ); |
| cvFree( &X ); |
| if( img_data != _image->data.fl ) |
| cvFree( &img_data ); |
| |
| return ((float) ((CvCNNStatModel*)model)->cls_labels->data.i[best_etal_idx]); |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNModelUpdate( |
| CvStatModel* _cnn_model, const CvMat* _train_data, int tflag, |
| const CvMat* _responses, const CvStatModelParams* _params, |
| const CvMat*, const CvMat* _sample_idx, |
| const CvMat*, const CvMat* ) |
| { |
| const float** out_train_data = 0; |
| CvMat* responses = 0; |
| CvMat* cls_labels = 0; |
| |
| CV_FUNCNAME("icvCNNModelUpdate"); |
| __BEGIN__; |
| |
| int n_images, img_size, i; |
| CvCNNStatModelParams* params = (CvCNNStatModelParams*)_params; |
| CvCNNStatModel* cnn_model = (CvCNNStatModel*)_cnn_model; |
| |
| if( !CV_IS_CNN(cnn_model) ) |
| CV_ERROR( CV_StsBadArg, "Invalid model" ); |
| |
| CV_CALL(cvPrepareTrainData( "cvTrainCNNClassifier", |
| _train_data, tflag, _responses, CV_VAR_CATEGORICAL, |
| 0, _sample_idx, false, &out_train_data, |
| &n_images, &img_size, &img_size, &responses, |
| &cls_labels, 0, 0 )); |
| |
| ICV_CHECK_CNN_MODEL_PARAMS(params); |
| |
| // Number of classes must be the same as when classifiers was created |
| if( !CV_ARE_SIZES_EQ(cls_labels, cnn_model->cls_labels) ) |
| CV_ERROR( CV_StsBadArg, "Number of classes must be left unchanged" ); |
| for( i = 0; i < cls_labels->cols; i++ ) |
| { |
| if( cls_labels->data.i[i] != cnn_model->cls_labels->data.i[i] ) |
| CV_ERROR( CV_StsBadArg, "Number of classes must be left unchanged" ); |
| } |
| |
| CV_CALL( icvTrainCNNetwork( cnn_model->network, out_train_data, responses, |
| cnn_model->etalons, params->grad_estim_type, params->max_iter, |
| params->start_iter )); |
| |
| __END__; |
| |
| cvFree( &out_train_data ); |
| cvReleaseMat( &responses ); |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNModelRelease( CvStatModel** cnn_model ) |
| { |
| CV_FUNCNAME("icvCNNModelRelease"); |
| __BEGIN__; |
| |
| CvCNNStatModel* cnn; |
| if( !cnn_model ) |
| CV_ERROR( CV_StsNullPtr, "Null double pointer" ); |
| |
| cnn = *(CvCNNStatModel**)cnn_model; |
| |
| cvReleaseMat( &cnn->cls_labels ); |
| cvReleaseMat( &cnn->etalons ); |
| cnn->network->release( &cnn->network ); |
| |
| cvFree( &cnn ); |
| |
| __END__; |
| |
| } |
| |
| /****************************************************************************************\ |
| * Network functions * |
| \****************************************************************************************/ |
| ML_IMPL CvCNNetwork* cvCreateCNNetwork( CvCNNLayer* first_layer ) |
| { |
| CvCNNetwork* network = 0; |
| |
| CV_FUNCNAME( "cvCreateCNNetwork" ); |
| __BEGIN__; |
| |
| if( !ICV_IS_CNN_LAYER(first_layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| CV_CALL(network = (CvCNNetwork*)cvAlloc( sizeof(CvCNNetwork) )); |
| memset( network, 0, sizeof(CvCNNetwork) ); |
| |
| network->layers = first_layer; |
| network->n_layers = 1; |
| network->release = icvCNNetworkRelease; |
| network->add_layer = icvCNNetworkAddLayer; |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 && network ) |
| cvFree( &network ); |
| |
| return network; |
| |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNetworkAddLayer( CvCNNetwork* network, CvCNNLayer* layer ) |
| { |
| CV_FUNCNAME( "icvCNNetworkAddLayer" ); |
| __BEGIN__; |
| |
| CvCNNLayer* prev_layer; |
| |
| if( network == NULL ) |
| CV_ERROR( CV_StsNullPtr, "Null <network> pointer" ); |
| |
| prev_layer = network->layers; |
| while( prev_layer->next_layer ) |
| prev_layer = prev_layer->next_layer; |
| |
| if( ICV_IS_CNN_FULLCONNECT_LAYER(layer) ) |
| { |
| if( layer->n_input_planes != prev_layer->output_width*prev_layer->output_height* |
| prev_layer->n_output_planes ) |
| CV_ERROR( CV_StsBadArg, "Unmatched size of the new layer" ); |
| if( layer->input_height != 1 || layer->output_height != 1 || |
| layer->input_width != 1 || layer->output_width != 1 ) |
| CV_ERROR( CV_StsBadArg, "Invalid size of the new layer" ); |
| } |
| else if( ICV_IS_CNN_CONVOLUTION_LAYER(layer) || ICV_IS_CNN_SUBSAMPLING_LAYER(layer) ) |
| { |
| if( prev_layer->n_output_planes != layer->n_input_planes || |
| prev_layer->output_height != layer->input_height || |
| prev_layer->output_width != layer->input_width ) |
| CV_ERROR( CV_StsBadArg, "Unmatched size of the new layer" ); |
| } |
| else |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| layer->prev_layer = prev_layer; |
| prev_layer->next_layer = layer; |
| network->n_layers++; |
| |
| __END__; |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNetworkRelease( CvCNNetwork** network_pptr ) |
| { |
| CV_FUNCNAME( "icvReleaseCNNetwork" ); |
| __BEGIN__; |
| |
| CvCNNetwork* network = 0; |
| CvCNNLayer* layer = 0, *next_layer = 0; |
| int k; |
| |
| if( network_pptr == NULL ) |
| CV_ERROR( CV_StsBadArg, "Null double pointer" ); |
| if( *network_pptr == NULL ) |
| return; |
| |
| network = *network_pptr; |
| layer = network->layers; |
| if( layer == NULL ) |
| CV_ERROR( CV_StsBadArg, "CNN is empty (does not contain any layer)" ); |
| |
| // k is the number of the layer to be deleted |
| for( k = 0; k < network->n_layers && layer; k++ ) |
| { |
| next_layer = layer->next_layer; |
| layer->release( &layer ); |
| layer = next_layer; |
| } |
| |
| if( k != network->n_layers || layer) |
| CV_ERROR( CV_StsBadArg, "Invalid network" ); |
| |
| cvFree( &network ); |
| |
| __END__; |
| } |
| |
| /****************************************************************************************\ |
| * Layer functions * |
| \****************************************************************************************/ |
| static CvCNNLayer* icvCreateCNNLayer( int layer_type, int header_size, |
| int n_input_planes, int input_height, int input_width, |
| int n_output_planes, int output_height, int output_width, |
| float init_learn_rate, int learn_rate_decrease_type, |
| CvCNNLayerRelease release, CvCNNLayerForward forward, CvCNNLayerBackward backward ) |
| { |
| CvCNNLayer* layer = 0; |
| |
| CV_FUNCNAME("icvCreateCNNLayer"); |
| __BEGIN__; |
| |
| CV_ASSERT( release && forward && backward ) |
| CV_ASSERT( header_size >= sizeof(CvCNNLayer) ) |
| |
| if( n_input_planes < 1 || n_output_planes < 1 || |
| input_height < 1 || input_width < 1 || |
| output_height < 1 || output_width < 1 || |
| input_height < output_height || |
| input_width < output_width ) |
| CV_ERROR( CV_StsBadArg, "Incorrect input or output parameters" ); |
| if( init_learn_rate < FLT_EPSILON ) |
| CV_ERROR( CV_StsBadArg, "Initial learning rate must be positive" ); |
| if( learn_rate_decrease_type != CV_CNN_LEARN_RATE_DECREASE_HYPERBOLICALLY && |
| learn_rate_decrease_type != CV_CNN_LEARN_RATE_DECREASE_SQRT_INV && |
| learn_rate_decrease_type != CV_CNN_LEARN_RATE_DECREASE_LOG_INV ) |
| CV_ERROR( CV_StsBadArg, "Invalid type of learning rate dynamics" ); |
| |
| CV_CALL(layer = (CvCNNLayer*)cvAlloc( header_size )); |
| memset( layer, 0, header_size ); |
| |
| layer->flags = ICV_CNN_LAYER|layer_type; |
| CV_ASSERT( ICV_IS_CNN_LAYER(layer) ) |
| |
| layer->n_input_planes = n_input_planes; |
| layer->input_height = input_height; |
| layer->input_width = input_width; |
| |
| layer->n_output_planes = n_output_planes; |
| layer->output_height = output_height; |
| layer->output_width = output_width; |
| |
| layer->init_learn_rate = init_learn_rate; |
| layer->learn_rate_decrease_type = learn_rate_decrease_type; |
| |
| layer->release = release; |
| layer->forward = forward; |
| layer->backward = backward; |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 && layer) |
| cvFree( &layer ); |
| |
| return layer; |
| } |
| |
| /****************************************************************************************/ |
| ML_IMPL CvCNNLayer* cvCreateCNNConvolutionLayer( |
| int n_input_planes, int input_height, int input_width, |
| int n_output_planes, int K, |
| float init_learn_rate, int learn_rate_decrease_type, |
| CvMat* connect_mask, CvMat* weights ) |
| |
| { |
| CvCNNConvolutionLayer* layer = 0; |
| |
| CV_FUNCNAME("cvCreateCNNConvolutionLayer"); |
| __BEGIN__; |
| |
| const int output_height = input_height - K + 1; |
| const int output_width = input_width - K + 1; |
| |
| if( K < 1 || init_learn_rate <= 0 ) |
| CV_ERROR( CV_StsBadArg, "Incorrect parameters" ); |
| |
| CV_CALL(layer = (CvCNNConvolutionLayer*)icvCreateCNNLayer( ICV_CNN_CONVOLUTION_LAYER, |
| sizeof(CvCNNConvolutionLayer), n_input_planes, input_height, input_width, |
| n_output_planes, output_height, output_width, |
| init_learn_rate, learn_rate_decrease_type, |
| icvCNNConvolutionRelease, icvCNNConvolutionForward, icvCNNConvolutionBackward )); |
| |
| layer->K = K; |
| CV_CALL(layer->weights = cvCreateMat( n_output_planes, K*K+1, CV_32FC1 )); |
| CV_CALL(layer->connect_mask = cvCreateMat( n_output_planes, n_input_planes, CV_8UC1)); |
| |
| if( weights ) |
| { |
| if( !ICV_IS_MAT_OF_TYPE( weights, CV_32FC1 ) ) |
| CV_ERROR( CV_StsBadSize, "Type of initial weights matrix must be CV_32FC1" ); |
| if( !CV_ARE_SIZES_EQ( weights, layer->weights ) ) |
| CV_ERROR( CV_StsBadSize, "Invalid size of initial weights matrix" ); |
| CV_CALL(cvCopy( weights, layer->weights )); |
| } |
| else |
| { |
| CvRNG rng = cvRNG( 0xFFFFFFFF ); |
| cvRandArr( &rng, layer->weights, CV_RAND_UNI, cvRealScalar(-1), cvRealScalar(1) ); |
| } |
| |
| if( connect_mask ) |
| { |
| if( !ICV_IS_MAT_OF_TYPE( connect_mask, CV_8UC1 ) ) |
| CV_ERROR( CV_StsBadSize, "Type of connection matrix must be CV_32FC1" ); |
| if( !CV_ARE_SIZES_EQ( connect_mask, layer->connect_mask ) ) |
| CV_ERROR( CV_StsBadSize, "Invalid size of connection matrix" ); |
| CV_CALL(cvCopy( connect_mask, layer->connect_mask )); |
| } |
| else |
| CV_CALL(cvSet( layer->connect_mask, cvRealScalar(1) )); |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 && layer ) |
| { |
| cvReleaseMat( &layer->weights ); |
| cvReleaseMat( &layer->connect_mask ); |
| cvFree( &layer ); |
| } |
| |
| return (CvCNNLayer*)layer; |
| } |
| |
| /****************************************************************************************/ |
| ML_IMPL CvCNNLayer* cvCreateCNNSubSamplingLayer( |
| int n_input_planes, int input_height, int input_width, |
| int sub_samp_scale, float a, float s, |
| float init_learn_rate, int learn_rate_decrease_type, CvMat* weights ) |
| |
| { |
| CvCNNSubSamplingLayer* layer = 0; |
| |
| CV_FUNCNAME("cvCreateCNNSubSamplingLayer"); |
| __BEGIN__; |
| |
| const int output_height = input_height/sub_samp_scale; |
| const int output_width = input_width/sub_samp_scale; |
| const int n_output_planes = n_input_planes; |
| |
| if( sub_samp_scale < 1 || a <= 0 || s <= 0) |
| CV_ERROR( CV_StsBadArg, "Incorrect parameters" ); |
| |
| CV_CALL(layer = (CvCNNSubSamplingLayer*)icvCreateCNNLayer( ICV_CNN_SUBSAMPLING_LAYER, |
| sizeof(CvCNNSubSamplingLayer), n_input_planes, input_height, input_width, |
| n_output_planes, output_height, output_width, |
| init_learn_rate, learn_rate_decrease_type, |
| icvCNNSubSamplingRelease, icvCNNSubSamplingForward, icvCNNSubSamplingBackward )); |
| |
| layer->sub_samp_scale = sub_samp_scale; |
| layer->a = a; |
| layer->s = s; |
| |
| CV_CALL(layer->sumX = |
| cvCreateMat( n_output_planes*output_width*output_height, 1, CV_32FC1 )); |
| CV_CALL(layer->exp2ssumWX = |
| cvCreateMat( n_output_planes*output_width*output_height, 1, CV_32FC1 )); |
| |
| cvZero( layer->sumX ); |
| cvZero( layer->exp2ssumWX ); |
| |
| CV_CALL(layer->weights = cvCreateMat( n_output_planes, 2, CV_32FC1 )); |
| if( weights ) |
| { |
| if( !ICV_IS_MAT_OF_TYPE( weights, CV_32FC1 ) ) |
| CV_ERROR( CV_StsBadSize, "Type of initial weights matrix must be CV_32FC1" ); |
| if( !CV_ARE_SIZES_EQ( weights, layer->weights ) ) |
| CV_ERROR( CV_StsBadSize, "Invalid size of initial weights matrix" ); |
| CV_CALL(cvCopy( weights, layer->weights )); |
| } |
| else |
| { |
| CvRNG rng = cvRNG( 0xFFFFFFFF ); |
| cvRandArr( &rng, layer->weights, CV_RAND_UNI, cvRealScalar(-1), cvRealScalar(1) ); |
| } |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 && layer ) |
| { |
| cvReleaseMat( &layer->exp2ssumWX ); |
| cvFree( &layer ); |
| } |
| |
| return (CvCNNLayer*)layer; |
| } |
| |
| /****************************************************************************************/ |
| ML_IMPL CvCNNLayer* cvCreateCNNFullConnectLayer( |
| int n_inputs, int n_outputs, float a, float s, |
| float init_learn_rate, int learn_rate_decrease_type, CvMat* weights ) |
| { |
| CvCNNFullConnectLayer* layer = 0; |
| |
| CV_FUNCNAME("cvCreateCNNFullConnectLayer"); |
| __BEGIN__; |
| |
| if( a <= 0 || s <= 0 || init_learn_rate <= 0) |
| CV_ERROR( CV_StsBadArg, "Incorrect parameters" ); |
| |
| CV_CALL(layer = (CvCNNFullConnectLayer*)icvCreateCNNLayer( ICV_CNN_FULLCONNECT_LAYER, |
| sizeof(CvCNNFullConnectLayer), n_inputs, 1, 1, n_outputs, 1, 1, |
| init_learn_rate, learn_rate_decrease_type, |
| icvCNNFullConnectRelease, icvCNNFullConnectForward, icvCNNFullConnectBackward )); |
| |
| layer->a = a; |
| layer->s = s; |
| |
| CV_CALL(layer->exp2ssumWX = cvCreateMat( n_outputs, 1, CV_32FC1 )); |
| cvZero( layer->exp2ssumWX ); |
| |
| CV_CALL(layer->weights = cvCreateMat( n_outputs, n_inputs+1, CV_32FC1 )); |
| if( weights ) |
| { |
| if( !ICV_IS_MAT_OF_TYPE( weights, CV_32FC1 ) ) |
| CV_ERROR( CV_StsBadSize, "Type of initial weights matrix must be CV_32FC1" ); |
| if( !CV_ARE_SIZES_EQ( weights, layer->weights ) ) |
| CV_ERROR( CV_StsBadSize, "Invalid size of initial weights matrix" ); |
| CV_CALL(cvCopy( weights, layer->weights )); |
| } |
| else |
| { |
| CvRNG rng = cvRNG( 0xFFFFFFFF ); |
| cvRandArr( &rng, layer->weights, CV_RAND_UNI, cvRealScalar(-1), cvRealScalar(1) ); |
| } |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 && layer ) |
| { |
| cvReleaseMat( &layer->exp2ssumWX ); |
| cvReleaseMat( &layer->weights ); |
| cvFree( &layer ); |
| } |
| |
| return (CvCNNLayer*)layer; |
| } |
| |
| |
| /****************************************************************************************\ |
| * Layer FORWARD functions * |
| \****************************************************************************************/ |
| static void icvCNNConvolutionForward( CvCNNLayer* _layer, |
| const CvMat* X, |
| CvMat* Y ) |
| { |
| CV_FUNCNAME("icvCNNConvolutionForward"); |
| |
| if( !ICV_IS_CNN_CONVOLUTION_LAYER(_layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| {__BEGIN__; |
| |
| const CvCNNConvolutionLayer* layer = (CvCNNConvolutionLayer*) _layer; |
| |
| const int K = layer->K; |
| const int n_weights_for_Yplane = K*K + 1; |
| |
| const int nXplanes = layer->n_input_planes; |
| const int Xheight = layer->input_height; |
| const int Xwidth = layer->input_width ; |
| const int Xsize = Xwidth*Xheight; |
| |
| const int nYplanes = layer->n_output_planes; |
| const int Yheight = layer->output_height; |
| const int Ywidth = layer->output_width; |
| const int Ysize = Ywidth*Yheight; |
| |
| int xx, yy, ni, no, kx, ky; |
| float *Yplane = 0, *Xplane = 0, *w = 0; |
| uchar* connect_mask_data = 0; |
| |
| CV_ASSERT( X->rows == nXplanes*Xsize && X->cols == 1 ); |
| CV_ASSERT( Y->rows == nYplanes*Ysize && Y->cols == 1 ); |
| |
| cvSetZero( Y ); |
| |
| Yplane = Y->data.fl; |
| connect_mask_data = layer->connect_mask->data.ptr; |
| w = layer->weights->data.fl; |
| for( no = 0; no < nYplanes; no++, Yplane += Ysize, w += n_weights_for_Yplane ) |
| { |
| Xplane = X->data.fl; |
| for( ni = 0; ni < nXplanes; ni++, Xplane += Xsize, connect_mask_data++ ) |
| { |
| if( *connect_mask_data ) |
| { |
| float* Yelem = Yplane; |
| |
| // Xheight-K+1 == Yheight && Xwidth-K+1 == Ywidth |
| for( yy = 0; yy < Xheight-K+1; yy++ ) |
| { |
| for( xx = 0; xx < Xwidth-K+1; xx++, Yelem++ ) |
| { |
| float* templ = Xplane+yy*Xwidth+xx; |
| float WX = 0; |
| for( ky = 0; ky < K; ky++, templ += Xwidth-K ) |
| { |
| for( kx = 0; kx < K; kx++, templ++ ) |
| { |
| WX += *templ*w[ky*K+kx]; |
| } |
| } |
| *Yelem += WX + w[K*K]; |
| } |
| } |
| } |
| } |
| } |
| }__END__; |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNSubSamplingForward( CvCNNLayer* _layer, |
| const CvMat* X, |
| CvMat* Y ) |
| { |
| CV_FUNCNAME("icvCNNSubSamplingForward"); |
| |
| if( !ICV_IS_CNN_SUBSAMPLING_LAYER(_layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| {__BEGIN__; |
| |
| const CvCNNSubSamplingLayer* layer = (CvCNNSubSamplingLayer*) _layer; |
| |
| const int sub_sampl_scale = layer->sub_samp_scale; |
| const int nplanes = layer->n_input_planes; |
| |
| const int Xheight = layer->input_height; |
| const int Xwidth = layer->input_width ; |
| const int Xsize = Xwidth*Xheight; |
| |
| const int Yheight = layer->output_height; |
| const int Ywidth = layer->output_width; |
| const int Ysize = Ywidth*Yheight; |
| |
| int xx, yy, ni, kx, ky; |
| float* sumX_data = 0, *w = 0; |
| CvMat sumX_sub_col, exp2ssumWX_sub_col; |
| |
| CV_ASSERT(X->rows == nplanes*Xsize && X->cols == 1); |
| CV_ASSERT(layer->exp2ssumWX->cols == 1 && layer->exp2ssumWX->rows == nplanes*Ysize); |
| |
| // update inner variable layer->exp2ssumWX, which will be used in back-progation |
| cvZero( layer->sumX ); |
| cvZero( layer->exp2ssumWX ); |
| |
| for( ky = 0; ky < sub_sampl_scale; ky++ ) |
| for( kx = 0; kx < sub_sampl_scale; kx++ ) |
| { |
| float* Xplane = X->data.fl; |
| sumX_data = layer->sumX->data.fl; |
| for( ni = 0; ni < nplanes; ni++, Xplane += Xsize ) |
| { |
| for( yy = 0; yy < Yheight; yy++ ) |
| for( xx = 0; xx < Ywidth; xx++, sumX_data++ ) |
| *sumX_data += Xplane[((yy+ky)*Xwidth+(xx+kx))]; |
| } |
| } |
| |
| w = layer->weights->data.fl; |
| cvGetRows( layer->sumX, &sumX_sub_col, 0, Ysize ); |
| cvGetRows( layer->exp2ssumWX, &exp2ssumWX_sub_col, 0, Ysize ); |
| for( ni = 0; ni < nplanes; ni++, w += 2 ) |
| { |
| CV_CALL(cvConvertScale( &sumX_sub_col, &exp2ssumWX_sub_col, w[0], w[1] )); |
| sumX_sub_col.data.fl += Ysize; |
| exp2ssumWX_sub_col.data.fl += Ysize; |
| } |
| |
| CV_CALL(cvScale( layer->exp2ssumWX, layer->exp2ssumWX, 2.0*layer->s )); |
| CV_CALL(cvExp( layer->exp2ssumWX, layer->exp2ssumWX )); |
| CV_CALL(cvMinS( layer->exp2ssumWX, FLT_MAX, layer->exp2ssumWX )); |
| //#ifdef _DEBUG |
| { |
| float* exp2ssumWX_data = layer->exp2ssumWX->data.fl; |
| for( ni = 0; ni < layer->exp2ssumWX->rows; ni++, exp2ssumWX_data++ ) |
| { |
| if( *exp2ssumWX_data == FLT_MAX ) |
| cvSetErrStatus( 1 ); |
| } |
| } |
| //#endif |
| // compute the output variable Y == ( a - 2a/(layer->exp2ssumWX + 1)) |
| CV_CALL(cvAddS( layer->exp2ssumWX, cvRealScalar(1), Y )); |
| CV_CALL(cvDiv( 0, Y, Y, -2.0*layer->a )); |
| CV_CALL(cvAddS( Y, cvRealScalar(layer->a), Y )); |
| |
| }__END__; |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNFullConnectForward( CvCNNLayer* _layer, const CvMat* X, CvMat* Y ) |
| { |
| CV_FUNCNAME("icvCNNFullConnectForward"); |
| |
| if( !ICV_IS_CNN_FULLCONNECT_LAYER(_layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| {__BEGIN__; |
| |
| const CvCNNFullConnectLayer* layer = (CvCNNFullConnectLayer*)_layer; |
| CvMat* weights = layer->weights; |
| CvMat sub_weights, bias; |
| |
| CV_ASSERT(X->cols == 1 && X->rows == layer->n_input_planes); |
| CV_ASSERT(Y->cols == 1 && Y->rows == layer->n_output_planes); |
| |
| CV_CALL(cvGetSubRect( weights, &sub_weights, |
| cvRect(0, 0, weights->cols-1, weights->rows ))); |
| CV_CALL(cvGetCol( weights, &bias, weights->cols-1)); |
| |
| // update inner variable layer->exp2ssumWX, which will be used in Back-Propagation |
| CV_CALL(cvGEMM( &sub_weights, X, 2*layer->s, &bias, 2*layer->s, layer->exp2ssumWX )); |
| CV_CALL(cvExp( layer->exp2ssumWX, layer->exp2ssumWX )); |
| CV_CALL(cvMinS( layer->exp2ssumWX, FLT_MAX, layer->exp2ssumWX )); |
| //#ifdef _DEBUG |
| { |
| float* exp2ssumWX_data = layer->exp2ssumWX->data.fl; |
| int i; |
| for( i = 0; i < layer->exp2ssumWX->rows; i++, exp2ssumWX_data++ ) |
| { |
| if( *exp2ssumWX_data == FLT_MAX ) |
| cvSetErrStatus( 1 ); |
| } |
| } |
| //#endif |
| // compute the output variable Y == ( a - 2a/(layer->exp2ssumWX + 1)) |
| CV_CALL(cvAddS( layer->exp2ssumWX, cvRealScalar(1), Y )); |
| CV_CALL(cvDiv( 0, Y, Y, -2.0*layer->a )); |
| CV_CALL(cvAddS( Y, cvRealScalar(layer->a), Y )); |
| |
| }__END__; |
| } |
| |
| /****************************************************************************************\ |
| * Layer BACKWARD functions * |
| \****************************************************************************************/ |
| |
| /* <dE_dY>, <dE_dX> should be row-vectors. |
| Function computes partial derivatives <dE_dX> |
| of the loss function with respect to the planes components |
| of the previous layer (X). |
| It is a basic function for back propagation method. |
| Input parameter <dE_dY> is the partial derivative of the |
| loss function with respect to the planes components |
| of the current layer. */ |
| static void icvCNNConvolutionBackward( |
| CvCNNLayer* _layer, int t, const CvMat* X, const CvMat* dE_dY, CvMat* dE_dX ) |
| { |
| CvMat* dY_dX = 0; |
| CvMat* dY_dW = 0; |
| CvMat* dE_dW = 0; |
| |
| CV_FUNCNAME("icvCNNConvolutionBackward"); |
| |
| if( !ICV_IS_CNN_CONVOLUTION_LAYER(_layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| {__BEGIN__; |
| |
| const CvCNNConvolutionLayer* layer = (CvCNNConvolutionLayer*) _layer; |
| |
| const int K = layer->K; |
| |
| const int n_X_planes = layer->n_input_planes; |
| const int X_plane_height = layer->input_height; |
| const int X_plane_width = layer->input_width; |
| const int X_plane_size = X_plane_height*X_plane_width; |
| |
| const int n_Y_planes = layer->n_output_planes; |
| const int Y_plane_height = layer->output_height; |
| const int Y_plane_width = layer->output_width; |
| const int Y_plane_size = Y_plane_height*Y_plane_width; |
| |
| int no, ni, yy, xx, ky, kx; |
| int X_idx = 0, Y_idx = 0; |
| |
| float *X_plane = 0, *w = 0; |
| |
| CvMat* weights = layer->weights; |
| |
| CV_ASSERT( t >= 1 ); |
| CV_ASSERT( n_Y_planes == weights->rows ); |
| |
| dY_dX = cvCreateMat( n_Y_planes*Y_plane_size, X->rows, CV_32FC1 ); |
| dY_dW = cvCreateMat( dY_dX->rows, weights->cols*weights->rows, CV_32FC1 ); |
| dE_dW = cvCreateMat( 1, dY_dW->cols, CV_32FC1 ); |
| |
| cvZero( dY_dX ); |
| cvZero( dY_dW ); |
| |
| // compute gradient of the loss function with respect to X and W |
| for( no = 0; no < n_Y_planes; no++, Y_idx += Y_plane_size ) |
| { |
| w = weights->data.fl + no*(K*K+1); |
| X_idx = 0; |
| X_plane = X->data.fl; |
| for( ni = 0; ni < n_X_planes; ni++, X_plane += X_plane_size ) |
| { |
| if( layer->connect_mask->data.ptr[ni*n_Y_planes+no] ) |
| { |
| for( yy = 0; yy < X_plane_height - K + 1; yy++ ) |
| { |
| for( xx = 0; xx < X_plane_width - K + 1; xx++ ) |
| { |
| for( ky = 0; ky < K; ky++ ) |
| { |
| for( kx = 0; kx < K; kx++ ) |
| { |
| CV_MAT_ELEM(*dY_dX, float, Y_idx+yy*Y_plane_width+xx, |
| X_idx+(yy+ky)*X_plane_width+(xx+kx)) = w[ky*K+kx]; |
| |
| // dY_dWi, i=1,...,K*K |
| CV_MAT_ELEM(*dY_dW, float, Y_idx+yy*Y_plane_width+xx, |
| no*(K*K+1)+ky*K+kx) += |
| X_plane[(yy+ky)*X_plane_width+(xx+kx)]; |
| } |
| } |
| // dY_dW(K*K+1)==1 because W(K*K+1) is bias |
| CV_MAT_ELEM(*dY_dW, float, Y_idx+yy*Y_plane_width+xx, |
| no*(K*K+1)+K*K) += 1; |
| } |
| } |
| } |
| X_idx += X_plane_size; |
| } |
| } |
| |
| CV_CALL(cvMatMul( dE_dY, dY_dW, dE_dW )); |
| CV_CALL(cvMatMul( dE_dY, dY_dX, dE_dX )); |
| |
| // update weights |
| { |
| CvMat dE_dW_mat; |
| float eta; |
| if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_LOG_INV ) |
| eta = -layer->init_learn_rate/logf(1+(float)t); |
| else if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_SQRT_INV ) |
| eta = -layer->init_learn_rate/sqrtf((float)t); |
| else |
| eta = -layer->init_learn_rate/(float)t; |
| cvReshape( dE_dW, &dE_dW_mat, 0, weights->rows ); |
| cvScaleAdd( &dE_dW_mat, cvRealScalar(eta), weights, weights ); |
| } |
| |
| }__END__; |
| |
| cvReleaseMat( &dY_dX ); |
| cvReleaseMat( &dY_dW ); |
| cvReleaseMat( &dE_dW ); |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNSubSamplingBackward( |
| CvCNNLayer* _layer, int t, const CvMat*, const CvMat* dE_dY, CvMat* dE_dX ) |
| { |
| // derivative of activation function |
| CvMat* dY_dX_elems = 0; // elements of matrix dY_dX |
| CvMat* dY_dW_elems = 0; // elements of matrix dY_dW |
| CvMat* dE_dW = 0; |
| |
| CV_FUNCNAME("icvCNNSubSamplingBackward"); |
| |
| if( !ICV_IS_CNN_SUBSAMPLING_LAYER(_layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| {__BEGIN__; |
| |
| const CvCNNSubSamplingLayer* layer = (CvCNNSubSamplingLayer*) _layer; |
| |
| const int Xwidth = layer->input_width; |
| const int Ywidth = layer->output_width; |
| const int Yheight = layer->output_height; |
| const int Ysize = Ywidth * Yheight; |
| const int scale = layer->sub_samp_scale; |
| const int k_max = layer->n_output_planes * Yheight; |
| |
| int k, i, j, m; |
| float* dY_dX_current_elem = 0, *dE_dX_start = 0, *dE_dW_data = 0, *w = 0; |
| CvMat dy_dw0, dy_dw1; |
| CvMat activ_func_der, sumX_row; |
| CvMat dE_dY_sub_row, dY_dX_sub_col, dy_dw0_sub_row, dy_dw1_sub_row; |
| |
| CV_CALL(dY_dX_elems = cvCreateMat( layer->sumX->rows, 1, CV_32FC1 )); |
| CV_CALL(dY_dW_elems = cvCreateMat( 2, layer->sumX->rows, CV_32FC1 )); |
| CV_CALL(dE_dW = cvCreateMat( 1, 2*layer->n_output_planes, CV_32FC1 )); |
| |
| // compute derivative of activ.func. |
| // ==<dY_dX_elems> = 4as*(layer->exp2ssumWX)/(layer->exp2ssumWX + 1)^2 |
| CV_CALL(cvAddS( layer->exp2ssumWX, cvRealScalar(1), dY_dX_elems )); |
| CV_CALL(cvPow( dY_dX_elems, dY_dX_elems, -2.0 )); |
| CV_CALL(cvMul( dY_dX_elems, layer->exp2ssumWX, dY_dX_elems, 4.0*layer->a*layer->s )); |
| |
| // compute <dE_dW> |
| // a) compute <dY_dW_elems> |
| cvReshape( dY_dX_elems, &activ_func_der, 0, 1 ); |
| cvGetRow( dY_dW_elems, &dy_dw0, 0 ); |
| cvGetRow( dY_dW_elems, &dy_dw1, 1 ); |
| CV_CALL(cvCopy( &activ_func_der, &dy_dw0 )); |
| CV_CALL(cvCopy( &activ_func_der, &dy_dw1 )); |
| |
| cvReshape( layer->sumX, &sumX_row, 0, 1 ); |
| cvMul( &dy_dw0, &sumX_row, &dy_dw0 ); |
| |
| // b) compute <dE_dW> = <dE_dY>*<dY_dW_elems> |
| cvGetCols( dE_dY, &dE_dY_sub_row, 0, Ysize ); |
| cvGetCols( &dy_dw0, &dy_dw0_sub_row, 0, Ysize ); |
| cvGetCols( &dy_dw1, &dy_dw1_sub_row, 0, Ysize ); |
| dE_dW_data = dE_dW->data.fl; |
| for( i = 0; i < layer->n_output_planes; i++ ) |
| { |
| *dE_dW_data++ = (float)cvDotProduct( &dE_dY_sub_row, &dy_dw0_sub_row ); |
| *dE_dW_data++ = (float)cvDotProduct( &dE_dY_sub_row, &dy_dw1_sub_row ); |
| |
| dE_dY_sub_row.data.fl += Ysize; |
| dy_dw0_sub_row.data.fl += Ysize; |
| dy_dw1_sub_row.data.fl += Ysize; |
| } |
| |
| // compute <dY_dX> = layer->weights*<dY_dX> |
| w = layer->weights->data.fl; |
| cvGetRows( dY_dX_elems, &dY_dX_sub_col, 0, Ysize ); |
| for( i = 0; i < layer->n_input_planes; i++, w++, dY_dX_sub_col.data.fl += Ysize ) |
| CV_CALL(cvConvertScale( &dY_dX_sub_col, &dY_dX_sub_col, (float)*w )); |
| |
| // compute <dE_dX> |
| CV_CALL(cvReshape( dY_dX_elems, dY_dX_elems, 0, 1 )); |
| CV_CALL(cvMul( dY_dX_elems, dE_dY, dY_dX_elems )); |
| |
| dY_dX_current_elem = dY_dX_elems->data.fl; |
| dE_dX_start = dE_dX->data.fl; |
| for( k = 0; k < k_max; k++ ) |
| { |
| for( i = 0; i < Ywidth; i++, dY_dX_current_elem++ ) |
| { |
| float* dE_dX_current_elem = dE_dX_start; |
| for( j = 0; j < scale; j++, dE_dX_current_elem += Xwidth - scale ) |
| { |
| for( m = 0; m < scale; m++, dE_dX_current_elem++ ) |
| *dE_dX_current_elem = *dY_dX_current_elem; |
| } |
| dE_dX_start += scale; |
| } |
| dE_dX_start += Xwidth * (scale - 1); |
| } |
| |
| // update weights |
| { |
| CvMat dE_dW_mat, *weights = layer->weights; |
| float eta; |
| if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_LOG_INV ) |
| eta = -layer->init_learn_rate/logf(1+(float)t); |
| else if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_SQRT_INV ) |
| eta = -layer->init_learn_rate/sqrtf((float)t); |
| else |
| eta = -layer->init_learn_rate/(float)t; |
| cvReshape( dE_dW, &dE_dW_mat, 0, weights->rows ); |
| cvScaleAdd( &dE_dW_mat, cvRealScalar(eta), weights, weights ); |
| } |
| |
| }__END__; |
| |
| cvReleaseMat( &dY_dX_elems ); |
| cvReleaseMat( &dY_dW_elems ); |
| cvReleaseMat( &dE_dW ); |
| } |
| |
| /****************************************************************************************/ |
| /* <dE_dY>, <dE_dX> should be row-vectors. |
| Function computes partial derivatives <dE_dX>, <dE_dW> |
| of the loss function with respect to the planes components |
| of the previous layer (X) and the weights of the current layer (W) |
| and updates weights od the current layer by using <dE_dW>. |
| It is a basic function for back propagation method. |
| Input parameter <dE_dY> is the partial derivative of the |
| loss function with respect to the planes components |
| of the current layer. */ |
| static void icvCNNFullConnectBackward( CvCNNLayer* _layer, |
| int t, |
| const CvMat* X, |
| const CvMat* dE_dY, |
| CvMat* dE_dX ) |
| { |
| CvMat* dE_dY_activ_func_der = 0; |
| CvMat* dE_dW = 0; |
| |
| CV_FUNCNAME( "icvCNNFullConnectBackward" ); |
| |
| if( !ICV_IS_CNN_FULLCONNECT_LAYER(_layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| {__BEGIN__; |
| |
| const CvCNNFullConnectLayer* layer = (CvCNNFullConnectLayer*)_layer; |
| const int n_outputs = layer->n_output_planes; |
| const int n_inputs = layer->n_input_planes; |
| |
| int i; |
| float* dE_dY_activ_func_der_data; |
| CvMat* weights = layer->weights; |
| CvMat sub_weights, Xtemplate, Xrow, exp2ssumWXrow; |
| |
| CV_ASSERT(X->cols == 1 && X->rows == n_inputs); |
| CV_ASSERT(dE_dY->rows == 1 && dE_dY->cols == n_outputs ); |
| CV_ASSERT(dE_dX->rows == 1 && dE_dX->cols == n_inputs ); |
| |
| // we violate the convetion about vector's orientation because |
| // here is more convenient to make this parameter a row-vector |
| CV_CALL(dE_dY_activ_func_der = cvCreateMat( 1, n_outputs, CV_32FC1 )); |
| CV_CALL(dE_dW = cvCreateMat( 1, weights->rows*weights->cols, CV_32FC1 )); |
| |
| // 1) compute gradients dE_dX and dE_dW |
| // activ_func_der == 4as*(layer->exp2ssumWX)/(layer->exp2ssumWX + 1)^2 |
| CV_CALL(cvReshape( layer->exp2ssumWX, &exp2ssumWXrow, 0, layer->exp2ssumWX->cols )); |
| CV_CALL(cvAddS( &exp2ssumWXrow, cvRealScalar(1), dE_dY_activ_func_der )); |
| CV_CALL(cvPow( dE_dY_activ_func_der, dE_dY_activ_func_der, -2.0 )); |
| CV_CALL(cvMul( dE_dY_activ_func_der, &exp2ssumWXrow, dE_dY_activ_func_der, |
| 4.0*layer->a*layer->s )); |
| CV_CALL(cvMul( dE_dY, dE_dY_activ_func_der, dE_dY_activ_func_der )); |
| |
| // sub_weights = d(W*(X|1))/dX |
| CV_CALL(cvGetSubRect( weights, &sub_weights, |
| cvRect(0, 0, weights->cols-1, weights->rows) )); |
| CV_CALL(cvMatMul( dE_dY_activ_func_der, &sub_weights, dE_dX )); |
| |
| cvReshape( X, &Xrow, 0, 1 ); |
| dE_dY_activ_func_der_data = dE_dY_activ_func_der->data.fl; |
| Xtemplate = cvMat( 1, n_inputs, CV_32FC1, dE_dW->data.fl ); |
| for( i = 0; i < n_outputs; i++, Xtemplate.data.fl += n_inputs + 1 ) |
| { |
| CV_CALL(cvConvertScale( &Xrow, &Xtemplate, *dE_dY_activ_func_der_data )); |
| Xtemplate.data.fl[n_inputs] = *dE_dY_activ_func_der_data++; |
| } |
| |
| // 2) update weights |
| { |
| CvMat dE_dW_mat; |
| float eta; |
| if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_LOG_INV ) |
| eta = -layer->init_learn_rate/logf(1+(float)t); |
| else if( layer->learn_rate_decrease_type == CV_CNN_LEARN_RATE_DECREASE_SQRT_INV ) |
| eta = -layer->init_learn_rate/sqrtf((float)t); |
| else |
| eta = -layer->init_learn_rate/(float)t; |
| cvReshape( dE_dW, &dE_dW_mat, 0, n_outputs ); |
| cvScaleAdd( &dE_dW_mat, cvRealScalar(eta), weights, weights ); |
| } |
| |
| }__END__; |
| |
| cvReleaseMat( &dE_dY_activ_func_der ); |
| cvReleaseMat( &dE_dW ); |
| } |
| |
| /****************************************************************************************\ |
| * Layer RELEASE functions * |
| \****************************************************************************************/ |
| static void icvCNNConvolutionRelease( CvCNNLayer** p_layer ) |
| { |
| CV_FUNCNAME("icvCNNConvolutionRelease"); |
| __BEGIN__; |
| |
| CvCNNConvolutionLayer* layer = 0; |
| |
| if( !p_layer ) |
| CV_ERROR( CV_StsNullPtr, "Null double pointer" ); |
| |
| layer = *(CvCNNConvolutionLayer**)p_layer; |
| |
| if( !layer ) |
| return; |
| if( !ICV_IS_CNN_CONVOLUTION_LAYER(layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| cvReleaseMat( &layer->weights ); |
| cvReleaseMat( &layer->connect_mask ); |
| cvFree( p_layer ); |
| |
| __END__; |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNSubSamplingRelease( CvCNNLayer** p_layer ) |
| { |
| CV_FUNCNAME("icvCNNSubSamplingRelease"); |
| __BEGIN__; |
| |
| CvCNNSubSamplingLayer* layer = 0; |
| |
| if( !p_layer ) |
| CV_ERROR( CV_StsNullPtr, "Null double pointer" ); |
| |
| layer = *(CvCNNSubSamplingLayer**)p_layer; |
| |
| if( !layer ) |
| return; |
| if( !ICV_IS_CNN_SUBSAMPLING_LAYER(layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| cvReleaseMat( &layer->exp2ssumWX ); |
| cvReleaseMat( &layer->weights ); |
| cvFree( p_layer ); |
| |
| __END__; |
| } |
| |
| /****************************************************************************************/ |
| static void icvCNNFullConnectRelease( CvCNNLayer** p_layer ) |
| { |
| CV_FUNCNAME("icvCNNFullConnectRelease"); |
| __BEGIN__; |
| |
| CvCNNFullConnectLayer* layer = 0; |
| |
| if( !p_layer ) |
| CV_ERROR( CV_StsNullPtr, "Null double pointer" ); |
| |
| layer = *(CvCNNFullConnectLayer**)p_layer; |
| |
| if( !layer ) |
| return; |
| if( !ICV_IS_CNN_FULLCONNECT_LAYER(layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| cvReleaseMat( &layer->exp2ssumWX ); |
| cvReleaseMat( &layer->weights ); |
| cvFree( p_layer ); |
| |
| __END__; |
| } |
| |
| /****************************************************************************************\ |
| * Read/Write CNN classifier * |
| \****************************************************************************************/ |
| static int icvIsCNNModel( const void* ptr ) |
| { |
| return CV_IS_CNN(ptr); |
| } |
| |
| /****************************************************************************************/ |
| static void icvReleaseCNNModel( void** ptr ) |
| { |
| CV_FUNCNAME("icvReleaseCNNModel"); |
| __BEGIN__; |
| |
| if( !ptr ) |
| CV_ERROR( CV_StsNullPtr, "NULL double pointer" ); |
| CV_ASSERT(CV_IS_CNN(*ptr)); |
| |
| icvCNNModelRelease( (CvStatModel**)ptr ); |
| |
| __END__; |
| } |
| |
| /****************************************************************************************/ |
| static CvCNNLayer* icvReadCNNLayer( CvFileStorage* fs, CvFileNode* node ) |
| { |
| CvCNNLayer* layer = 0; |
| CvMat* weights = 0; |
| CvMat* connect_mask = 0; |
| |
| CV_FUNCNAME("icvReadCNNLayer"); |
| __BEGIN__; |
| |
| int n_input_planes, input_height, input_width; |
| int n_output_planes, output_height, output_width; |
| int learn_type, layer_type; |
| float init_learn_rate; |
| |
| CV_CALL(n_input_planes = cvReadIntByName( fs, node, "n_input_planes", -1 )); |
| CV_CALL(input_height = cvReadIntByName( fs, node, "input_height", -1 )); |
| CV_CALL(input_width = cvReadIntByName( fs, node, "input_width", -1 )); |
| CV_CALL(n_output_planes = cvReadIntByName( fs, node, "n_output_planes", -1 )); |
| CV_CALL(output_height = cvReadIntByName( fs, node, "output_height", -1 )); |
| CV_CALL(output_width = cvReadIntByName( fs, node, "output_width", -1 )); |
| CV_CALL(layer_type = cvReadIntByName( fs, node, "layer_type", -1 )); |
| |
| CV_CALL(init_learn_rate = (float)cvReadRealByName( fs, node, "init_learn_rate", -1 )); |
| CV_CALL(learn_type = cvReadIntByName( fs, node, "learn_rate_decrease_type", -1 )); |
| CV_CALL(weights = (CvMat*)cvReadByName( fs, node, "weights" )); |
| |
| if( n_input_planes < 0 || input_height < 0 || input_width < 0 || |
| n_output_planes < 0 || output_height < 0 || output_width < 0 || |
| init_learn_rate < 0 || learn_type < 0 || layer_type < 0 || !weights ) |
| CV_ERROR( CV_StsParseError, "" ); |
| |
| if( layer_type == ICV_CNN_CONVOLUTION_LAYER ) |
| { |
| const int K = input_height - output_height + 1; |
| if( K <= 0 || K != input_width - output_width + 1 ) |
| CV_ERROR( CV_StsBadArg, "Invalid <K>" ); |
| |
| CV_CALL(connect_mask = (CvMat*)cvReadByName( fs, node, "connect_mask" )); |
| if( !connect_mask ) |
| CV_ERROR( CV_StsParseError, "Missing <connect mask>" ); |
| |
| CV_CALL(layer = cvCreateCNNConvolutionLayer( |
| n_input_planes, input_height, input_width, n_output_planes, K, |
| init_learn_rate, learn_type, connect_mask, weights )); |
| } |
| else if( layer_type == ICV_CNN_SUBSAMPLING_LAYER ) |
| { |
| float a, s; |
| const int sub_samp_scale = input_height/output_height; |
| |
| if( sub_samp_scale <= 0 || sub_samp_scale != input_width/output_width ) |
| CV_ERROR( CV_StsBadArg, "Invalid <sub_samp_scale>" ); |
| |
| CV_CALL(a = (float)cvReadRealByName( fs, node, "a", -1 )); |
| CV_CALL(s = (float)cvReadRealByName( fs, node, "s", -1 )); |
| if( a < 0 || s < 0 ) |
| CV_ERROR( CV_StsParseError, "Missing <a> or <s>" ); |
| |
| CV_CALL(layer = cvCreateCNNSubSamplingLayer( |
| n_input_planes, input_height, input_width, sub_samp_scale, |
| a, s, init_learn_rate, learn_type, weights )); |
| } |
| else if( layer_type == ICV_CNN_FULLCONNECT_LAYER ) |
| { |
| float a, s; |
| CV_CALL(a = (float)cvReadRealByName( fs, node, "a", -1 )); |
| CV_CALL(s = (float)cvReadRealByName( fs, node, "s", -1 )); |
| if( a < 0 || s < 0 ) |
| CV_ERROR( CV_StsParseError, "" ); |
| if( input_height != 1 || input_width != 1 || |
| output_height != 1 || output_width != 1 ) |
| CV_ERROR( CV_StsBadArg, "" ); |
| |
| CV_CALL(layer = cvCreateCNNFullConnectLayer( n_input_planes, n_output_planes, |
| a, s, init_learn_rate, learn_type, weights )); |
| } |
| else |
| CV_ERROR( CV_StsBadArg, "Invalid <layer_type>" ); |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 && layer ) |
| layer->release( &layer ); |
| |
| cvReleaseMat( &weights ); |
| cvReleaseMat( &connect_mask ); |
| |
| return layer; |
| } |
| |
| /****************************************************************************************/ |
| static void icvWriteCNNLayer( CvFileStorage* fs, CvCNNLayer* layer ) |
| { |
| CV_FUNCNAME ("icvWriteCNNLayer"); |
| __BEGIN__; |
| |
| if( !ICV_IS_CNN_LAYER(layer) ) |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| CV_CALL( cvStartWriteStruct( fs, NULL, CV_NODE_MAP, "opencv-ml-cnn-layer" )); |
| |
| CV_CALL(cvWriteInt( fs, "n_input_planes", layer->n_input_planes )); |
| CV_CALL(cvWriteInt( fs, "input_height", layer->input_height )); |
| CV_CALL(cvWriteInt( fs, "input_width", layer->input_width )); |
| CV_CALL(cvWriteInt( fs, "n_output_planes", layer->n_output_planes )); |
| CV_CALL(cvWriteInt( fs, "output_height", layer->output_height )); |
| CV_CALL(cvWriteInt( fs, "output_width", layer->output_width )); |
| CV_CALL(cvWriteInt( fs, "learn_rate_decrease_type", layer->learn_rate_decrease_type)); |
| CV_CALL(cvWriteReal( fs, "init_learn_rate", layer->init_learn_rate )); |
| CV_CALL(cvWrite( fs, "weights", layer->weights )); |
| |
| if( ICV_IS_CNN_CONVOLUTION_LAYER( layer )) |
| { |
| CvCNNConvolutionLayer* l = (CvCNNConvolutionLayer*)layer; |
| CV_CALL(cvWriteInt( fs, "layer_type", ICV_CNN_CONVOLUTION_LAYER )); |
| CV_CALL(cvWrite( fs, "connect_mask", l->connect_mask )); |
| } |
| else if( ICV_IS_CNN_SUBSAMPLING_LAYER( layer ) ) |
| { |
| CvCNNSubSamplingLayer* l = (CvCNNSubSamplingLayer*)layer; |
| CV_CALL(cvWriteInt( fs, "layer_type", ICV_CNN_SUBSAMPLING_LAYER )); |
| CV_CALL(cvWriteReal( fs, "a", l->a )); |
| CV_CALL(cvWriteReal( fs, "s", l->s )); |
| } |
| else if( ICV_IS_CNN_FULLCONNECT_LAYER( layer ) ) |
| { |
| CvCNNFullConnectLayer* l = (CvCNNFullConnectLayer*)layer; |
| CV_CALL(cvWriteInt( fs, "layer_type", ICV_CNN_FULLCONNECT_LAYER )); |
| CV_CALL(cvWriteReal( fs, "a", l->a )); |
| CV_CALL(cvWriteReal( fs, "s", l->s )); |
| } |
| else |
| CV_ERROR( CV_StsBadArg, "Invalid layer" ); |
| |
| CV_CALL( cvEndWriteStruct( fs )); //"opencv-ml-cnn-layer" |
| |
| __END__; |
| } |
| |
| /****************************************************************************************/ |
| static void* icvReadCNNModel( CvFileStorage* fs, CvFileNode* root_node ) |
| { |
| CvCNNStatModel* cnn = 0; |
| CvCNNLayer* layer = 0; |
| |
| CV_FUNCNAME("icvReadCNNModel"); |
| __BEGIN__; |
| |
| CvFileNode* node; |
| CvSeq* seq; |
| CvSeqReader reader; |
| int i; |
| |
| CV_CALL(cnn = (CvCNNStatModel*)cvCreateStatModel( |
| CV_STAT_MODEL_MAGIC_VAL|CV_CNN_MAGIC_VAL, sizeof(CvCNNStatModel), |
| icvCNNModelRelease, icvCNNModelPredict, icvCNNModelUpdate )); |
| |
| CV_CALL(cnn->etalons = (CvMat*)cvReadByName( fs, root_node, "etalons" )); |
| CV_CALL(cnn->cls_labels = (CvMat*)cvReadByName( fs, root_node, "cls_labels" )); |
| |
| if( !cnn->etalons || !cnn->cls_labels ) |
| CV_ERROR( CV_StsParseError, "No <etalons> or <cls_labels> in CNN model" ); |
| |
| CV_CALL( node = cvGetFileNodeByName( fs, root_node, "network" )); |
| seq = node->data.seq; |
| if( !CV_NODE_IS_SEQ(node->tag) ) |
| CV_ERROR( CV_StsBadArg, "" ); |
| |
| CV_CALL( cvStartReadSeq( seq, &reader, 0 )); |
| CV_CALL(layer = icvReadCNNLayer( fs, (CvFileNode*)reader.ptr )); |
| CV_CALL(cnn->network = cvCreateCNNetwork( layer )); |
| |
| for( i = 1; i < seq->total; i++ ) |
| { |
| CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); |
| CV_CALL(layer = icvReadCNNLayer( fs, (CvFileNode*)reader.ptr )); |
| CV_CALL(cnn->network->add_layer( cnn->network, layer )); |
| } |
| |
| __END__; |
| |
| if( cvGetErrStatus() < 0 ) |
| { |
| if( cnn ) cnn->release( (CvStatModel**)&cnn ); |
| if( layer ) layer->release( &layer ); |
| } |
| return (void*)cnn; |
| } |
| |
| /****************************************************************************************/ |
| static void |
| icvWriteCNNModel( CvFileStorage* fs, const char* name, |
| const void* struct_ptr, CvAttrList ) |
| |
| { |
| CV_FUNCNAME ("icvWriteCNNModel"); |
| __BEGIN__; |
| |
| CvCNNStatModel* cnn = (CvCNNStatModel*)struct_ptr; |
| int n_layers, i; |
| CvCNNLayer* layer; |
| |
| if( !CV_IS_CNN(cnn) ) |
| CV_ERROR( CV_StsBadArg, "Invalid pointer" ); |
| |
| n_layers = cnn->network->n_layers; |
| |
| CV_CALL( cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_ML_CNN )); |
| |
| CV_CALL(cvWrite( fs, "etalons", cnn->etalons )); |
| CV_CALL(cvWrite( fs, "cls_labels", cnn->cls_labels )); |
| |
| CV_CALL( cvStartWriteStruct( fs, "network", CV_NODE_SEQ )); |
| |
| layer = cnn->network->layers; |
| for( i = 0; i < n_layers && layer; i++, layer = layer->next_layer ) |
| CV_CALL(icvWriteCNNLayer( fs, layer )); |
| if( i < n_layers || layer ) |
| CV_ERROR( CV_StsBadArg, "Invalid network" ); |
| |
| CV_CALL( cvEndWriteStruct( fs )); //"network" |
| CV_CALL( cvEndWriteStruct( fs )); //"opencv-ml-cnn" |
| |
| __END__; |
| } |
| |
| static int icvRegisterCNNStatModelType() |
| { |
| CvTypeInfo info; |
| |
| info.header_size = sizeof( info ); |
| info.is_instance = icvIsCNNModel; |
| info.release = icvReleaseCNNModel; |
| info.read = icvReadCNNModel; |
| info.write = icvWriteCNNModel; |
| info.clone = NULL; |
| info.type_name = CV_TYPE_NAME_ML_CNN; |
| cvRegisterType( &info ); |
| |
| return 1; |
| } // End of icvRegisterCNNStatModelType |
| |
| static int cnn = icvRegisterCNNStatModelType(); |
| |
| #endif |
| |
| // End of file |