πŸ“˜ Programming/OpenCV

[OpenCV] νžˆμŠ€ν† κ·Έλž¨ λͺ…μ„Έν™” μ†ŒμŠ€ μ½”λ“œ (C++)

ν•œμ½”λ”© 2020. 12. 31. 14:22
728x90
728x90
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include <Windows.h>
#include <string.h>

using namespace cv;
using namespace std;

Mat histogram, histogramImage;	// λ ˆλ‚˜ μ˜μƒκ³Ό λ ˆλ‚˜ μ˜μƒμ˜ νžˆμŠ€ν† κ·Έλž¨
Mat histogram2, histogramImage2;	// CPU μ˜μƒκ³Ό CPU μ˜μƒμ˜ νžˆμŠ€ν† κ·Έλž¨
Mat histogramNew;				// μ—­ν‰ν™œν™” λ˜μ–΄ μƒμ„±λœ 룩업-ν…Œμ΄λΈ”
Mat image, image2;				// λΆˆλŸ¬μ˜€λŠ” 이미지 1,2
Mat histogramSum, histogramSum2;	// λ ˆλ‚˜, CPU μ˜μƒμ„ ν‰ν™œν™” ν•œ κ²°κ³Όλ₯Ό λ‹΄λŠ” ν…Œμ΄λΈ”1, 2
Mat tmp, tmp2;

// params : image
// νžˆμŠ€ν† κ·Έλž¨μ„ 확인할 μ˜μƒ

// params : histogram
// κ³„μ‚°λ˜μ–΄ λ°˜ν™˜λ  νžˆμŠ€ν† κ·Έλž¨

// params : bins
// νžˆμŠ€ν† κ·Έλž¨μ˜ xμΆ• μš”μ†Œ 수

// params : range_max
// νžˆμŠ€ν† κ·Έλž¨μ˜ xμΆ• μ΅œλŒ€ μš”μ†Œ 수

// 과제 : μ—­ν‰ν™œν™” κ³Όμ •λ§Œ μΆ”κ°€ν•˜λ©΄ λœλ‹€.

// 이미지에 λŒ€ν•œ νžˆμŠ€ν† κ·Έλž¨ 계산
void CalcHistogram(Mat image, Mat& histogram, int bins, int range_max = 256) {

	// νžˆμŠ€ν† κ·Έλž¨ 값을 μ €μž₯ν•  Mat μΈμŠ€ν„΄μŠ€ 생성, 0으둜 μ΄ˆκΈ°ν™”
	histogram = Mat(bins, 1, CV_32F, Scalar(0));

	// xμΆ• κ°’ 간격
	float gap = (float)range_max / bins;

	// μ˜μƒμ„ μˆœνšŒν•˜λ©΄μ„œ ν™”μ†Œλ₯Ό 처리
	for (int i = 0; i < image.rows; i++) {
		for (int j = 0; j < image.cols; j++) {
			// var : workPixel
			// μ²˜λ¦¬ν•  ν™”μ†Œ
			// auto : 컴파일 μ‹œμ μ—μ„œ μ»΄νŒŒμΌλŸ¬κ°€ λ¬Έλ§₯을 보고 μžλ™μœΌλ‘œ λ³€μˆ˜ νƒ€μž…μ„ 지정, ν…œν”Œλ¦Ώκ°™μ€ 곳에 μ‚¬μš©μ‹œ 편리
			auto workPixel = image.at<uchar>(i, j);

			// ν•΄λ‹Ή ν™”μ†Œκ°€ λ“€μ–΄κ°ˆ μ˜μ—­μ„ 계산
			int idx = (int)(workPixel / gap);

			// νžˆμŠ€ν† κ·Έλž¨μ— μ˜μ—­μ— 값을 λˆ„μ 
			histogram.at<float>(idx)++;
		}
	}
}

string OpenFileDialog()
{
	char name[MAX_PATH] = { 0, };
	OPENFILENAMEA ofn;
	ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(OPENFILENAMEA);
	ofn.hwndOwner = NULL;
	ofn.lpstrFilter = "λͺ¨λ“ νŒŒμΌ(*.*)\0*.*\0";
	ofn.lpstrFile = name;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = "";

	string strName;

	if (GetOpenFileNameA(&ofn))
		strName = name;

	return strName;
}

