IT/AI\ML

[python/OpenCV] 이미지 처리 예제 Q16~Q20

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

문제링크

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

 

yoyoyo-yo/Gasyori100knock

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

github.com


Q16. Sobel필터(Sobelフィルタ)

Sobel필터(3x3)를 적용해보자.

Sobel필터도 선 추출하는 필터이며, 아래와 같이 정의되어 있다.

1) 세로 방향

2) 가로 방향

이것은 prewitt 필터의 중심 부분에 가중치를 부여하는 필터이다.

A16. Sobel필터(Sobelフィルタ)의 답안

import cv2
import numpy as np

# Gray scale
def BGR2GRAY(img):
	b = img[:, :, 0].copy()
	g = img[:, :, 1].copy()
	r = img[:, :, 2].copy()

	# Gray scale
	out = 0.2126 * r + 0.7152 * g + 0.0722 * b
	out = out.astype(np.uint8)

	return out


# sobel filter
def sobel_filter(img, K_size=3):
	if len(img.shape) == 3:
		H, W, C = img.shape
	else:
		img = np.expand_dims(img, axis=-1)
		H, W, C = img.shape

	# Zero padding
	pad = K_size // 2
	out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
	out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float)
	tmp = out.copy()

	out_v = out.copy()
	out_h = out.copy()

	## Sobel vertical
	Kv = [[1., 2., 1.],[0., 0., 0.], [-1., -2., -1.]]
	## Sobel horizontal
	Kh = [[1., 0., -1.],[2., 0., -2.],[1., 0., -1.]]

	# filtering
	for y in range(H):
		for x in range(W):
			out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y: y + K_size, x: x + K_size]))
			out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y: y + K_size, x: x + K_size]))

	out_v = np.clip(out_v, 0, 255)
	out_h = np.clip(out_h, 0, 255)

	out_v = out_v[pad: pad + H, pad: pad + W].astype(np.uint8)
	out_h = out_h[pad: pad + H, pad: pad + W].astype(np.uint8)

	return out_v, out_h

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

# grayscale
gray = BGR2GRAY(img)

# sobel filtering
out_v, out_h = sobel_filter(gray, K_size=3)

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

cv2.imwrite("out_h.jpg", out_h)
cv2.imshow("result", out_h)
cv2.waitKey(0)
cv2.destroyAllWindows()

15번의 답안에서 커널의 숫자만 바꿨다.

import cv2
import numpy as np

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

gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

kernel_v = np.array([
    [1, 0, -1],
    [2, 0, -2],
    [1, 0, -1]
    ])

kernel_h = np.array([
    [1, 2, 1],
    [0, 0, 0],
    [-1,-2, -1]
    ])

out_v = cv2.filter2D(gray, -1, kernel_v)
out_h = cv2.filter2D(gray, -1, kernel_h)

cv2.imwrite("out_v.jpg", out_v)
cv2.imshow("result", out_v)
cv2.waitKey(0)

cv2.imwrite("out_h.jpg", out_h)
cv2.imshow("result", out_h)
cv2.waitKey(0)
cv2.destroyAllWindows()

Q17. Laplacian필터(Laplacianフィルタ、ラプラシアンフィルタ)

Laplacian필터를 적용해보자.

Laplacian필터는 휘도의 2차 미분을 취하는 것으로 선 검출을 하는 필터이다.

디지털 이미지는 분산데이터로 존재하기 때문에,

x방향・y방향의 1차미분은 다음의 식과 같이 정의 되어 있다. (미분 필터와 같다.)

계속해서 2차미분은 다음과 같이 정의되어 있다.

사실 위의 식보다 라플라시안은 x, y의 양쪽의 편미분의 합이 되어, 다음 식으로 정의된다.

이것을 커널로 표현하자면 결국 아래와 같은 행렬이 된다.

 

A17. Laplacian필터(Laplacianフィルタ、ラプラシアンフィルタ)의 답안

import cv2
import numpy as np


# Gray scale
def BGR2GRAY(img):
	b = img[:, :, 0].copy()
	g = img[:, :, 1].copy()
	r = img[:, :, 2].copy()

	# Gray scale
	out = 0.2126 * r + 0.7152 * g + 0.0722 * b
	out = out.astype(np.uint8)

	return out

