IT/AI\ML

[python/OpenCV] 이미지 처리 예제 Q26~Q30

개발자 두더지 2020. 4. 20. 20:22
728x90

문제 링크

https://github.com/yoyoyo-yo/Gasyori100knock/tree/master/Question_21_30

 

yoyoyo-yo/Gasyori100knock

画像処理100本ノックして画像処理を画像処理して画像処理するためのもの For Japanese, English and Chinese - yoyoyo-yo/Gasyori100knock

github.com


Q26. Bi-linear보간(Bi-linear補間)

 ​Bi-linear보간을 적용하여 이미지를 1.5배 확대시켜보자. Bi-linear보간이란 주변의 4화소의 거리에 따른 가중치를 매김으로써 확대에 의한 화소 변질을 보완하는 방법이다.

1. 확대 이미지의 좌표(x', y')를 확대률a로 나누어 floor(x'/a, y'/a)를 구한다.

2. 원본 이미지의 (x'/a, y'/a)의 주변 4화소인 I(x,y), I(x+1,y), I(x,y+1), I(x+1, y+1)를 구한다.

3. 각각의 화소와 (x'/a, y'/a)와의 거리 d를 구해 가중치를 부여한다. w = d / Sum d

4. 다음 식에 의해 확대 이미지의 화소 (x',y')를 구한다. dx = x'/a - x , dy = y'/a - y

A26. Bi-linear보간(Bi-linear補間)의 답안

import cv2
import numpy as np
import matplotlib.pyplot as plt


# Bi-Linear interpolation
def bl_interpolate(img, ax=1., ay=1.):
	H, W, C = img.shape

	aH = int(ay * H)
	aW = int(ax * W)

	# get position of resized image
	y = np.arange(aH).repeat(aW).reshape(aH, -1)
	x = np.tile(np.arange(aW), (aH, 1))

	# get position of original position
	y = (y / ay)
	x = (x / ax)

	ix = np.floor(x).astype(np.int)
	iy = np.floor(y).astype(np.int)

	ix = np.minimum(ix, W-2)
	iy = np.minimum(iy, H-2)

	# get distance 
	dx = x - ix
	dy = y - iy

	dx = np.repeat(np.expand_dims(dx, axis=-1), 3, axis=-1)
	dy = np.repeat(np.expand_dims(dy, axis=-1), 3, axis=-1)

	# interpolation
	out = (1-dx) * (1-dy) * img[iy, ix] + dx * (1 - dy) * img[iy, ix+1] + (1 - dx) * dy * img[iy+1, ix] + dx * dy * img[iy+1, ix+1]

	out = np.clip(out, 0, 255)
	out = out.astype(np.uint8)

	return out


# Read image
img = cv2.imread("assets/imori.jpg").astype(np.float)

# Bilinear interpolation
out = bl_interpolate(img, ax=1.5, ay=1.5)

# Save result
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.imwrite("out.jpg", out)
import cv2
import numpy as NumPy

img = cv2.imread("assets/imori.jpg")
    
# OpenCV
out = cv2.resize(
    img, (img.shape[1]*2, img.shape[0]*2), interpolation=cv2.INTER_LINEAR)

cv2.imshow("result",out)
cv2.waitKey(0)
cv2.destroyAllWindows()
<numpy.repeat()>

numpy.repeat(a, repeats, axis=None)

- a :입력 배열
- repeats : int array of ints각 요소에 대한 반복의 숫자.
반복은 주어진 축의 모양과 동일하게 된다.
- axis = None : int, optional값을 반복할 축.
기본적으로 평평한 배열을 입력하고, 평평한 배열을 리턴한다.
> 리턴값: ndarray

https://docs.scipy.org/doc/numpy/reference/generated/numpy.repeat.html

Q27. Bi-cubic보간(Bi-cubic補間)

Bi-cubic보간을 적용하여 이미지를 1.5배 확대시켜보자.

Bi-cubic보간은 Bi-linear을 확장한 것으로 주변의 16화소로 부터 보간을 적용하는 것을 의미한다.

각각의 화소와의 거리는 다음의 식으로 정의되어 있다.

