Search
Duplicate

OpenCV로 배우는 영상 처리 및 응용/ 영상 분할 및 특징 처리

허프 변환

직선 검출 방법 중 가장 널리 사용되는 것은 허프 변환(Hough transform)이다.
허프 변환은 영상 내의 선, 원 뿐만 아니라 임의의 형태를 지닌 물체를 감지해 내는 대표적인 기술로서 데이터 손실 및 왜곡이 포함된 영상에서도 직선을 잘 추출한다.

허프 변환의 좌표계

허프 변환은 아래의 수식과 같이 직교 좌표계로 표현되는 영상의 에지 점들을 오른쪽 식의 극좌표계로 옮겨 검출하고자 하는 물체의 파라미터(ρ,θ)(\rho, \theta)를 추출하는 방법이다.
y=ax+bρ=xcosθ+ysinθy = ax + b \leftrightarrow \rho = x \cdot cos \theta + y \cdot sin \theta
직교 좌표계에서는 수직선일 경우 기울기가 무한대가 될 수 있으며, 검출되는 직선의 간격이 동일하지 않아서 검출 속도와 정밀도에서 문제가 될 수 있다.
반면 극좌표계를 사용하면 직선이 원점에서 떨어진 수직 거리(ρ)(\rho)와 이 수직선이 x축과 이루는 각도(θ)(\theta)를 이용해서 직선을 표현할 수 있다.
또한 직선의 수직거리와 각도를 일정한 간격으로 검출함으로써 정밀한 직선 검출이 가능하다.
직교 좌표계에서 하나의 직선상에 놓인 좌표들을 허프 변환 좌표계로 표시하면 아래 그림과 같다.
직선 A 상에 검출된 3개의 지점 a1,a2,a3a_{1}, a_{2}, a_{3}가 있다고 가정하자.
먼저 직선 A는 ρ1=xcosθ1+ysinθ1\rho_{1} = x \cdot cos \theta_{1} + y \cdot sin \theta_{1}  로 표현할 수 있다.
이 수식에 따라 직선 A는 허프 변환 좌표계에서 한 점 (ρ1,θ1\rho_{1}, \theta_{1})의 좌표로 나타난다.
또한 영상 좌표에서 3개의 지점(a1,a2,a3a_{1}, a_{2}, a_{3})은 허프 변환 좌표(ρ1,θ1\rho_{1}, \theta_{1})를 같이 공유한다.
또한 직교 좌표계에서 한 점은 허프 변환 좌표계에서는 곡선으로 표현된다.
즉, a1a_{1} 좌표에서 그려질 수 있는 수많은 직선이 존재하며, 많은 (ρi,θi)(\rho_{i}, \theta_{i})로 표현되어, 결과적으로 아래 그림의 b에서 a1a_{1} 곡선을 이룬다.

허프 변환의 전체 과정

허프 변환은 다음과 같은 세부적인 과정을 거쳐서 수행된다.
허프 변환 좌표계에서 행렬 구성
영상 내 모든 화소의 직선 여부 검사
직선 인지 좌표에 대한 허프 변환 누적 행렬 구성
허프 누적 행렬의 지역 최대값 선정
임계값 이상인 누적값(직선) 선별
직선 (ρi,θi)(\rho_{i}, \theta_{i})을 누적값 기준으로 내림차순 정렬
먼저 영상의 크기에 맞게 허프 변환 좌표계를 위한 행렬을 구성한다. 이 행렬은 검출된 직선을 좌표 (ρ,θ)(\rho, \theta)로 표시해서 값을 누적하기 위한 것이다.
ρ\rho는 ρmax-\rho_{max}에서 +ρmax+\rho_{max}까지의 범위를 가지고, θ\theta는 0π0 - \pi까지의 범위를 가지며, 다음의 수식으로 계산된다.
여기서 height, width는 원본 영상의 크기이며, acc_h, acc_w는 허프 누적 행렬의 크기이다.
ρmaxρρmax,ρmax=height+width-\rho_{max} \leq \rho \leq \rho_{max}, \rho_{max} = height + width
acch=ρmax×2Δρacc_h = {\rho_{max} \times 2 \over \Delta \rho}
0θθmax,θmax=π0 \leq \theta \leq \theta_{max}, \theta_{max} = \pi
accw=πΔθacc_w = {\pi \over \Delta \theta}
따라서 허프 행렬의 크기는 거리 간격(Δρ)(\Delta \rho)과 각도 간격(Δθ)(\Delta \theta)에 따라 조절되며, 거리 간격과 각도 간격이 작을수록 행렬의 크기가 커져서 해상도가 높아진다.
예컨대 각도 간격을 2도로 하면 가로(θ)(\theta) 방향으로 90개의 원소가 필요하며, 1도 간격으로 하면, 180개의 원소가 필요하다.

