/*------------------------------------------------------------------------------------------*\

   This file contains material supporting chapter 4 of the cookbook: 

   Computer Vision Programming using the OpenCV Library

   Second Edition

   by Robert Laganiere, Packt Publishing, 2013.

   This program is free software; permission is hereby granted to use, copy, modify,

   and distribute this source code, or portions thereof, for any purpose, without fee,

   subject to the restriction that the copyright notice may not be removed

   or altered from any source or altered source distribution.

   The software is released on an as-is basis and without any warranties of any kind.

   In particular, the software is not guaranteed to be fault-tolerant or free from failure.

   The author disclaims all warranties with regard to this software, any use,

   and any consequent failure, is purely the responsibility of the user.

 

   Copyright (C) 2013 Robert Laganiere, www.laganiere.name

\*------------------------------------------------------------------------------------------*/

#if !defined HISTOGRAM

#define HISTOGRAM

#include <opencv2/core/core.hpp>

#include <opencv2/imgproc/imgproc.hpp>

// To create histograms of gray-level images

class Histogram1D {

  private:

    int histSize[1];         // number of bins in histogram

        float hranges[2];        // range of values

    const float* ranges[1];  // pointer to the different value ranges

    int channels[1];         // channel number to be examined

  public:

        Histogram1D() {

                // Prepare default arguments for 1D histogram

                histSize[0]= 256;   // 256 bins

                hranges[0]= 0.0;    // from 0 (inclusive)

                hranges[1]= 256.0;  // to 256 (exclusive)

                ranges[0]= hranges;

                channels[0]= 0;     // we look at channel 0

        }

        // Sets the channel on which histogram will be calculated.

        // By default it is channel 0.

        void setChannel(int c) {

                channels[0]= c;

        }

        // Gets the channel used.

        int getChannel() {

                return channels[0];

        }

        // Sets the range for the pixel values.

        // By default it is [0,256[

        void setRange(float minValue, float maxValue) {

                hranges[0]= minValue;

                hranges[1]= maxValue;

        }

        // Gets the min pixel value.

        float getMinValue() {

                return hranges[0];

        }

        // Gets the max pixel value.

        float getMaxValue() {

                return hranges[1];

        }

        // Sets the number of bins in histogram.

        // By default it is 256.

        void setNBins(int nbins) {

                histSize[0]= nbins;

        }

        // Gets the number of bins in histogram.

        int getNBins() {

                return histSize[0];

        }

        // Computes the 1D histogram.

        cv::Mat getHistogram(const cv::Mat &image) {

                cv::Mat hist;

                // Compute histogram

                cv::calcHist(&image,

                        1,                      // histogram of 1 image only

                        channels,       // the channel used

                        cv::Mat(),      // no mask is used

                        hist,           // the resulting histogram

                        1,                      // it is a 1D histogram

                        histSize,       // number of bins

                        ranges          // pixel value range

                );

                return hist;

        }

        // Computes the 1D histogram and returns an image of it.

        cv::Mat getHistogramImage(const cv::Mat &image, int zoom=1){

                // Compute histogram first

                cv::Mat hist= getHistogram(image);

                // Creates image

                return getImageOfHistogram(hist, zoom);

        }

        // Create an image representing a histogram

        cv::Mat getImageOfHistogram(const cv::Mat &hist, int zoom=1) {

                // Get min and max bin values

                double maxVal=0;

                double minVal=0;

                cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);

                // Square image on which to display histogram

                cv::Mat histImg(histSize[0]*zoom, histSize[0]*zoom, CV_8U,cv::Scalar(255));

                // set highest point at 90% of nbins (i.e. image height)

                int hpt = static_cast<int>(0.9*histSize[0]);

                // Draw vertical line for each bin

                for( int h = 0; h < histSize[0]; h++ ) {

                        float binVal = hist.at<float>(h);

                        if (binVal>0) {

                          int intensity = static_cast<int>(binVal*hpt/maxVal);

                          cv::line(histImg,cv::Point(h*zoom,histSize[0]*zoom),

                                  cv::Point(h*zoom,(histSize[0]-intensity)*zoom),cv::Scalar(0),zoom$

                        }

                }

                return histImg;

        }

        // Equalizes the source image.

        cv::Mat equalize(const cv::Mat &image) {

                cv::Mat result;

                cv::equalizeHist(image,result);

                return result;

        }

