OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝/ 영상의 밝기와 명암비 조절

영상의 밝기 조절

그레이스케일 영상 다루기

  • 과거에 개발된 영상 처리 알고리즘이 주로 그레이스케일 영상을 대상으로 개발되었기 때문에, 특별히 컬러 정보를 이용해야 하는 경우가 아니면 컬러 영상도 그레이스케일 영상으로 변환해서 사용하는 경우가 많다.
    • 컬러 영상은 3개 채널을 갖고 있기 때문에 그레이스케일 영상보다 3배 더 수행 시간이 길다.

영상의 밝기 조절

  • 영상의 밝기(brightness) 조절이란 영상의 전체적인 밝기를 조절하여 좀 더 밝거나 어두운 영상을 만드는 작업으로, 영상의 밝기를 조절하려면 입력 영상의 모든 픽셀에 일정 값을 더하거나 빼는 작업을 수행한다.
  • 영상의 밝기 조절 수식은 다음과 같다.

dst(x, y) = src(x, y) + n

  • src는 입력 영상, dst는 출력 영상이다. n이 +면 밝기는 증가하고, -면 밝기는 감소한다.

  • 픽셀 값은 0-255 사이의 값을 갖기 때문에 255보다 큰 값이 나오면 255로 맞춰지고, 0보다 작은 값이 나오면 0으로 맞춰야한다. 이런 연산을 OpenCV에서는 포화(saturate) 연산이라고 한다.

saturate(x) = \begin{cases} 0 & x < 0 \\ 255 & x > 255 \\ x & otherwise \end{cases}

dst(x, y) = saturate(src(x, y) + n)

영상의 명암비 조절

기본적인 명암비 조절 방법

  • 명암비란 영상에서 밝은 영역과 어두운 영역 사이에 드러나는 밝기 차이의 강도를 의미하고, 명암 대비 또는 큰트라스트(contrast)라고 한다.
    • 영상이 전반적으로 어둡거나 전반적으로 밝은 픽셀로만 구성된 경우 명암비가 낮다고 하고 밝은 영역과 어두운 영역이 골고루 섞인 경우 명암비가 높다고 한다.
  • 밝기 조절이 영상의 모든 픽셀에 대해 덧셈 연산을 하는 것에 비해 명암비 조절은 곱셈 연산을 사용하여 구현한다.

dst(x, y) = saturate(s \cdot src(x, y))

  • src는 입력 영상, dst는 출력 영상이다. s는 0보다 큰 양의 실수로 s가 1보다 작은 경우 명암비가 낮아지고 1보다 큰 경우 명암비가 높아지는 효과가 있다.

효과적인 명암비 조절 방법

  • 명암비를 효과적으로 조절하려면 밝은 픽셀은 더욱 밝게, 어두운 픽셀은 더욱 어둡게 변경해야 한다.
    • 이때 픽셀 값이 밝고 어둡다는 기준은 그레이스케일 범위의 중간값인 128을 기준으로 하거나, 입력 영상의 평균 밝기를 구하여 기준으로 삼을 수도 있다.
    • 그레이스케일 범위의 중간값인 128을 기준으로 명암비를 조절하는 방식은 다음과 같다.

dst(x, y) = src(x, y) + (src(x, y) - 128) \cdot \alpha

  • \alpha 는 -1보다 크거나 같은 실수

히스토그램 분석

히스토그램 구하기

  • 영상의 히스토그램(histogram)이란 영사으이 픽셀값 분포를 그래프 형태로 표현한 것을 말한다.
    • 그레이스케일 영상의 경우, 각 그레이스케일 값에 해당하는 픽셀의 개수를 구하고 이를 막대 그래프로 표현함으로써 히스토그램을 구할 수 있다.
    • 컬러 영상일 경우 세 개의 색상 성분 조합에 따른 픽셀 개수를 계산하여 히스토그램을 구할 수 있다.
  • 아래와 같이 영상의 픽셀 정보가 주어졌다면 히스토그램은 그 오른쪽의 그래프와 같다.

  • 히스토그램 그래프에서 가로축을 히스토그램의 빈(bin)이라고 한다.
    • 고로 위 그림의 히스토그램에서 빈의 개수는 8개가 된다.
  • 히스토그램의 빈 개수가 항상 픽셀 값 범위와 같아야 하는 것은 아닌데, 경우에 따라 히스토그램의 빈 개수를 픽셀 값 범위보다 작게 설정할 수도 있다.
    • 예컨대 위 이미지의 8개의 밝기 값을 가질 수 있는 영상에서 히스토그램의 빈의 개수를 아래와 같이 4로 설정할 수도 있다.

  • OpenCV에서 영상의 히스토그램을 구할 때는 calcHist() 함수를 이용한다. 
    • calcHist() 함수는 한 장의 영상 뿐만 아니라 여러 장의 영상으로부터 히스토그램을 구할 수도 있고, 여러 채널로부터 히스토그램을 구할 수도 있고, 빈 개수를 조절할 수도 있다.
  • (calcHist() 함수에 대한 설명과 예제 코드 생략)

히스토그램 스트레칭

  • 히스토그램 스트레칭(histogram stretching)은 영상의 히스토그램이 그레이스케일 전 구간에 걸쳐서 나타나도록 변경하는 선형 변환 기법이다.
    • 보통 명암비가 낮은 영상은 시스토그램이 특정 구간에 집중되어 나타나게 되는데, 이러한 히스토그램을 고무줄 잡아 늘이듯이 펼쳐서 히스토그램 그래프가 그레이스케일 전 구간에서 나타나도록 변환하는 기법이다.
    • 히스토그램 스트레칭을 수행한 영상은 명암비가 높아지기 때문에 대체로 보기 좋은 사진이 된다.

