인지야공

인지야공/강의 요약/1번째 글

영상처리 — 픽셀에서 특징 매칭까지

한 학기 강의를 관통하는 줄기는 하나였다. 영상은 행렬이고, 처리는 필터이며, 변환은 행렬곱이고, 보간은 픽셀 보정이다. 이 한 줄에 나머지가 다 걸려 있다.

빛에서 배열까지

단계하는 일
영상 센서빛을 전하로 바꾼다. CCD 는 고화질, CMOS 는 저전력·저가
표본화 Sampling공간을 잘라 해상도를 정한다. 픽셀 수가 결정된다
양자화 Quantization각 픽셀의 아날로그 값을 nn 비트 2진수로 표현한다

결과가 2차원 배열 f(x,y)f(x, y) 다. 0≤x≤M−10 \le x \le M-1, 0≤y≤N−10 \le y \le N-1 이고 1 프레임은 M×NM \times N 픽셀이다.

  • Gray image — 8 bits
  • Color image — 8(R) + 8(G) + 8(B) = 24 bits

색에 대한 두 사람의 발견이 여기 깔려 있다. 뉴턴이 가시광선의 파장이 400~750nm 임을 밝혔고, 맥스웰이 모든 색을 RGB 세 기본색의 조합으로 만들 수 있음을 보였다. 사람 망막에 세 종류의 추상체(cone)가 있다는 사실과 맞물린다. 카메라가 CFA(Color Filter Array)를 쓰는 이유도 같다.

처리의 수준은 셋으로 나눈다.

수준예
저수준 Low-level잡음 제거, 대비 개선, 선명화
중수준 Mid-level영상 특징 추출
고수준 High-level영상 분할, 사물 인식

OpenCV

인텔이 공개한 컴퓨터 비전 라이브러리다. 원래는 인텔 칩의 성능을 평가할 목적으로 만든 IPL(Image Processing Library) 기반이었다는 게 재미있는 지점이다.

버전
2006 v1.0C 기반 API
2009 v2.0C++ 기반 API
2015 v3.1고급 영상 처리 (특징 추출 등)
2018 v4.0신경망·딥러닝 알고리즘

바퀴를 다시 발명하지 말자(reinventing the wheel 방지)는 것이 이 라이브러리를 쓰는 이유다.

import cv2 as cv

img = cv.imread('경로')
cv.imshow('윈도우명', img)
cv.waitKey(0)
cv.destroyAllWindows()

OpenCV 영상은 numpy.ndarray 다. 이게 전부다. img.shape 은 (height, width, channel) 이고 인덱싱은 img[y, x, c] — 행이 먼저고 열이 나중이다. 수학의 (x,y)(x, y) 와 순서가 반대라 여기서 계속 헷갈렸다.

채널 순서도 함정이다. OpenCV 는 BGR 이다.

b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]

gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

명암 변환 공식은 이렇게 정의돼 있다.

I=round(0.299R+0.587G+0.114B)I = \text{round}(0.299R + 0.587G + 0.114B)

세 계수가 다른 이유는 사람 눈이 초록에 가장 민감하기 때문이다. 단순 평균을 쓰면 사람이 보기에 어색한 흑백이 나온다.

색 공간은 둘을 쓴다. RGB 와 HSV(색상·채도·명도) 인데, HSV 는 조명 변화에 강건하다. 같은 물체가 밝은 데서든 어두운 데서든 H 값은 비슷하게 유지된다.

웹캠은 이 틀이다.

cap = cv.VideoCapture(0, cv.CAP_DSHOW)
while True:
    ret, frame = cap.read()
    cv.imshow('cam', frame)
    if cv.waitKey(1) == ord('q'):
        break
cap.release()

그리기와 이벤트도 API 한 줄씩이다.

cv.line(img, pt1, pt2, color, thickness)
cv.rectangle(img, pt1, pt2, color)
cv.circle(img, center, r, color)
cv.putText(img, text, pos, font, scale, color)
cv.setMouseCallback(window, function)

연산의 세 갈래

강의에서 가장 유용했던 분류다. 화소값을 어디에서 받아 오느냐로 나눈다.

연산값을 어디서 받나
점 연산자기 자신에게서
영역 연산이웃 화소들에게서
기하 연산기하학적 변환이 정해 주는 곳에서

점 연산

# 감마 보정
out = np.uint8(255 * ((img / 255) ** gamma))

# 나란히 붙여 비교
np.hstack([img1, img2, img3])

히스토그램 평활화는 히스토그램을 평평하게 만들어 명암 대비를 높인다. xx 축이 명암(0~255), yy 축이 카운트일 때 그 yy 를 고르게 펴는 기법이다.

