Contrast stretching in a nutshell

Published by: 0

ContrastLow

Is there anything in this picture? Apparently not! This is a perfect example for a low contrast image, where all the image data are packed within a very small range of colours.

The histogram for the given image looks like this.

7

Histogram is the graph, in which image pixel densities are plotted against their colour levels. Here X- axis goes from 0..255 representing the 255 colour levels and Y- axis represents number of pixels per each color level. It can be seen that all the pixels are compacted in a very small color range in this picture. The idea behind the contrast stretching is stretching the histogram so that the color levels of widely populated areas be separated.

Theory behind Contrast Stretching

cs

We calculate equations for the 3 areas in the graph to assign pixel values in each areas to new values.

In this curve, r2 to r3 is the area which is highly populated. Therefore we stretch that area more than either sides. Suppose, s2 is the output for r2 and r3 is the output for s3.

For a pixel in 0 to r2 color levels, output values can be calculated like follows.

Let α is the slope of r1 to r2, Pin is the input pixel value and Pout is the output pixel value.

r2, r3, s2 and s3 have to be determined.

alpha

Now for r2 to r3,

bb

For r3 to 255,

mm

This approach is called a piece wise operation.

C++ implementation

[cpp]
#include <iostream>
#include "opencv\cv.h"
#include "opencv2\highgui\highgui.hpp"

using namespace cv;
using namespace std;

/**I have defined 2 routines for the clarity.
createHisto is to create the histogram of a given image.
getStretched is the implementation of piecewise operation*/

Mat createHisto(Mat Histo, int h_hi, int h_wi, int b_wi, int *hist, Scalar lineCol);
int getStretched(int val);

int main() {
Mat img = imread("ContrastLow.jpg", CV_LOAD_IMAGE_GRAYSCALE);
cout << img.size;
int hist[256];
for (int i = 0; i < 256; i++) {
hist[i] = 0;
}

//create histo
for (int i = 0; i < img.rows; i++){
for (int j = 0; j < img.cols; j++){
hist[((int)img.at<uchar>(i, j))]++;
}
}

for (int i = 0; i < 256; i++){
cout << "old : " << i << " = " << hist[i] << endl;
}
int h_hi = 600;
int h_wi = 512;
int b_wi = cvRound((double)h_wi / 256);
Mat Histo(h_hi, h_wi, CV_64FC3, Scalar(0, 0, 0));
Histo = createHisto(Histo, h_hi, h_wi, b_wi, hist,Scalar(100,255,0));

imshow("Histo",Histo);
//namedWindow("Original", WINDOW_AUTOSIZE);
imshow("Original", img);

//create strecthed
Mat stretched(img.rows, img.cols, CV_8UC1);
int val;

uchar intensity;
float result;
for (int i = 0; i < img.rows; i++){
for (int j = 0; j < img.cols; j++) {
val = (int)img.at<uchar>(i, j);
int out = getStretched(val);
stretched.at<uchar>(i, j) = saturate_cast<uchar>(out);
}
}

imshow("Streteched", stretched);

int strHist[256];
for (int i = 0; i < 256; i++){
strHist[i] = 0;
}

for (int i = 0; i < stretched.rows; i++){
for (int j = 0; j < stretched.cols; j++){
strHist[((int)stretched.at<uchar>(i, j))]++;
}
}
for (int i = 0; i < 256; i++){
cout << "new : "<<i <<" = "<< strHist[i]<<endl;
}

Mat strHisto(h_hi, h_wi, CV_64FC4, Scalar(0, 0, 0));
Histo = createHisto(strHisto, h_hi, h_wi, b_wi, strHist, Scalar(0, 100, 255));
imshow("str", strHisto);

waitKey(0);
return 0;
}

/**This function is to calculate the histogram.
It returns the Mat image of the histogram on given parameters*/
Mat createHisto(Mat Histo, int h_hi, int h_wi, int b_wi, int *hist, Scalar lineCol) {
int max = hist[0];
for (int i = 0; i < 256; i++) {
if (hist[i]>max){
max = hist[i];
}
}

for (int i = 0; i < 256; i++){
hist[i] = ((double)hist[i] / max)*Histo.cols;
}

for (int i = 0; i < 256; i++) {
line(Histo, Point(b_wi*i, h_hi), Point(b_wi*i, h_hi – hist[i]), lineCol);
}
return Histo;
}
/**This is the implementation of piecewise operation. a,b,c,d are equalants to r2,r3,s2,s3 */
int getStretched(int val) {

int a = 105;
int b = 122;
int c = 0;
int d = 255;

float result =0;

if (val <= a) {
result = val*c / a;
}
else if (val>a && val <= b){
result = float ((d – c)*(val – a) / (b – a)) + c;
}
else {
result = float ((255 – d)*(val – b) / (255 – b)) + d;
}
return (int)result;
}

[/cpp]

 

Results

Initial image and histogram.

orig hist1

 

Resulting image and the histogram

stre hist2

 

Cheers!