์ ๋ฒ ํฌ์คํ ์ผ๋ก ํ์คํ ๊ทธ๋จ Equalization์ ๋ฐฐ์ ๋ค. Histogram Equalization์ ํ์คํ ๊ทธ๋จ์ ๋ถํฌ๋ฅผ ๊ท ์ผํ๊ฒ ๋ง๋๋ ๋ฐฉ๋ฒ์ด๋ค.
์ค๋์, ๊ท ์ผํ ๋ถํฌ๋ฅผ๋์ด์์ "ํน์ ํ ๋ถํฌ"๋ก ๋ฐ๊พธ๋ Histogram Matching์ ๋ํด์ ๋ฐฐ์ด๋ค.
์ด ๊ณผ์ ์์ Histogram Equalization์ด ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ equalization์ ๋ํด์ ์ด๋์ ๋ ์๊ณ ์์ด์ผ ํ๋ค.
Histogram Matching
ํน์ ํ ํ์คํ ๊ทธ๋จ ํํ๋ก ์ด๋ฏธ์ง์ ์๋ณธ ํ์คํ ๊ทธ๋จ์ ๋ณํํ๋ ๊ฒ์ histogram matching์ด๋ผ๊ณ ํ๋ค.
์ฆ, ์๋ณธ ํ์คํ ๊ทธ๋จ S๋ฅผ ๋ณํํ์ฌ ํ์คํ ๊ทธ๋จ Z๋ก ๋ง๋ ๋ค.

Histogram Matching ๊ณผ์
์์ ์ฌ์ง์ผ๋ก ์ ์ ์๋ฏ์ด Histogram Equalization์ ํตํด์ Transfer Function T์ G๋ฅผ ๊ตฌํ ์ ์๋ค.
์ด๋ ๊ฒ ๊ตฌํ ๋๊ฐ์ Transfer Funciton์ ํ์ฉํ๋ฉด S->Z๋ก ํ์คํ ๊ทธ๋จ ๋งค์นญ์ ํ ์ ์๋ค.
1. ๋จผ์ ์๋ณธ๊ณผ ๋ชฉํ ํ์คํ ๊ทธ๋จ์ equalization function์ ๊ตฌํ๋ค.
2. ์ด ๋ function์ ํ์ฉํด s->z๋ก intensity ๋งคํ์ ํ ์ ์๋ค.
Histogram Matching OpenCV ์ค์ต
์ปฌ๋ฌ์ ๊ทธ๋ ์ด์ค์ผ์ผ ์ด๋ฏธ์ง ๋ชจ๋์์ ์ ์ฉํ ์์๋ฅผ ๋ค๊ณ ์๋ค.
์ปฌ๋ฌ ์ด๋ฏธ์ง์ ๊ฒฝ์ฐ์๋ YUV๋ณํ ํ์ฌ Y(๋ฐ๊ธฐ)์์๋จ ๋ณํํ์ฌ ์ฌ์ฉํ๋ค.
(๋ฐ๋ผ์ ๋ฐ๊ธฐ๋ง ๋ณํ๋ฏ๋ก, ํ์คํ ๊ทธ๋จ์ grayscale๊ณผ ๋์ผํ ๊ฒ์ด๋ค)
Grayscale Image
๊ฐ์ฅ ์์ ์ฌ์ง๊ณผ ํ์คํ ๊ทธ๋จ์ด ๋ณํํ ์ฌ์ง
์ค๊ฐ ํ์คํ ๊ทธ๋จ๊ณผ ์ฌ์ง์ด ๋งค์นญ ๋์์ด๋ค.
๊ฐ์ฅ ์๋์์ ์ฌ์ง๊ณผ ํ์คํ ๊ทธ๋จ์ด ๋งค์นญ ํ์ ์ฌ์ง๊ณผ ํ์คํ ๊ทธ๋จ์ด๋ค.
๊ฒฐ๊ณผ๋ฅผ ์ดํด๋ณด๋ฉด ํ์คํ ๊ทธ๋จ ๋ชจ์์ด ์ ์ฌํ๊ฒ ๋งค์นญ๋ ๊ฒ์ ์ ์ ์๋ค






