영상의 밝기 조절

그레이스케일 영상 다루기

  • 많은 CV 알고리즘이 그레이스케일 영상을 사용
  • 그레이스케일 영상은 픽셀당 밝기 정보 하나만 포함으로 단순하고 메모리를 적게 사용하여 연산 시간을 감소시킴

  • OpenCV에서 그레이스케일 영상을 다루는 코드
Mat img1 = imread("lenna.bmp", IMREAD_GRAYSCALE);

Mat img2(rows, cols, CV_8UC1)

Mat img3("lenna.bmp", IMREAD_COLOR);

Mat img4;
cvtColor(img3, img4, COLOR_BGR2GRAY)

영상의 화소 처리(point processing)

  • 입력 영상의 특정 좌표 픽셀 값을 변경하여 출력 영상의 해당 좌표 픽셀 값으로 설정하는 연산

image

  • 반전, 밝기 조절, 명암비 조절, 이진화등이 가능함
  • 결과 영상의 픽셀 값이 정해진 범위에 있어야함
image imageimage

영상의 밝기 조절

  • 영상 전체 밝기를 일괄적으로 밝게 만들거나 어둡게 만드는 연산

image

  • 밝기 조절 수식

image

image

image

#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;
	}

	// Mat dst = src + 50; 대신 아래와 같이
	Mat dst;
	dst = src + 50; // add(Src,50,dst); 와 같음

	imshow("src", src);
	imshow("dst", dst);
	waitKey();
}

영상의 반전

  • 영상 내의 모든 픽셀 값을 각각 그레이스케일 최댓값에서 뺀 값으로 설정
    • 밝은 픽셀은 어둡게, 어두운 픽셀은 밝게 변경하는 연산
  • 컬러 영상에 대해서는 각각의 색상 성분에 대해 반전

image

  • 영상 반전 수식

image

image

#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;
	}

	Mat dst = 255 - src;

	imshow("src", src);
	imshow("dst", dst);
	waitKey();
}

영상의 밝기 조절 직접 구현

int main()
{
	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

#if 0
	Mat dst = src + 50;
	
#else
	Mat dst(src.rows, src.cols, src.type()); // src의 영상과 동일한 크기의 영상 dst 만들기

	for (int j = 0; j < src.rows; j++) {
		for (int i = 0; i < src.cols; i++) {
		
			int v = src.at<uchar>(j,i) - 50;
			// if (v > 255) v = 255;
			// if (v < 0) v = 0;
			// v = (v > 255) ? 255 : (v < 0) ? 0 : v;
			dst.at<uchar>(j, i) = saturate_cast<uchar>(src.at<uchar>(j, i) + 50); // src의 각 픽셀에 50을 더한 값을 dst에 저장
			// 행인 j 먼저 열인 i는 두번째
			
			// 포화 연산 함수를 사용하는 이유는?
			// uchar형인 src.at<uchar>(j,i)과 int형인 50을 더하면 int 형의 변수가 나오는데 dst.at<uchar>(j,i)는 또 uchar 형임
			// 두 개의 합인 int형이 uchar 형으로 변환이 되면서 값의 변화가 생김
            // src.at<uchar>이 210 로 설정하고 2개를 더하면 260이므로 256를 넘어버려서 uchar에는 저장이 안되서 260-256 = 4 값이 저장됨
            // 따라서 기존 이미지의 밝은 부분에 대해서 포화 연산이 제대로 적용되지 않아 이상한 값이 나오는 것을 확인 할 수 있음
            // 0 보다 작아질때도 마찬가지임
            // 따라서 saturate_cast() 포화 연산 함수를 사용해 간단하게 해결 할 수 있음
		}
	}
#endif

	imshow("src", src);
	imshow("dst", dst);
	waitKey();
}
  • 포화 연산 함수를 적용 안했을 때 결과

image

  • 포화 연산 함수를 적용했을 때 결과

image

평균 밝기 보정 프로그램

  • 입력 영상의 평균 밝기가 그레이스케일 128이 되도록 보정

  • 입력 영상에(128 - 평균 밝기) 값을 더함

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main(int argc, char* argv[])
{
	if (argc < 2) {
		cerr << "Usage: adjmean.exe <filename>" << endl;
		return -1;
	}

	Mat src = imread(argv[1], IMREAD_GRAYSCALE); // argv[1] 영상을 그레이스케일 형태로 불러와 src에 저장

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	int s = 0;
	
	for (int j = 0; j < src.rows; j++) {
		for (int i = 0; i < src.cols; i++) {
			s += src.at<uchar>(j, i); 	// 입력 영상인 src의 평균 밝기 구하기
		}
	}

	int m = s / (src.rows * src.cols);
	//int m = sum(src)[0] / src.total();
	//int m = mean(src)[0];

	cout << "Mean value: " << m << endl;

	Mat dst = src + (128 - m); 	// 평균 밝기가 128이 되도록 밝기 보정하기
	
	imshow("src", src);
	imshow("dst", dst);

	waitKey();
}
  • lenna.bmp의 평균 밝기는 124이므로 src에 4를 더해서 dst 값으로 저장한 것이므로 사람 눈으로 구별이 어려움

image

  • columbia.bmp의 평균 밝기는 84로 src에 44를 더해서 dst 값으로 저장한 것으로 입력 영상보다 전체적으로 밝아진 출력 영상을 확인할 수 있음

image

  • tiffany.bmp의 평균 밝기는 211이므로 src에 83을 빼서 dst 값으로 저장한 것으로 입력 영상보다 전체적으로 어두워진 출력 영상을 확인할 수 있음

image