Search
Duplicate
1️⃣

영상처리의 시작: 샘플링, 카메라, 영상, 파일 구조, 색 공간

2020/12/23~2021/02/08, 2023/10/28

영상 획득과 샘플링

연속적인 함수디지털화하는 과정을 샘플링이라고 한다.

샘플링 이론(sampling theorem)

샘플링 주파수가 적어도 해당 함수 주파수 성분의 2배 이상이 되어야만 해당 함수의 샘플로부터 연속함수를 복원할 수 있다.

앨리어싱(aliasing)

영상과 디지털 영상

영상

영상은 함수처럼 바라볼 수 있다. 영상은 원점이 좌상단이고 픽셀 강도라는 함수값을 가진 함수다.
현실(아날로그 세상)에서는 임의로 잡은 두 지점 사이에 무한한 점이 존재한다.
디지털 영상을 얻기 위해 동일한 간격으로 샘플링하여 위치를 양자화한다.
이렇게 양자화되어 샘플링된 점의 밝기를 화소 또는 픽셀(pixel: picture + element)이라고 한다.
현실에서는 한 점의 밝기를 0과 1 사이의 수로 표현한다면 표현 가능한 밝기의 종류는 무한하다.
밝기를 일정한 간격으로 나눠서 양자화한다.
화소 또는 픽셀의 밝기를 강도(intensity)라고 한다.
주어진 하나의 화소를 둘러싸고 있는 화소들을 이웃(neighborhood)이라고 한다.

디지털영상의 종류

이진(binary) 영상

0과 1의 두 개의 밝기값만을 갖는 영상을 binary 영상, 흑백영상이라고 한다.

회색조(grayscale) 영상

검은색 (0) 과 흰색 (1. 또는 255) 사이의 밝기값을 가지는 영상을 그레이스케일 영상이라고 한다.
Mat img1 = imread("example.bmp", IMREAD_GRAYSCALE);
C++
복사

천연컬러(true color) 영상

빛의 삼원색에 대한 밝기값을 가지는 컬러 영상이다.
일반적으로 픽셀 강도(intensity)는 0부터 255까지 256단계로 나누어서 표현한다.
256단계이므로 하나의 픽셀 강도를 표현하기 위해 8bit가 필요하다.
천연컬러 영상의 경우 R, G, B 총 3개 채널이 모여야 하므로, (28)3=24(2^{8})^{3} = 24 비트가 필요하다.
그래서 256 단계로 나누는 천연컬러 영상을 24비트 컬러 영상이라고 부르기도 한다.
Mat img2 = imread("example.bmp", IMREAD_COLOR);
C++
복사
회색조 영상으로 불러들인 영상을 다음과 같이 강제로 변환하면 B, G, R 색 성분이 모두 같은 영상이 생성된다.
cvtColor(img1, img1_to3ch, COLOR_GRAY2BGR);
C++
복사
여기에서 주의해야 할 부분이 있다. 천연컬러 영상을 회색조 영상으로 변환하는 수식은 단순히 (r+g+b)/3(r+g+b)/3 이 아니다. 천연컬러 영상을 회색조 영상으로 변환하는 수식은 다음과 같다.
Y=0.299r+0.587g+0.114bY=0.299r+0.587g+0.114b

인덱스(index) 영상

인덱스 영상이란 저장이나 전송의 효율을 위해 컬러 맵이나 컬러 파레트에 대한 인덱스로 구성된 영상이다. 색상이 직접 저장되어 있는 것이 아니고, 특정 색상에 대한 인덱스가 저장되어 있다.
보통 영상은 표현할 수 있는 색상들 중 일부만을 빈번하게 사용한다는 점에 착안한다.
내가 사용할 색상들을 모아둔 파레트를 컬러맵(color map)이라고 한다.
kk개의 컬러 색상을 표현하는 (n,n)(n,n) 크기의 이미지가 있다고 생각해 보자.
1.
천연컬러 영상으로 표현하는 경우 색상을 표현하는 일에 필요한 바이트 수: nn3n * n * 3
2.
인덱스 영상으로 표현하는 경우 필요한 바이트 수: k3k*3(컬러맵) +k3k * 3nnn * n
k3k*3이 충분히 작아 1바이트만으로도 접근할 수 있는 경우에만 위 식이 성립한다.
따라서, kk가 충분히 작다면 인덱스 영상으로 표현하는 것이 훨씬 효율적이다.