Color Image
์์ ์ฌ์ง๊ณผ ํ์คํ ๊ทธ๋จ์ด ๋ณํํ ์ฌ์ง
๋๋ฒ์งธ ํ์คํ ๊ทธ๋จ๊ณผ ์ฌ์ง์ด ๋งค์นญ ๋์์ด๋ค.
์ปฌ๋ฌ ์ด๋ฏธ์ง๋ BGR์ฑ๋์ YUV ์ฑ๋๋ก ๋ฐ๊พธ์ด Y์ฑ๋์๋ง ์ ์ฉํ๋ค.
Y๋ ์ด๋ฏธ์ง์ ๋ฐ๊ธฐ๋ฅผ ์๋ฏธํ๊ณ , U,V๋ ์ด๋ฏธ์ง ํฝ์ ์ ์์์ ๊ฒฐ์ ํ๋ค.
ํ์คํ ๊ทธ๋จ ๋งค์นญ์ ๊ฒฐ๊ณผ๋ ๋ฐ๋ผ์ ์ด๋ฏธ์ง์ ์์์ ๋ณด๋ ๊ทธ๋๋ก, ๋ฐ๊ธฐ ์ ๋ณด๋ง ๋งค์นญ๋๋ค.




์๋์ ๋งค์นญ ๊ฒฐ๊ณผ๋ฅผ ์ดํด๋ณด๋ฉด ํ์คํ ๊ทธ๋จ ๋ชจ์์ด ์ ์ฌํ๊ฒ ๋งค์นญ๋ ๊ฒ์ ์ ์ ์๋ค.


