[영상신호처리] Disparity Map 개념 이해

2021 - 1 영상신호처리

  • Rimg1, Limg1 이미지에 7x7 window를 적용한 disparity Map 구성
  • Rimg1, Limg1 이미지에 21x21 window를 적용한 disparity Map 구성
  • Rimg2, Limg2 이미지에 7x7 window를 적용한 disparity Map 구성
  • Rimg2, Limg2 이미지에 21x21 window를 적용한 disparity Map 구성
  • discussion
    - 7x7 window와 21x21 window의 효과 및 차이점
    - 왜 disparity Map의 성능은 좋지 않은가?

1. Rimg1, Limg1 이미지에 7x7 window를 적용한 disparity Map 구성

첫번째 과제의 목표는, 스테레오 카메라의 영상차를 이용한 disparity Map을 그려내는 것이었다. 이를 위해 다음과 같은 과정이 필요하겠다는 생각이 들었다.

∎ 먼저 Rimg와 Limg의 7x7 window내의 픽셀 밝기차를 구한다. 이 경우 총 49개의 계산값들이 나올 것인데, 그를 다 더한 후 평균을 내야겠다. 또 Rimg를 기준으로, Limg의 window를 오른쪽 x 축 방향으로 0~30 픽셀씩 움직여 가면서, 두 이미지의 픽셀 밝기차의 평균들을 계속 구해야겠다. 이런 식으로 찾은 30개의 샘플 중에서, 최솟값이 나오는 지점을 알아내야 겠다..

∎ 최솟값이 나온 지점의 픽셀을 찾았을 때, 그로부터 Rimg와 Limg가 얼마나 떨어져 있는지를 알아내야겠다. 그리고 그를 그 픽셀에서의 disparity 값으로 정해야겠다.

∎ Rimg와 Limg의 disparity 값을 새로운 영상에 담아 준 다음, 새로운 영상에 담긴 값을 0~255 사이로 Normalize 해서 출력해야겠다.

이렇게 개괄적으로 계획을 짠 후, 이를 실제로 구현하기 위해 다음과 같은 함수 2개를 구상했다.

Compute disparity 함수

void Compute_Disparity(BYTE **Limg, BYTE **Rimg, BYTE **img1, int w, int h)

- Limg와 Rimg를 비교해 픽셀의 밝기차가 최소가 되는 지점을 찾은 다음, 
두 픽셀이 떨어진 disparity를 구해 새로운 영상 이미지 img1에 넣어주는 함수이다.
void Compute_Disparity(BYTE **Limg, BYTE **Rimg, BYTE **img1, int w, int h)
{
//7*7 window에 대해 Disparity를 계산하는 함수이다.

int imageX, imageY, windowX, windowY; 

  //imageX, imageY는 전체 이미지의 배열을 조회하기 위한 변수이고, 
  // windowX, windowY는 7*7 window 배열을 조회하기 위한 변수이다.
int delta;
  //변수 delta는, Rimg와 Limg간 얼마나 떨어져 있는지 0에서 30 픽셀까지 이동시킬 목적의 변수이다.
int sub;
  //변수 sub는 7*7 window 내의 Rimg와 Limg의 영상 밝기차이를 1개의 픽셀에 대해서만 구하는 변수이다.
double sum = 0.;
  //변수 sum은, 7*7 window 내의 Rimg와 Limg의 영상 밝기차이, 즉 변수 sub의 값을 다 더한 다음 평균을 내주는 변수이다.
double min = 255.;
  //변수 min은 위의 변수, sum의 최솟값을 탐색하기 위한 기준값이다. 초기의 min 값은 변수 sum이 가질 수 있는 최댓값, 255로 초기화 되어있다.

int disparity;
  //변수 disparity는 두 이미지의 밝기차가 최소가 되는 delta를 찾았을 때, 그를 그 위치의 disparity 값으로 지정하는 변수이다.



for(imageY=3; imageY<h-3; imageY++) {
for(imageX=3; imageX<w-3; imageX++) {
  //7*7 window를 적용할 것이므로, 이미지 조회의 시작 지점을 (3,3) 으로 잡았다.

for(delta = 0; delta < 30; delta++) {
  //그 후, delta를 0에서 30까지 이동시켜가며 두 이미지의 밝기차를 구할 것이다.
for(windowY=imageY-3; windowY<imageY+4; windowY++) {
	for(windowX=imageX-3; windowX<imageX+4; windowX++) {
      sub = abs((Limg[windowY][windowX+delta] - Rimg[windowY][windowX]));
      //delta는 x에 대해서만 증가하므로, delta를 x축에 대해서만 증가시켜보며 
      //두 이미지의 픽셀 밝기값의 절댓값을 구한다.
      //그리고 이 밝기 차이 값을 변수 sub에 넣어준다.
      
      sum += sub; //그리고 이렇게 구한 변수 sub를 sum에 더해준다.
      }
}

sum = sum / 49;
// sum을 7*7 window의 모든 데이터 갯수인 49로 나누어, 평균을 내어준다. 
// 이렇게 하면 sum이 최소 0에서 255까지의 값을 가지게 된다는 것을 알 수 있다.

if(sum <= min) {

min = sum;
disparity = delta;
// 7*7 window의 49개의 계산 값들 중 sum 값이 최소가 되는 지점을 찾는 코드이다. 최소가 되는 지점을 찾으면 그 때의 sum 값을 새로운 min 값으로 갱신하고,
// 최소의 sum 값이 나오는 지점의 거리차이, delta를 두이미지의 disparity로 저장한다.

}
sum=0;
//window 픽셀 밝기의 평균 값들은 49개의 데이터들에 대해 반복적으로 적용해야 하므로, sum을 한차례 쓰고 난 후엔 꼭 0으로 초기화 해준다.

}

img1[imageY][imageX] = (BYTE)disparity;
// 새로운 영상, img1에 이미지 픽셀에서 채택된 disparity 값을 넣어준다.
min = 255.;
//또한 위의 과정을 반복해야 하므로, min 값도 초기화 해준다.
}
}
}

