/*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
// For Open Source Computer Vision Library
//
// 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*/
/****************************************************************************************\
Calculation of a texture descriptors from GLCM (Grey Level Co-occurrence Matrix'es)
The code was submitted by Daniel Eaton [danieljameseaton@yahoo.com]
\****************************************************************************************/
#include "_cvaux.h"
#include <math.h>
#include <assert.h>
#define CV_MAX_NUM_GREY_LEVELS_8U 256
struct CvGLCM
{
int matrixSideLength;
int numMatrices;
double*** matrices;
int numLookupTableElements;
int forwardLookupTable[CV_MAX_NUM_GREY_LEVELS_8U];
int reverseLookupTable[CV_MAX_NUM_GREY_LEVELS_8U];
double** descriptors;
int numDescriptors;
int descriptorOptimizationType;
int optimizationType;
};
static void icvCreateGLCM_LookupTable_8u_C1R( const uchar* srcImageData, int srcImageStep,
CvSize srcImageSize, CvGLCM* destGLCM,
int* steps, int numSteps, int* memorySteps );
static void
icvCreateGLCMDescriptors_AllowDoubleNest( CvGLCM* destGLCM, int matrixIndex );
CV_IMPL CvGLCM*
cvCreateGLCM( const IplImage* srcImage,
int stepMagnitude,
const int* srcStepDirections,/* should be static array..
or if not the user should handle de-allocation */
int numStepDirections,
int optimizationType )
{
static const int defaultStepDirections[] = { 0,1, -1,1, -1,0, -1,-1 };
int* memorySteps = 0;
CvGLCM* newGLCM = 0;
int* stepDirections = 0;
CV_FUNCNAME( "cvCreateGLCM" );
__BEGIN__;
uchar* srcImageData = 0;
CvSize srcImageSize;
int srcImageStep;
int stepLoop;
const int maxNumGreyLevels8u = CV_MAX_NUM_GREY_LEVELS_8U;
if( !srcImage )
CV_ERROR( CV_StsNullPtr, "" );
if( srcImage->nChannels != 1 )
CV_ERROR( CV_BadNumChannels, "Number of channels must be 1");
if( srcImage->depth != IPL_DEPTH_8U )
CV_ERROR( CV_BadDepth, "Depth must be equal IPL_DEPTH_8U");
// no Directions provided, use the default ones - 0 deg, 45, 90, 135
if( !srcStepDirections )
{
srcStepDirections = defaultStepDirections;
}
CV_CALL( stepDirections = (int*)cvAlloc( numStepDirections*2*sizeof(stepDirections[0])));
memcpy( stepDirections, srcStepDirections, numStepDirections*2*sizeof(stepDirections[0]));
cvGetImageRawData( srcImage, &srcImageData, &srcImageStep, &srcImageSize );
// roll together Directions and magnitudes together with knowledge of image (step)
CV_CALL( memorySteps = (int*)cvAlloc( numStepDirections*sizeof(memorySteps[0])));
for( stepLoop = 0; stepLoop < numStepDirections; stepLoop++ )
{
stepDirections[stepLoop*2 + 0] *= stepMagnitude;
stepDirections[stepLoop*2 + 1] *= stepMagnitude;
memorySteps[stepLoop] = stepDirections[stepLoop*2 + 0]*srcImageStep +
stepDirections[stepLoop*2 + 1];
}
CV_CALL( newGLCM = (CvGLCM*)cvAlloc(sizeof(newGLCM)));
memset( newGLCM, 0, sizeof(newGLCM) );
newGLCM->matrices = 0;
newGLCM->numMatrices = numStepDirections;
newGLCM->optimizationType = optimizationType;
if( optimizationType <= CV_GLCM_OPTIMIZATION_LUT )
{
int lookupTableLoop, imageColLoop, imageRowLoop, lineOffset = 0;
// if optimization type is set to lut, then make one for the image
if( optimizationType == CV_GLCM_OPTIMIZATION_LUT )
{
for( imageRowLoop = 0; imageRowLoop < srcImageSize.height;
imageRowLoop++, lineOffset += srcImageStep )
{
for( imageColLoop = 0; imageColLoop < srcImageSize.width; imageColLoop++ )
{
newGLCM->forwardLookupTable[srcImageData[lineOffset+imageColLoop]]=1;
}
}
newGLCM->numLookupTableElements = 0;
for( lookupTableLoop = 0; lookupTableLoop < maxNumGreyLevels8u; lookupTableLoop++ )
{
if( newGLCM->forwardLookupTable[ lookupTableLoop ] != 0 )
{
newGLCM->forwardLookupTable[ lookupTableLoop ] =
newGLCM->numLookupTableElements;
newGLCM->reverseLookupTable[ newGLCM->numLookupTableElements ] =
lookupTableLoop;
newGLCM->numLookupTableElements++;
}
}
}
// otherwise make a "LUT" which contains all the gray-levels (for code-reuse)
else if( optimizationType == CV_GLCM_OPTIMIZATION_NONE )
{
for( lookupTableLoop = 0; lookupTableLoop <maxNumGreyLevels8u; lookupTableLoop++ )
{
newGLCM->forwardLookupTable[ lookupTableLoop ] = lookupTableLoop;
newGLCM->reverseLookupTable[ lookupTableLoop ] = lookupTableLoop;
}
newGLCM->numLookupTableElements = maxNumGreyLevels8u;
}
newGLCM->matrixSideLength = newGLCM->numLookupTableElements;
icvCreateGLCM_LookupTable_8u_C1R( srcImageData, srcImageStep, srcImageSize,
newGLCM, stepDirections,
numStepDirections, memorySteps );
}
else if( optimizationType == CV_GLCM_OPTIMIZATION_HISTOGRAM )
{
CV_ERROR( CV_StsBadFlag, "Histogram-based method is not implemented" );
/* newGLCM->numMatrices *= 2;
newGLCM->matrixSideLength = maxNumGreyLevels8u*2;
icvCreateGLCM_Histogram_8uC1R( srcImageStep, srcImageSize, srcImageData,
newGLCM, numStepDirections,
stepDirections, memorySteps );
*/
}
__END__;
cvFree( &memorySteps );
cvFree( &stepDirections );
if( cvGetErrStatus() < 0 )
{
cvFree( &newGLCM );
}
return newGLCM;
}
CV_IMPL void
cvReleaseGLCM( CvGLCM** GLCM, int flag )
{
CV_FUNCNAME( "cvReleaseGLCM" );
__BEGIN__;
int matrixLoop;
if( !GLCM )
CV_ERROR( CV_StsNullPtr, "" );
if( *GLCM )
EXIT; // repeated deallocation: just skip it.
if( (flag == CV_GLCM_GLCM || flag == CV_GLCM_ALL) && (*GLCM)->matrices )
{
for( matrixLoop = 0; matrixLoop < (*GLCM)->numMatrices; matrixLoop++ )
{
if( (*GLCM)->matrices[ matrixLoop ] )
{
cvFree( (*GLCM)->matrices[matrixLoop] );
cvFree( (*GLCM)->matrices + matrixLoop );
}
}
cvFree( &((*GLCM)->matrices) );
}
if( (flag == CV_GLCM_DESC || flag == CV_GLCM_ALL) && (*GLCM)->descriptors )
{
for( matrixLoop = 0; matrixLoop < (*GLCM)->numMatrices; matrixLoop++ )
{
cvFree( (*GLCM)->descriptors + matrixLoop );
}
cvFree( &((*GLCM)->descriptors) );
}
if( flag == CV_GLCM_ALL )
{
cvFree( GLCM );
}
__END__;
}
static void
icvCreateGLCM_LookupTable_8u_C1R( const uchar* srcImageData,
int srcImageStep,
CvSize srcImageSize,
CvGLCM* destGLCM,
int* steps,
int numSteps,
int* memorySteps )
{
int* stepIncrementsCounter = 0;
CV_FUNCNAME( "icvCreateGLCM_LookupTable_8u_C1R" );
__BEGIN__;
int matrixSideLength = destGLCM->matrixSideLength;
int stepLoop, sideLoop1, sideLoop2;
int colLoop, rowLoop, lineOffset = 0;
double*** matrices = 0;
// allocate memory to the matrices
CV_CALL( destGLCM->matrices = (double***)cvAlloc( sizeof(matrices[0])*numSteps ));
matrices = destGLCM->matrices;
for( stepLoop=0; stepLoop<numSteps; stepLoop++ )
{
CV_CALL( matrices[stepLoop] = (double**)cvAlloc( sizeof(matrices[0])*matrixSideLength ));
CV_CALL( matrices[stepLoop][0] = (double*)cvAlloc( sizeof(matrices[0][0])*
matrixSideLength*matrixSideLength ));
memset( matrices[stepLoop][0], 0, matrixSideLength*matrixSideLength*
sizeof(matrices[0][0]) );
for( sideLoop1 = 1; sideLoop1 < matrixSideLength; sideLoop1++ )
{
matrices[stepLoop][sideLoop1] = matrices[stepLoop][sideLoop1-1] + matrixSideLength;
}
}
CV_CALL( stepIncrementsCounter = (int*)cvAlloc( numSteps*sizeof(stepIncrementsCounter[0])));
memset( stepIncrementsCounter, 0, numSteps*sizeof(stepIncrementsCounter[0]) );
// generate GLCM for each step
for( rowLoop=0; rowLoop<srcImageSize.height; rowLoop++, lineOffset+=srcImageStep )
{
for( colLoop=0; colLoop<srcImageSize.width; colLoop++ )
{
int pixelValue1 = destGLCM->forwardLookupTable[srcImageData[lineOffset + colLoop]];
for( stepLoop=0; stepLoop<numSteps; stepLoop++ )
{
int col2, row2;
row2 = rowLoop + steps[stepLoop*2 + 0];
col2 = colLoop + steps[stepLoop*2 + 1];
if( col2>=0 && row2>=0 && col2<srcImageSize.width && row2<srcImageSize.height )
{
int memoryStep = memorySteps[ stepLoop ];
int pixelValue2 = destGLCM->forwardLookupTable[ srcImageData[ lineOffset + colLoop + memoryStep ] ];
// maintain symmetry
matrices[stepLoop][pixelValue1][pixelValue2] ++;
matrices[stepLoop][pixelValue2][pixelValue1] ++;
// incremenet counter of total number of increments
stepIncrementsCounter[stepLoop] += 2;
}
}
}
}
// normalize matrices. each element is a probability of gray value i,j adjacency in direction/magnitude k
for( sideLoop1=0; sideLoop1<matrixSideLength; sideLoop1++ )
{
for( sideLoop2=0; sideLoop2<matrixSideLength; sideLoop2++ )
{
for( stepLoop=0; stepLoop<numSteps; stepLoop++ )
{
matrices[stepLoop][sideLoop1][sideLoop2] /= double(stepIncrementsCounter[stepLoop]);
}
}
}
destGLCM->matrices = matrices;
__END__;
cvFree( &stepIncrementsCounter );
if( cvGetErrStatus() < 0 )
cvReleaseGLCM( &destGLCM, CV_GLCM_GLCM );
}
CV_IMPL void
cvCreateGLCMDescriptors( CvGLCM* destGLCM, int descriptorOptimizationType )
{
CV_FUNCNAME( "cvCreateGLCMDescriptors" );
__BEGIN__;
int matrixLoop;
if( !destGLCM )
CV_ERROR( CV_StsNullPtr, "" );
if( !(destGLCM->matrices) )
CV_ERROR( CV_StsNullPtr, "Matrices are not allocated" );
CV_CALL( cvReleaseGLCM( &destGLCM, CV_GLCM_DESC ));
if( destGLCM->optimizationType != CV_GLCM_OPTIMIZATION_HISTOGRAM )
{
destGLCM->descriptorOptimizationType = destGLCM->numDescriptors = descriptorOptimizationType;
}
else
{
CV_ERROR( CV_StsBadFlag, "Histogram-based method is not implemented" );
// destGLCM->descriptorOptimizationType = destGLCM->numDescriptors = CV_GLCMDESC_OPTIMIZATION_HISTOGRAM;
}
CV_CALL( destGLCM->descriptors = (double**)
cvAlloc( destGLCM->numMatrices*sizeof(destGLCM->descriptors[0])));
for( matrixLoop = 0; matrixLoop < destGLCM->numMatrices; matrixLoop ++ )
{
CV_CALL( destGLCM->descriptors[ matrixLoop ] =
(double*)cvAlloc( destGLCM->numDescriptors*sizeof(destGLCM->descriptors[0][0])));
memset( destGLCM->descriptors[matrixLoop], 0, destGLCM->numDescriptors*sizeof(double) );
switch( destGLCM->descriptorOptimizationType )
{
case CV_GLCMDESC_OPTIMIZATION_ALLOWDOUBLENEST:
icvCreateGLCMDescriptors_AllowDoubleNest( destGLCM, matrixLoop );
break;
default:
CV_ERROR( CV_StsBadFlag,
"descriptorOptimizationType different from CV_GLCMDESC_OPTIMIZATION_ALLOWDOUBLENEST\n"
"is not supported" );
/*
case CV_GLCMDESC_OPTIMIZATION_ALLOWTRIPLENEST:
icvCreateGLCMDescriptors_AllowTripleNest( destGLCM, matrixLoop );
break;
case CV_GLCMDESC_OPTIMIZATION_HISTOGRAM:
if(matrixLoop < destGLCM->numMatrices>>1)
icvCreateGLCMDescriptors_Histogram( destGLCM, matrixLoop);
break;
*/
}
}
__END__;
if( cvGetErrStatus() < 0 )
cvReleaseGLCM( &destGLCM, CV_GLCM_DESC );
}
static void
icvCreateGLCMDescriptors_AllowDoubleNest( CvGLCM* destGLCM, int matrixIndex )
{
int sideLoop1, sideLoop2;
int matrixSideLength = destGLCM->matrixSideLength;
double** matrix = destGLCM->matrices[ matrixIndex ];
double* descriptors = destGLCM->descriptors[ matrixIndex ];
double* marginalProbability =
(double*)cvAlloc( matrixSideLength * sizeof(marginalProbability[0]));
memset( marginalProbability, 0, matrixSideLength * sizeof(double) );
double maximumProbability = 0;
double marginalProbabilityEntropy = 0;
double correlationMean = 0, correlationStdDeviation = 0, correlationProductTerm = 0;
for( sideLoop1=0; sideLoop1<matrixSideLength; sideLoop1++ )
{
int actualSideLoop1 = destGLCM->reverseLookupTable[ sideLoop1 ];
for( sideLoop2=0; sideLoop2<matrixSideLength; sideLoop2++ )
{
double entryValue = matrix[ sideLoop1 ][ sideLoop2 ];
int actualSideLoop2 = destGLCM->reverseLookupTable[ sideLoop2 ];
int sideLoopDifference = actualSideLoop1 - actualSideLoop2;
int sideLoopDifferenceSquared = sideLoopDifference*sideLoopDifference;
marginalProbability[ sideLoop1 ] += entryValue;
correlationMean += actualSideLoop1*entryValue;
maximumProbability = MAX( maximumProbability, entryValue );
if( actualSideLoop2 > actualSideLoop1 )
{
descriptors[ CV_GLCMDESC_CONTRAST ] += sideLoopDifferenceSquared * entryValue;
}
descriptors[ CV_GLCMDESC_HOMOGENITY ] += entryValue / ( 1.0 + sideLoopDifferenceSquared );
if( entryValue > 0 )
{
descriptors[ CV_GLCMDESC_ENTROPY ] += entryValue * log( entryValue );
}
descriptors[ CV_GLCMDESC_ENERGY ] += entryValue*entryValue;
}
if( marginalProbability>0 )
marginalProbabilityEntropy += marginalProbability[ actualSideLoop1 ]*log(marginalProbability[ actualSideLoop1 ]);
}
marginalProbabilityEntropy = -marginalProbabilityEntropy;
descriptors[ CV_GLCMDESC_CONTRAST ] += descriptors[ CV_GLCMDESC_CONTRAST ];
descriptors[ CV_GLCMDESC_ENTROPY ] = -descriptors[ CV_GLCMDESC_ENTROPY ];
descriptors[ CV_GLCMDESC_MAXIMUMPROBABILITY ] = maximumProbability;
double HXY = 0, HXY1 = 0, HXY2 = 0;
HXY = descriptors[ CV_GLCMDESC_ENTROPY ];
for( sideLoop1=0; sideLoop1<matrixSideLength; sideLoop1++ )
{
double sideEntryValueSum = 0;
int actualSideLoop1 = destGLCM->reverseLookupTable[ sideLoop1 ];
for( sideLoop2=0; sideLoop2<matrixSideLength; sideLoop2++ )
{
double entryValue = matrix[ sideLoop1 ][ sideLoop2 ];
sideEntryValueSum += entryValue;
int actualSideLoop2 = destGLCM->reverseLookupTable[ sideLoop2 ];
correlationProductTerm += (actualSideLoop1 - correlationMean) * (actualSideLoop2 - correlationMean) * entryValue;
double clusterTerm = actualSideLoop1 + actualSideLoop2 - correlationMean - correlationMean;
descriptors[ CV_GLCMDESC_CLUSTERTENDENCY ] += clusterTerm * clusterTerm * entryValue;
descriptors[ CV_GLCMDESC_CLUSTERSHADE ] += clusterTerm * clusterTerm * clusterTerm * entryValue;
double HXYValue = marginalProbability[ actualSideLoop1 ] * marginalProbability[ actualSideLoop2 ];
if( HXYValue>0 )
{
double HXYValueLog = log( HXYValue );
HXY1 += entryValue * HXYValueLog;
HXY2 += HXYValue * HXYValueLog;
}
}
correlationStdDeviation += (actualSideLoop1-correlationMean) * (actualSideLoop1-correlationMean) * sideEntryValueSum;
}
HXY1 =- HXY1;
HXY2 =- HXY2;
descriptors[ CV_GLCMDESC_CORRELATIONINFO1 ] = ( HXY - HXY1 ) / ( correlationMean );
descriptors[ CV_GLCMDESC_CORRELATIONINFO2 ] = sqrt( 1.0 - exp( -2.0 * (HXY2 - HXY ) ) );
correlationStdDeviation = sqrt( correlationStdDeviation );
descriptors[ CV_GLCMDESC_CORRELATION ] = correlationProductTerm / (correlationStdDeviation*correlationStdDeviation );
delete [] marginalProbability;
}
CV_IMPL double cvGetGLCMDescriptor( CvGLCM* GLCM, int step, int descriptor )
{
double value = DBL_MAX;
CV_FUNCNAME( "cvGetGLCMDescriptor" );
__BEGIN__;
if( !GLCM )
CV_ERROR( CV_StsNullPtr, "" );
if( !(GLCM->descriptors) )
CV_ERROR( CV_StsNullPtr, "" );
if( (unsigned)step >= (unsigned)(GLCM->numMatrices))
CV_ERROR( CV_StsOutOfRange, "step is not in 0 .. GLCM->numMatrices - 1" );
if( (unsigned)descriptor >= (unsigned)(GLCM->numDescriptors))
CV_ERROR( CV_StsOutOfRange, "descriptor is not in 0 .. GLCM->numDescriptors - 1" );
value = GLCM->descriptors[step][descriptor];
__END__;
return value;
}
CV_IMPL void
cvGetGLCMDescriptorStatistics( CvGLCM* GLCM, int descriptor,
double* _average, double* _standardDeviation )
{
CV_FUNCNAME( "cvGetGLCMDescriptorStatistics" );
if( _average )
*_average = DBL_MAX;
if( _standardDeviation )
*_standardDeviation = DBL_MAX;
__BEGIN__;
int matrixLoop, numMatrices;
double average = 0, squareSum = 0;
if( !GLCM )
CV_ERROR( CV_StsNullPtr, "" );
if( !(GLCM->descriptors))
CV_ERROR( CV_StsNullPtr, "Descriptors are not calculated" );
if( (unsigned)descriptor >= (unsigned)(GLCM->numDescriptors) )
CV_ERROR( CV_StsOutOfRange, "Descriptor index is out of range" );
numMatrices = GLCM->numMatrices;
for( matrixLoop = 0; matrixLoop < numMatrices; matrixLoop++ )
{
double temp = GLCM->descriptors[ matrixLoop ][ descriptor ];
average += temp;
squareSum += temp*temp;
}
average /= numMatrices;
if( _average )
*_average = average;
if( _standardDeviation )
*_standardDeviation = sqrt( (squareSum - average*average*numMatrices)/(numMatrices-1));
__END__;
}
CV_IMPL IplImage*
cvCreateGLCMImage( CvGLCM* GLCM, int step )
{
IplImage* dest = 0;
CV_FUNCNAME( "cvCreateGLCMImage" );
__BEGIN__;
float* destData;
int sideLoop1, sideLoop2;
if( !GLCM )
CV_ERROR( CV_StsNullPtr, "" );
if( !(GLCM->matrices) )
CV_ERROR( CV_StsNullPtr, "Matrices are not allocated" );
if( (unsigned)step >= (unsigned)(GLCM->numMatrices) )
CV_ERROR( CV_StsOutOfRange, "The step index is out of range" );
dest = cvCreateImage( cvSize( GLCM->matrixSideLength, GLCM->matrixSideLength ), IPL_DEPTH_32F, 1 );
destData = (float*)(dest->imageData);
for( sideLoop1 = 0; sideLoop1 < GLCM->matrixSideLength;
sideLoop1++, (float*&)destData += dest->widthStep )
{
for( sideLoop2=0; sideLoop2 < GLCM->matrixSideLength; sideLoop2++ )
{
double matrixValue = GLCM->matrices[step][sideLoop1][sideLoop2];
destData[ sideLoop2 ] = (float)matrixValue;
}
}
__END__;
if( cvGetErrStatus() < 0 )
cvReleaseImage( &dest );
return dest;
}