압축 영상과 비압축 영상

영상의 종류
무압축 영상
영상의 밝기값을 전부 다 그대로 보관하고 있는 영상.
압축 영상
손실 압축 영상
JPG 파일 포맷을 사용하는 영상
손실 압축 알고리즘은 영상에서 사람의 눈에 잘 보이지 않는 것을 제거하는 방식으로 작동한다.
비손실(무손실) 압축 영상
GIF, PNG, TIF 파일 포맷을 사용하는 영상
영상을 압축하지만 원래의 영상 밝기값을 완벽하게 복원할 수 있다.
GIF 파일 형식도 비손실 압축 영상이다. 우리가 가진 영상들을 GIF로 변환했을 때 화질이 열화된다고 느끼는 이유는 GIF 파일 자체가 트루컬러 지원이 안되고 256색상 이하의 영상만을 지원하기 때문이다.

BMP 파일의 구조

비트맵과 대조되는 개념은 벡터 그래픽스다.
BMP 파일은 윈도우 운영체제에서 사용하는 비트맵 파일 포맷이다. 따라서 비트맵 ≠ BMP.
비트맵이 파일 포맷이라고 생각하는 것은 오개념이다.
BMP뿐 아니라 JPG, GIF, PNG, TIF도 비트맵이다.
BMP 파일의 중요한 구성요소들
1.
비트맵 정보 헤더: 비트맵 영상의 가로세로 크기, 이미지의 크기, 픽셀 강도를 표현하기 위해 사용하는 비트의 수 등 정보들이 담긴다.
2.
색상 테이블: 픽셀 데이터 영역에서 사용할 수 있는 팔레트. 하나의 색상을 표현하기 위해 사용하는 RGBQUAD라는 구조체가 있다. RGBQUAD 구조체가 배열로 늘어선 형태를 가지고 있다. RGBQUAD는 색상 표현을 위한 3바이트에 여분의 1바이트 패딩을 더해 4바이트를 사용한다. 그레이스케일 비트맵의 경우 RGBQUAD 배열은 [RGBQUAD(0, 0, 0, 0), RGBQUAD(1, 1, 1, 0), RGBQUAD(2, 2, 2, 0) …] 와 같이 생겼다.
컴퓨터는 3바이트 단위로 처리하는 것보다 4바이트 단위로 처리하는 것이 더 효율적이기 때문에 1바이트를 더 사용한다고 한다.
3.
픽셀 데이터: 천연컬러 영상이 아닌 경우 색상 테이블의 RGBQUAD 구조체 인덱스값을 저장하고 있다. 그레이스케일 영상과 같이 구조체 인덱스가 256개 이하인 경우에는 하나의 픽셀을 표현하기 위해 1개 바이트만을 사용한다. 트루컬러 영상의 경우에는 색상 테이블을 사용하지 않고, 하나의 픽셀을 표현하기 위해 3개 바이트를 사용한다.
영상마다 하나의 픽셀을 표현하기 위해 끊어 읽어야 하는 바이트의 수가 다르다. 비트맵 정보 헤더biBitCount 정보가 저장되어 있다. 가령 트루컬러의 경우 B, G, R을 각각 1바이트씩 저장해야 하므로 16진수로 24(16*2+4=36)가 저장되어 있다.

영상 생성 과정

카메라는 스크린에 비친 아주아주 작은 나무의 모습을 수집한다.
실제 세계의 물체에서 출발한 빛이 작은 구멍을 지나 상자 속 스크린에 비치게 된다.
샘플링 대상은 스크린에 비추어진 빛이다.

핀홀카메라의 트레이드오프와 렌즈의 필요

바늘구멍이 크면 선명하지만 어두운 영상을 얻게 된다.
바늘구멍이 작으면 상의 밝기는 밝아지지만, 선명하지 못한 영상을 얻게 된다.
밝으면서 선명한 상을 얻기 위해서 렌즈가 필요하다. 구멍의 크기를 키워도 빛을 모아 명확한 상을 만들어 줄 수 있기 때문이다.

컬러 영상의 획득

