【OpenCV】HSV를 직교 좌표계로 변환 표시

개요



이미지 분석 시 HSV 색 공간을 사용하는 경우가 많다고 생각합니다. 특히 H(색조)와 S(채도)를 산점도에 플롯하면 이미지의 색채의 특징을 잘 파악할 수 있습니다.

단, H 와 S 는 국 좌표 공간의 파라미터입니다. 이 때문에 H는 0~360도(OpenCV에서는 0~180)의 위상각이 됩니다. 여기에 문제가 있습니다. H 가 0 부근과 H 가 360 부근은 同じような色相 를 나타내게 되어 버립니다. 값이 크게 다르지만 특성은 매우 유사하다는 것은 바람직하지 않습니다. 기계 학습 외, 수리 통계 처리를 실시하려면, 궁리가 필요하겠지요.

그래서 국 좌표에서 직교 좌표로 변환하면 잘 처리 할 수 ​​있다고 생각합니다. 조금 시도해 보았습니다.

HSV 색 공간 : H와 S의 산점도



시험에 붉은 (H가 0 부근 및 OpenCV의 180 부근의 값을 취하는 점이 많다) 이미지를 조사해 보겠습니다. 샘플을 lena로 해 보겠습니다.



필요한 라이브러리를 가져옵니다.
import cv2
from matplotlib import pyplot as plt

lena를 읽고 OpenCV에서 읽은 BGR 형식의 이미지를 HSV 변환합니다.
#matplotlib에서도 표시하기 위해 함께 RGB 형식도 만들어 둡니다.
lena_bgr = cv2.imread('lena.png')

lena_hsv = cv2.cvtColor(lena_bgr,cv2.COLOR_BGR2HSV)
lena_rgb = cv2.cvtColor(lena_bgr,cv2.COLOR_BGR2RGB)

이미지의 색채의 특성을 보기 위해 가로축 H, 세로축 S로 산점도를 만들어 봅니다.
plt.figure(figsize=(12,4))
plt.subplot(1,3,1)
plt.imshow(lena_rgb)

plt.subplot(1,3,2)
plt.xlim(0,180)
plt.ylim(0,255)
plt.grid()
plt.scatter(x=lena_hsv[:,:,0],y=lena_hsv[:,:,1],alpha=0.01,marker='x',color='g')

plt.show()



채도는 넓은 범위에 분포되어 있지만 色相は 0付近、あるいは180付近 에 편향되어 있습니다. lena가 붉은 이미지임을 잘 보여줍니다.

다만 「붉은」이기 때문에 산포도상의 점이 2개의 그룹으로 나누어지는 것은 조금 혼란을 초래하네요. SVM 다른 머신러닝을 할 때도 별로 편리하지 않을 수 있습니다. 그래서 이것을 직교 좌표계로 나타내는 것을 생각합니다.

직교 좌표계에 의한 색채의 표현



직교 좌표계로 변환하는 것은 어렵지 않습니다. H(색조)가 위상, S(채도)가 절대값이므로,
\begin{align}
x = S\,\cos(\,H\,)\\
\\y = S\,\sin(\,H\,)
\end{align}

이것으로 구할 수 있네요.

주의가 필요한 것은, OpenCV에서는 H(색조)를 0~360이 아니고, 0~179로 취급하고 있다는 점입니다. 계산할 때는 H 의 값을 2배로 해야 합니다.
# 8 비트의 부호없는 정수에서는, 255까지 밖에 나타낼 수 없기 때문에 편의상입니다

또한, numpy의 삼각 함수는 단위에 라디안을 사용하고 있으므로, 거기도 주의해 주세요.

그럼, 직교 좌표계로 변환해 보겠습니다.
lena_temp = lena_hsv.astype(np.float32)
lena_xyv = np.zeros(lena_hsv.shape).astype(np.float32)

# 位相の単位をラジアンに変換
# x = cos((2*H) * 2pi / 360) = cos(H * pi / 90)
# y = sin((2*H) * 2pi / 360) = sin(H * pi / 90)

lena_xyv[:,:,0] = lena_temp[:,:,1] * np.cos(lena_temp[:,:,0]*(np.pi)/90)
lena_xyv[:,:,1] = lena_temp[:,:,1] * np.sin(lena_temp[:,:,0]*(np.pi)/90)
lena_xyv[:,:,2] = lena_hsv[:,:,2]

sin, cos 는 실수 계산이므로, float32 에 형태 변환한 인스턴스를 만들어 작업하고 있습니다.

또, 명도 V 는 여기에서는 사용하지 않지만, 만약을 위해, 보존해 두었습니다.

결과 비교



앞에서와 같이 원래 이미지와 H-S 산점도, 거기에 이번에 직교 좌표 변환한 x-y 산점도를 나란히 봅니다.
plt.figure(figsize=(12,4))
plt.subplot(1,3,1)
plt.title('lena')
plt.imshow(lena_rgb)

plt.subplot(1,3,2)
plt.title('H-S')
plt.xlim(0,180)
plt.ylim(0,255)
plt.grid()
plt.scatter(x=lena_hsv[:,:,0],y=lena_hsv[:,:,1],alpha=0.2)

plt.subplot(1,3,3)
plt.title('x-y')
plt.xlim(-255,255)
plt.ylim(-255,255)
plt.grid()
plt.scatter(x=lena_xyv[:,:,0],y=lena_xyv[:,:,1],alpha=0.01,marker='x',color='r')
plt.show()



직교 좌표계에서 보면, 붉은 레나가 한 덩어리가 되어 보다 잘 특징을 나타내고 있는 것 같습니다.

색조, 채도를 사용해 기계 학습할 때에는, 직교 좌표계로 생각하면 잘 되는 경우도 많지 않을까요.

좋은 웹페이지 즐겨찾기