Histogram Matching OpenCV Code
์ฌ์ฉ ํจ์๊ฐ ๋ง์์ ธ์ ๋ฐ๋ก ํค๋ํ์ผ์ ๋ง๋ค์๋ค.
histogram.h
#pragma once
#ifndef HISTOGRAM_H
#define HISTOGRAM_H
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// ์ด๋ฏธ์ง๋ฅผ ๊ทธ๋ ์ด์ค์ผ์ผ๋ก ๋ณํ
inline Mat convertToGrayscale(const Mat& img) {
Mat grayImg;
if (img.channels() == 3)
cvtColor(img, grayImg, COLOR_BGR2GRAY);
else
grayImg = img;
return grayImg;
}
// ํ์คํ ๊ทธ๋จ ๊ณ์ฐ
inline vector<Mat> calculateHistogram(const Mat& img) {
vector<Mat> histograms;
if (img.channels() == 1) { // Grayscale Image
Mat hist;
int histSize = 256;
float range[] = { 0, 256 };
const float* histRange = { range };
calcHist(&img, 1, 0, Mat(), hist, 1, &histSize, &histRange);
histograms.push_back(hist);
}
else if (img.channels() == 3) { // Color Image (BGR or RGB)
vector<Mat> channels;
split(img, channels);
int histSize = 256;
float range[] = { 0, 256 };
const float* histRange = { range };
for (int i = 0; i < 3; ++i) {
Mat hist;
calcHist(&channels[i], 1, 0, Mat(), hist, 1, &histSize, &histRange);
histograms.push_back(hist);
}
}
return histograms;
}
// ํ๋ฅ ๋ฐ๋ ํจ์(PDF) ๊ณ์ฐ
inline Mat computePDF(const Mat& hist) {
return hist / sum(hist)[0];
}
// ๋์ ๋ถํฌ ํจ์(CDF) ๊ณ์ฐ
inline Mat computeCDF(const Mat& pdf) {
Mat cdf = pdf.clone();
for (int i = 1; i < cdf.rows; ++i) {
cdf.at<float>(i) += cdf.at<float>(i - 1);
}
return cdf;
}
// ๋งค์นญ์ ์ํ Lookup Table ์์ฑ
inline Mat generateLookupTable(const Mat& sourceCDF, const Mat& referenceCDF) {
Mat lookupTable(1, 256, CV_8U);
for (int i = 0; i < 256; ++i) {
float sourceValue = sourceCDF.at<float>(i);
uchar matchedValue = 0;
for (int j = 0; j < 256; ++j) {
if (referenceCDF.at<float>(j) >= sourceValue) {
matchedValue = static_cast<uchar>(j);
break;
}
}
lookupTable.at<uchar>(i) = matchedValue;
}
return lookupTable;
}
// Lookup Table ์ ์ฉ
inline Mat applyLookupTable(const Mat& img, const Mat& lookupTable) {
Mat result;
LUT(img, lookupTable, result);
return result;
}
// ํ์คํ ๊ทธ๋จ ๋งค์นญinline Mat histogramMatching(
inline Mat histogramMatching(
const Mat& src,
const Mat& ref,
vector<Mat>& hist_src,
vector<Mat>& hist_dst,
vector<Mat>& hist_result,
bool Color = false)
{
if (src.channels() == 3 && Color) { // ์ปฌ๋ฌ ์ด๋ฏธ์ง ์ฒ๋ฆฌ (YUV ๋ณํ ํ Y ์ฑ๋ ๋งค์นญ)
Mat srcYUV, refYUV;
cvtColor(src, srcYUV, COLOR_BGR2YUV);
cvtColor(ref, refYUV, COLOR_BGR2YUV);
// ์ฑ๋ ๋ถ๋ฆฌ
vector<Mat> srcChannels, refChannels;
split(srcYUV, srcChannels);
split(refYUV, refChannels);
// Y ์ฑ๋์ ํ์คํ ๊ทธ๋จ ๊ณ์ฐ
hist_src = calculateHistogram(srcChannels[0]);
hist_dst = calculateHistogram(refChannels[0]);
// PDF ๋ฐ CDF ๊ณ์ฐ
Mat pdf_src = computePDF(hist_src[0]);
Mat pdf_dst = computePDF(hist_dst[0]);
Mat cdf_src = computeCDF(pdf_src);
Mat cdf_dst = computeCDF(pdf_dst);
// Lookup Table ์์ฑ ๋ฐ ์ ์ฉ
Mat lookupTable = generateLookupTable(cdf_src, cdf_dst);
srcChannels[0] = applyLookupTable(srcChannels[0], lookupTable);
// ๋งค์นญ๋ ์ด๋ฏธ์ง ๋ณํฉ ๋ฐ ์์ ๋ณต์
Mat matchedYUV, result;
merge(srcChannels, matchedYUV);
cvtColor(matchedYUV, result, COLOR_YUV2BGR);
// ๋งค์นญ๋ ์ด๋ฏธ์ง์ Y ์ฑ๋ ํ์คํ ๊ทธ๋จ ๊ณ์ฐ
hist_result = calculateHistogram(srcChannels[0]);
return result;
}
else { // ๊ทธ๋ ์ด์ค์ผ์ผ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
Mat srcGray = convertToGrayscale(src);
Mat refGray = convertToGrayscale(ref);
hist_src = calculateHistogram(srcGray);
hist_dst = calculateHistogram(refGray);
Mat pdf_src = computePDF(hist_src[0]);
Mat pdf_dst = computePDF(hist_dst[0]);
Mat cdf_src = computeCDF(pdf_src);
Mat cdf_dst = computeCDF(pdf_dst);
Mat lookupTable = generateLookupTable(cdf_src, cdf_dst);
Mat result = applyLookupTable(srcGray, lookupTable);
hist_result = calculateHistogram(result);
return result;
}
}
// ํ์คํ ๊ทธ๋จ ์ด๋ฏธ์ง๋ก ๊ทธ๋ฆฌ๊ธฐ
inline Mat drawHistogram(
const vector<Mat>& hists, // ํ์คํ ๊ทธ๋จ ๋ชฉ๋ก (Y, BGR or YUV)
int histSize = 256,
int hist_w = 512,
int hist_h = 500,
int margin = 60) {
Mat histImg(hist_h + 2 * margin, hist_w + 2 * margin, CV_8UC3, Scalar(0, 0, 0));
Scalar colors[3] = { Scalar(255, 255, 255), Scalar(0, 255, 0), Scalar(0, 0, 255) }; // B, G, R or Y, U, V
int channelCount = hists.size();
for (int ch = 0; ch < channelCount; ++ch) {
Mat hist = hists[ch];
// ์ ๊ทํํ์ฌ ์ต๋๊ฐ์ hist_h์ ๋ง์ถค
normalize(hist, hist, 0, hist_h, NORM_MINMAX);
for (int i = 1; i < histSize; ++i) {
line(histImg,
Point(margin + (i - 1) * (hist_w / histSize), margin + hist_h - cvRound(hist.at<float>(i - 1))),
Point(margin + i * (hist_w / histSize), margin + hist_h - cvRound(hist.at<float>(i))),
colors[ch % 3], 1); // ์ ์ผ๋ก ํ์
}
}
// ์ถ ๊ทธ๋ฆฌ๊ธฐ
line(histImg, Point(margin - 2, margin), Point(margin - 2, margin + hist_h+2), Scalar(255, 255, 255)); // Y์ถ
line(histImg, Point(margin - 2, margin + hist_h+2), Point(margin + hist_w, margin + hist_h+2), Scalar(255, 255, 255)); // X์ถ
return histImg;
}
#endif // HISTOGRAM_H
hist_matching.cpp
#include "histogram.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat img_src = imread("sunset.jpg", IMREAD_COLOR);
Mat img_dst = imread("lake.jpg", IMREAD_COLOR);
if (img_src.empty() || img_dst.empty()) {
cout << "Error loading images!" << endl;
return -1;
}
vector<Mat> hist_src, hist_dst, hist_result;
vector<Mat> hist_src_g, hist_dst_g, hist_result_g;
imshow("Source Image", img_src);
hist_src = calculateHistogram(img_src);
// For Grayscale Matching
Mat result_gray = histogramMatching(img_src, img_dst, hist_src_g, hist_dst_g, hist_result_g, false);
imshow("Source Image", convertToGrayscale(img_src));
imshow("Reference Image", convertToGrayscale(img_dst));
imshow("Matched Image (Grayscale)", result_gray);
imshow("Source Histogram (Grayscale)", drawHistogram(hist_src_g));
imshow("Reference Histogram (Grayscale)", drawHistogram(hist_dst_g));
imshow("Matched Histogram (Grayscale)", drawHistogram(hist_result_g));
// For Color Matching
Mat result_color = histogramMatching(img_src, img_dst, hist_src, hist_dst, hist_result, true);
imshow("Source Image (Color)", img_src);
imshow("Reference Image (Color)", img_dst);
imshow("Matched Image (Color)", result_color);
imshow("Source Histogram (Y Channel)", drawHistogram(hist_src));
imshow("Reference Histogram (Y Channel)", drawHistogram(hist_dst));
imshow("Matched Histogram (Y Channel)", drawHistogram(hist_result));
waitKey(0);
return 0;
}
'๐ฆAI > Computer Vision' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Computer Vision/OpenCV] 11. Spatial Filtering (0) | 2025.04.02 |
---|---|
[Computer Vision/OpenCV] 10. Spatial Filtering & Convolution ๊ฐ๋ (0) | 2025.03.31 |
[Computer Vision/OpenCV] 8. Histogram Equalization (0) | 2025.03.23 |
[Computer Vision/OpenCV] 7. Histogram Stretching (0) | 2025.03.23 |
[Computer Vision/OpenCV] 6. Histogram (0) | 2025.03.21 |