IDL/Image Processing

마스킹(Masking) 기법의 이해와 응용

이상우_idl 2021. 5. 17. 14:24
728x90
반응형

마스킹(Masking) 기법은 어떤 배열을 대상으로 하여 배열 내 모든 값들 중 특정한 조건을 만족하는 것들만 선별하는 기법입니다. 주로 2차원 이미지 배열에 대하여 적용하는 경우가 많기는 하지만, 어차피 기법 자체는 배열의 차원을 가리지 않기 때문에 어떤 형태의 배열이든 적용 대상이 될 수 있습니다. 사실 이 기법에 대해서는 제가 이 블로그에서 올렸던 다른 게시물들, 특히 이미지 처리와 관련된 내용들에서 여러번 언급을 한 적은 있습니다. 하지만 오늘은 이 기법 자체에만 촛점을 맞추어서 그 개념 및 관련 예제들을 함께 소개해보고자 합니다.

 

우선 마스킹 기법의 원리에 대한 이해를 돕기 위하여 간단하게 1차원 배열에 대하여 적용을 해보겠습니다. 먼저 5개의 정수값들로 구성된 배열 img를 정의합니다. 그리고 img에 대하여 마스킹 기법을 적용하여 그 결과를 mask라는 배열로 얻게 되는데, 그 과정은 다음과 같습니다.

 

img = [80, 60, 75, 95, 50]
mask = img GE 80

PRINT, mask

 

위의 내용에서 "img GE 80"라고 적은 부분이 바로 마스킹 기법을 적용한 내용인데요. IDL에서 마스킹 기법을 적용하기 위해서는 이와 같이 대상 배열에 대하여 논리연산자를 사용해야 합니다. 논리연산자라는 것은 IF와 같은 조건형 구문에서 논리 판단을 위해 사용하는 것으로서 GT, GE, LT, LE, EQ, NE 등과 같은 단순 논리연산자와 AND, OR, ~ 등과 같은 복합 논리연산자가 있습니다. 참(True) 또는 거짓(False)으로 판별할 수 있는 논리식의 형태로 IF문에서 흔히 사용하지만, 이러한 문법을 위와 같이 적용하면 바로 마스킹 기법을 적용하게 되는 것입니다.

 

따라서 위의 두번째 줄의 내용의 의미는, img라는 배열의 모든 값들에 대하여 80보다 크거나 같다(Greater than or Equal)는 조건는 만족하는지 여부를 일일이 확인하고 그 결과를 mask라는 배열로 담으라는 것입니다. 판단의 결과는 1 또는 0으로 얻어집니다. 즉 조건을 만족하는 경우에는 1이 되고, 그렇지 않은 경우에는 0이 됩니다. 결국 mask라는 배열은 1 또는 0의 값들로 구성된 배열이 됩니다. 실제로 PRINT 명령에 의하여 출력된 내용은 다음과 같습니다.

 

1  0  0  1  0

 

이러한 결과가 얻어진 이유는 배열 img의 값들을 보면 쉽게 알 수 있습니다. 즉 80보다 크거나 같다른 만족하는 값들은 80과 95이고, 이 값들의 위치가 첫번째 및 네번째이기 때문입니다. 다음 그림은 여기까지의 과정을 도식적으로 나타낸 것입니다.

 

 

마스킹 기법에서 논리연산자를 사용하는 방식은 IF 구문을 사용할 때와 동일합니다. 즉 그 결과가 참 또는 거짓으로 나올 수 있는 논리식이면 되고, 그 형태는 단순 논리식이든 복합 논리식 상관이 없습니다. 그러므로 다음과 같은 방식도 가능합니다.

 

mask = img GT 70 AND img LT 90

PRINT, mask

 

즉 70보다 크고 90보다 작다는 조건을 만족하는 경우를 선별해내기 위한 마스킹 기법의 적용 사례입니다. 그리고 그 결과는 다음과 같습니다.

 

1  0  1  0  0

 

일단 이 정도면 마스킹 기법의 원리에 대해서는 충분히 설명이 되었을 것 같습니다. 이번에는 2차원 배열을 대상으로 하는 경우에 대해서도 예제와 함께 살펴보겠습니다. 어차피 차원만 다를 뿐 기본적인 원리는 동일합니다. 예제로 사용할 2차원 데이터는 JPG 파일로부터 읽어오겠습니다. 이 파일은 아래 첨부합니다.

 

bw0406.jpg
0.12MB

 

그리고 파일을 읽어서 img라는 2차원 배열로 얻는 과정은 다음과 같습니다.

 

file = 'bw0406.jpg'
READ_JPEG, file, img
HELP, img
PRINT, MIN(img), MAX(img)

 

여기서 HELP 및 PRINT에 의하여 출력된 내용을 보면, img는 1024x768의 구조를 갖는 바이트(Byte)형 배열이고 배열 내 값들의 범위는 0~255입니다. 먼저 원본 데이터인 img를 표출해본다면 그 과정은 다음과 같습니다.

 

sz = SIZE(img, /DIM)

win0 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)
i0 = IMAGE(img, MARGIN=0, /CURRENT)

 

여기서는 SIZE 함수를 사용하여 배열 img의 가로 및 세로 방향 크기를 얻고 이를 그래픽 창의 크기로 반영하였습니다. 그리고 IMAGE 함수를 사용하여 표출할 때 MARGIN의 값을 0으로 설정함으로써 그래픽 창 내에서 이미지 표출시 여백이 생기지 않도록 하였습니다. 표출된 모습은 다음과 같습니다.

 

 

