이미지 명도, 대비 조절

이미지 명도(brightness)대비(contrast)를 조절하는 수치 연산을 설명한다. C++로 작성한 OpenCV 코드를 사용한다.


명도

명도는 이미지의 밝기이다.

오른쪽 이미지의 명도가 더 크다.

$$X = saturate(X + n)$$

grayscale 이미지($X$)는 0 ~ 255 값으로 이루어진 2차원 행렬이다. 0에 가까울수록 어둡고, 255에 가까울수록 밝다. 따라서 행렬 각 원소에 대해 값을 더하거나 빼는 방법을 이용해 밝기를 조절한다. 여기서 saturate 연산은 값이 0보다 작으면 0으로, 255보다 크면 255로  범위를 조정하는 함수다. 픽셀 값은 unsigned char로 항상 0 ~ 255 사이 값을 가지기 때문에 반드시 saturate 연산을 거쳐야 한다.

using namespace cv;

Mat src = imread("img.jpg", IMREAD_GRAYSCALE);
if (src.empty()) return;

int change = 100; // random int
Mat dst = src + change;

OpenCV 연산자는 값이 0 ~ 255 사이가 되도록 내부에서 포화(saturate) 연산을 수행한다.

컬러 이미지 명도

컬러 이미지를 불러와서 위와 같은 방식으로 밝기를 올려보자.

imread("img.jpg", IMREAD_COLOR);

그럼 사진 색상이 왜곡되는 경우가 발생한다. 이미지에 특정 채널(색상)이 강하기 때문이다. 이때는 HSV 이미지로 불러와야 한다. HSV는 색상(Hue), 채도(Saturation), 명도(Value)를 분리해 다루기 때문에 색상 왜곡 없이 밝기를 조절할 수 있다.

using namespace cv;

imread("img.jpg", IMREAD_COLOR);
if (src.empty()) return;

// COLOR to HSV
Mat hsv_image;
cvtColor(src, hsv_image, COLOR_BGR2HSV);

vector<Mat> hsv_channels;
split(hsv_image, hsv_channels);

int brightness_value = 100; // random int
hsv_channels[2] = min(hsv_channels[2] + brightness_value, 255);

merge(hsv_channels, hsv_image);

// HSV to COLOR
Mat dst;
cvtColor(hsv_image, dst, COLOR_HSV2BGR);


대비

대비는 밝은 영역과 어두운 영역 사이의 차이이다. 밝은 부분과 어두운 부분의 차이가 클수록 대비가 크다.

왼쪽 이미지의 대비가 더 크다.

$$X = saturate(X \cdot \alpha)$$

using namespace cv;

Mat src = imread("img.jpg", IMREAD_COLOR);
if (src.empty()) return;

float alpha1 = 1.3f;
float alpha2 = 0.5f;

Mat dst1 = src * alpha1;
Mat dst2 = src * alpha2;

곱하는 $\alpha$가 0 ~ 1 사이면 대비가 감소하고, 1 이상이면 대비가 증가한다. 하지만 곱셈은 값이 급격하게 증가한다. 값이 조금만 커져도 최댓값인 255를 넘기 쉽다. 따라서 조금 더 효과적인 방법을 사용해야 한다.

대비 증감 조절

$$X = X + (X - c) \cdot \alpha$$

$$X = saturate(X)$$

  • $c$: 기준값. 예를 들어, 픽셀의 평균 또는 128(= $round(255 / 2)$)을 사용할 수 있다.
  • $\alpha$: 변화율. -1 ~ 0 사이일 때 대비가 감소하고, 0 보다 크면 대비가 증가한다.
using namespace cv;

Mat src = imread("img.jpg", IMREAD_COLOR);
if (src.empty()) return;

float alpha1 = 1.5f;
float alpha2 = -0.5f;
int criteria = 128;

Mat dst1 = src + (src - criteria) * alpha1;
Mat dst2 = src + (src - criteria) * alpha2;