위의 변수들을 좀 더 자세하게 설명하고자, 다음과 같은 그림 개요를 작성해보았다. 빨간 글씨로 작성된 부분이 내가 만든 변수의 이름들이다.

INormalize2D 함수

void INormalize2D(BYTE **img1, BYTE **img2, int w, int h)

- img1에는 disparity의 값,0~30까지의 값이 담겨 있기 때문에, 
이를 0~255의 값으로 normalize 해준 다음 새로운 영상 이미지 img2에 넣어주는 함수이다.
void INormalize2D(BYTE **img1, BYTE **img2, int w, int h)
{
int x, y;

int min = 9999;
int max = -9999;
int val;

for (y=3; y<h-3; y++) {
for (x=3; x<w-3; x++) {
  //위의 Compute_disparity 함수의 적용으로 인해, img1은 (3,3)지점의 픽셀부터 
  // 유의미한 disparity 값이 존재하므로 여기서도 (3,3)부터 조회해준다.
  
  val = img1[y][x];
  if (val > max) max = val;
  if (val < min) min = val;
  //img1에 담긴 disparity의 min값과, max값을 계산해주는 과정이다.
  }
}


double dfactor = 255 / (max-min) ;
for (y=3; y<h-3; y++) {
	for (x=3; x<w-3; x++) {
		img2[y][x] = (BYTE)((img1[y][x] - min) * dfactor);
		// 구한 max와 min 값을 바탕으로, 0에서 30사이의 disparity 값을 normalize 시켜주고, 
        // 그를 새로운 이미지 img2에 담아준다.
		}
	}
}

위의 코드를 적용해, 나온 7*7 window의 disparity Map 출력결과는 다음과 같다.


그림에서 확인할 수 있듯이, 상대적으로 가까운 전등은 밝게 출력이 되고, 그보다 멀리 있는 석고상은 전등보다 어두운 색으로 출력이 되고, 카메라, 탁자 등은 그보다 더 어두운 색으로 출력되고 있다. Disparity Map 구성을 통해 물체와 카메라 간 얼마나 거리가 떨어져 있는지 확인할 수 있는 것이다.

2. Rimg1, Limg1 이미지에 21x21 window를 적용한 disparity Map 구성

같은 과정으로, 이번에는 위의 코드에서 windowX, windowY 값만 바꾸어 21x21 window에 대해서도 disparity map을 구성해 보았다. 결과는 다음과 같다.

7x7 window 적용시와 21x21 window 적용시 Map의 형태에 특징적인 변화가 나타났음을 확인 할 수 있었는데, 이에 대한 discussion에 대해서도 뒤에서 차차 언급해볼 것이다.

3. Rimg2, Limg2 이미지에 7x7 window를 적용한 disparity Map 구성

다음은 Rimg2, Limg2에 대해서도, 7x7 window를 적용해 disparity Map을 구성한 모습이다.

위 Map을 통해서도, 계단의 층계에 따라 계단이 가까워질수록 점점 밝게 출력되는 것을 확인할 수 있었고, 배경은 계단에 비해 상대적으로 멀리 떨어져 있으므로 어둡게 출력되는 것을 확인할 수 있었다.

4. Rimg2, Limg2 이미지에 21x21 window를 적용한 disparity Map 구성

다음은 Rimg2, Limg2에 대해, 21x21 window를 적용해 disparity Map을 구성한 모습이다.