        // Stretches the source image.

        cv::Mat stretch(const cv::Mat &image, int minValue=0) {

                // Compute histogram first

                cv::Mat hist= getHistogram(image);

                // find left extremity of the histogram

                int imin= 0;

                for( ; imin < histSize[0]; imin++ ) {

            // ignore bins with less than minValue entries

                        if (hist.at<float>(imin) > minValue)

                                break;

                }

                // find right extremity of the histogram

                int imax= histSize[0]-1;

                for( ; imax >= 0; imax-- ) {

         // ignore bins with less than minValue entries

                        if (hist.at<float>(imax) > minValue)

                                break;

                }

                // Create lookup table

                int dims[1]={256};

                cv::Mat lookup(1,dims,CV_8U);

                for (int i=0; i<256; i++) {

                        if (i < imin) lookup.at<uchar>(i)= 0;

                        else if (i > imax) lookup.at<uchar>(i)= 255;

                        else lookup.at<uchar>(i)= static_cast<uchar>(255.0*(i-imin)/(imax-imin)+0.5$

                }

                // Apply lookup table

                cv::Mat result;

                result= applyLookUp(image,lookup);

                return result;

        }

        // Applies a lookup table transforming an input image into a 1-channel image

    cv::Mat applyLookUp(const cv::Mat& image, // input image

      const cv::Mat& lookup) { // 1x256 uchar matrix

      // the output image

      cv::Mat result;

      // apply lookup table

      cv::LUT(image,lookup,result);

      return result;

    }

        // Applies a lookup table transforming an input image into a 1-channel image

        // this is a test version with iterator; always use function cv::LUT

        cv::Mat applyLookUpWithIterator(const cv::Mat& image, const cv::Mat& lookup) {

                // Set output image (always 1-channel)

                cv::Mat result(image.rows,image.cols,CV_8U);

                cv::Mat_<uchar>::iterator itr= result.begin<uchar>();

                // Iterates over the input image

                cv::Mat_<uchar>::const_iterator it= image.begin<uchar>();

                cv::Mat_<uchar>::const_iterator itend= image.end<uchar>();

                // Applies lookup to each pixel

                for ( ; it!= itend; ++it, ++itr) {

                        *itr= lookup.at<uchar>(*it);

                }

                return result;

        }

};

#endif

#if !defined COLHISTOGRAM

#define COLHISTOGRAM

#include <opencv2/core/core.hpp>

#include <opencv2/imgproc/imgproc.hpp>

class ColorHistogram {

  private:

    int histSize[3];        // size of each dimension

        float hranges[2];       // range of values

    const float* ranges[3]; // array of ranges for each dimension

    int channels[3];        // channel to be considered

  public:

        ColorHistogram() {

                // Prepare default arguments for a color histogram

                // each dimension has equal size and range

                histSize[0]= histSize[1]= histSize[2]= 256;

                hranges[0]= 0.0;    // BRG range from 0 to 256

                hranges[1]= 256.0;

                ranges[0]= hranges; // in this class,  

                ranges[1]= hranges; // all channels have the same range

                ranges[2]= hranges;

                channels[0]= 0;         // the three channels

                channels[1]= 1;

                channels[2]= 2;

        }

        // set histogram size for each dimension

        void setSize(int size) {

                // each dimension has equal size

                histSize[0]= histSize[1]= histSize[2]= size;

        }

        // Computes the histogram.

        cv::Mat getHistogram(const cv::Mat &image) {

                cv::Mat hist;

                // BGR color histogram

                hranges[0]= 0.0;    // BRG range

                hranges[1]= 256.0;

                channels[0]= 0;         // the three channels

                channels[1]= 1;

                channels[2]= 2;

                // Compute histogram

                cv::calcHist(&image,

                        1,                      // histogram of 1 image only

                        channels,       // the channel used

                        cv::Mat(),      // no mask is used

                        hist,           // the resulting histogram

                        3,                      // it is a 3D histogram

                        histSize,       // number of bins

                        ranges          // pixel value range

                );

                return hist;

        }

        // Computes the histogram.