# laplacian filter
def laplacian_filter(img, K_size=3):
	if len(img.shape) == 3:
		H, W, C = img.shape
	else:
		H, W = img.shape

	# zero padding
	pad = K_size // 2
	out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
	out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float)
	tmp = out.copy()

	# laplacian kernle
	K = [[0., 1., 0.],[1., -4., 1.], [0., 1., 0.]]

	# filtering
	for y in range(H):
		for x in range(W):
			out[pad + y, pad + x] = np.sum(K * (tmp[y: y + K_size, x: x + K_size]))

	out = np.clip(out, 0, 255)
	out = out[pad: pad + H, pad: pad + W].astype(np.uint8)

	return out

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

# grayscale
gray = BGR2GRAY(img)

# prewitt filtering
out = laplacian_filter(gray, K_size=3)


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

위의 경우 코드는 파이썬으로 하드 코딩한 것이고 아래는 15, 16번의 커널의 숫자만 변경한 내용이다.

import cv2
import numpy as np

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

gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

kernel= np.array([
    [0, 1, 0],
    [1, -4, 1],
    [0, 1, 0]
    ])

out = cv2.filter2D(gray, -1, kernel)

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

그리고 마지막으로 OpenCV로 작성한 코드는 아래와 같다.

https://algorithm.joho.info/programming/python/opencv-laplacian-filter-py/

 

【Python/OpenCV】ラプラシアンフィルタで輪郭検出(エッジ抽出)

Python+OpenCVでラプラシアンフィルタを「NumPy」「cv2.filter2D」「cv2.Laplacian」で実装し、輪郭検出する方法をソースコード付きで解説します。

algorithm.joho.info

import cv2
import numpy as np

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

gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

out = cv2.Laplacian(img,cv2.CV_32F, ksize=3)

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

Q18. Emboss필터(Embossフィルタ、エンボスフィルタ)

Emboss필터를 적용해보자.

Emboss필터란 윤곽을 뚜렷히 하는 필터로 다음과 같이 정의되어 있다.

A18. Emboss필터(Embossフィルタ、エンボスフィルタ)의 답안

import cv2
import numpy as np

# Gray scale
def BGR2GRAY(img):
	b = img[:, :, 0].copy()
	g = img[:, :, 1].copy()
	r = img[:, :, 2].copy()

	# Gray scale
	out = 0.2126 * r + 0.7152 * g + 0.0722 * b
	out = out.astype(np.uint8)

	return out

# emboss filter
def emboss_filter(img, K_size=3):
	H, W, C = img.shape

	# zero padding
	pad = K_size // 2
	out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
	out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float)
	tmp = out.copy()

	# emboss kernel
	K = [[-2., -1., 0.],[-1., 1., 1.], [0., 1., 2.]]

	# filtering
	for y in range(H):
		for x in range(W):
			out[pad + y, pad + x] = np.sum(K * (tmp[y: y + K_size, x: x + K_size]))

	out = np.clip(out, 0, 255)
	out = out[pad: pad + H, pad: pad + W].astype(np.uint8)

	return out


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

# grayscale
gray = BGR2GRAY(img)

# emboss filtering
out = emboss_filter(gray, K_size=3)


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

여기서도 역시 커널의 값만 변경한 코드

참고자료

https://algorithm.joho.info/programming/python/opencv-emboss-filter-py/

 

【Python/OpenCV】エンボスフィルタで加工

この記事では、Python+OpenCVでエンボスフィルタを「NumPy」「cv2.filter2D」で実装し、エンボス加工する方法をソースコード付きで解説します。

algorithm.joho.info

import numpy as np

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

gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

kernel= np.array([
    [-2, -1, 0],
    [-1, 1, 1],
    [0, 1, 2]
    ])

out = cv2.filter2D(gray, -1, kernel)


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

Q19. LoG필터(LoGフィルタ)

LoG필터(sigma = 3, 커널 사이즈 = 5)를 적용해, imori_noise.jpg의 선을 검출해보자.

LoG필터란 Laplacian of Gaussian으로,

가우시간 필터로 이미지의 평활화한 후에 라플라시안 필터로 윤곽을 잡아내는 필터이다.

Laplacian필터는 2차 미분을 취하므로 노이즈가 강조되는 것을 막기 위해 우선 Gaussian필터로 노이즈를 정리를 한다.

LoG필터는 다음 식으로 정의된다.

라플라 시안을 ∇^2, 가우시안 필터를 G, 이미지를 I로 한다.