이진화는 임계값을 자동으로 잡아 주는 오츄 알고리즘을 쓴다.

hist = cv.calcHist([img], [2], None, [256], [0, 256])
_, binary = cv.threshold(img[:, :, 2], 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

cv.THRESH_OTSU 를 더하면 첫 인자로 준 임계값(위에서는 0)은 무시되고 알고리즘이 스스로 찾는다. 조명이 균일한 영상에서 잘 먹는다.

모폴로지

이진 영상의 모양을 다듬는다. 네 가지가 짝을 이룬다.

연산정의영역
팽창 Dilation구조 요소에 걸치면 1 로늘어난다 (+)
침식 Erosion이웃에 0 이 하나라도 있으면 0 으로줄어든다 (−)
열림 Opening침식 후 팽창대체로 유지 (=)
닫힘 Closing팽창 후 침식대체로 유지 (=)

열림은 작은 잡음 점을 없애고, 닫힘은 작은 구멍을 메운다. 둘 다 전체 영역 크기는 거의 유지된다는 것이 요점이다. 연결 요소를 셀 때는 4-연결성과 8-연결성 중 무엇을 쓰는지에 따라 결과가 달라진다.

영역 연산 — 컨볼루션

컨볼루션은 이웃 화소를 고려한 연산이다. 필터(커널)를 씌워 새 값을 만든다. 커널만 바꾸면 전혀 다른 일을 한다.

A. 스무딩          B. 샤프닝              C. 엠보싱
1/9 1/9 1/9        0 -1  0    -1 -1 -1    -1  0  0
1/9 1/9 1/9       -1  4 -1    -1  8 -1     0  0  0
1/9 1/9 1/9        0 -1  0    -1 -1 -1     0  0  1

샤프닝 커널의 가운데가 5 인 형태도 자주 쓴다.

kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
sharpened = cv.filter2D(image, -1, kernel)

blurred = cv.GaussianBlur(img, (5, 5), 0)

가우시안 필터는 이 식이다.

G(x)=1σ2πexp⁡(−x22σ2)G(x) = \frac{1}{\sigma\sqrt{2\pi}} \exp\left(-\frac{x^2}{2\sigma^2}\right)

σ\sigma 가 클수록 넓게 뭉갠다.

데이터 타입에서 사고가 난다. OpenCV 의 기본은 uint8(0~255)인데 합성곱은 음수를 만들 수 있다. 샤프닝이나 미분 필터를 쓰면 값이 넘치거나 잘린다. float 로 바꾸거나 정규화한 뒤 되돌린다.

cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3)   # x 방향 — 출력 타입을 float 로
cv.Sobel(img, cv.CV_64F, 0, 1, ksize=3)   # y 방향

기하 연산과 보간

이동·회전·스케일링이다. 동차 좌표를 쓰면 전부 행렬곱 하나로 표현된다.

[x′,y′,1]=[x,y,1]⋅H[x', y', 1] = [x, y, 1] \cdot H

장점여러 변환을 하나의 행렬로 합칠 수 있다
단점에일리어싱 — 픽셀이 누락돼 중간에 빈다
해결역방향 매핑 — 출력에서 입력을 거꾸로 찾아온다

정방향으로 매핑하면 출력의 어떤 픽셀은 아무 입력도 받지 못해 구멍이 난다. 반대로 출력 픽셀마다 “나는 입력의 어디서 왔나”를 계산하면 빈 곳이 생기지 않는다. 이 방향 전환 하나가 에일리어싱을 없앤다.

실수 좌표를 정수 좌표로 옮기는 것이 보간이다.

보간방식결과
최근접 이웃 INTER_NEAREST반올림빠르지만 계단 현상
양선형 INTER_LINEAR주변 4개 픽셀의 걸치는 비율로 선형 결합자연스럽다. 기본값
INTER_CUBIC16개 픽셀더 부드럽지만 느리다
INTER_AREA영역 평균축소할 때 유리
resized = cv.resize(img, (800, 600), interpolation=cv.INTER_LINEAR)

엣지

엣지는 특성이 다른 곳의 경계다. 영역 분할이 “유사 화소를 묶는” 접근이라면 엣지는 “달라지는 곳을 찾는” 접근이고, 둘은 서로의 쌍대 문제다.

미분으로 찾는다.

컨볼루션 마스크
1차 미분[-1, 1]
2차 미분[1, -2, 1]

캐니 엣지

edges = cv.Canny(img, Tlow, Thigh)

