해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.
※ 감마 보정
항상 영상의 밝기는 0에서 255사이의 값을 가진다. 그러면 밝기값이 100인 픽셀은 밝기값이 200인 픽셀보다 정확히 절반의 밝기로 보일까? 그건 모니터나 프린터 등 디스플레이 종류에 따라 달라진다. 즉, 디스플레이 장치는 입력신호에 따른 출력 밝기가 비선형적이다. 그래서 입력 신호(모니터에 가해지는 전압)에 100을 넣으면 100이 나오고 200을 넣으면 200이 나오는 선형적 관계가 아니다.
위 그래프는 디스플레이의 입력신호마다 실제 모니터가 출력해 우리 눈에 보이는 밝기를 나타낸다. 일반 LCD 모니터는 위 그래프와 같이 입력 신호의 세기를 올려도 밝기가 천천히 증가하다가 신호의 세기가 커질수록 밝기가 급격하게 증가한다. 그래서 밝은 부분은 더 밝게, 어두운 부분은 더 어둡게 보여 전체적으로 영상이 명암은 높되 밝기는 어두워지는 효가가 있다. 따라서 우리 눈은 위에서 보이는 선형 직선 함수와 같이 왜곡이 없는 영상에 익숙 하기 때문에 모니터의 출력을 보정해 줘야 한다.
위 그래프는 다음과 같은 지수 함수로 나타낼 수 있다.
(출력 밝기) = (상수) * (입력 밝기)
지수 r(감마)가 입출력 곡선의 특징을 결정하는 값으로 위 곡선을 감마 곡선이라고 부른다. 일반 LCD 모니터의 경우 감마 값이 2.5정도 되고 우리 눈에 가장 보기 좋은 감마 값은 직선 형태를 갖도록 하는 감마값 1이다.
- 감마 보정 방법
예를 들어 LCD 모니터의 감마 값이 2.5다면 감마 보정 후에 감마 값이 1이 되도록 하면 되기 때문에 감마 값에 2.5를 나누어 주면 된다. 즉, 아래와 같은 식이 된다.
(보정 밝기) = (입력 밝기)
따라서 주어진 디스플레이 장치의 감마 값을 나타내는 변수 dGamma가 주어졌을 때 디스플레이 장치의 감마 보정을 수행하는 코드는 다음과 같다.
pOut[c] = (BYTE)(pow(pIn[c]/255.0 , 1/dGamma)*255 + 0.5);
pIn 과 pOut은 입력 영상과 출력 영상의 픽셀 포인터를 나타낸다. pow는 지수함수인데 입력 픽셀 값을 255.0으로 나누고 있다. 그 이유는 감마 곡선이 실수 값 0에서 1 사이의 영역에서 정의 되기 때문이다. 입력 픽셀 값을 255로 나눴기 때문에 마지막에 다시 255를 곱해주었고 거기에 0.5를 더하고 BYTE 형변환으로 반올림 계산을 하였다. 여기서 앞 포스트에서 배운 BYTE형 값 범위를 조정해주는 CLIP 매크로를 사용하지 않았는 데 그 이유는 1/dGamma 값 즉, 지수가 0에서 1사이 값이여서 원래 픽셀 이상의 값을 가질 수 없기 때문이다.
- 룩업 테이블로 감마 보정 속도 올리기
바로 위에서 구현한 감마 보정 코드는 영상의 모든 픽셀에 대해서 pow 메서드를 실행한다. 만약 입력 영상이 가로 640, 세로 480, 컬러 영상이라면 pow 연산을 640*480*3 즉, 921600번 해야 한다. 실제 픽셀 데이터는 0에서 255사이 즉, 256개이기 때문에 pow 메서드의 매개변수로 최대 256 가지가 들어갈 수 있다. 하지만 앞에서 말한 640*480 입력 영상에서의 921600개의 매개변수 전달에 의한 pow 연산은 매우 중복된다는 것을 알 수 있다. 이러한, 비효율을 해결하는 하나의 방법이 룩업 테이블(lookup table)을 이용한 연산이다. 룩업 테이블이란 미리 계산된 결과를 저장한 테이블로, 데이터마다 직접 계산하는 것보다 미리 계산된 결과를 저장된 메모리에서 읽어오는 것이 더 효율적이라고 판단될 때 사용하는 방법이다. 이는 영상 처리에서도 많이 활용된다. 미리 0에서 255까지의 정수 값에 대해서 pow 메서드의 결과를 배열에 저장해 꺼내서 사용하면 중복된 pow 연산 없앨 수 있다. 밑의 코드가 룩업 테이블을 구현한 감마 보정 구현한 예이다.
CByteImage& GammaImage(CByteImage& inImage, CByteImage& outImage) { double gamma = 1.0 / m_dGamma; BYTE arrPow[255]; for(int n=0; n<255; n++) { arrPow[n] = (BYTE)(pow(n/255.0, gamma)*255 + 0.5); } int nWStep = inImage.GetWidth() * inImage.GetChannel(); int nHeight = inImage.GetHeight(); int r,c; for(r=0; r<nHeight ; r++) { BYTE * pIn = inImage.GetPtr(r); BYTE * pOut = outImage.GetPtr(r); for(c=0; c<nWStep; c++) { pOut[c] = arrPow[pIn[c]]; } } return outImage; }
'영상처리 프로그래밍' 카테고리의 다른 글
영상의 광학적 변환(4) - 히스토그램 (0) | 2017.06.11 |
---|---|
영상의 광학적 변환(3) - 영상 잡음 감소 (0) | 2017.06.10 |
영상의 광학적 변환(1) - 밝기와 명암 조절 (0) | 2017.06.10 |
픽셀 단위 영상 처리 - 영상 사칙/논리 연산 (0) | 2017.06.09 |
영상 입출력 프로그램 만들기(4) - 대화상자기반 영상 출력 (0) | 2017.06.09 |
영상의 광학적 변환(1) - 밝기와 명암 조절
해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.
※ 영상의 광학적 변환
: 영상의 밝기와 색 특성 등을 목적에 따라 변환시키는 것으로, 사람이 눈으로 보기에 더 좋은영상을 만들거나, 다른 영상 처리 알고리즘을 수행하기에 앞서 더 좋은 입력 영상을 제공하기 위한 수단이다.
※ 영상의 밝기와 명암 조절
: 영상의 밝기와 명암 조절은 영상 특성값들 가운데서도 영상이 눈에 잘 보이도록 조절할 수 있는 가장 중요한 특성이다. 영상의 사칙 연산에서 영상의 덧셈과 뻴셈이 밝기 조절에 해당하고, 영상의 곱셈과 나눗셈이 명암 조절에 해당한다.
1. 밝기 조절 : (결과 영상) = (입력 영상) + (밝기 조절 상수)
ex)
CByteImage image = imgeIn + 100;
2. 명암 조절 : (결과 영상) = {(입력 영상) - (밝기 조절 상수)} * (명암 조절 상수)
ex)
CByteImage image = (imageIn - 10) * 2;
명암 조절의 경우 명암 조절 상수를 곱하기 전에 밝기 조절 상수를 빼주는 것을 볼 수 있다. 그 이유는 1보다 큰 명암 조절 상수를 곱했을 때 모든 픽셀 값이 증가하여 영상 전체 밝기가 과도하게 증가하는 것을 보정하기 위한 것이다.
※ 자동 명암 조절
영상을 더욱 잘 보이게 하려면 밝기값과 명암 값을 적절하게 조합해야한다. 그렇지 않으면 영상의 특정 부분에 있는 사물을 알아보기 어려울 수 있다.
영상의 밝기가 너무 낮거나 높으면 어두운 부분이나 밝은 부분에서 밝기값 포화 상태가 발생하여 이 부분의 정보가 사라지는 문제가 있다. 위 사진을 보다시피 밝기 왼쪽 그림은 어두운 부분을 거의 알아볼 수 없고 오른쪽 그림은 밝은 부분을 거의 알아볼 없다. 또한 영상이 너무 낮으면 명암 왼쪽 그림과 같이 밝은 부분과 어두운 부분의 밝기 차이가 줄어들어 영상이 전체적으로 탁하게 보이게 된다. 명암이 너무 넢으면 오른쪽 그림과 같이 밝은 부분이나 어두운 부분에서 포화상태가 발생할 수 있는 문제점이 있다.
그래서 우리 눈에 잘 보이는 적절한 밝기와 명암 값을 가지는 영상이란 밝은 부분과 어두운 부분의 차이가 충분히 들어나면서 각 부분에서 포화상태가 나타나지 않는 영상이다. 즉, 밑의 그림과 같이 밝기값의 차이를 증폭시키면서도 밝기의 최댓값과 최솟값이 각각 255와 0을 넘어가지 않도록 하면 가장 또렷하게 보이는 영상을 만들 수 있다.
위 그림은 그래프의 가로축은 영상을 1차원이라고 가정했을 때 픽셀의 위치이고 세로 축은 각 픽셀의 밝기값이다. 자동으로 적절한 밝기와 명암을 찾으려면 최소 밝기를 0으로 최대 밝기를 255로 하여 밝기값의 모든 영역을 활용하게 하면 된다. 이를 수행하는 픽셀 값 변환 식은 다음과 같다.
(결과 영상) = {(입력 영상) - (최소 밝기)}/{(최대 밝기) - (최소 밝기)} * 255
입력 영상에서 최소 밝기를 뺌으로써 최소 밝기값을 0으로 만들어주고 거기에 최대 밝기에서 최소 밝기 뺀 값을 나눈 후 255를 곱해 최대 밝기값을 255로 만들어 명암을 최대한 증가 시켰다.
하지만 위와 같은 변환은 단순히, 명암을 조절할 때 영상의 밝기에 상수 값을 곱하는 선형 변환을 수행한 것이기에 영상이 보기 많이 좋아지기에는 제한되는 부분이 많다. 또한 이 방법은 원본 영상에 최소 밝기가 0인 픽셀과 최대 밝기가 255인 픽셀이 있을 경우 명암이 전혀 개선되지 않는 문제점이 있다. 더 나은 영상을 얻으려면 밝기값이 0에서 255 사이에 골고루 분포해야 하는 데 이 때 영상의 히스토그램을 이용하면 된다. 이에 대한 부분은 다음에 다룰 예정이다.
'영상처리 프로그래밍' 카테고리의 다른 글
영상의 광학적 변환(3) - 영상 잡음 감소 (0) | 2017.06.10 |
---|---|
영상의 광학적 변환(2) - 영상 감마 보정 (0) | 2017.06.10 |
픽셀 단위 영상 처리 - 영상 사칙/논리 연산 (0) | 2017.06.09 |
영상 입출력 프로그램 만들기(4) - 대화상자기반 영상 출력 (0) | 2017.06.09 |
영상 입출력 프로그램 만들기(3) - MDI에서 화면 출력 (0) | 2017.06.07 |