다음의 식에 의해 가우시안 필터링 후에 라플라시안 필터를 적용하는 조작을,

가우시안 필터를 라플라시안한 것으로 필터링하는 조작으로 치환하는 것이 가능하다.

2차 미분은 다음의 식

y에 관하여는 x의 부분을 교체하기만 하면 되므로 LoG필터는 다음과 같은 식이 된다.

A19. LoG필터(LoGフィルタ)의 답안

import cv2
import numpy as np



# Gray scale
def BGR2GRAY(img):
	b = img[:, :, 0].copy()
	g = img[:, :, 1].copy()
	r = img[:, :, 2].copy()

	# Gray scale
	out = 0.2126 * r + 0.7152 * g + 0.0722 * b
	out = out.astype(np.uint8)

	return out

# LoG filter
def LoG_filter(img, K_size=5, sigma=3):
	if len(img.shape) == 3:
		H, W, C = img.shape
	else:
		H, W = img.shape

	# zero padding
	pad = K_size // 2
	out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
	out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float)
	tmp = out.copy()

	# LoG Kernel
	K = np.zeros((K_size, K_size), dtype=np.float)
	for x in range(-pad, -pad + K_size):
		for y in range(-pad, -pad + K_size):
			K[y + pad, x + pad] = (x ** 2 + y ** 2 - 2 * (sigma ** 2)) * np.exp( - (x ** 2 + y ** 2) / (2 * (sigma ** 2)))
	K /= (2 * np.pi * (sigma ** 6))
	K /= K.sum()

	"""
	K = np.array([[0, 0, 1, 0, 0],
								[0, 1, 2, 1, 0],
								[1, 2, -16, 2, 1],
								[0, 1, 2, 1, 0],
								[0, 0, 1, 0, 0]])
	"""
	print(K)

	# filtering
	for y in range(H):
		for x in range(W):
			out[pad + y, pad + x] = np.sum(K * tmp[y: y + K_size, x: x + K_size])

	out = np.clip(out, 0, 255)
	out = out[pad: pad + H, pad: pad + W].astype(np.uint8)

	return out


# Read image
img = cv2.imread("assets/imori_noise.jpg")

# grayscale
gray = BGR2GRAY(img)

# LoG filtering
out = LoG_filter(gray, K_size=5, sigma=1)

# Save result
cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
<cv2.converScaleAbs()함수>

cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])

스케일링 한 후에 절대값을 계산하고, 결과를 8비트로 변환하는 함수
- src : 입력 배열
- dst : 출력 배열
- alpha : (옵션) scale 요소
- beta : (옵션) 스케일링된 값에 델타 추가


Q20. 히스토그램 표시(ヒストグラム表示)

matplotlib을 사용하여 imori_dark.jpg의 히스토그램을 표시해보자.

히스토그램이랑 화소의 출현 횟수를 그래프로 표시하는 것이다.

matplotlib에는 hist()라는 함수가 있으므로, 이것을 이용하여 답을 도출해보자.

A20. 히스토그램 표시(ヒストグラム表示)의 답안

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

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

# Display histogram
plt.hist(img.ravel(), bins=255, rwidth=0.8, range=(0, 255))
plt.savefig("out.png")
plt.show()
<matplotlib.pyplot.hist()의 인수>

matplotlib.pyplot.hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, hold=None, data=None, **kwargs)
인수 의미 비고
x, y 데이터의 배열

- 입력 필수

- 복수 지정 가능

bins 클래스의 막대 수 (계급의 수) - 기본 값 10
range bins의 범위(최대값, 최소값)을 지정할 수 있음 - 기본 값 (x.min(), x.max())
normed 정규화 설정 - 기본 값 False
histtype

히스토그램의 종류를 선택

종류로는 bar, barstacked, step, stepfilled이 있음

- 기본 값 bar
orientation 그래프의 방향을 수직(vertical)으로 할지, 수평(horizontal)으로 할지에 대해 설정 - 기본 값은vertical
rwidth 막대의 폭 지정 - 기본값 None(지정하지 않음)
log 세로축을 로그의 눈금으로 표시할지 여부를 지정 - 기본값 False
color 히스토그램의 색상 지정 - 기본값 None(지정하지 않음)
label 차트에 범례 표시 여부 - 기본값 None(지정하지 않음)

 

 
728x90