// νžˆμŠ€ν† κ·Έλž¨
void GetHistogramImage(Mat histogram, Mat& histogramImage, Size size = Size(256, 200)) {

	// 255둜 빈 νžˆμŠ€ν† κ·Έλž¨ μ˜μƒμ„ 생성
	histogramImage = Mat(size, CV_8U, Scalar(255));

	// νžˆμŠ€ν† κ·Έλž¨ ν•œ μ˜μ—­μ„ ν‘œν˜„ν•  λ„ˆλΉ„
	float gap = (float)(histogramImage.cols / histogram.rows);

	// νžˆμŠ€ν† κ·Έλž¨μ˜ λΉˆλ„κ°’μ„ μ΅œμ†Œκ°€ 0, μ΅œλŒ€κ°€ 좜λ ₯ μ˜μƒμ˜ 높이값을 갖도둝 μ‘°μ •
	normalize(histogram, histogram, 0, histogramImage.rows, NORM_MINMAX);

	for (int i = 0; i < histogram.rows; i++) {
		// νžˆμŠ€ν† κ·Έλž¨ λ§‰λŒ€μ‚¬κ°ν˜•μ˜ μ‹œμž‘κ³Ό 끝의 Xμ’Œν‘œ
		float sx = i * gap;
		float ex = (i + 1) * gap;

		// μ‚¬κ°ν˜•μ˜ 쒌츑 ν•˜λ‹¨μ’Œν‘œμ™€ 우츑 상단 μ’Œν‘œ
		Point2f pt_Ib(sx, 0), pt_rt(ex, histogram.at<float>(i));

		// μ‚¬κ°ν˜•μ΄ 높이 값을 가지면 μ‚¬κ°ν˜•μ„ κ·Έλ¦Ό
		if (pt_rt.y > 0)
			rectangle(histogramImage, pt_Ib, pt_rt, Scalar(0), -1);
	}

	// XμΆ• κΈ°μ€€μœΌλ‘œ μ˜μƒμ„ λ’€μ§‘μŒ (μ˜μƒμ—μ„œ μ’Œν‘œκ³„λŠ” μ•„λž˜κ°€ μ–‘μ˜ 값을 κ°€μ§€λ―€λ‘œ, μœ„κ°€ μ–‘μ˜ 값을 κ°–λŠ” μ’Œν‘œκ³„λ‘œ λ³€ν™˜)
	flip(histogramImage, histogramImage, 0);
}

// νžˆμŠ€ν† κ·Έλž¨1 보기
void ShowHistogram1() {
	auto fileName = OpenFileDialog();
	image = imread(fileName, IMREAD_GRAYSCALE);

	if (image.empty()) {
		cout << "파일 읽기 μ‹€νŒ¨" << endl;
		return;
	}

	CalcHistogram(image, histogram, 256);

	cout << histogram.t() << endl;
	GetHistogramImage(histogram, histogramImage);

	imshow("원본 μ˜μƒ 1", image);
	imshow("νžˆμŠ€ν† κ·Έλž¨ 1", histogramImage);
	waitKey();
}

// νžˆμŠ€ν† κ·Έλž¨2 보기
void ShowHistogram2() {
	auto fileName2 = OpenFileDialog();
	image2 = imread(fileName2, IMREAD_GRAYSCALE);

	if (image2.empty()) {
		cout << "파일 읽기 μ‹€νŒ¨" << endl;
		return;
	}

	CalcHistogram(image2, histogram2, 256);

	cout << histogram2.t() << endl;
	GetHistogramImage(histogram2, histogramImage2);
	imshow("원본 μ˜μƒ 2", image2);
	imshow("νžˆμŠ€ν† κ·Έλž¨ 2", histogramImage2);
	waitKey();
}

// ν‰ν™œν™” μž‘μ—… - ν‰ν™œν™” κΈ°λ²•μœΌλ‘œ λͺ…μ•” 뢄포가 λΉˆμ•½ν•œ μ˜μƒμ„ κ· μΌν•˜κ²Œ λ§Œλ“¬
void CalcNormalizedCumulativeFrequency(Mat histogram, Mat& histogramSum) {

	// νžˆμŠ€ν† κ·Έλž¨κ°’μ„ μ €μž₯ν•  MatμΈμŠ€ν„΄μŠ€ 생성, 0으둜 μ΄ˆκΈ°ν™”
	histogramSum = Mat(histogram.rows, 1, CV_32F, Scalar(0));

	// νžˆμŠ€ν† κ·Έλž¨μ˜ λˆ„μ  합을 계산
	float sum = 0;
	for (int i = 0; i < histogram.rows; i++) {
		sum += histogram.at<float>(i);
		histogramSum.at<float>(i) = sum;
	}

	// μ •κ·œν™”
	for (int i = 0; i < histogramSum.rows; i++) 
		histogramSum.at<float>(i) = histogramSum.at<float>(i) / sum * 255.0;
}