        cv::SparseMat getSparseHistogram(const cv::Mat &image) {

                cv::SparseMat hist(3,        // number of dimensions

                                       histSize, // size of each dimension

                                                   CV_32F);

                // BGR color histogram

                hranges[0]= 0.0;    // BRG range

                hranges[1]= 256.0;

                channels[0]= 0;         // the three channels

                channels[1]= 1;

                channels[2]= 2;

                // Compute histogram

                cv::calcHist(&image,

                        1,                      // histogram of 1 image only

                        channels,       // the channel used

                        cv::Mat(),      // no mask is used

                        hist,           // the resulting histogram

                        3,                      // it is a 3D histogram

                        histSize,       // number of bins

                        ranges          // pixel value range

                );

                return hist;

        }

        // Computes the 1D Hue histogram with a mask.

        // BGR source image is converted to HSV

        // Pixels with low saturation are ignored

        cv::Mat getHueHistogram(const cv::Mat &image,

                             int minSaturation=0) {

                cv::Mat hist;

                // Convert to HSV colour space

                cv::Mat hsv;

                cv::cvtColor(image, hsv, CV_BGR2HSV);

                // Mask to be used (or not)

                cv::Mat mask;

                if (minSaturation>0) {

                        // Spliting the 3 channels into 3 images

                        std::vector<cv::Mat> v;

                        cv::split(hsv,v);

                        // Mask out the low saturated pixels

                        cv::threshold(v[1],mask,minSaturation,255,

                                 cv::THRESH_BINARY);

                }

                // Prepare arguments for a 1D hue histogram

                hranges[0]= 0.0;    // range is from 0 to 180

                hranges[1]= 180.0;

                channels[0]= 0;    // the hue channel

                // Compute histogram

                cv::calcHist(&hsv,

                        1,                      // histogram of 1 image only

                        channels,       // the channel used

                        mask,           // binary mask

                        hist,           // the resulting histogram

                        1,                      // it is a 1D histogram

                        histSize,       // number of bins

                        ranges          // pixel value range

                );

                return hist;

        }

        // Computes the 2D ab histogram.

        // BGR source image is converted to Lab

        cv::Mat getabHistogram(const cv::Mat &image) {

                cv::Mat hist;

                // Convert to Lab color space

                cv::Mat lab;

                cv::cvtColor(image, lab, CV_BGR2Lab);

                // Prepare arguments for a 2D color histogram

                hranges[0]= 0;

                hranges[1]= 256.0;

                channels[0]= 1; // the two channels used are ab

                channels[1]= 2;

                // Compute histogram

                cv::calcHist(&lab,

                        1,                      // histogram of 1 image only

                        channels,       // the channel used

                        cv::Mat(),      // no mask is used

                        hist,           // the resulting histogram

                        2,                      // it is a 2D histogram

                        histSize,       // number of bins

                        ranges          // pixel value range

                );

                return hist;

        }

};

#endif

show_histogram_image.cpp

#include <iostream>

using namespace std;

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

#include "histogram.h"

int main()

{

        // Read input image

        cv::Mat image= cv::imread("lions_drinking.jpg",0);

        if (!image.data)

                return 0;

    // Display the image

        cv::namedWindow("Image");

        cv::imshow("Image",image);

        // The histogram object

        Histogram1D h;

    // Compute the histogram

        cv::Mat histo= h.getHistogram(image);

        // Loop over each bin

        for (int i=0; i<256; i++)

                cout << "Value " << i << " = " << histo.at<float>(i) << endl; 

        // Display a histogram as an image

        cv::namedWindow("Histogram");

        cv::imshow("Histogram",h.getHistogramImage(image));

        cv::waitKey();

        return 0;

}

show_equalized_image_histogram.cpp

#include <iostream>

using namespace std;

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

#include "histogram.h"

int main()

{

        // Read input image

        cv::Mat image= cv::imread("lions_drinking.jpg",0);

        if (!image.data)

                return 0;

       // Display the image

        cv::namedWindow("Image");

        cv::imshow("Image",image);

        // The histogram object

        Histogram1D h;

        // Equalize the image

        cv::Mat eq= h.equalize(image);

        // Show the result

        cv::namedWindow("Equalized Image");

        cv::imshow("Equalized Image",eq);

        // Show the new histogram

        cv::namedWindow("Equalized Histogram");

        cv::imshow("Equalized Histogram",h.getHistogramImage(eq));

        cv::waitKey();

        return 0;

}