더북(TheBook)

그렇다면 그림 6-36(a)를 그림 6-36(C)처럼 만들려면 어떻게 해야 할까? 그 해답이 그림 6-36(b)에 나타난 그래프이다. 그림 6-36(b)와 같이 (Gmin, 0)과 (Gmax, 255)를 잇는 직선을 생성하고, 이를 변환 함수로 사용하면 히스토그램 스트레칭이 수행된다. 이 직선의 방정식을 구하려면 직선의 기울기와 y 절편을 구하면 된다. 직선의 기울기는 255/(Gmax-Gmin)가 되며, y 절편은 비례식을 이용하여 구하면 -255·Gmin/(Gmax-Gmin)이 된다. 그러므로 직선의 방정식은 다음과 같다.

결국 앞에서 소개한 히스토그램 스트레칭 함수는 위와 같은 방식으로 구한 것으로 볼 수 있다.

그러면 위 수식을 이용하여 히스토그램 스트레칭 함수를 구현해보자. 함수의 이름은 IppHistogramStretching을 사용하기로 하고, IppEnhance.h 파일에 다음과 같은 함수의 선언을 추가하자.

void IppHistogramStretching(IppByteImage& img);

IppHistogramStretching 함수의 정의는 소스 6-21에 나타내었다. IppHistogramStretching 함수 중간에서 gray_max 값과 gray_min 값이 동일한 경우에는 원본 영상의 값 조절 없이 함수를 그대로 종료하도록 설정하였다. 이는 영상 전체가 단일 그레이스케일 값을 갖는 경우이며, 이런 경우에는 히스토그램 스트레칭의 의미가 없다. 게다가 gray_max 값과 gray_min 값이 동일할 경우, for 루프 안에서 0으로 나누는 연산이 발생하는 것을 방지하는 효과도 있다.

소스 6-21 히스토그램 스트레칭 함수(IppEnhance.cpp)
void IppHistogramStretching(IppByteImage& img)
{
    int size = img.GetSize();
    BYTE* p = img.GetPixels();

    // 최대, 최소 그레이스케일 값 계산
    BYTE gray_max, gray_min;
    gray_max = gray_min = p[0];
    for (int i = 1; i < size; i++)
    {
        if (gray_max < p[i]) gray_max = p[i];
        if (gray_min > p[i]) gray_min = p[i];
    }

    if (gray_max == gray_min)
        return;

    // 히스토그램 스트레칭
    for (int i = 0; i < size; i++)
    {
        p[i] = (p[i] - gray_min) * 255 / (gray_max - gray_min);
    }
}
Note | 히스토그램 스트레칭 개선 방법

히스토그램 스트레칭을 수행하기 위해서는 영상에서 가장 낮은 픽셀 값(Gmin)과 가장 높은 픽셀 값(Gmax)을 구하여 변환 함수 수식을 구해야 한다. 만약 영상을 획득하는 과정에서 영상에 잡음이 추가되어, 오직 한두 개의 픽셀이 유난히 크거나 작은 그레이스케일 값을 가지고 있다면 어떻게 될까? 극단적인 예로, 입력 영상에 그레이스케일 값이 0인 픽셀과 255인 픽셀이 하나씩 존재하고 있다면 변환 함수는 y=x 형태의 그래프가 될 것이며, 히스토그램 스트레칭 결과 영상은 입력 영상과 완전히 동일할 것이다.

이러한 예외 상황을 처리하려면 Gmin과 Gmax를 구할 때, 단순하게 그레이스케일 최솟값과 최댓값을 사용하지 않고 약간의 여유를 두는 것이 좋다. 예를 들어 영상에 존재하는 그레이스케일 값 중에서 상위 5%에 해당하는 그레이스케일 값을 Gmax로 사용하고, 하위 5%에 해당하는 그레이스케일 값을 Gmin으로 사용하여 변환 함수를 구성하는 방식이다. 이와 같은 방식을 사용하면 극단적인 잡음에 영향을 받지 않을 뿐만 아니라 결과 영상의 명암비도 조금 더 커지는 효과가 발생한다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.