이제 배열 img에 대하여 마스킹 기법을 적용해봅시다. 그 과정은 다음과 같습니다.

 

mask = img GT 90
HELP, mask
PRINT, MIN(mask), MAX(mask)

 

여기서 "img GT 90"이라는 부분이 마스킹 기법에 해당되며, img 배열 내 모든 값들에 대하여 90보다 큰가 여부를 일일이 확인하여 그 결과를 mask라는 배열로 얻은 것입니다. HELP 및 PRINT에 의하여 출력된 내용은 다음과 같습니다.

 

MASK            BYTE      = Array[1024, 768]
   0   1

 

이와 같이 마스킹 처리의 결과 배열을 보면 차원과 크기 모두 원본 데이터와 동일하지만 내부적으로는 0 또는 1의 값들만 존재하는 바이트형 배열이 됩니다. 그러면 결과의 모습이 어떤가를 보기 위하여 다음과 같은 과정에 의하여 별도의 그래픽창을 띄우고 mask를 표출해봅시다.

 

win1 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)
i1 = IMAGE(mask*1, MARGIN=0, /CURRENT)

 

그런데 여기서 IMAGE 함수의 내용을 잘 보면 표출의 대상인 배열 mask에 정수 1을 곱했는데요. 원래 mask 자체는 바이트형 배열인데, IMAGE 함수의 대상이 바이트형 배열인 경우에는 값을 그대로 색상으로 반영합니다. 즉 0과 1의 값이 그대로 색상값 0과 1로 대응되어 표출되는데, 이렇게 되면 컬러테이블을 구성하는 256개의 색상들 중 맨 앞의 0번과 1번에 해당되는 두 색상이 사용됩니다. 그레이스케일의 색상들로 구성된 디폴트 컬러테이블에서는 0과 1 모두 완전 검정색에 가까우므로 육안으로는 두 색상의 구분이 거의 불가능합니다. 그런데 IMAGE 함수의 대상 배열의 자료형이 바이트형이 아닌 경우에는 최소값부터 최대값까지의 범위를 무조건 0~255의 색상값들로 대응시키는 바이트스케일링(Byte-Scaling)이 적용됩니다. 즉 배열값의 범위가 0~1인 경우 색상은 0~255로 대응되므로, 배열값 0은 색상값 0으로 그리고 배열값 1은 색상값 255로 표현됩니다. 그러면 0과 1이 각각 검정색과 흰색으로 표현되기 때문에 육안으로도 두 값을 서로 구분하여 보기가 쉬워집니다. 이러한 처리를 하기 위하여 일부러 mask*1로 표시한 것입니다. 즉 mask 자체는 바이트형이지만 mask*1은 정수형이 된다는 점을 이용한 하나의 요령이라고 보면 됩니다. 어쨌든 표출된 모습은 다음 그림과 같습니다.

 

 

즉 원본 배열인 img 내에서 90보다 큰 화소들은 mask 배열 내에서의 값이 1이 되고 그림상에서는 흰색으로 보입니다. 그리고 원본 배열인 img 내에서 나머지 화소들은 mask 배열 내에서의 값이 0이 되고 그림상에서는 검정색으로 보입니다. 이러한 방식으로 특정한 조건을 만족하는 것들과 그렇지 않은 것들을 이분법적으로 구분할 수 있게 됩니다. 바로 이것이 바로 마스킹(Masking) 기법입니다. 그리고 마스킹의 결과를 얻는데서만 그치지 않고 다음과 같이 추가적으로 응용하는 것도 가능합니다.

 

masked_img = img * mask
HELP, masked_img
PRINT, MIN(masked_img), MAX(masked_img)

 

즉 이와 같이 마스킹의 결과 배열인 mask를 원본 데이터 배열인 img와 곱하는 것입니다. 이렇게 하여 얻어지는 결과 배열인 masked_img의 배열 값들은 원본 데이터 값에 1이 곱해진 부분과 0이 곱해진 부분들로 나눠지게 됩니다. 그러면 1이 곱해진 부분은 원본 데이터 값이 그대로 살아남는 반면, 0이 곱해진 부분은 그냥 0이 되어버립니다. HELP 및 PRINT에 의하여 출력된 내용은 다음과 같습니다.

 

MASKED_IMG      BYTE      = Array[1024, 768]
   0 255

 

이와 같이 masked_img는 바이트형 배열이고 값 범위는 0~255가 됩니다. 이제 masked_img를 표출해서 그 모습을 직접 봅시다. 그 과정은 다음과 같습니다.

 

win2 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)
i2 = IMAGE(masked_img, MARGIN=0, /CURRENT)

 

그리고 표출된 모습은 다음과 같습니다.

 

 

이와 같이 마스킹의 결과인 mask에서 1이었던 부분은 원본 데이터의 모습이 그대로 살아있는 반면 0이었던 부분은 완전히 죽어버린 것을 확인할 수 있습니다. 물론 이것은 마스킹의 결과를 응용하는 하나의 예입니다. 마스킹의 결과는 후속 처리 과정에서 목적에 따라 얼마든지 다양한 방법으로 활용할 수 있다는 점을 염두에 두시면 좋을 것 같습니다.

반응형