
1. 영상의 밝기 조절
밝기 조절

#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
#if 0
Mat dst;
dst = src + 50;
#else
Mat dst(src.rows, src.cols, src.type());
for (int j = 0; j < src.rows; j++) {
for (int i = 0; i < src.cols; i++) {
dst.at<uchar>(j, i) = saturate_cast<uchar>(src.at<uchar>(j, i) + 50); // saturate_cast를 통해 255값을 넘어가면 255값으로 cast 해준다.
}
}
#endif
imshow("src", src);
imshow("dst", dst);
waitKey();
}
실행 결과

2. 히스토그램 분석
히스토그램
영상의 픽셀 값 분포를 그래프의 형태로 표현한 것
예를 들어 그레이스케일 영상에서 각 그레이스케일 값에 해당하는 픽셀의 개수를 구하고 이를 막대 그래프의 형태로 표현
픽셀 값이 0~7 사이인 영상의 히스토그램 예

#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(void)
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
// 히스토그램
int hist[256] = {};
for (int y = 0; y < src.rows; y++) {
for (int x = 0; x < src.cols; x++) {
hist[src.at<uchar>(y, x)]++;
}
}
// 정규화된 히스토그램
int size = (int)src.total();
float nhist[256] = {};
for (int i = 0; i < 256; i++) {
nhist[i] = (float)hist[i] / size;
}
// 히스토그램 그래프 그리기
int histMax = 0;
for (int i = 0; i < 256; i++) {
if (hist[i] > histMax) histMax = hist[i];
}
Mat imgHist(100, 256, CV_8UC1, Scalar(255));
for (int i = 0; i < 256; i++) {
line(imgHist, Point(i, 100),
Point(i, 100 - cvRound(hist[i] * 100 / histMax)), Scalar(0));
}
imshow("src", src);
imshow("hist", imgHist);
waitKey();
}
실행 결과

calcHist

#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
Mat calcGrayHist(const Mat& img)
{
CV_Assert(img.type() == CV_8U);
Mat hist;
int channels[] = { 0 };
int dims = 1;
const int histSize[] = { 256 };
float graylevel[] = { 0, 256 };
const float* ranges[] = { graylevel };
calcHist(&img, 1, channels, noArray(), hist, dims, histSize, ranges);
return hist;
}
Mat getGrayHistImage(const Mat& hist)
{
CV_Assert(hist.type() == CV_32FC1);
CV_Assert(hist.size() == Size(1, 256));
double histMax = 0.;
minMaxLoc(hist, 0, &histMax);
Mat imgHist(100, 256, CV_8UC1, Scalar(255));
for (int i = 0; i < 256; i++) {
line(imgHist, Point(i, 100),
Point(i, 100 - cvRound(hist.at<float>(i, 0) * 100 / histMax)), Scalar(0));
}
return imgHist;
}
int main()
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
Mat hist = calcGrayHist(src);
Mat imgHist = getGrayHistImage(hist);
imshow("src", src);
imshow("hist", imgHist);
waitKey();
}
히스토그램 스트레칭
영상의 히스토그램이 그레이스케일 전 구간에서 걸쳐 나타나도록 변경하는 선형 변환 기법
특정 구간에 집중되어 나타난 히스토그램을 마치 고무줄을 늘이듯이 펼쳐서 그레이스케일 범위 전 구간에서 히스토그램이 골고루 나타나도록 변환
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
Mat calcGrayHist(const Mat& img)
{
CV_Assert(img.type() == CV_8U);
Mat hist;
int channels[] = { 0 };
int dims = 1;
const int histSize[] = { 256 };
float graylevel[] = { 0, 256 };
const float* ranges[] = { graylevel };
calcHist(&img, 1, channels, noArray(), hist, dims, histSize, ranges, true);
return hist;
}
Mat getGrayHistImage(const Mat& hist)
{
CV_Assert(!hist.empty());
CV_Assert(hist.type() == CV_32F);
double histMax = 0.;
minMaxLoc(hist, 0, &histMax);
Mat imgHist(100, 256, CV_8UC1, Scalar(255));
for (int i = 0; i < 256; i++) {
line(imgHist, Point(i, 100),
Point(i, 100 - cvRound(hist.at<float>(i) * 100 / histMax)), Scalar(0));
}
return imgHist;
}
int main()
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
double gmin, gmax;
minMaxLoc(src, &gmin, &gmax);
Mat dst = (src - gmin) * 255 / (gmax - gmin);
imshow("src", src);
imshow("dst", dst);
imshow("hist_src", getGrayHistImage(calcGrayHist(src)));
imshow("hist_dst", getGrayHistImage(calcGrayHist(dst)));
waitKey();
}
실행 결과

히스토그램 평활화


#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
Mat calcGrayHist(const Mat& img)
{
CV_Assert(img.type() == CV_8UC1);
Mat hist;
int channels[] = { 0 };
int dims = 1;
const int histSize[] = { 256 };
float graylevel[] = { 0, 256 };
const float* ranges[] = { graylevel };
calcHist(&img, 1, channels, noArray(), hist, dims, histSize, ranges, true);
return hist;
}
Mat getGrayHistImage(const Mat& hist)
{
CV_Assert(!hist.empty());
CV_Assert(hist.type() == CV_32FC1);
CV_Assert(hist.size() == Size(1, 256));
double histMax = 0.;
minMaxLoc(hist, 0, &histMax);
Mat imgHist(100, 256, CV_8UC1, Scalar(255));
for (int i = 0; i < 256; i++) {
line(imgHist, Point(i, 100),
Point(i, 100 - cvRound(hist.at<float>(i) * 100 / histMax)), Scalar(0));
}
return imgHist;
}
int main()
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
#if 1
Mat dst;
equalizeHist(src, dst);
#else
Mat dst(src.rows, src.cols, src.type());
int hist[256] = {};
for (int y = 0; y < src.rows; y++)
for (int x = 0; x < src.cols; x++)
hist[src.at<uchar>(y, x)]++;
int size = (int)src.total();
float cdf[256] = {};
cdf[0] = float(hist[0]) / size;
for (int i = 1; i < 256; i++)
cdf[i] = cdf[i - 1] + float(hist[i]) / size;
for (int y = 0; y < src.rows; y++) {
for (int x = 0; x < src.cols; x++) {
dst.at<uchar>(y, x) = uchar(cdf[src.at<uchar>(y, x)] * 255);
}
}
#endif
imshow("src", src);
imshow("dst", dst);
imshow("hist_src", getGrayHistImage(calcGrayHist(src)));
imshow("hist_dst", getGrayHistImage(calcGrayHist(dst)));
waitKey();
}
실행 결과