렌즈는 핀홀 카메라의 문제점을 극복했다.
렌즈를 거쳐 모은 빛을 전압으로 바꾸어 디지털화하는 도구가 CCD(Charged Couple Device)이다.
CCD와 비슷한 목적으로 CMOS를 사용하기도 한다.
CCD
CMOS
Dynamic Range
일반적으로 높음
일반적으로 낮음
노이즈
일반적으로 낮음
일반적으로 높음
셔터
일반적으로 글로벌 셔터
일반적으로 롤링 셔터
이미지센서의 크기
상대적으로 큰 편 (품질: )
상대적으로 작은 편 (모바일: )
전력 소비
상대적으로 적은 편
CCD, CMOS 등을 이미지 센서(image sensor)라고 부른다.
하지만 CCD는 빛의 강도(intensity)를 측정할 수 있을 뿐이지 색상(color)을 담지는 못한다.
자연광을 세 개의 R, G, B 채널로 분리하기 위해 카메라 내부에 프리즘을 배치하기도 했다.
프리즘은 빛의 파장(색상)마다 굴절률이 다르다는 원리를 이용하여 원색을 수집한다.
하지만 프리즘을 사용하는 경우 여러 개의 CCD가 필요해져 구조가 복잡하고 단가가 올라간다.
특정 색상의 빛만 투과시켜 주는 필터(filter)를 통해 이 문제를 해결하고자 했다.
이 방법을 이용하면 CCD 1장만으로 컬러 영상을 얻을 수 있다.
픽셀마다 다른 색상 필터를 번갈아 가며 배치하기 때문에, 하나의 픽셀은 하나의 색상만 수집이 가능하다.
원래 모든 파장대의 신호를 입력받을 수 있는 센서이지만, 특정 파장대만 입력받을 수 있도록 필터를 씌우기 때문에 수집되는 광량 자체가 그레이스케일 이미지에 비해 열등해진다.
하나의 색상만을 수집하며 다른 색상의 강도를 측정하지 못하는 문제를 보간법을 통해 해결한다.
보간법을 사용하는 과정에서 정확하지 않은 값이 생성될 위험과 그 과정에서 블러링이 생겨나는 문제가 있을 수 있다.
위: 베이어 패턴이 없음, 아래: 베이어 패턴이 있음
1개의 이미지 센서로부터 RGB 영상을 얻기 위해 설계된 필터를 베이어 필터(bayer filter)라고 한다. 베이어 필터에서 R, G, B 필터를 교차해 배치하면서 수집하지 못하는 색상의 빛을 채워넣는 것을 디모자이킹(Demosaicing)이라고 한다. 디모자이킹을 위해 다양한 보간(interpolation)법이 사용된다.
디모자이킹 전 영상에서 초록색 픽셀이 더 많은 이유는 베이어 필터 자체에 초록색 필터가 가장 많기 때문이다. 다시 초록색 필터가 가장 많은 이유는 우리의 눈이 초록색에 민감하여 초록이 어색하게 보이면 이미지가 부자연스럽게 보일 수 있기 때문이다. 초록색 정보를 다른 원색에 비해 더욱 촘촘하게 샘플링하여 디모자이킹을 해 주어야 우리 눈이 더 즐거워한다고 한다.

디모자이킹 실습

RAW PNG 영상을 가져온다.
일반적으로 우리가 접하는 PNG 영상과는 달리, RAW PNG 영상은 베이어 필터를 통해 수집한 픽셀 강도값, 즉, 디모자이킹되지 않은 픽셀 강도값을 담고 있다.
RAW PNG에서 좌상단부터 가로세로 10개 픽셀만 확대해 보자. 아래와 같은 격자무늬를 확인할 수 있는데, 이것이 베이어 필터를 거친 후, 아무런 처리도 하지 않은 픽셀 강도이기 때문이다.
디모자이킹 결과 영상은 아래와 같다.

색 공간

