티스토리 뷰

히스토그램(Histogram) : 명암값이 나타난 빈도수 [0, L-1]사이의 명암값 각각이 영상에 몇번 나타나는지 표시

아래 그림은 L=256(0~255)의 명암값을 가지는 사진이다.

이 사진의 히스토그램을 구하면 다음과 같다.

누적 히스토그램(Cumulative Histogram) : 히스토그램의 빈도수에 따른 누적값

위의 히스토그램을 기준으로 누적 히스토그램을 구하면 다음과 같이 명암값이 커짐에 따라 누적되어 최대치에 다다르는 것을 확인 할 수 있다.

정규 히스토그램(Normalized Histogram) : 히스토그램의 모든 값을 더하면 1이 되도록 변환

히스토그램 평활화(Histogram Equalization) : 영상 품질 개선을 위한 명암의 동적 범위 수정

위에서 구한 히스토그램을 기준으로 다음과 같이 평활화하게 된다. 중앙에 몰려있던 명암값을 전체적으로 분산시킨 것을 확인 할 수 있다.
 

 

 


Source Code 

Histogram.h

#define HISTOGRAM_SIZE 256

#define LEVEL 256


namespace LibImageProcessing {

class Histogram

{

public:

Histogram();

~Histogram();


void run(cv::Mat &image);


public:

void getHistogram(cv::Mat &image, int *hist);

void getCumulativeHistogram(int *hist, int *cumHist);

void getNormalizedHistogram(int imageSize, int *cumHist, int *norHist);

void getHistogramEqualization(int *hist, int *norHist, int *equHist);


private:

void showHistogram(int *);

};

}

Histogram.cpp

#include "Histogram.h"

namespace LibImageProcessing {

Histogram::Histogram() { }

Histogram::~Histogram() { }


void Histogram::run(cv::Mat &image) {

CV_Assert(image.type() == CV_8UC1);

CV_Assert(image.channels() == 1);


int imageSize = image.size().width * image.size().height;


// histogram

int histogram[HISTOGRAM_SIZE];

memset(histogram, 0, HISTOGRAM_SIZE * sizeof(int));


getHistogram(image, histogram);


// cumulative histogram

int cumulativeHistogram[HISTOGRAM_SIZE];

memset(cumulativeHistogram, 0, HISTOGRAM_SIZE * sizeof(int));


getCumulativeHistogram(histogram, cumulativeHistogram);

// histogram normalize

int normalizedHistogram[HISTOGRAM_SIZE];

memset(normalizedHistogram, 0, HISTOGRAM_SIZE * sizeof(int));


getNormalizedHistogram(imageSize, cumulativeHistogram, normalizedHistogram);

// histogram equalization

int histogramEqualization[HISTOGRAM_SIZE];

memset(histogramEqualization, 0, HISTOGRAM_SIZE * sizeof(int));


getHistogramEqualization(histogram, normalizedHistogram, histogramEqualization);


// convert from original image to enhanced image

cv::Mat newImage = image.clone();

for (int y = 0; y < image.rows; y++)

{

for (int x = 0; x < image.cols; x++)

{

newImage.at<uchar>(y, x) = normalizedHistogram[image.at<uchar>(y, x)];

}

}


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

cv::imshow("converted image", newImage);

cv::waitKey(0);

}


void Histogram::getHistogram(cv::Mat &image, int *hist)

{

int cols = image.cols;

int rows = image.rows;


uchar *imageData = image.data;

int imageSize = image.size().width * image.size().height;

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

{

++hist[imageData[i]];

}

}


void Histogram::getCumulativeHistogram(int *hist, int *cumHist)

{

for (int i = 1; i < HISTOGRAM_SIZE; i++)

{

cumHist[i] = cumHist[i - 1] + hist[i];

}

}


void Histogram::getNormalizedHistogram(int imageSize, int *cumHist, int *norHist)

{

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

{

norHist[i] = ((float)cumHist[i] / imageSize) * (LEVEL - 1);

}

}


void Histogram::getHistogramEqualization(int *hist, int *norHist, int *equHist)

{

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

{

equHist[norHist[i]] += hist[i];

}

}


void Histogram::showHistogram(int *hist) {

// create canvas image

cv::Mat base = cv::Mat::zeros(256, 256, CV_8UC1);


// get max value in array

int maxValue = 0;

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

{

maxValue = (maxValue < hist[i]) ? hist[i] : maxValue;

}


// draw line

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

{

int scaledHeight = ((float)hist[i] / maxValue) * 255;

cout << scaledHeight << endl;

cv::line(base, cv::Point(i, 255), cv::Point(i, 255 - scaledHeight), cv::Scalar(255), 1, CV_AA);

}


cv::namedWindow("histogram", CV_NORMAL);

cv::imshow("histogram", base);

cv::waitKey(0);

}

}