허프 누적 행렬 구성

다음으로 영상 내의 모든 좌표에서 직선인지 여부를 점검한다. 허프 변환에 입력되는 영상은 먼저 이진화나 모폴로지와 같은 다양한 전처리를 수행하여 잡음을 제거하고 직선 성분을 검출한다.
일반적으로 캐니 에지 검출과 같이 이진 영상을 허프 변환의 입력 영상으로 사용한다. 따라서 입력 영상에서 0보다 큰 화소를 직선으로 간주한다.
다음의 hough_coord() 함수는 영상 공간에서 직선으로 인지된 좌표(x, y)를 허프 변환 좌표계로 변환하여 곡선을 구성하는 알고리즘 이다.
void hough_coord(Mat image, Mat& acc_mat, double rho, double theta) { int acc_h = (image.rows + image.cols) * 2 / rho; int acc_w = CV_PI / theta; acc_mat = Mat(acc_h, acc_w, CV_32S, Scalar(0)); for (int y = 0; y < image.rows; y++) { for (int x = 0; x < image.cols; x++) { Point pt(x, y); if (image.at<uchar>(pt) > 0) { for (int t = 0; t < acc_w; t++) { double radian = t * theta; float r = pt.x * cos(radian) + pt.y * sin(radian); r = cvRound(r / rho + acc_mat.rows / 2); acc_mat.at<int>(r, t)++; } } } } }
C++
입력 영상의 좌표를 조회하는 2중 반복문 내에서 화소값이 0보다 크면 직선으로 인지한다.
직선으로 인지되면, 다시 0 ~ acc_w 까지의 반복문을 구성하고, 각도 간격(Δθ)(\Delta \theta)만큼 증가한다. 즉, 0~180도까지 증가한다.
이 과정에서 입력된 좌표(pt)의 x, y와 각도(θt)(\theta_{t})를 허프 변환 수식에 대입해서 (ρt)(\rho_{t})를 계산한다.
거리와 각도를 좌표로 사용해 허프 누적행렬의 해당 원소값을 1 증가한다.
결과적으로 직선으로 인지된 입력 영상의 각 좌표들에 대해 0~180도로 회전하며 허프 변환 수식에 따라 거리와 각도로 좌표를 계산하고, 이 좌표에 해당하는 허프 누적행렬의 원소에 값을 누적한다.
아래 그림은 3개의 좌표가 있는 에지 영상으로 허프 누적행렬을 구성한 예이다.
영상에서 3개 좌표에 대해 허프 누적행렬은 3개의 곡선이 구성된다. 3개의 곡선이 겹치는 좌표 (ρt1,θt1)(\rho_{t1}, \theta_{t1})에서 누적값이 3으로 가장 많다. 또한 이 ρt1,θt1\rho_{t1}, \theta_{t1} 좌표는 영상의 3점을 잇는 직선이 된다.
따라서 허프 누적행렬에서 원소의 값이 특정값 이상인 좌표들을 검색하면, 특정값 이상인 길이를 갖는 직선들을 ρt,θt\rho_{t}, \theta_{t} 좌표로 찾을 수 있다.
또한 이 좌표로 허프 변환 수식을 다음과 같이 변경하여 직선을 구성할 수 있다.
ρ=xcosθ+ysinθy=cosθsinθx+ρsinθ\rho = x \cdot cos \theta + y \cdot sin \theta \to y = - {cos \theta \over sin \theta} x + {\rho \over sin \theta}

