https://hyeon-jae.tistory.com/119
기하학적 변환 (1)
1. 이동(Translation)■ 기존 좌표를 \( x_{\text{old}}, y_{\text{old}} \)라고 했을 때, 이미지를 이동하는 방법은 기존 좌표에 이동시키려는 거리 \( d_1, d_2 \)를 더해주면 된다. \[ \begin{cases} x_{\text{new}} = x_{\
hyeon-jae.tistory.com
■ 이미지 이동, 확대/축소, 회전, 어핀 변환, 원근 변환은 모두 변환 행렬을 이용해서 이미지 변환을 수행할 수 있었다.
■ 하지만, 변환 행렬로 구할 수 없는 모양의 변환이 있는데, 바로 렌즈 왜곡 변환이다.
■ 렌즈 왜곡 변환에는 리매핑, 오목 렌즈/볼록 렌즈 왜곡, 방사 왜곡이 있다.
1. 리매핑(Remapping)
■ 리매핑은 이미지의 각 픽셀 위치를 사용자가 정의한 새로운 좌표에 재배치해서 이미지를 변환하는 기법이다.
■ OpenCV에서 cv2.remap(src, mapx, mapy, interpolation, dst, borderMode, borderValue) 함수로 이미지에 리매핑을 적용할 수 있다.
- src는 입력 이미지
- mapx와 mapy는 x축 변형, y축 변형으로, x축과 y축으로 이동할 좌표이며 입력 이미지와 동일한 크기를 가져야 한다. dtype = float32
- 예를 들어 mapx[0][0] = 5, mapy[0][0] = 3으로 지정했다면, src(입력 이미지) 좌표 (0, 0)에 있는 픽셀을 좌표 (5, 3)으로 이동하라는 의미이다.
- mapx와 mapy는 초깃값으로 원래 이미지의 좌표 값을 지정하는 것이 좋다. 이동시키고 싶은 픽셀에 대해서만 새로운 좌표를 지정해서 리매핑을 수행할 수 있기 때문이다.
- 예를 들어 mapx와 mapy를 0으로 초기화하는 경우 원본(입력) 이미지의 (0, 0) 픽셀만 참조하기 때문에 원본 이미지의 맨 왼쪽 상단 픽셀 값을 그대로 참조한다.
- 반면, 입력 이미지의 좌표 값으로 초기화할 경우, 예를 들어 mapx = x, mapy = y 일 때 가로로 100픽셀만 왼쪽으로 이동하고 싶으면 mapx = x - 100으로 간단히 지정할 수 있다. 0이나 의미 없는 값으로 초기화한다면, x - 100을 지정하기 위해 불필요한 작업이 많아진다.
- 나머지 파라미터는 cv2.warpAffine( )과 동일하다.
img = cv2.imread('777.jpg')
rows, cols = img.shape[:2]
mapx = cols - mapx -1 # x축 좌표 뒤집기
mapy = rows - mapy -1 # y축 좌표 뒤집기
## remap 적용
fliped = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
plt.subplot(121),plt.imshow(img),plt.title('original'),plt.xticks([]); plt.yticks([])
plt.subplot(122),plt.imshow(fliped),plt.title('fliped'),plt.xticks([]); plt.yticks([])
plt.show()
■ cv2.remap( ) 함수를 이용해 변환 행렬로 표현할 수 없는 비선형 변환을 적용할 수 있다.
img = cv2.imread('005006192.jpg')
rows, cols = img.shape[:2]
## 매핑 배열 초기화
mapy, mapx = np.indices((rows, cols),dtype=np.float32)
mapy.shape, mapx.shape
```#결과#```
((1440, 1080), (1440, 1080))
````````````
l, amp = 20, 15 # 파장과 진폭
mapx[1][0], amp, np.sin(mapy/l)[1][0]
```#결과#```
(0.0, 15, 0.04997917)
````````````
## 비선형 매핑
sinx = mapx + amp * np.sin(mapy/l)
cosy = mapy + amp * np.cos(mapx/l)
sinx[1][0]
```#결과#```
0.74968755
````````````
img_sinx = cv2.remap(img, sinx, mapy, cv2.INTER_LINEAR) # x축만 sin 곡선 적용
img_cosy = cv2.remap(img, mapx, cosy, cv2.BORDER_REPLICATE) # y축만 cos 곡선 적용
img_sinx_cosy = cv2.remap(img, sinx, cosy, cv2.INTER_LINEAR, None, cv2.BORDER_REPLICATE) # x, y 축 모두 적용
titles = ['original', 'img_sinx', 'img_cosy', 'img_sinx_cosy']
images = [img, img_sinx, img_cosy, img_sinx_cosy]
for i in range(4):
plt.subplot(1, 4, i+1)
plt.imshow(images[i][:,:,::-1])
plt.title(titles[i])
plt.xticks([]); plt.yticks([])
plt.subplots_adjust(wspace=0.5)
plt.show()
2. 오목/볼록 렌즈 왜곡(Lens Distortion)
■ 오목/볼록 렌즈 왜곡 처리는 이미지를 직교 좌표계보다 극 좌표계로 변환해서 처리하는 것이 더 수월하기 때문에 다음과 같은 단계를 따른다.
- (1) 좌상단 기준 좌표에서 -1 ~ 1로 정규화된 중심점 기준 좌표로 변경
- 극 좌표계에서 처리하기 위한 첫 번째 단계로, 기존 이미지의 전체 좌표를 -1 ~ 1 범위로 정규화해서 정규화된 중심점을 기준으로 대칭적인 좌표계를 가지게 하여 렌즈 왜곡 계산을 수월하게 하기 위해서이다.
- (2) 직교 좌표계를 극 좌표계로 변환
- 렌즈 왜곡 효과는 반지름(r)에 따른 변형을 가정한다. 중심에서 멀어질수록 어떤 비율로 픽셀이 늘어나거나 줄어드는 방식이다.
- 직교 좌표계 (x, y)를 극 좌표계 (r, θ)로 변환하면, 반지름 r만(정확히는 원점으로부터의 거리) 조정하면 왜곡 효과를 줄 수 있다.
- 즉, 직교 좌표계에서 렌즈 왜곡 효과를 적용하는 것보다 극 좌표계에서 왜곡 효과를 적용하는 것이 더 수월하다.
- 이 작업은 OpenCV의 cv2.cartToPolar(x, y) 함수를 사용하면 된다. 함수 결과로 r, θ 값이 반환된다.
(3) 렌즈 왜곡 지수 적용
- 극 좌표 공간에서 r 값에 비선형 함수를 적용한다.
- r ** exp 형태로 r 값을 조정하면 r이 클수록 더 많이 변형되거나 작을수록 덜 변형되는 식의 비선형 왜곡이 가능하다. 이를 통해 렌즈 왜곡 효과를 낼 수 있다.
(4) 극 좌표계를 다시 직교 좌표계로 변환
- 렌즈 왜곡 처리를 끝낸 후에는 다시 이미지에 매핑하기 위해 극 좌표계에서 직교 좌표계로 되돌려야 한다.
- 이 작업은 OpenCV의 cv2.polarToCart(r, theta) 함수를 사용하면 된다. 함수 결과로 x, y 값이 반환된다.
(5) 중심점 기준에서 좌상단(원래 이미지 좌표계)로 변경
- 왜곡 처리까지의 과정을 모두 중심점 기준으로 진행했기 때문에 최종적으로 OpenCV의 cv2.remap( ) 함수를 사용하기 위해서는 원래 이미지 좌표계로 되돌려야 한다.
img = cv2.imread('img1.jpg')
rows, cols = img.shape[:2]
## 렌즈 값(이미지 왜곡 지수) 지정
exp = 2 # 1보다 크면 볼록 렌즈, # 1이면 원본과 동일 # 0과 1 사이 값이면 오목 렌즈
scale = 1 # 렌즈 효과 적용할 영역 크기 (0 ~ 1) # 1이면 100%
## 매핑 배열 초기화
mapy, mapx = np.indices((rows, cols),dtype=np.float32)
## 규화된 중심점 기준 좌표로 변경
mapx = 2*mapx/(cols-1)-1
mapy = 2*mapy/(rows-1)-1
## 직교 좌표계를 극 좌표계로 변환
r, theta = cv2.cartToPolar(mapx, mapy)
## 렌즈 효과
# 왜곡 지수 적용 - 왜곡할 영역 중심 확대/축소
r[r< scale] = r[r<scale] **exp # 반지름이 scale보다 작은 범위에 있는 좌표만
## 극 좌표계에서 다시 직교 좌표계로 변환
mapx, mapy = cv2.polarToCart(r, theta)
## 중심점 기준에서 좌상단(원래 이미지 좌표계)로 변경
mapx = ((mapx + 1)*cols-1)/2
mapy = ((mapy + 1)*rows-1)/2
## 렌즈 왜곡 효과 적용
distorted = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
plt.subplot(121),plt.imshow(img[:,:,::-1]),plt.title('original'),plt.xticks([]); plt.yticks([])
plt.subplot(122),plt.imshow(distorted[:,:,::-1]),plt.title('distorted'),plt.xticks([]); plt.yticks([])
plt.show()
3. 방사 왜곡(Radial Distortion)
■ 방사 (렌즈) 왜곡은 볼록렌즈의 굴절률에 의한 것으로 영상/이미지의 왜곡 정도는 중심에서의 거리에 의해 결정된다.
■ 다음 그림처럼 바깥쪽 부분이 중심으로부터 멀어질수록 바깥 방향으로 굽어져 직선이 곡선처럼 보이는 배럴 왜곡(barrel distortiaon)이 있고,
■ 직선이 중심으로 갈수록 안쪽으로 휘어져 바깥쪽 부분이 안쪽으로 들어가는 듯한 형태의 핀쿠션 왜곡(pinsushion distortion)이 있다.
■ 방사 왜곡을 적용하는 과정은 오목/볼록 렌즈 왜곡과 비슷하다. 단, 3개의 왜곡 지수를 지정하며, 3개의 왜곡 지수의 값에 따라 왜곡의 정도와 왜곡의 유형(배럴 왜곡, 핀쿠션 왜곡)을 제어할 수 있다.
img = cv2.imread('img1.jpg')
rows, cols = img.shape[:2]
mapy, mapx = np.indices((rows, cols),dtype=np.float32)
## 왜곡 계수 설정
k1, k2, k3 = 0.5, 0.2, 0.0 # 배럴 왜곡
# k1, k2, k3 = -0.3, 0, 0 # 핀큐션 왜곡
mapx = 2*mapx/(cols-1)-1
mapy = 2*mapy/(rows-1)-1
r, theta = cv2.cartToPolar(mapx, mapy)
## 방사 렌즈 왜곡 연산
# 왜곡 지수 적용
ru = r*(1+k1*(r**2) + k2*(r**4) + k3*(r**6))
## 베럴 왜곡 효과 적용
barrel_distorted = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
## 왜곡 계수 설정
k1, k2, k3 = -0.3, 0, 0 # 핀큐션 왜곡
## 핀쿠션 왜곡 효과 적용
# pinsushion_distorted = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
'OpenCV' 카테고리의 다른 글
모폴로지(Morphology) 연산, 이미지 피라미드(Image Pyramid) (0) | 2024.12.21 |
---|---|
필터와 블러링 (0) | 2024.12.18 |
기하학적 변환 (1) (0) | 2024.12.16 |
이미지 프로세싱 (3) (0) | 2024.12.15 |
이미지 프로세싱 (2) (0) | 2024.12.14 |