자세한 설명은 주석처리함
출처 : 책, OpenCV 4 로 배우는 컴퓨터 비전과 머신러닝
영상의 밝기를 100만큼 증가하기
void brightness1(){
Mat img3 = imread("lenna.bmp", IMREAD_COLOR);
Mat img4;
cvtColor(img3, img4, COLOR_BGR2GRAY); // 컬러 영상으로 변환
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
// 밝기 조절과 같은 작업을 수행할 때 덧셈 연산자 재정의를 사용하여 코드를 작성하는 것이 더욱 빠르고 간편
Mat dst = src + 100; // 내부적으로 포화연산이 일어남. x > 255 일 때 x=255
//dst = src - 100; // x < 0 일 때 x=0
imshow("src", src);
imshow("dst", dst);
waitKey(0);
}
포화연산을 고려하지 않은 영상의 밝기 증가
void brightness2() {
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
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) = src.at<uchar>(j, i) + 100;
// Mat 행렬의 원소 값 참조 방법을 사용하면 어렵지 않게 밝기 조절을 직접 구현할 수 있지만
// 포화연산이 자동으로 적용되진 않음
}
}
// 밝은 픽셀 주변에서 급격하게 어두운 픽셀이 나타나는 것은 오버 플로우가 발생했기 때문
// 정수 256을 16진수로 표현하면 0x100이고, 이 값을 unsigned char 자료형에 대입하면
// 하위 1바이트만 대입되기 때문에 변수 a에는 0x00이 저장됨. 257을 대입하려고 하면 실제로는 0x01이 저장됨
// 255보다 큰 값이 되는 픽셀은 오히려 0에 가까운 어두운 픽셀로 바뀌게 되는 것
imshow("src", src);
imshow("dst", dst);
waitKey(0);
}
포화연산을 고려한 영상의 밝기 증가
void brightness3() {
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
Mat dst(src.rows, src.cols, src.type());
// 포화연산을 고려한 영상의 밝기 증가
for (int j = 0; j < src.rows; j++) {
for (int i = 0; i < src.cols; i++) {
//int v = src.at<uchar>(j, i) + 100;
//dst.at<uchar>(j, i) = saturate_cast<uchar>(v);
dst.at<uchar>(j, i) = saturate_cast<uchar>(src.at<uchar>(j, i) + 100);
}
}
imshow("src", src);
imshow("dst", dst);
waitKey(0);
}
트랙바를 이용한 영상의 밝기 조절
void on_brightness(int pos, void* userdata) {
Mat src = *(Mat*)userdata;
Mat dst = src + pos;
imshow("dst", dst);
}
void brightness4() {
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
namedWindow("dst");
createTrackbar("Brightness", "dst", 0, 100, on_brightness, (void*)&src);
on_brightness(0, (void*)&src); // 트랙바를 조절하지 않더라도 프로그램 실행시에 imshow()구문이 실행되도록 강제로 콜백함수 호출
waitKey(0);
destroyAllWindows();
}
기본적인 영상의 명암비 증가
void contrast1() {
// 명암비란 영상에서 밝은 영역과 어두운 영역 사이에 드러나는 밝기 차이의 강도를 의미함
// 명암 대비 또는 콘트라스트(contrast)라고도 함
// 영상이 전반적으로 어둡거나 또는 전반적으로 밝은 픽셀로만 구성된 경우, 명암비가 낮다고 표현함
// 반면에 밝은 영역과 어두운 영역이 골고루 섞여 있는 영상은 명암비가 높다고 말함
// 명암비가 낮은 영상은 객체 간의 구분이 잘 되지 않아서 전반적으로 흐릿하게 느껴짐
// 명암비가 높은 영상은 사물의 구분이 잘 되며 선명한 느낌을 줌
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
float s = 2.f;
Mat dst = s * src;
// OpenCV는 C/C++ 실수형 자료형과 Mat 객체 사이의 곱셈 연산자 재정의도 제공함
// 이때 결과 행렬에 대해 포화 연산도 함께 수행함
// 흰색으로 나타나는 영역이 너무 많아 사물의 윤곽 구분이 더 어려워짐
// 사실상 픽셀 값에 단순히 상수를 곱하여 명암비를 조절하는 방식은 실전에서는 잘 사용되지 않음
imshow("src", src);
imshow("dst", dst);
waitKey(0);
destroyAllWindows();
}
효과적인 영상의 명암비 조절
void contrast2() {
// 효과적인 명암비 조절 방법 : 명암비를 효과적으로 높이기 위해서는 밝은 픽셀은 더욱 밝게, 어두운 픽셀은 더욱 어두워지게 변경
// 그레이스케일 범위 중간값인 128을 기준으로 설정할 수도 있고, 입력 영상의 평균 밝기를 구하여 기준으로 삼을 수도 있음
// 입력 영상의 픽셀 값이 128보다 크면 더욱 밝게 만들고, 128보다 작으면 픽셀 값을 더 작게 만드는 방식
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
float alpha = 1.f;
Mat dst = src + (src - 128) * alpha;
// 이 수식은 src 픽셀값이 128일 때 dst가 128이어서 항상 (128, 128) 좌표를 지나감
// 그리고 alpha 에 의해 기울기가 변경됨
// α > 0이면 기울기가 1보다 큰 직선의 방정식이며, 이는 명암비를 증가시키는 변환 함수임
//float alpha = 1.f;
//cout << "mean(src)" << mean(src) << endl;
//Mat dst = src + (src - mean(src)) * alpha;
//α 의 범위가 α > 0이면 기울기가 1보다 큰 직선의 방정식이며, 이는 명암비를 증가시키는 변환 함수
//float alpha = 2.f;
//Mat dst = src + (src - 128) * alpha;
//float alpha = -0.5f;
//Mat dst = src + (src - 128) * alpha;
//α의 범위가 - 1 < α < 0이면 기울기가 0부터 1 사이의 직선이 되며, 이는 명암비를 감소시키는 변환 함수
imshow("src", src);
imshow("dst", dst);
waitKey(0);
destroyAllWindows();
}
그레이스케일 영상의 히스토그램 그래프 그리기
// 영상의 히스토그램(Histogram)이란 영상의 픽셀값 분포를 그래프 형태로 표현한 것을 의미함
// 히스토그램 그래프에서 가로축을 히스토그램의 빈(bin)이라고 함
// 그레이스케일 영상의 경우 256개의 빈을 갖는 히스토그램을 구하는 것이 일반적
Mat calcGrayHist(const Mat& img) {
CV_Assert(img.type() == CV_8UC1);
// 매크로 함수. 인자가 false이면 에러가 발생하며 프로그램 종료
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);
/*
void calcHist(const Mat * images, int nimages,
const int* channels, InputArray mask,
OutputArray hist, int dims, const int* histSize,
const float** ranges, bool uniform = true, bool accumulate = false);
*/
// images : 입력영상의 주소
// nimages : 입력영상 개수
// channels : 히스토그램을 구할 채널을 나타내는 정수형 배열
// mask : 마스크 영상. mask 인자에 Mat() 또는 noArray()를 지정하면 입력 영상 전체에 대해 히스토그램을 구함
// hist : 출력 히스토그램. CV_32F 깊이를 사용하는 dims-차원의 행렬
// dims : 출력 히스토그램의 차원 수
// histSize : 각 차원의 히스토그램 배열 크기를 나타내는 배열(각 차원의 히스토그램 빈 개수를 나타내는 배열)
// ranges : 각 차원의 히스토그램 범위. 등간격(uniform=true) 히스토그램이면 ranges[i]는 각 차원의 최솟값과 최댓값으로 구성된 배열
// [최솟값, 최대값)범위 : 대괄호는 포함을 의미하고 소괄호는 포함하지 않음을 의미. ex.변수 x의 범위가 [a,b) 라는 것은 a <= x < b
// 비등간격(uniform=false) 히스토그램이면 ranges[i]는 각각의 구역을 나타내는 histSize[i]+1개의 원소로 구성된 배열
// uniform : 히스토그램 빈의 간격이 균등한지 나타내는 플래그. default true
// accumulate : 누적 플래그, 이 값이 true이면 hist 배열을 초기화하지 않고 누적하여 히스토그램을 계산. default false
return hist; // 반환되는 hist는 CV_32FC1 타입을 갖는 256×1 크기의 행렬
}
Mat getGrayHistImage(const Mat& hist) {
// 히스토그램 행렬을 막대그래프 형태로 나타내려면 직접 hist 행렬을 참조하여 막대그래프 영상을 생성해야함
CV_Assert(hist.type()==CV_32FC1);
CV_Assert(hist.size() == Size(1, 256));
double histMax;
minMaxLoc(hist, 0, &histMax); // hist 행렬 원소의 최댓값을 histMax 변수에 저장. 최솟값은 관심이 없으므로 두번째 인자는 0
Mat imgHist(100, 256, CV_8UC1, Scalar(255));
// 흰색으로 초기화된 100x256(막대그래프 길이가 100픽셀이 되도록) 크기의 새영상 imgHist를 생성
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));
// 반복문과 line함수를 사용하여 각각의 빈에 대한 히스토그램 그래프를 그림
// 최댓값은 100픽셀에 해당하는 검은색 직선이 그려지고 나머지 막대는 100픽셀보다 짧은 길이의 직선으로 표현됨
}
return imgHist; // hist 행렬로부터 구한 히스토그램 영상을 반환
}
void histogram() {
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
//Mat hist = calcGrayHist(src);
//Mat hist_img = getGrayHistImage(hist);
imshow("src", src);
//imshow("srcHist", hist_img);
imshow("srcHist", getGrayHistImage(calcGrayHist(src)));
waitKey(0);
destroyAllWindows();
}
'OpenCV' 카테고리의 다른 글
OpenCV ] 유용한 함수들. sum(), mean(), minMaxLoc(), normalize(), cvRound() (0) | 2022.08.18 |
---|---|
OpenCV ] 연산 시간 측정, TickMeter 클래스 또는 getTickCount() 와 getTickFrequency() 함수 사용 (0) | 2022.08.18 |
OpenCV ] setTo(), copyTo() 함수와 마스크 연산 (0) | 2022.08.18 |
OpenCV ] 데이터 파일 입출력 (0) | 2022.08.18 |
OpenCV ] 트랙바 (슬라이더 컨트롤) 사용하기 (0) | 2022.08.18 |