// λ ˆλ‚˜μ™€ CPUμ˜μƒμ„ λͺ¨λ‘ ν‰ν™œν™” ν•œ λ‹€μŒ, ν‰ν™œν™”λœ CPUλ₯Ό μ—­ν‰ν™œν™”ν•˜μ—¬ λ£©μ—…ν…Œμ΄λΈ”μ„ λ§Œλ“€κ³  λ ˆλ‚˜μ˜ λ§€ν•‘ν•˜λŠ” ν•¨μˆ˜
void HistogramEqualizing() {

	// 1. λ ˆλ‚˜ μ˜μƒ ν‰ν™œν™” μž‘μ—…

	// νžˆμŠ€ν† κ·Έλž¨ 생성
	CalcHistogram(image, histogram, 256);

	// λˆ„μ  λΉˆλ„ 수 계산 및 μ •κ·œν™”
	CalcNormalizedCumulativeFrequency(histogram, histogramSum);

	// μ •κ·œν™”λœ λˆ„μ  λΉˆλ„ 수λ₯Ό 기반으둜 기쑴의 ν™”μ†Œλ₯Ό λ³€ν™˜
	// ν™”μ†Œκ°’ μ œμ–΄
	tmp = Mat(image.size(), image.type());

	for (int i = 0; i < image.rows; i++) 
		for (int j = 0; j < image.cols; j++) 
			tmp.at <uchar>(i, j) = (uchar)histogramSum.at<float>(image.at<uchar>(i, j));

	// γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘

	// 2, cpu 사진 ν‰ν™œν™” μž‘μ—…

	// νžˆμŠ€ν† κ·Έλž¨ 생성
	CalcHistogram(image2, histogram2, 256);

	// λˆ„μ  λΉˆλ„ 수 계산 및 μ •κ·œν™”
	CalcNormalizedCumulativeFrequency(histogram2, histogramSum2);

	// μ •κ·œν™”λœ λˆ„μ  λΉˆλ„ 수λ₯Ό 기반으둜 기쑴의 ν™”μ†Œλ₯Ό λ³€ν™˜
	// ν™”μ†Œκ°’ μ œμ–΄
	tmp2 = Mat(image2.size(), image2.type());

	for (int i = 0; i < image2.rows; i++) 
		for (int j = 0; j < image2.cols; j++) 
			tmp2.at <uchar>(i, j) = (uchar)histogramSum2.at<float>(image2.at<uchar>(i, j));

	// γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘γ…‘
}

// μ—­ν‰ν™œν™”μ™€ λ£©μ—…ν…Œμ΄λΈ” λ§€ν•‘ν•˜λŠ” ν•¨μˆ˜
void GetResult()
{
	// 3. cpu 사진 μ—­ν‰ν™œν™” μž‘μ—…

	// νžˆμŠ€ν† κ·Έλž¨ 생성
	CalcHistogram(tmp2, histogramNew, 256);

	int image_1 = 254;
	int image_2 = 255;

	// μ •κ·œν™”λœ 값을 λͺ…암에 ν•΄λ‹Ήν•˜λŠ” 값을 μ—­ν•¨μˆ˜λ‘œ λ§Œλ“€μ–΄μ„œ 
	// histogramNew에 μ—­ν•¨μˆ˜λ‘œ λ§€ν•‘λœ μ—­ν‰ν™œν™” ν…Œμ΄λΈ”μ„ μ €μž₯ν•œλ‹€.

	while (image_1 >= 0) {	// λͺ¨λ‘ λ§€ν•‘ν•˜λ©΄ μŠ€νƒ‘ (image_1 이 0이되면 μ—­ν•¨μˆ˜ 맀핑이 λλ‚œλ‹€.)

		for (int i = histogramSum2.at<float>(image_1); i <= histogramSum2.at<float>(image_2); i++)
			histogramNew.at<float>(i) = image_2;

		image_2 = image_1--;
	}

	/*
		image_2	= 255 -> 254 -> 253 ------> 1
		image_1 = 254 -> 253 -> 252 ------> 0
		new = 0 -> 254 -> 253 ------------> 2
	*/

	// 4. 룩업-ν…Œμ΄λΈ” 맀핑 μž‘μ—…

	// μ •κ·œν™”λœ λˆ„μ  λΉˆλ„ 수λ₯Ό 기반으둜 기쑴의 ν™”μ†Œλ₯Ό λ³€ν™˜
	// ν™”μ†Œκ°’ μ œμ–΄
	auto result = Mat(tmp.size(), tmp.type());

	// 룩업-ν…Œμ΄λΈ”μ„ 톡해 λ ˆλ‚˜μ˜ 사진을 λͺ…μ„Έν™”ν•œλ‹€.
	for (int i = 0; i < tmp.rows; i++)
		for (int j = 0; j < tmp.cols; j++)
			result.at <uchar>(i, j) = (uchar)histogramNew.at <float>(tmp.at<uchar>(i, j));

	// 처리된 μ˜μƒμ˜ νžˆμŠ€ν† κ·Έλž¨ 계산
	CalcHistogram(result, histogramNew, 256);
	GetHistogramImage(histogramNew, histogramImage);

	// λͺ…μ„Έν™” κ²°κ³Ό 좜λ ₯
	imshow("λͺ…μ„Έν™” κ²°κ³Ό μ˜μƒ", result);
	imshow("λͺ…μ„Έν™” κ²°κ³Ό νžˆμŠ€ν† κ·Έλž¨", histogramImage);
	waitKey();
}

int main()
{
	ShowHistogram1();	// νžˆμŠ€ν† κ·Έλž¨1 보기
	ShowHistogram2();	// νžˆμŠ€ν† κ·Έλž¨2 보기
	HistogramEqualizing();	// 두 μ˜μƒ ν‰ν™œν™” μž‘μ—…
	GetResult();	// μ—­ν‰ν™œν™”λ‘œ λ£©μ—…ν…Œμ΄λΈ” 생성 ν›„ λ ˆλ‚˜ μ˜μƒμ˜ 맀핑
}

 

 

728x90
λ°˜μ‘ν˜•