허프 누적 행렬의 지역 최대값 선정

계산된 누적 행렬에서 좌표들을 반환받으면 위 그림과 같이 한 곳에 비슷한 직선이 여러 개 선택되어 반환될 수 있다. 이 경우 정확한 하나의 직선을 찾지 못하게 된다.
또한 비슷한 위치, 비슷한 각도에서 여러 개의 직선이 검출되어서 다른 지점에 있는 직선들을 검출하지 못할 수도 있다.
이 문제를 해결하는 방법으로 마스크를 이용한 방법이 있다. 허프 누적행렬을 작은 블록으로 나누고 각 블록에서 가장 큰 값만을 유지시키고 나머지 값은 제거하는 것이다.
여기서 나누어진 블록을 마스크(mask)라 부르며, 마스크 내에서 가장 큰 값을 지역 최대값 (local maximum)이라 한다.
아래 그림은 허프 누적행렬에 3 x 7 크기의 마스크를 적용하여 지역 최대값을 구한 예이다. 두 개의 점선 사각형으로 마스크를 표시해서 예시를 나타내었다.
누적행렬 좌상단의 예시 1은 17이 가장 큰 값으로 유지되고, 마스크 주위의 나머지 값은 0으로 제거된다. 같은 방식으로 우하단의 예시 2는 11이 가장 큰 값으로 주위의 9, 10이 제거된다.
이렇게 함으로써 비슷한 위치의 직선을 하나만 검출해서 직선 검출의 정확도를 높일 수 있다..
다음은 허프 누적행렬에서 마스크 내의 지역 최대값만을 유지시키는 함수 acc_mask()를 구현한 것이다.
void acc_mask(Mat acc_mat, Mat& acc_dst, Size size, int thresh) { acc_dst = Mat(acc_mat.size(), CV_32S, Scalar(0)); Point h_m = size / 2; for (int r = h_m.y; r < acc_mat.rows - h_m.y; r++) { for (int t = h_m.x; t < acc_mat.cols - h_m.x; t++) { Point center = Point(t, r) - h_m; int c_value = acc_mat.at<int>(center); if (c_value >= thresh) { double maxVal = 0; for (int u = 0; u < size.height; u++) { for (int v = 0; v < size.width; v++) { Point start = center + Point(v, u); if (start != center && acc_mat.at<int>(start) > maxVal) { maxVal = acc_mat.at<int>(start); } } } Rect rect(center, size); if (c_value > maxVal) { acc_dst.at<int>(center) = c_value; acc_mat(rect).setTo(0); } } } } }
C++

임계값 이상인 누적값(직선) 선별

이제 직선들을 가져와 보자. 다음의 thres_lines() 함수는 중복이 제거된 누적행렬의 원소 중에서 임계값보다 큰 값을 선별하여 직선들을 lines 행렬에 저장한다.
여기서 하나의 직선(line)은 3개의 원소를 갖는 Matx13f 형으로 선언하여 간편하게 초기화 한다.
누적행렬의 좌표(r, t)를 수직거리(rho)와 각도(radian)로 계산하여 두 개의 원소를 구성하고, 세 번째 원소로 누적값(value)을 지정한다.
void thres_lines(Mat acc_dst, Mat& lines, double _rho, double theta, int thresh) { for (int r = 0; r < acc_dst.rows; r++) { for (int t = 0; t < acc_dst.cols; t++) { float value = (float)acc_dst.at<int>(r, t); if (value >= thresh) { float rho = (float)((r - acc_dst.rows / 2) * _rho); float radian = (float)(t * theta); Matx13f line(rho, radian, value); lines.push_back((Mat)line); } } } }
C++