위의 21x21 window map 또한 7x7 과 비슷하게 출력되었지만, 명확한 차이점이 존재했다. 이에 대한 discussion을 지금부터 시작해보고자 한다.

5. discussion

7x7 window와 21x21 window의 효과 및 차이점

두 Map의 특징을 비교해보자. 7x7 은 이미지 속 물체들의 경계가 뚜렷하게 나타난다는 것과, 물체간의 원근정도가 더 다양한 대비차이로 드러나고 있음을 확인할 수 있다. 하지만 노이즈가 많이 끼어있음을 확인할 수 있으며, 인접한 지역이라도 픽셀의 밝기가 하얀색에서 갑자기 까만색으로 바뀌는 등 오차가 많이 발생함을 확인할 수 있었다.

그와 비교해 21x21의 window에서는, 물체간의 경계가 흐릿하며, 물체간의 원근정도도 모호하게 나타난다는 점이 특징이었다. 그러나 7x7에 비해 노이즈는 더 잘 처리하고 있음을 확인할 수 있었다.

이렇게 windowSize를 조정했을 때 변화가 나타나는 원인에 대해서 생각해보았다. 첫 번째로는 windowSIze를 크게할수록 비교하는 픽셀수가 많아지기 때문에, disparity의 계산오차도 적어질 것이라는 것이었다. 적은 픽셀수의 윈도우는 그만큼 픽셀끼리 비교하는 횟수가 적기 때문에, 비슷한 밝기 값을 가지는 픽셀을 잘 구분해내지 못할 것이다. 이로 인해 실제 disparity 값과 오차가 나는 부분이 많이 등장할 것이고, 이런 부분이 노이즈로 출력되게 된다. (이를 테면 7x7 Map에서 오른쪽 상단 모서리 같은 부분이 노이즈가 많이 껴있음을 확인할 수 있다.)

또한, windowSize를 크게 할수록 이미지 픽셀간에 병합되려는 성질도 강하기 때문에, Map의 이미지가 흐려질 것이다. 이로 인해 21x21의 disparity Map이 흐리게 출력된 것이다.
위와 같은 결과의 관찰을 통해 다음과 같은 결론을 내릴 수 있었다.

windowSize를 작게 하면 물체 간의 경계는 뚜렷하면서, 노이즈는 많이 끼어있는
Map을 얻을 수 있다.
windowSize를 크게 하면 물체간의 경계는 모호하면서 노이즈는 많이 감소된
Map을 얻을 수 있다.

왜 disparity Map의 성능은 좋지 않은가?

위의 사진들에서 보면 알 수 있듯이, disparity Map의 결과가 그렇게 좋은 편은 아니다. 이 이유에 대한 탐구를 진행해보았는데, 지금 작성한 코드가 단순히 픽셀간의 밝기 값 차이만 비교하는 프로그램이기 때문에 그럴 것이라는 결론을 내릴 수 있었다. 단순히 거리값만 비교하는 과정엔 많은 예외 사항이 존재한다.

1) 원래 disparity와 다른 disparity를 가진 지점에서 밝기 값이 더 적은 차이를 가질 수 있으며, 2) 가까이 있는 물체에서 멀리 있는 물체의 경계로 window가 이동할 때, 그 경계 값에서 픽셀 밝기의 변동이 많이 생기므로 단순히 밝기차이를 더하는 것만으로는 명확한 disparity 값을 잘 찾아내지 못할 가능성이 다분하다. 위의 7x7 window에서, 물체간의 경계사이에 특히 하얀색 노이즈들이 많이 끼어있는 것이 이를 뒷받침해준다. 이러한 오차를 해결하기 위해 무작정 windowSize를 키우려 한다면, 물론 물체 경계의 노이즈는 줄일 수 있을 것이다. 3) 하지만 여러 개의 픽셀들이 같은 값으로 처리될 가능성도 높아져 물체간의 경계가 모호하고 흐린 Map이 출력된다. 이러한 예외를 전부 반영해주지 못하기 때문에 disparity Map의 결과가 예상한 것보다 좋지 못한 것이다.

고로, 더 나은 disparity Map을 얻기 위해선 픽셀 밝기 값 차이를 더 잘 비교해줄 알고리즘을 추가적으로 이용한다거나, 노이즈를 canceling 해줄 효과적인 방법을 추가적으로 도입할 필요성이 있다. 흥미가 생겨 인터넷을 탐색해보니, disparity Map의 성능을 좋게 해줄 알고리즘으로 belief propagation 등 을 사용하거나, 픽셀간의 거리 탐색에서 SAD(Sum of Absolute difference, 본 코드에서 사용한 방법) 대신 SSD(Sum of Squared difference)나 NCC 등을 사용해 밝기 차의 문제를 해결하고 있음을 새롭게 알게 되었다.

좋은 웹페이지 즐겨찾기