가중치는 거리에 따라 다음의 함수에 의해 정해진다.

a 는 많은 경우 -1을 취한다.

대체로 이미지의 청색 픽셀 거리는 |t| <= 1、녹색은 1< |t| <=2 의 가중치가 된다.

위의 화소의 가중치를 사용해, 다음의 식으로 확대 이미지의 화소가 계산된다.

각각의 화소와 가중치를 곱한 합을 가중치의 합으로 나눈다.

 

A27. Bi-cubic보간(Bi-cubic補間)의 답안

import cv2
import numpy as np
import matplotlib.pyplot as plt


# Bi-cubic interpolation
def bc_interpolate(img, ax=1., ay=1.):
	H, W, C = img.shape

	aH = int(ay * H)
	aW = int(ax * W)

	# get positions of resized image
	y = np.arange(aH).repeat(aW).reshape(aH, -1)
	x = np.tile(np.arange(aW), (aH, 1))
	y = (y / ay)
	x = (x / ax)

	# get positions of original image
	ix = np.floor(x).astype(np.int)
	iy = np.floor(y).astype(np.int)

	ix = np.minimum(ix, W-1)
	iy = np.minimum(iy, H-1)

	# get distance of each position of original image
	dx2 = x - ix
	dy2 = y - iy
	dx1 = dx2 + 1
	dy1 = dy2 + 1
	dx3 = 1 - dx2
	dy3 = 1 - dy2
	dx4 = 1 + dx3
	dy4 = 1 + dy3

	dxs = [dx1, dx2, dx3, dx4]
	dys = [dy1, dy2, dy3, dy4]

	# bi-cubic weight
	def weight(t):
		a = -1.
		at = np.abs(t)
		w = np.zeros_like(t)
		ind = np.where(at <= 1)
		w[ind] = ((a+2) * np.power(at, 3) - (a+3) * np.power(at, 2) + 1)[ind]
		ind = np.where((at > 1) & (at <= 2))
		w[ind] = (a*np.power(at, 3) - 5*a*np.power(at, 2) + 8*a*at - 4*a)[ind]
		return w

	w_sum = np.zeros((aH, aW, C), dtype=np.float32)
	out = np.zeros((aH, aW, C), dtype=np.float32)

	# interpolate
	for j in range(-1, 3):
		for i in range(-1, 3):
			ind_x = np.minimum(np.maximum(ix + i, 0), W-1)
			ind_y = np.minimum(np.maximum(iy + j, 0), H-1)

			wx = weight(dxs[i+1])
			wy = weight(dys[j+1])
			wx = np.repeat(np.expand_dims(wx, axis=-1), 3, axis=-1)
			wy = np.repeat(np.expand_dims(wy, axis=-1), 3, axis=-1)

			w_sum += wx * wy
			out += wx * wy * img[ind_y, ind_x]

	out /= w_sum
	out = np.clip(out, 0, 255)
	out = out.astype(np.uint8)

	return out


# Read image
img = cv2.imread("assets/imori.jpg").astype(np.float32)

# Bi-cubic interpolation
out = bc_interpolate(img, ax=1.5, ay=1.5)

# Save result
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.imwrite("out.jpg", out)

https://algorithm.joho.info/programming/python/opencv-bicibic-interpolation-py/

 

【Python/OpenCV】画像の拡大・縮小【バイキュービック補間法】

Python版OpenCVのcv2resizeでバイキュービック補間法を実装し、画像のサイズを変更(拡大・縮小)する方法をソースコード付きで解説します。

algorithm.joho.info

위의 링크를 참고하여 작성한 코드는 아래와 같다.

import cv2

img = cv2.imread("assets/imori.jpg")
    
out = cv2.resize(
    img, (img.shape[1]*2, img.shape[0]*2), interpolation=cv2.INTER_CUBIC)

cv2.imshow("result",out)
cv2.waitKey(0)
cv2.destroyAllWindows()
<numpy.power()>

numpy.power(x1, x2, /, out=None, *, where=True, casting='same_kind', order='K', dtype=None, subok=True[, signature, extobj]) = <ufunc 'power'>

첫번째 입력 어레이의 값들을 두 번째 입력 어레이의 값으로 거듭 제곱 계산하는 함수
- x1 : 기본 베이스 배열
- x2 : 지수
x1.shape != x2.shape인 경우 공통 모양으로 출력된다.
- out : ndarray, None, or tuple of ndarray and None, optional
결과가 저장되는 위치.
작성한 경우 브로드캐스트되는 형태가 지정되어 있어야 한다.
지정하지 않은 경우에는 새로 할당된 어레이가 반환된다.
tuple(오직 키워드 인수로만 가능) 은 반드시 출력되는 배열의 숫자의 길이와 반드시 동일해야한다.
- where : array_like, optional
출력되는 배열 중 조건이 참인 곳의 위치에 ufunch결과가 삽입된다.
초기화되지 않은 출력 배열이 out=None으로 지정되어 생성된 경우 해당 배열 내의 위치가 조건의 거짓인 경우 초기화되지 않은 상태로 유지된다는 점을 주의하라.
**kwargs
> 리턴값 : ndarray

https://docs.scipy.org/doc/numpy/reference/generated/numpy.power.html
<numpy.abs()>

절대값을 구하는 함수이다.스칼라 입력값에 대해서는 절대값을 반환하고, 복소수 입력에 대해서는 크기를 반환한다.numpy.avsolute()함수를 짧게 줄여서 사용하는 것이 바로 numpy.abs()이다.참고로 복소수가 아닌 경우네는 fabs함수로 빠르게 연산가능하다.

numpy.absolute(x, /, out=None, *, where=True, casting='same_kind', order='K', dtype=None, subok=True[, signature, extobj]) = <ufunc 'absolute'>

- x : 입력 배열
- out : ndarray, None, or tuple of ndarray and None, optional
- where : array_like, optional
**kwargs
> 리턴값 : ndarray

https://docs.scipy.org/doc/numpy/reference/generated/numpy.absolute.html

<numpy.zeros_like()>

주어진 배열과 같은 형태의 데이터 타입을 갖는 0으로 채워진 어레이를 반환하는 함수이다.

numpy.zeros_like(a, dtype=None, order='K', subok=True, shape=None)

-a : array_like 반환되는 배열과 같은 데이터 타입으로 정의되어 있는 요소의 shape
-dtype : data-type, optional
-order : {`C`,F``,`A`or`K`}, optional 메모리에 오버라이드하는 결과 `C`의 뜻은 C-order ‘F’의 뜻은 F-order ‘A’ 의 뜻은 포트란이 인접한 경우 ‘F’ 이고, 그렇지 않으면 ‘C’라는 의미이다. ‘K’ 는 가장 가까운 레이아웃에 일치한다는 뜻이다.
- subok : bool, optional 만약 참이라면, a타입의 서브 클래스를 사용하여 새롭게 생성되는 배열이고, 그렇지 않으면 일반 클래스 배열이다. 기본값은 참이다.
- shape : int or sequence of ints, optional 결과 값의 모양을 재정의한다. 만약 order=’K’이고 지수의 수가 바뀌지 않는 숫자라면, order를 그대로 유지할 것이고, 그렇지 않으면 order=’C’ 가 된다.
> 리턴 : ndarray

https://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros_like.html


Q28. 아핀 변환(アフィン変換)-평행이동

아핀 변환을 이용하여 이미지의 x방향으로 +30, y방향으로-30 평행이동 해보자.

아핀의 변환은 3x3의 행렬을 이용하여 이미지를 변환시키는 것이다.

참고로 이미지 변환에 관련된 것들로는

(1)평행이동 (2)확대축소 (3)회전 (4) 스크류(비틀기) 가 있다.

원본의 (x,y), 변환후의 이미지의 (x',y')를 가정한 후

이미지의 확대축소를 수식으로 표현하자면 아래와 같다.

한편, 평행이동은 다음의 식으로 적용할 수 있다.

위의 수식들을 하나의 수식으로 정리하자면 아래와 같다.

그러나 원본에 대해 1픽셀씩 적용하게 되면,

처리 후의 이미지에 값이 제대로 할당되지 않을 가능성이 생긴다.

따라서 처리후 이미지의 각 픽셀에 대해 아핀의 변환의 역변환하여 값을 할당하는 원본의 좌표를 구할 필요가 있다.

아핀읜 변환의 역변환은 다음의 식이 된다.

이번의 평행이동 문제에서는 다음의 식을 이용하자.

tx, ty는 평행이동의 픽셀 이동거리가 이다.

A28. 아핀 변환(アフィン変換)-평행이동의 답안

import cv2
import numpy as np
import matplotlib.pyplot as plt


# Affine
def affine(img, a, b, c, d, tx, ty):
  	H, W, C = img.shape

	# temporary image
	img = np.zeros((H+2, W+2, C), dtype=np.float32)
	img[1:H+1, 1:W+1] = _img

	# get new image shape
	H_new = np.round(H * d).astype(np.int)
	W_new = np.round(W * a).astype(np.int)
	out = np.zeros((H_new+1, W_new+1, C), dtype=np.float32)

	# get position of new image
	x_new = np.tile(np.arange(W_new), (H_new, 1))
	y_new = np.arange(H_new).repeat(W_new).reshape(H_new, -1)

	# get position of original image by affine
	adbc = a * d - b * c
	x = np.round((d * x_new  - b * y_new) / adbc).astype(np.int) - tx + 1
	y = np.round((-c * x_new + a * y_new) / adbc).astype(np.int) - ty + 1

	x = np.minimum(np.maximum(x, 0), W+1).astype(np.int)
	y = np.minimum(np.maximum(y, 0), H+1).astype(np.int)

	# assgin pixcel to new image
	out[y_new, x_new] = img[y, x]

	out = out[:H_new, :W_new]
	out = out.astype(np.uint8)

	return out


# Read image
_img = cv2.imread("assets/imori.jpg").astype(np.float32)

# Affine
out = affine(_img, a=1, b=0, c=0, d=1, tx=30, ty=-30)

# Save result
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.imwrite("out.jpg", out)

https://qiita.com/koshian2/items/c133e2e10c261b8646bf

 

完全に理解するアフィン変換 - Qiita

アフィン変換の真価を知ったら実はかなり強かった、という話。我々はアフィン変換の本当のすごさを知らない。 サンプル 非常に複雑な変換に見えますが、たった1回のアフィン変換でやっています。この記事の処理を組み合わせていけばこの処理が...

qiita.com

https://note.nkmk.me/python-opencv-warp-affine-perspective/

 

Python, OpenCVで幾何変換(アフィン変換・射影変換など) | note.nkmk.me

Python, OpenCVで画像の幾何変換(線形変換・アフィン変換・射影変換)を行うには関数cv2.warpAffine()およびcv2.warpPerspective()を使う。ここでは以下の内容について説明する。幾何変換(幾何学的変換)の種類線形変換同次座標で表す変換アフィン変換射影変換 線形変換 同次座標で表す変換アフィン変換射影変換 アフィン...

note.nkmk.me

첫 번째 링크는 이미지 변환에 대한 설명이 깔끔하게 정리된 qiita링크이며, 아래는 python, OpenCV를 활용한 예제 코드를 정리해 놓은 블로그이다.

import cv2
import numpy as np

img = cv2.imread("assets/imori.jpg")

w, h = img.shape[:2]

mat = np.array([
    [1, 0, 30],
    [0, 1, -30]
    ],dtype=np.float32)

out = cv2.warpAffine(img, mat, (w, h))

cv2.imshow("result",out)
cv2.waitKey(0)
cv2.destroyAllWindows()

위의 두 링크를 참고하여 작성한 코드는 위와 같다.

<cv2.warpAffine()>


cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])


- src : 입력 이미지

- dst : 출력 이미지 (dsize, src와 같은 타입을 가진 이미지)

- M : 2x3 변환 매트릭스

- dsize : 출력 이미지의 사이즈

- flags : 보간 방법의 조합 (resize() 를 참고)

M이 역변환을 의미하는( \texttt{dst}\rightarrow\texttt{src} ) 선택 플래그 WARP_INVERSE_MAP

- borderMode : 픽셀 추정 메소드 (borderInterpolate()를 참고);

borderMode=BORDER_TRANSPARENT일 때,

이것은 변환하고자하는 이미지에 해당하는 이상치로 입력 이미지가 함수에 의해 변환되지 않았음을 의미

- borderValue : 일정한 테두리 안에 사용된 값, 기본값은 0

https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html?highlight=warpaffine

<cv2.borderInterpolate()>

borderInterpolate()는 외부 삽입된 픽셀의 근원 좌표를 구하는 함수이다.

int borderInterpolate(int p, int len, int borderType)

- p : 외부 삽인된 픽셀의 한 축의 좌표(0기준) 이것은 <0 또는 >=len이다.
- len : 대응하는 축에 따른 배열 길이
- borderType : 경계의 종류
BORDER_TRANSPARENT과 BORDER_ISOLATED을 제외한 BORDER*의 것들 중 하나다.
borderType == BORDER_CONSTAN의 경우, 이 함수 p와 len에 대해서 항상 -1을 반환한다.

http://opencv.jp/opencv-2.1/cpp/image_filtering.html#borderInterpolate

Q29. 아핀 변환(アフィン;warpaffine変換) - 확대축소(拡大縮小)

아핀변환을 이용하여 출력해야 할 결과물은 두 가지이다.

(1) x방향으로 1.3배、y방향으로 0.8배로 사이즈를 조정해 출력해보자.

(2) (1)을 적용한 상태에서 x방향으로 +30, y방향으로 -30 평행한 결과물을 출력하자.

A29. 아핀 변환(アフィン;warpaffine変換) - 확대축소(拡大縮小)의 답안

import cv2
import numpy as np
import matplotlib.pyplot as plt


# Affine
def affine(img, a, b, c, d, tx, ty):
  	H, W, C = img.shape

	# temporary image
	img = np.zeros((H+2, W+2, C), dtype=np.float32)
	img[1:H+1, 1:W+1] = _img

	# get new image shape
	H_new = np.round(H * d).astype(np.int)
	W_new = np.round(W * a).astype(np.int)
	out = np.zeros((H_new+1, W_new+1, C), dtype=np.float32)

	# get position of new image
	x_new = np.tile(np.arange(W_new), (H_new, 1))
	y_new = np.arange(H_new).repeat(W_new).reshape(H_new, -1)

	# get position of original image by affine
	adbc = a * d - b * c
	x = np.round((d * x_new  - b * y_new) / adbc).astype(np.int) - tx + 1
	y = np.round((-c * x_new + a * y_new) / adbc).astype(np.int) - ty + 1

	x = np.minimum(np.maximum(x, 0), W+1).astype(np.int)
	y = np.minimum(np.maximum(y, 0), H+1).astype(np.int)

	# assgin pixcel to new image
	out[y_new, x_new] = img[y, x]

	out = out[:H_new, :W_new]
	out = out.astype(np.uint8)

	return out


# Read image
_img = cv2.imread("assets/imori.jpg").astype(np.float32)

# Affine
out = affine(_img, a=1.3, b=0, c=0, d=0.8, tx=30, ty=-30)


# Save result
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.imwrite("out.jpg", out)

문제 28과 cv2.resize()를 이용하여 작성한 코드는 아래와 같다.

import cv2
import numpy as np

img = cv2.imread("assets/imori.jpg")

r_size = cv2.resize(img, dsize=None, fx=1.3 , fy=0.8)

h, w = r_size.shape[:2]

mat = np.array([
    [1, 0, 30],
    [0, 1, -30]
],dtype=np.float32)

out = cv2.warpAffine(r_size, mat, (w,h))

cv2.imshow("result1", r_size)
cv2.imshow("result2", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
<cv2.resize()>

dst = cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])

- src: 입력 이미지
- dsize: 출력 사이즈
- fx: x 방향의 배율
- fy: y 방향의 배율
- interpolation: 보간방법
  1) cv2.INTER_NEAREST: 최근방 보간
  2) cv2.INTER_LINEAR: 바이라이너 보간
  3) cv2.INTER_CUBIC: 바이큐빅 보간
  4) cv2.INTER_AREA: 픽셀 영역 관계를 이용한 리샘플링
  5) cv2.INTER_LANCZOS4: Lanczos 보간
- dst: 출력 이미지

Q30. 아핀 변환(アフィン;warpaffine変換) - 회전(回転)

이번 문제 역시 두 가지 출력 결과를 내야한다.

(1) 아핀변환을 이용하여, 반시계방향으로 30도 회전한 결과물은 출력한다.

(2) 아핀변환을 이용하여, 반시계방향으로 30도 회전한 이미지를 중심부에 고정하여, 가능하면 검은 부분이 보이지 않도록 출력해보자.

(그러나 단순한 아핀 변환을 실행하면 이미지가 잘리므로 고안이 필요하다.)

아핀 변환에 있어서, 반시계방향으로 A도 회전할 때 다음의 식이 적용된다.

A30. 아핀 변환(アフィン;warpaffine変換) - 회전(回転)의 답안

import cv2
import numpy as np
import matplotlib.pyplot as plt


# affine
def affine(img, a, b, c, d, tx, ty):
	H, W, C = _img.shape

	# temporary image
	img = np.zeros((H+2, W+2, C), dtype=np.float32)
	img[1:H+1, 1:W+1] = _img

	# get shape of new image
	H_new = np.round(H).astype(np.int)
	W_new = np.round(W).astype(np.int)
	out = np.zeros((H_new, W_new, C), dtype=np.float32)

	# get position of new image
	x_new = np.tile(np.arange(W_new), (H_new, 1))
	y_new = np.arange(H_new).repeat(W_new).reshape(H_new, -1)

	# get position of original image by affine
	adbc = a * d - b * c
	x = np.round((d * x_new  - b * y_new) / adbc).astype(np.int) - tx + 1
	y = np.round((-c * x_new + a * y_new) / adbc).astype(np.int) - ty + 1

	# adjust center by affine
	dcx = (x.max() + x.min()) // 2 - W // 2
	dcy = (y.max() + y.min()) // 2 - H // 2

	x -= dcx
	y -= dcy

	x = np.clip(x, 0, W + 1)
	y = np.clip(y, 0, H + 1)

	# assign pixcel
	out[y_new, x_new] = img[y, x]
	out = out.astype(np.uint8)

	return out

# Read image
_img = cv2.imread("assets/imori.jpg").astype(np.float32)


# Affine
A = 30.
theta = - np.pi * A / 180.

out = affine(_img, a=np.cos(theta), b=-np.sin(theta), c=np.sin(theta), d=np.cos(theta),
 tx=0, ty=0)


# Save result
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.imwrite("out.jpg", out)

28번 링크 두 가지를 참고하여 작성해 본 코드는 아래와 같다. 조금 더 친절한 설명은 아래의 링크를 참고

https://qiita.com/kenfukaya/items/77b49856e17a6882d422

 

OpenCVで画像を回転させてみた - Qiita

なにをしたか? →OpenCVで画像を回転 実装手順 ライブラリのインポート 画像の読み込み 回転の中心を指定 回転処理 画像の保存 ※筆者はJupyterNotebookを使用しています 実際にやってみた ...

qiita.com

import cv2
import numpy as np

img = cv2.imread("assets/imori.jpg")

w, h = img.shape[:2]

angel=30.0

rotation = cv2.getRotationMatrix2D((0,0), angel, 1.0)
rotation_center = cv2.getRotationMatrix2D((w/2.0, h/2.0), angel, 1.0)

out1 = cv2.warpAffine(img, rotation, (w,h))
out2 = cv2.warpAffine(img, rotation_center, (w,h))


cv2.imshow("result1",out1)
cv2.imshow("result2",out2)
cv2.waitKey(0)
cv2.destroyAllWindows()

(cf) 딥러닝에서 Q27은 UpSampling Q28~30은 Augmentation이름으로 사용된다.

728x90