직선을 누적값 기준으로 내림차순 정렬

다음은 선별된 직선들 중에서 긴 직선이 먼저 선택되도록 하기 위해 누적값이 큰 직선부터 저장해야 한다.
이것은 직선들(line)을 누적값 기준으로 내림차순 정렬함으로써 쉽게 구현할 수 있다.
정렬 알고리즘은 직접 구현하지 않고 OpenCV에서 제공하는 cv::sortIdx() 함수를 이용하여 다음의 sort_lines() 함수로 구현한다.
cv:sortIdx() 함수는 행렬 원소를 정렬하고, 정렬 원소의 원본 인덱스를 반환한다.
void sort_lines(Mat lines, vector<Vec2f>& s_lines) { Mat acc = lines.col(2), idx; sortIdx(acc, idx, SORT_EVERY_COLUMN + SORT_DESCENDING); for (int i = 0; i < idx.rows; i++) { int id = idx.at<int>(i); float rho = lines.at<float>(id, 0); float radian = lines.at<float>(id, 1); s_lines.push_back(Vec2f(rho, radian)); } }
C++
앞서 thres_lines() 함수에서 반환받은 직선들(lines)은 수직거리, 각도, 누적값이 저장된 행렬이다.
이 행렬에서 2번열(0번 열부터 시작)이 누적값들이기 때문에 Mat::col() 함수로 2번 열만 가져와 acc 행렬에 저장한다.
그리고 cv::sortIdx() 함수로 acc 행렬을 행단위 내림차순(SORT_EVERY_COLUMN + SORT_DESCENDING) 정렬을 한다. 그러면 두 번째 인수(idx)로 정렬값에 대한 원본 인덱스가 반환되기 때문에 idx에 내림차순으로 정렬된 원본 인덱스가 저장된다.
idx 행렬의 전체 원소를 조회해서 원본 인덱스를 id에 저장한다. 그리고 원본 인덱스(id)로 lines 행렬(직선들)에서 한 원소를 가져온다. 여기서 lines 행렬에서 0번 열이 수직거리(rho)이며, 1번 열이 각도(radian)이다.
그리고 수직거리와 각도를 Vec2f 형으로 선언해서 s_lines 벡터에 추가한다. 그러면 s_lines 벡터에 누적값에 따라서 내림차순으로 정렬된 직선 좌표가 저장된다.

코너 검출

