Image Processing, Linear stretch and OpenCV

In attempt to recognize objects by examining images, various Image processing and analysis techniques are applied. This article briefly describes linear stretch algorithm and its use within OpenCV.

Linear stretch technique can be applied to images where substantial lack of contrast can result in false identification of objects, its spacial relationship and significance. Contrast enhancement by linear stretch can be applied to images with very low or very high variations of brightness. To apply the Linear stretch algorithm an image needs to be converted into gray-scale and all 8bit pixels and its values are recorded into histogram.

Histogram will contain all 256 grey-levels ( 0 – 255 ) in so called bins and each pixel value will take place in the bin represented with its own value. When the histogram and the image is created, maximum ( OMAX ) and minimum ( OMIN ) values are identified.

Linear stretch is applied to the histogram as follows:

  • create a histogram of the original image
  • set new maximum ( NMAX ) and new minimum ( NMIN ) values
  • calculate number of bins in the original histogram where value of bins = ( OMAX – OMIN )
  • calculate spacing for a new histogram so space = ( NMAX – NMIN ) / ( OMAX – OMIN )
  • create a new histogram with corresponding positions for new bins ( Nb ) represented by
  • use new histogram to create new image

The previous formula can be represented by simplified version of c++ code as follows:

#include <iostream> 
 
using namespace std; 
int main() { 
 
   const int NMIN = 0; 
   const int NMAX = 255; 
   const int OMIN = 60; 
   const int OMAX = 65; 
   int space = ( NMAX - NMIN ) / ( OMAX - OMIN ) ; 
   int bins = ( OMAX - OMIN ); 
 
   for ( int j = 0; j <= bins; j++ ) { 
   	std::cout << j + OMIN << ": " << NMIN + ( j * space ) << endl; 
   } 
return 0; 
}

COMPILE:

g++ bins.cpp -o bins

OUTPUT:

60: 0
61: 51
62: 102
63: 153
64: 204
65: 255

The c++ code above is really simplified version of the linear stretch algorithm. In the next section we are going use OpenCV library to do this task.

Using OpenCV library we can take advantage of cvNormalize function. This function takes at minimum five arguments ( original image, new image, NMIN, NMAX and normalization type ). The following OpenCV c++ code takes sample image as a single argument. The following c++ code will apply cvNormalize function to a sample image and create histogram for original as well as normalized image.

#include "cv.h"
#include "highgui.h"

void create_histogram_image(IplImage*, IplImage*);

int main( int argc, char** argv )

{
//load color image specified by first argument
IplImage *source = cvLoadImage( argv[1]);

// create new image structure 
// for the grayscale output image
IplImage *gray_img = cvCreateImage( 
cvSize( source->width, source->height ), IPL_DEPTH_8U, 1 );

// set type CV_RGB2GRAY to convert 
// RGB image to grayscale 
cvCvtColor( source, gray_img, CV_RGB2GRAY );

// create new image structure 
// to hold histogram image
IplImage *hist_img = cvCreateImage(cvSize(300,240), 8, 1);
cvSet( hist_img, cvScalarAll(255), 0 );

// create new image structure 
// to hold stretched output image
IplImage *stretched_img = cvCreateImage( 
cvSize( source->width, source->height ), IPL_DEPTH_8U, 1 );

// create new image structure 
// to hold histogram image
IplImage *stretched_hist_img = cvCreateImage(cvSize(300,240), 8, 1);
cvSet( stretched_hist_img, cvScalarAll(255), 0 );

// create new image structure 
// to hold stretched output image
IplImage *equalized_img = cvCreateImage( 
cvSize( source->width, source->height ), IPL_DEPTH_8U, 1 );

// cvNormalize function call to apply linear stretch
cvNormalize(gray_img, stretched_img, 0, 255, CV_MINMAX);


// create histogram of the original image
create_histogram_image(gray_img, hist_img);
// create histogram of the new image.
create_histogram_image(stretched_img, stretched_hist_img);

// display all images
cvNamedWindow( "Original Gray-scale Image", 1 );
cvShowImage( "Original Gray-scale Image",gray_img);

cvNamedWindow( "Stretched Gray-scale Image", 1 );
cvShowImage( "Stretched Gray-scale Image",stretched_img);

cvNamedWindow( "Gray-scale Image Histogram", 1 );
cvShowImage( "Gray-scale Image Histogram",hist_img);

cvNamedWindow( "Stretched Image Histogram", 1 );
cvShowImage( "Stretched Image Histogram",stretched_hist_img);
// wait indefinitely for keystroke
cvWaitKey(0);

return 0;
}

void create_histogram_image(IplImage* gray_img, IplImage* hist_img) {
CvHistogram *hist;

int hist_size = 256;     
float range[]={0,256};
float* ranges[] = { range };
float max_value = 0.0;
float w_scale = 0.0#000000;">;

// create array to hold histogram values
hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);
 
// calculate histogram values 
cvCalcHist( &gray_img, hist, 0, NULL );
 
// Get the minimum and maximum values of the histogram 
cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );
 
// set height by using maximim value
cvScale( hist->bins, hist->bins, ((float)hist_img->height)/max_value, 0 );

// calculate width
w_scale = ((float)hist_img->width)/hist_size;
 
// plot the histogram 
for( int i = 0; i < hist_size; i++ ) {

     cvRectangle( hist_img, cvPoint((int)i*w_scale , hist_img->height),
     cvPoint((int)(i+1)*w_scale, hist_img->height - cvRound(cvGetReal1D(hist->bins,i))),
     cvScalar(0), -1, 8, 0 );

	}
}

COMPILE:

g++ `pkg-config opencv --cflags --libs` normalize.cpp -o normalize

EXECUTE:

./normalize sample.png

OUTPUT:

sample.png ( original RGB image )

In the next step we have converted RGB image to a gray-scale:

using cvNormalize we have applied linear stretch:

Now we can compare histograms of both images.

Histogram of the original gray-scale image:

Histogram of the new stretched image: