| OpenCV 5.0.0alpha Open Source Computer Vision

Loading...
Searching...
No Matches
Histogram Equalization

Prev Tutorial: Affine Transformations
Next Tutorial: Histogram Calculation

Original author | Ana Huamán
Compatibility | OpenCV >= 3.0

# Goal

In this tutorial you will learn:

- What an image histogram is and why it is useful
- To equalize histograms of images by using the OpenCV function cv::equalizeHist

# Theory

## What is an Image Histogram?

- It is a graphical representation of the intensity distribution of an image.
- It quantifies the number of pixels for each intensity value considered.

## What is Histogram Equalization?

- It is a method that improves the contrast in an image, in order to stretch out the intensity range (see also the corresponding Wikipedia entry).
- To make it clearer, from the image above, you can see that the pixels seem clustered around the middle of the available range of intensities. What Histogram Equalization does is to stretch out this range. Take a look at the figure below: The green circles indicate the underpopulated intensities. After applying the equalization, we get an histogram like the figure in the center. The resulting image is shown in the picture at right.

## How does it work?

- Equalization implies mapping one distribution (the given histogram) to another distribution (a wider and more uniform distribution of intensity values) so the intensity values are spread over the whole range.

- To accomplish the equalization effect, the remapping should be the cumulative distribution function (cdf) (more details, refer to Learning OpenCV). For the histogram \(H(i)\), its cumulative distribution \(H^{'}(i)\) is:

  \[H^{'}(i) = \sum_{0 \le j < i} H(j)\]

  To use this as a remapping function, we have to normalize \(H^{'}(i)\) such that the maximum value is 255 ( or the maximum value for the intensity of the image ). From the example above, the cumulative function is:

- Finally, we use a simple remapping procedure to obtain the intensity values of the equalized image:

  \[equalized( x, y ) = H^{'}( src(x,y) )\]

# Code

- What does this program do?
  - Loads an image
  - Convert the original image to grayscale
  - Equalize the Histogram by using the OpenCV function cv::equalizeHist
  - Display the source and equalized images in a window.

C++

- Downloadable code: Click here
- Code at glance:
  #include "opencv2/imgcodecs.hpp"
  #include "opencv2/highgui.hpp"
  #include "opencv2/imgproc.hpp"
  #include <iostream>
  using namespace cv;
  using namespace std;
  int main( int argc, char** argv )
  {
  CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
  Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
  if( src.empty() )
  {
  cout << "Could not open or find the image!\n" << endl;
  cout << "Usage: " << argv[0] << " <Input image>" << endl;
  return -1;
  }
  cvtColor( src, src, COLOR_BGR2GRAY );
  Mat dst;
  equalizeHist( src, dst );
  imshow( "Source image", src );
  imshow( "Equalized Image", dst );
  waitKey();
  return 0;
  }
  cv::CommandLineParser
  Designed for command line parsing.
  Definition utility.hpp:890
  cv::Mat
  n-dimensional dense array class
  Definition mat.hpp:951
  cv::Mat::empty
  bool empty() const
  Returns true if the array has no elements.
  cv::String
  std::string String
  Definition cvstd.hpp:151
  highgui.hpp
  main
  int main(int argc, char *argv[])
  Definition highgui_qt.cpp:3
  imgcodecs.hpp
  imgproc.hpp
  cv
  Definition core.hpp:107
  std
  STL namespace.

Java

- Downloadable code: Click here
- Code at glance:
  import org.opencv.core.Core;
  import org.opencv.core.Mat;
  import org.opencv.highgui.HighGui;
  import org.opencv.imgcodecs.Imgcodecs;
  import org.opencv.imgproc.Imgproc;
  class EqualizeHist {
  public void run(String[] args) {
  String filename = args.length > 0 ? args[0] : "../data/lena.jpg";
  Mat src = Imgcodecs.imread(filename);
  if (src.empty()) {
  System.err.println("Cannot read image: " + filename);
  System.exit(0);
  }
  Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
  Mat dst = new Mat();
  Imgproc.equalizeHist( src, dst );
  HighGui.imshow( "Source image", src );
  HighGui.imshow( "Equalized Image", dst );
  HighGui.waitKey(0);
  System.exit(0);
  }
  }
  public class EqualizeHistDemo {
  public static void main(String[] args) {
  // Load the native OpenCV library
  System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  new EqualizeHist().run(args);
  }
  }

Python

- Downloadable code: Click here
- Code at glance:
  from __future__ import print_function
  import cv2 as cv
  import argparse
  parser = argparse.ArgumentParser(description='Code for Histogram Equalization tutorial.')
  parser.add_argument('--input', help='Path to input image.', default='lena.jpg')
  args = parser.parse_args()
  src = cv.imread(cv.samples.findFile(args.input))
  if src is None:
  print('Could not open or find the image:', args.input)
  exit(0)
  src = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
  dst = cv.equalizeHist(src)
  cv.imshow('Source image', src)
  cv.imshow('Equalized Image', dst)
  cv.waitKey()
  cv::samples::findFile
  cv::String findFile(const cv::String &relative_path, bool required=true, bool silentMode=false)
  Try to find requested data file.
  cv::imshow
  void imshow(const String &winname, InputArray mat)
  Displays an image in the specified window.
  cv::waitKey
  int waitKey(int delay=0)
  Waits for a pressed key.
  cv::imread
  CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
  Loads an image from a file.
  cv::cvtColor
  void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
  Converts an image from one color space to another.
  cv::equalizeHist
  void equalizeHist(InputArray src, OutputArray dst)
  Equalizes the histogram of a grayscale image.

# Explanation

- Load the source image:

  C++
  CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
  Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
  if( src.empty() )
  {
  cout << "Could not open or find the image!\n" << endl;
  cout << "Usage: " << argv[0] << " <Input image>" << endl;
  return -1;
  }
  Java
  String filename = args.length > 0 ? args[0] : "../data/lena.jpg";
  Mat src = Imgcodecs.imread(filename);
  if (src.empty()) {
  System.err.println("Cannot read image: " + filename);
  System.exit(0);
  }
  Python
  parser = argparse.ArgumentParser(description='Code for Histogram Equalization tutorial.')
  parser.add_argument('--input', help='Path to input image.', default='lena.jpg')
  args = parser.parse_args()
  src = cv.imread(cv.samples.findFile(args.input))
  if src is None:
  print('Could not open or find the image:', args.input)
  exit(0)

- Convert it to grayscale:

  C++
  cvtColor( src, src, COLOR_BGR2GRAY );
  Java
  Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
  Python
  src = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

- Apply histogram equalization with the function cv::equalizeHist :

  C++
  Mat dst;
  equalizeHist( src, dst );
  Java
  Mat dst = new Mat();
  Imgproc.equalizeHist( src, dst );
  Python
  dst = cv.equalizeHist(src)

  As it can be easily seen, the only arguments are the original image and the output (equalized) image.

- Display both images (original and equalized):

  C++
  imshow( "Source image", src );
  imshow( "Equalized Image", dst );
  Java
  HighGui.imshow( "Source image", src );
  HighGui.imshow( "Equalized Image", dst );
  Python
  cv.imshow('Source image', src)
  cv.imshow('Equalized Image', dst)

- Wait until user exists the program

  C++
  waitKey();
  Java
  HighGui.waitKey(0);
  Python
  cv.waitKey()

# Results

1. To appreciate better the results of equalization, let's introduce an image with not much contrast, such as:

which, by the way, has this histogram:

notice that the pixels are clustered around the center of the histogram.

1. After applying the equalization with our program, we get this result:

this image has certainly more contrast. Check out its new histogram like this:

Notice how the number of pixels is more distributed through the intensity range.

Note
  Are you wondering how did we draw the Histogram figures shown above? Check out the following tutorial!