영상 내에는 에지나 직선과 같은 다양한 특징 정보들이 있다. 그러나 직선 정보는 영상 구조 파악 및 객체 검출에는 도움이 되지만, 영상 매칭에는 큰 도움이 되지 않는다.
또한 에지는 강도와 방향 정보만 가지므로 영상 매칭을 하기엔 정보가 부족하다.
에지나 직선처럼 영상 처리에 중요한 특징 정보로 사용되는 꼭지점 혹은 코너라 부르는 특징점이 있다. 이 코는 아래 그림과 같이 영상에서 경계가 만나는 지점의 특정한 모양을 갖는 곳을 가리킨다.
이 코너 정보들 중에서 영상의 왜곡에도 불변하는 특징을 가진 지점들이 영상 매칭에 유용하게 사용될 수 있다. 여기서는 코너 검출기 중 하나인 해리스(Harris) 코너 검출기의 원리에 대해 알아볼 것이다.
코너 점은 위 그림과 같이 모든 방향에서 영상의 밝기 변화가 커야 한다. A, C 지점은 모든 방향으로 밝기의 변화가 크다. 그러나 B, D의 경우는 모든 방향으로 밝기 변화가 거의 없다.
이 아이디어는 모라벡(moravec)에 의해서 다음 식과 같이 영상 변화량(SSD: Sum of Squared Difference)으로 정리된다. 여기서 w(x, y)는 지정된 크기의 윈도우로서 해당 범위만 1의 값을 갖는 마스크이다.
E(u,v)=yxw(x,y)(I(x+u,y+v)I(x,y))2E(u, v) = \sum_{y} \sum_{x} w(x, y) \cdot (I(x+u, y+v) - I(x, y))^{2}
영상 변화량(SSD)은 현재 화소에서 u, v 방향으로 이동했을 때의 밝기 변화량의 제곱을 합한 것이기 때문에, 모든 방향에서 밝기 변화가 커야 큰 값을 갖게 된다.
모라벡은 구현을 위해서 (u, v)를 (1, 0), (1, 1), (0, 1), (-1, 1)의 4개 방향으로 한정시켜서 SSD를 구하고, 그 중에서 최소값을 해당 픽셀의 영상 변화량으로 지정해서 ‘특징 가능성’ 값으로 결정한다.
모라벡 알고리즘은 마스크가 0과 1의 값만 갖는 이진 윈도우를 사용하여 노이즈에 취약하다. 또한 4개 방향으로 한정 시켰기 때문에 45도 간격의 에지만 고려할 수 있다.
이런 문제를 해결하여 좀 더 정밀한 코너 검출을 위해 해리스는 다음과 같은 개선 방안을 제시했다.
먼저 이진 윈도우인 w(u, v) 대신 점진적으로 변화하는 가우시안 마스크 G(x, y)를 적용하여 다음과 같은 수식으로 확장한다. 이를 통해 노이즈에 대한 민감도를 낮춘다.
E(u,v)=yxG(x,y)(I(x+u,y+v)I(x,y))2E(u, v) = \sum_{y} \sum_{x} G(x, y) \cdot (I(x+u, y+v) - I(x, y))^{2}
또한 4개 방향에 대해서만 코너를 검출하는 것을 모든 방향에서 검출할 수 있도록 미분을 도입하여 다음과 같이 변경한다.
I(x+u,y+v)I(x,y)+vdy(x,y)+udx(x,y)E(u,v)yxG(x,y)(vdy(x,y)+udx(x,y))2I(x+u, y+v) \cong I(x, y) + vd_{y}(x, y) + ud_{x} (x, y) \\ E(u, v) \cong \sum_{y} \sum_{x} G(x, y) \cdot (vd_{y}(x, y) + ud_{x}(x, y))^{2}
여기서 특징 가능성을 직접 계산하는 대신 위의 수식을 다음과 같이 행렬 M의 식으로 정리한다.
E(u,v)yxG(x,y)(vdy(x,y)+udx(x,y))2=yxG(x,y)(v2dy2+u2dx2+2vudxdy)=yxG(x,y)(u,v)(dx2dxdydxdydy)(uv)=(uv)M(uv)M=yxG(x,y) (dx2dxdydxdydy)E(u, v) \cong \sum_{y} \sum_{x} G(x, y) \cdot (vd_{y}(x, y) + ud_{x}(x, y))^{2} \\ = \sum_{y} \sum_{x} G(x, y) \cdot (v^{2}d_{y}^{2} + u^{2} d_{x}^{2} + 2vud_{x}d_{y}) \\ = \sum_{y} \sum_{x} G(x, y) \cdot (u, v) \left( \begin{array}{rr} d_{x}^{2} & d_{x} d_{y} \\ d_{x} d_{y} & d_{y} \end{array} \right) \left( \begin{array}{r} u \\ v \end{array} \right) \\ = (u v) M \left( \begin{array}{r} u \\ v \end{array} \right) \\ M = \sum_{y} \sum_{x} G(x, y)  \left( \begin{array}{rr} d_{x}^{2} & d_{x} d_{y} \\ d_{x} d_{y} & d_{y} \end{array} \right)
모라벡 알고리즘은 (u, v)를 변화시켜 E(u, v) 맵을 구해서 코너 여부를 판단하는 반면, 해리스 알고리즘은 위 수식의 행렬 M에서 고유벡터를 구하면 경계선 방향에 수직인 벡터 두 개를 얻을 수 있다.
이 행렬 M의 고유값 (λ1,λ2)(\lambda_{1}, \lambda_{2})으로 다음의 식과 같이 코너 응답 함수(corner response function)를 계산한다. 여기서 k는 상수값으로 일반적으로 0.04~0.06 정도가 적당하다.
R=λ1,λ2k(λ1+λ2)2R = \lambda_{1}, \lambda_{2} - k \cdot (\lambda_{1} + \lambda_{2})^{2}
다만 고유값을 계산하려면 고유값 분해의 복잡한 과정을 거쳐야 하기 때문에 해리스 검출기에서는 직접 고유값을 구하지 않고 다음과 같이 행렬식(det)과 대각합(trace)를 통해서 코너 응답 함수로 사용한다.
M=(dx2dxdydxdydy)=(accb)M = \left( \begin{array}{rr} d_{x}^{2} & d_{x} d_{y} \\ d_{x} d_{y} & d_{y} \end{array} \right) = \left( \begin{array}{rr} a & c \\ c & b \end{array} \right)
R=det(M)ktrace(M)2=(abc2)k(a+b)2R = det(M) - k \cdot trace(M)^{2} = (ab - c^{2}) - k \cdot (a + b)^{2}
해리스 코너 검출 방법은 영상의 평행이동, 회전 변환에는 불변(invariant)하는 특징이 있고, 어파인(affine) 변환이나 조명(illumination) 변화에도 어느 정도 강인성이 있다. 하지만 영상의 크기(scale) 변화에는 영향을 받는다.
실제 해리스 코너 검출기를 구현하려면 다음과 같은 과정을 거친다.
소벨 마스크로 미분 행렬 계산 (dx,dy)(dx, dy)
미분 행렬의 곱 계산 (dx2,dy2,dxy)(dx^{2}, dy^{2}, dxy)
곱 행렬에 가우시안 마스크 적용
코너 응답합수  계산 C=det(M)ktrace(M)2C = det(M) - k \cdot trace(M)^{2}
비최대치 억제

k-최근접 이웃 분류기

k-최근접 이웃 분류기의 이해

최근접 이웃 알고리즘은 기존에 가지고 있는 데이터들을 일정한 규칙에 의해 분류된 상태에서 새로운 입력 데이터의 종류를 예측하는 분류 알고리즘이다.
이 방법은 기존의 학습된 여러 클래스의 샘플들을 각각 좌표로 표시하여 두고, 새로운 미지으 샘플이 입력될 때, 학습 클래스의 샘플들과 새 샘플의 거리가 가장 가까운(nearest) 클래스로 분류한다.
여기서 ‘가장 가까운 거리’는 미지의 샘플과 학습 클래스 샘플간의 유사도가 가장 높은 것을 의미한다. 이 거리 측정 방법은 샘플의 형태와 종류에 따라 다양한데, 대표적으로 유클리드 거리(euclidean distance), 해밍 거리(hamming distance), 차분 절대값 등을 이용한다.
최근접 이웃 방법 중에서 가장 많이 사용되는 것은 학습된 클래스들에서 여러 개(k개)의 이웃을 선출하고 이를 이용하여 미지의 샘플들을 분류하는 방법이다.
즉, 미지의 샘플과 가까운 이웃으로 선출된 여러 개의 클래스 샘플들 중에서 가장 많은 수를 가진 클래스로 미지의 샘플들을 분류하는 방법이다. 이러한 분류 과정을 k-최근접 이웃 분류(k-Nearest Neighbors: k-NN)라고 한다.

k-NN 응용

숫자 영상에서 숫자를 인식하는 예제. 아래 5단계를 따른다.
학습 영상의 생성
학습 영상에서 셀 영상 분리
숫자 객체 위치 검색 및 분리
숫자 객체 중앙 배치 및 크기 정규화
최종 예제 프로그램
(위 내용 중 3, 4 번째 항목만 내용 정리)

숫자 객체 위치 검색 및 분리

각 셀에서 숫자 객체가 동일한 위치에 놓여야 학습의 효과와 분류의 정확도가 높아진다. 따라서 아래 그림과 같이 숫자객체를 셀 영역의 중앙에 위치시킨다.
이를 위해 먼저 숫자객체의 위치를 바르게 찾고, 숫자객체를 분리해야 한다.
숫자 객체의 위치 인식은 아래 그림과 같이 투영(projection) 히스토그램을 활용한다.
투영은 다음 식과 같이 영상의 화소값들을 수직 혹은 수평으로 합산하여 나타내는 히스토그램이다.
OpenCV에서 제공하는 cv:reduce() 함수가 행렬 원소를 가로방향 혹은 세로 방향을 감축하기 때문에 투영의 수식과 일치한다. 따라서 이 함수를 이용해서 투영을 구현한다.
hstov(x)=y=0h1f(x,y)hstov(y)=x=0w1f(x,y)hsto_v(x) = \sum_{y=0}^{h-1} f(x, y) \\ hsto_v(y) = \sum_{x=0}^{w-1} f(x, y)

숫자 객체 중앙 배치 및 크기 정규화

다음은 숫자객체를 셀 영역의 중앙에 위치시켜야 한다.
먼저 아래 그림의 오른쪽 그림에서 숫자객체의 가로와 세로 길이 중 긴것을 택하여 그 크기(big)로 정방영상(square)을 만든다.
다음으로 정방영상과 숫자객체의 크기 차이(dx, dy)를 계산한다.
이것은 두 행렬(square, number) 크기의 차분으로 구할 수 있다.
이때 중앙사각형의 시작위치(start)는 이 차분을 좌우, 상하로 양분해야 하기 때문에 2로 나누어준다.
다음으로 시작위치(start)에서 숫자객체 크기로 사각형을 만들면 정방영상의 중앙을 가리키는 중앙사각형(middle)을 만들 수 있다.
그리고 정방영상에서 중앙사각형을 관심영역으로 참조한 후 이 영역에 숫자객체(number)를 복사하면 숫자 객체가 셀 영역의 중앙에 배치된다.

영상 워핑과 영상 모핑