임계값을 두 개 쓰는 것이 핵심이다. 거짓 긍정을 줄이기 위해서다.

  1. 엣지 강도가 ThighT_{high} 이상인 화소에서 추적을 시작한다
  2. 이어지는 추적은 TlowT_{low} 이상이면 계속한다

강한 엣지에서 출발해 약한 엣지를 따라가는 방식이라, 고립된 약한 잡음은 살아남지 못한다. 다만 물체의 경계와 그림자의 경계를 구별하지는 못한다. 이건 캐니의 한계다.

contours, _ = cv.findContours(img, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)

허프 변환

끊긴 엣지를 모아 직선이나 원을 찾는다. 투표로 동작한다.

  1. (ρ,θ)(\rho, \theta) 를 축으로 하는 투표 공간(누적 배열)을 만든다
  2. 영상의 각 점 (x,y)(x, y) 에 대해 가능한 모든 θ\theta 로 ρ=xcos⁡θ+ysin⁡θ\rho = x\cos\theta + y\sin\theta 를 계산하고 해당 칸에 표를 던진다
  3. 표를 가장 많이 받은 칸의 (ρ,θ)(\rho, \theta) 가 곧 영상에 있는 직선이다

각 점이 자기가 속할 수 있는 모든 직선에 투표한다고 이해하면 된다. 한 직선 위의 점들은 전부 같은 칸에 표를 몰아주므로 그 칸이 튀어 오른다. 엣지가 중간중간 끊겨 있어도 살아남는 이유가 여기 있다.

RANSAC 은 다른 접근이다. 난수로 표본을 뽑아 추정을 반복하고 가장 신뢰할 만한 값을 고른다. 이상치가 섞여 있어도 견디는 강인한 추정 기법이다.

영역 분할

방법
이진화임계값으로 나눈다
워터셰드지형의 물이 고이듯 영역을 키운다
SLIC 슈퍼화소k-평균 군집화와 유사하게 화소 할당과 군집 중심 갱신을 반복

슈퍼화소는 비슷한 화소를 묶어 처리 단위를 키우는 것이다. 픽셀 수만 개를 슈퍼화소 수백 개로 줄이면 뒤따르는 연산이 훨씬 가벼워진다. scikit-image 에 구현이 있다.

특징

영역 특징 — LBP 와 LTP

방식차원
LBP Local Binary Pattern중심 화소와 주위 화소의 명암을 비교해 텍스처를 측정256
LTP Local Ternary Pattern작은 명암 변화에 민감한 LBP 의 단점을 개선512

LBP 는 크다/작다 둘로만 나눠서 잡음에 흔들린다. LTP 는 “비슷함”이라는 중간 단계를 넣어 세 값을 쓰고, 그래서 차원이 두 배가 된다.

지역 특징

대응점 문제 — 이웃한 두 영상에서 같은 물체의 같은 곳을 쌍으로 맺는 일이다. 파노라마, 물체 인식·추적, 스테레오 비전, 카메라 캘리브레이션이 전부 여기에 걸려 있다.

지역 특징의 발상은 이렇게 정리됐다.

  • 좁은 지역만 보고 특징점을 결정한다
  • “물체의 모퉁이 위치”라는 완고한 생각을 포기한다
  • 위치의 정확함보다 반복성이 중요하다

같은 장면을 다시 찍었을 때 같은 점이 다시 잡히기만 하면, 그 점이 사람 눈에 의미 있는 모퉁이인지는 상관없다는 뜻이다.

지역 특징은 위치·스케일·방향·특징 기술자로 표현하고, 갖춰야 할 조건은 여섯이다 — 반복성, 불변성, 분별력, 지역성, 적당한 양, 계산 효율.

발전 순서는 이렇다.

모라벡 (제곱차 합) → 해리스 특징점 (가우시안 적용) → SIFT (DoG: Difference of Gaussian)

매칭

매칭은 컴퓨터 비전의 여러 문제에서 핵심 역할을 한다. 특징점을 뽑았으면 짝을 지어야 한다.

  • 빠른 매칭 — FLANN, FAISS
  • 호모그래피 추정 — 3차원 투영 방정식, 매칭 쌍, 최소제곱법, RANSAC

여기서도 RANSAC 이 나온다. 매칭 쌍에는 항상 잘못된 짝이 섞이는데, 최소제곱법만 쓰면 그 몇 개가 전체 변환을 망친다. RANSAC 으로 다수가 동의하는 변환을 고르는 것이 실무의 답이다.

마지막 주에 나온 비전 에이전트는 여기에 능동성을 더한 개념이다 — 환경에서 영상을 획득할 뿐 아니라 환경에 영향을 미치는 기능까지 갖춘 것.

표시는 이 브라우저에만 남는다. 서버로 가는 것은 없다.