말을 색 공간이라고 무섭게 해서 그렇지 결국 색상을 수치로 표현하는 방법이다.
RGB
HSV
Hue: 색상. 빨강은 0도, 노랑은 60도, 초록은 120도 이런 식으로 표현한다.
색상을 8비트로 표현해야 하기 때문에 값의 범위를 [0,180)[0, 180) 으로 제한하고 2를 곱해 사용한다.
Saturation: 채도. 색의 탁하거나 선명한 정도를 의미한다. 범위는 [0,255][0, 255] 이다.
Value: 명도. 빛의 밝기를 의미한다. 범위는 [0,255][0, 255] 이다.
YCbCr (또는 YCrCb)
Y: 휘도, 쉽게 말해 밝기 정보. 그레이스케일 정보와 비슷하다고 생각할 수 있다.
Cb, Cr: 쉽게 말해 색상 성분이다.
동일한 피사체의 사진을 밝게 찍거나 어둡게 찍었을 때, 촬영된 영상을 YCbCr 색 공간으로 표현한다면 두 영상이 Y 성분만 다른 값을 가질 것이라고 짐작해볼 수 있다.
딥러닝을 이용한 컴퓨터비전을 공부하기 위해서 고전적 영상처리를 공부하는 사람들이 많을 것이다. 최근에는 색 공간을 크게 신경쓰지 않고 신경망에 RGB 표현의 영상을 입력하고 있다. 색 공간의 변형도 필요하다면 뉴럴 네트워크 내부에서 알아서 수행될 것이라고 생각하기 때문이다. 하지만 고전적 영상처리 방법들을 다룰 때 색 공간에 대한 지식은 해석의 측면에서 도움이 된다.
아래 영상을 이용해 색 공간 변환의 효용성에 대한 예를 들어 보자. RGB 색 공간에서 초록색 멘토스 영역을 추출하고 싶으면 어떤 작업을 수행해야 할까?
m&m
직관적으로 생각해볼 때, RGB 중 G 성분이 R과 B에 비해 상대적으로 높은 멘토스라면 초록색 멘토스라고 생각할 수 있겠다. 식으로 표현하면 다음과 같다. pgp_g는 해당 위치 pp의 초록 성분 픽셀 강도이다. pr,pbp_r, p_b는 각각 위치 pp의 빨강, 파랑 성분의 픽셀 강도이다.
I(p)={0255if (150<pg) and (pr<pg) and (pb<pg)I(p) = \begin{cases}% 0 &\qquad\text{} \\ 255 &\qquad\text{if } (150<p_g) \text{ and }(p_r<p_g) \text{ and } (p_b<p_g) \end{cases}
import cv2 import numpy as np import matplotlib.pyplot as plt image = cv2.imread('img.png') image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) r, g, b = cv2.split(image) mask = np.where((g > 150) & (r < g) & (b < g), 255, 0).astype('uint8') plt.figure(1) plt.subplot(1, 2, 1) plt.imshow(image) plt.subplot(1, 2, 2) plt.imshow(mask)
Python
복사
하지만 이렇게 설정해 둔다면 원본 영상이 어두워지는 경우 검출력이 떨어지게 된다.
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image = image * 0.6 image = image.astype(np.uint8)
C++
복사
이를 보완하기 위해 식을 다음과 같이 변경할 수 있겠다. kr,kbk_r,k_b는 임의의 계수이다.
I(p)={0255if (krpr<pg) and (kbpb<pg)I(p) = \begin{cases}% 0 &\qquad\text{} \\ 255 &\qquad\text{if } (k_rp_r<p_g) \text{ and } (k_bp_b<p_g) \end{cases}
하지만 이러한 수식은 의미를 이해하기 어렵고 연산도 어렵다. HSV 색 공간에서 영상을 바라본다면 문제를 쉽게 해결할 수 있다. php_h는 해당 위치 pp의 색상(hue)성분을 의미한다. 초록색 성분은 120도 근방에 위치하므로, php_h가 120을 2로 나눈 60 근처일 때 초록색일 것이다.
I(p)={0255if (Tm<ph<TM)I(p) = \begin{cases}% 0 &\qquad\text{} \\ 255 &\qquad\text{if }(T_m<p_h<T_M) \end{cases}
import cv2 import numpy as np image = cv2.imread('img.png') image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) h, s, v = mask = cv2.split(hsv) lower_green = 40 upper_green = 75 mask = np.where((h > lower_green) & (h < upper_green), 255, 0).astype('uint8') plt.figure(1) plt.subplot(1, 2, 1) plt.imshow(image) plt.subplot(1, 2, 2) plt.imshow(mask)
Python
복사
비슷한 예로, 내가 하고자 하는 작업이, ‘어두운 영역을 특정한 색상으로 채우는 것’이라고 해 보자. RGB 색 공간에서 해당 작업을 식으로 표현하면 아래와 같다.
I(p)v, if (pr+pg+pb)<TI(p) \leftarrow v,\ \mathrm{if}\ (p_r+p_g+p_b)<T
같은 작업을 YCbCr 공간에서 수행한다면 아래와 같이 표현할 수 있을 것이다.
I(p)v, if Y<TI(p) \leftarrow v,\ \mathrm{if}\ Y<T
이러한 관점 변화만으로도 사고를 단순화할 수 있다.