영상 워핑은 하나의 영상에서 비선형적인 특정한 규칙에 따라 입력 영상을 재추출(resampling)하여 영상의 형태를 변형시키는 기술이다.
이 기술은 나사(NASA)에서 인공위성이나 우주선으로부터 전송된 영상이 렌즈의 변형이나 신호의 왜곡 등으로 인해 일그러지는 경우가 많아서 이를 복원하는 용도로 처음 사용되었다.
영상을 여러 다른 방향으로 늘이거나 크기를 조절하는 기법으로 순수한 스케일링과 달리 크기 변화의 정도가 영상 전체에 대해 균일하지 않은 것이 특징이다.
특히 고무판 위에 여상이 있는 것과 같이 임의의 형태로 구부리는 것과 같은 효과를 낸다는 의미에서 고무 시트 변환(Rubber Sheet Transform)이라고도 한다.
영상 워핑 기술은 렌즈 왜곡 보정, 스테레오 영상 정합, 파노라마 영상 합성 등에 사용될 수 있다.
영상 모핑은 조지 루카스가 설립한 특수 효과 전문회사인 ILM이 개발한 기법으로 변형(metamorphosis)란 단어에서 유래되었다.
이것은 하나의 영상에서 형체가 전혀 다른 영상으로 변하도록 하는 기법을 말한다. 즉, 아래 그림과 같이 두 개의 서로 다른 영상 사이의 변화하는 과정을 서서히 나타내는 것이다.
워핑의 기법을 이용해서 모핑을 수행할 수 있다.
영상 워핑의 종류와 방법은 다양하게 있다. 다음의 규칙에 따라 원본 영상의 좌표를 목적 영상의 좌표로 재배치하면 워핑 영상이 완성 된다.
x=x+ratio(pt2.xpt1.x)y=yx' = x + ratio \cdot (pt2.x - pt1.x) \\ y' = y
ratiox<pt1.xxpt1.x, x>pt1.xwidthxwidth=pt1.xratio \Rightarrow x < pt1.x \Rightarrow {x \over pt1.x},  x > pt1.x \Rightarrow {width - x \over width = pt1.x}
일반적으로 카메라로 찍은 영상은 여러 이유에 의해 왜곡된다. 여기서 왜곡되는 요인을 카메라 외부 파라미터와 내부 파라미터로 구분할 수 있다.
카메라는 3차원인 세상의 실세계 영상을 2차원의 평면 영상으로 맺히게 하기 때문에 기하학적인 왜곡이 발생하게 된다. 이것은 카메라 외부 파라미터에 의한 왜곡에 해당하며, 대표적으로 원근 투시 왜곡이 있다.
또한 캡쳐된 영상은 렌즈, 초점거리, 렌즈와 이미지 센서가 이루는 각 등과 같은 카메라 내부의 기구적인 부분에 의해 상당한 영향을 받는데, 이러한 요인을 내부 파라미터 요인이라 한다.
영상 좌표로부터 실세계의 3차원 좌표를 계산하거나 실세계의 3차원 좌표를 영상에 투영된 위치로 계산해야 하는 경우가 있다. 이때 카메라 내부 요인을 제거해야만 보다 정확한 좌표의 계산이 가능하다.
여기서 내부 요인의 파라미터 값을 구하는 과정을 카메라 캘리브레이션(camera calibration)이라 한다.
카메라 영상은 3차원 공간상의 점들을 2차원 영상 평면에 투영함으로써 얻어지는데, 핀홀(pinhole) 카메라 모델에서 이러한 변환 관계는 다음과 같이 모델링 한다.
s(uv1)=AM[XYZ1]=[fx0Cx0fyCy001 ][r11r12r13t1r21r22r23t2r31r32r33t3][XYZ1]s \left( \begin{array}{rrr} u \\ v \\ 1 \end{array} \right) = A \cdot M \cdot \left[ \begin{array}{rrrr} X \\ Y \\ Z \\ 1 \end{array} \right] \\ = \left[ \begin{array}{rrr} f_{x} & 0 & C_{x} \\ 0 & f_{y} & C_{y} \\ 0 & 0 & 1  \end{array} \right] \cdot \left[ \begin{array}{rrrr} r_{11} & r_{12} & r_{13} & t_{1} \\ r_{21} & r_{22} & r_{23} & t_{2} \\ r_{31} & r_{32} & r_{33} & t_{3} \end{array} \right] \cdot \left[ \begin{array}{rrrr} X \\ Y \\ Z \\ 1 \end{array} \right]
여기서 X, Y, Z는 실세계의 3차원 좌표이며, u, v는 2차원 영상 평면에 투영된 좌표이다.
A는 카메라 내부 파라미터(intrinsic parameters)이고, M은 카메라 외부 파라미터이다.
카메라 내부 파라미터의 요인으로는 초점거리(focal length)와 주점(principal point) 등이 있다.
초점거리 (fx,fy)(f_{x}, f_{y})는 아래 그림에서와 같이 렌즈에서 이미지 센서까지의 거리를 말한다.
보통의 디지털 카메라에서는 mm 단위로 표현되지만 카메라 모델에서는 픽셀(pixel) 단위로 표현된다.
초점거리가 두 개의 값으로 표현되는 이유는 이미지 센서의 가로 방향과 세로 방향의 셀 간격이 다를 수 있기 때문이다.
주점 (cx,cy)(c_{x}, c_{y})은 카메라 렌즈의 중심에서 이미지 센서에 내린 수선의 영상 좌표로서 일반적으로 말하는 영상 중심점과는 다른 의미이다.
예컨대 카메라 조립과정에서 오차로 인해 렌즈와 이미지 센서가 수평이 어긋나면 주점과 영상중심은 다른 값을 가질 수 있다.