1676 lines
62 KiB
C++
1676 lines
62 KiB
C++
/*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 "precomp.hpp"
|
|
|
|
#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
|