dst(x, y) = { src(x, y) - G_{min} \over G_{max} - G_{min}} \times 255

  • 위 식에서 src와 dst는 각각 입력, 출력 영상을 의미하고, G_{max}, G_{min} 는 입력 영상의 픽셀 값 중에서 가장 큰 그레이스케일 값과 가장 작은 그레이스케일 값을 의미한다.
  • 아래 이미지는 왼쪽의 이미지를 히스토그램 스트레칭 하여 오른쪽의 결과를 얻은 것이다.

  • 히스토그램 스트레칭을 위한 함수는 OpenCV에서 제공하지 않는다.
    • 그러나 minMaxLoc() 함수 등을 이용하여 쉽게 구현할 수 있다.
void histogram_stretching()
{
Mat src = imread("hawkes.bmp", IMREAD_GRAYSCALE);

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

double gmin, gmax;
minMaxLoc(src, &gmin, &gmax);

Mat dst = (src - gmin) * 255 / (gmax - gmin);

imshow("src", src);
imshow("srcHist", getGrayHistImage(calcGrayHist(src)));
imshow("dst", dst);
imshow("dstHist", getGrayHistImage(calcGrayHist(dst)));

waitKey();
destroyAllWindows();
}

히스토그램 평활화

  • 히스토그램 평활화(histogram equalization)은 히스토그램 스트레칭과 더불어 영상의 픽셀 값 분포가 그레이스케일 전체 영역에서 골고루 나타나도록 변경하는 알고리즘의 하나이다.
    • 히스토그램 평활화는 히스토그램 그래프에서 특정 그레이스케일 값 근방에서 픽셀 분포가 너무 많이 뭉쳐 있는 경우 이를 넓게 펼쳐 주는 방식으로 픽셀 값 분포를 조절한다.
    • 히스토그램 평활화는 히스토그램 균등화 또는 히스토그램 평탄화라고도 한다.
  • 히스토그램 평활화를 구현하기 위해서는 먼저 히스토그램을 구해야 한다. 
    • 그레이스케일 영상의 히스토그램을 그레이스케일 값 g에 대한 함수 h(g)로 표현하면, h(g)는 영상에서 그레이스케일 값이 g인 픽셀 개수를 나타낸다.
    • 히스토그램 평활화를 계산하기 위해서는 h(g)로부터 히스토그램 누적 함수 H(g)를 구해야 한다. 히스토그램 누적 함수 H(g)는 다음 수식으로 정의된다.

H(g) = \sum_{0 \leq i \leq g} h(i)

  • 히스토그램 평활화는 이 히스토그램 누적 함수 H(g)를 픽셀 값 변환 함수로 사용한다. 다만 H(g) 값의 범위가 보통 그레이스케일 값의 범위(0~255) 보다 훨씬 크기 때문에 H(g) 함수의 최댓값이 255가 되도록 정규화 과정을 거쳐야 한다.
    • 만약 입력 영상의 픽셀 개수를 N이라고 표기하면 히스토그램 평활화는 다음과 같이 정의 된다.

dst(x, y) = round(H(src(x,y)) \times {L_{max} \over N})

  • 위 식에서 L_{max} 는 영상이 가질 수 있는 최대 밝기 값을 의미하며 일반적인 그레이스케일 영상의 경우 L_{max} = 255 이다.
    • round()는 반올림 함수
  • 아래 이미지의 (a)와 같이 4 x 4 크기의 영상이 0~7 사이의 밝기를 가졌다고 가정하고, 이 영상으로부터 구한 히스토그램 h(g)와 히스토그램 누적 함수 H(g)를 표로 나타내면 아래 이미지의 (b)가 된다.
    • 영상의 픽셀 개수가 16개이므로 H(7)에 해당하는 값이 16인 것을 확인할 수 있다.
    • 테스트로 사용한 영상은 최대 밝기 값이 7이고 전체 픽셀 개수가 16이므로 정규화를 위한 상수는 7/16을 사용해야 한다. 즉, H(g)에 7/16을 곱한 값을 결과 영상의 픽셀 값으로 설정한다.
    • 다만 H(g)에 7/16을 곱한 결과가 실수로 계산되기 때문에 이를 반올림하여 결과 영상의 픽셀 값으로 설정한다.
    • 이러한 연산 과정을 나타낸 것이 아래 이미지의 (c)이다.
    • 최종 평활화 과정을 거친 결과는 (d)를 통해 확인할 수 있다.

  • OpenCV는 그레이스케일 영상의 히스토그램 평활화를 수행하는 equalizeHist() 함수를 제공한다.
    • equalizeHist() 함수는 CV_8UC1 타입을 사용하는 그레이스케일 영상만 입력으로 받는다.
void histogram_equalization()
{
Mat src = imread("hawkes.bmp", IMREAD_GRAYSCALE);

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

Mat dst;
equalizeHist(src, dst);

imshow("src", src);
imshow("srcHist", getGrayHistImage(calcGrayHist(src)));
imshow("dst", dst);
imshow("dstHist", getGrayHistImage(calcGrayHist(dst)));

waitKey();
destroyAllWindows();
}
[ssba]

The author

지성을 추구하는 사람/ suyeongpark@abyne.com

댓글 남기기

This site uses Akismet to reduce spam. Learn how your comment data is processed.