HSV 색 공간은 색을 Hue(색상), Saturation(채도), Value(명도) 3가지 값으로 표현하는 색 모델입니다. RGB처럼 빨강·초록·파랑의 빛 세기로 색을 표현하는 방식이 아니라, 사람이 색을 인식하는 방식에 더 가깝게 색을 나눕니다.
특히 OpenCV, 로봇 비전, 드론 카메라 영상 처리에서 특정 색상 물체 검출에 많이 사용됩니다.
1. HSV의 구성
HSV는 다음 3개 성분으로 구성됩니다.
| 성분 | 의미 | 설명 |
|---|---|---|
| H | Hue | 색상 |
| S | Saturation | 채도 |
| V | Value | 명도, 밝기 |


2. Hue, 색상
Hue는 색의 종류를 의미합니다.
예를 들어:
| 색 | Hue 의미 |
|---|---|
| 빨강 | Red 계열 |
| 노랑 | Yellow 계열 |
| 초록 | Green 계열 |
| 파랑 | Blue 계열 |
| 보라 | Purple 계열 |
Hue는 보통 원형 각도로 표현합니다.

0° = 빨강
60° = 노랑
120° = 초록
180° = 청록
240° = 파랑
300° = 자홍
360° = 다시 빨강
즉 Hue는 색상환입니다.
Red
/ \
Magenta Yellow
| |
Blue Green
\ /
Cyan
중요한 점은 Hue는 원형 값이라는 것입니다.
예를 들어 빨강은 0도 근처이기도 하고 360도 근처이기도 합니다.
Red ≈ 0°
Red ≈ 360°
그래서 빨간색을 검출할 때는 범위를 두 개로 나누는 경우가 많습니다.
0° 근처 빨강
+
360° 근처 빨강
OpenCV에서는 Hue 범위가 일반적인 0~360이 아니라 0~179입니다.
일반 HSV Hue: 0 ~ 360
OpenCV Hue: 0 ~ 179
즉 OpenCV에서는 각도를 절반으로 줄여서 저장합니다.
| 일반 Hue | OpenCV Hue |
|---|---|
| 0° | 0 |
| 60° | 30 |
| 120° | 60 |
| 180° | 90 |
| 240° | 120 |
| 300° | 150 |
| 360° | 179 또는 0 |
3. Saturation, 채도
Saturation은 색의 선명함입니다.
채도가 높으면 색이 진하고 선명합니다.
채도가 낮으면 회색에 가까워집니다.
S 높음 → 선명한 빨강
S 낮음 → 회색빛 빨강, 탁한 색
S = 0 → 완전한 무채색, 회색 계열
예를 들어 같은 빨강이라도:
S 높음: 강한 빨강
S 낮음: 연한 회색빛 빨강
OpenCV에서는 Saturation 범위가 보통 0~255입니다.
S = 0 → 무채색
S = 255 → 가장 선명한 색
채도는 색상 검출에서 매우 중요합니다.
예를 들어 흰색, 회색, 검은색은 Hue 값이 안정적이지 않습니다. 왜냐하면 채도가 낮으면 “무슨 색인지”가 애매해지기 때문입니다.
S가 낮음 → Hue가 의미 없어짐
그래서 색상 검출을 할 때는 보통 S의 최소값을 둡니다.
lower = (H_min, S_min, V_min)
upper = (H_max, S_max, V_max)
예:
lower_blue = (100, 80, 50)
upper_blue = (130, 255, 255)
여기서 S_min = 80은 너무 흐리거나 회색에 가까운 색을 제외하겠다는 의미입니다.
4. Value, 명도
Value는 밝기입니다.
Value가 높으면 밝고, 낮으면 어둡습니다.
V 높음 → 밝은 색
V 낮음 → 어두운 색
V = 0 → 검정
OpenCV에서는 Value 범위도 보통 0~255입니다.
V = 0 → 검정
V = 255 → 가장 밝음
예를 들어 파란색 물체라도 조명이 약하면 V 값이 낮아집니다.
밝은 파랑 → V 높음
어두운 파랑 → V 낮음
색상 검출에서 V는 그림자나 어두운 영역을 제거하는 데 많이 사용됩니다.
lower_blue = (100, 80, 50)
여기서 V_min = 50은 너무 어두운 영역은 제외하겠다는 뜻입니다.
5. HSV가 RGB보다 색 검출에 유리한 이유
RGB에서는 밝기가 바뀌면 R, G, B 값이 모두 같이 변합니다.
예를 들어 같은 빨간색 물체라도 조명이 강할 때와 약할 때 RGB 값은 크게 달라집니다.
밝은 빨강: R=255, G=40, B=40
어두운 빨강: R=120, G=10, B=10
RGB에서는 밝기 변화 때문에 색상 기준을 잡기 어렵습니다.
반면 HSV에서는 색상과 밝기가 분리됩니다.
H → 색상
S → 선명도
V → 밝기
그래서 같은 빨간색이면 조명이 조금 바뀌어도 H 값은 비교적 비슷하게 유지됩니다.
밝은 빨강: H ≈ red
어두운 빨강: H ≈ red
물론 현실에서는 카메라 노이즈, 자동 화이트밸런스, 반사광 때문에 H도 완전히 고정되지는 않습니다. 그래도 RGB보다 색상 기반 검출에는 훨씬 실용적입니다.
6. HSV 원기둥 모델
HSV는 보통 원기둥 또는 원뿔 형태로 설명합니다.
V 밝음
↑
|
|
|
|
+------------→ S 채도
/
/
H 색상 회전
조금 더 직관적으로 보면:
위에서 보면: Hue 색상환
중심에서 바깥으로 갈수록: Saturation 증가
아래에서 위로 갈수록: Value 증가
즉:
H = 색상 방향
S = 중심에서 얼마나 멀리 떨어졌는가
V = 얼마나 밝은가
중심축은 채도가 0이므로 회색 계열입니다.
S = 0 → 흰색, 회색, 검은색
Value에 따라 다음처럼 됩니다.
V 높음 + S 낮음 → 흰색 또는 밝은 회색
V 중간 + S 낮음 → 회색
V 낮음 + S 낮음 → 검정
7. OpenCV에서 HSV 범위
OpenCV에서 cv2.cvtColor()로 BGR 이미지를 HSV로 변환하면 값 범위는 다음과 같습니다.
H: 0 ~ 179
S: 0 ~ 255
V: 0 ~ 255
주의할 점은 OpenCV는 기본 이미지 포맷이 RGB가 아니라 BGR입니다.
그래서 일반적으로 이렇게 변환합니다.
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
RGB 이미지라면 다음을 사용합니다.
hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)
8. HSV 색상 검출 예제
파란색 물체를 검출하는 예제입니다.
import cv2
import numpy as np
frame = cv2.imread("image.jpg")
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_blue = np.array([100, 80, 50])
upper_blue = np.array([130, 255, 255])
mask = cv2.inRange(hsv, lower_blue, upper_blue)
result = cv2.bitwise_and(frame, frame, mask=mask)
cv2.imshow("Original", frame)
cv2.imshow("Mask", mask)
cv2.imshow("Blue Object", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
여기서 핵심은 이 부분입니다.
lower_blue = np.array([100, 80, 50])
upper_blue = np.array([130, 255, 255])
의미는 다음과 같습니다.
Hue: 100 ~ 130 → 파란색 계열
Saturation: 80 ~ 255 → 어느 정도 선명한 색만
Value: 50 ~ 255 → 너무 어두운 영역 제외
9. 빨간색 검출이 까다로운 이유
OpenCV HSV에서 빨간색은 Hue 값의 양쪽 끝에 걸칩니다.
OpenCV Hue 0 근처 → 빨강
OpenCV Hue 179 근처 → 빨강
그래서 빨간색은 보통 두 범위로 나누어 검출합니다.
lower_red1 = np.array([0, 80, 50])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 80, 50])
upper_red2 = np.array([179, 255, 255])
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = mask1 + mask2
이렇게 해야 0 근처 빨강과 179 근처 빨강을 모두 잡을 수 있습니다.
10. 자주 쓰는 OpenCV HSV 색상 범위
카메라, 조명, 렌즈, 화이트밸런스에 따라 값은 달라질 수 있습니다. 아래 값은 시작점으로만 보는 게 좋습니다.
| 색상 | Lower HSV | Upper HSV |
|---|---|---|
| 빨강 1 | (0, 80, 50) | (10, 255, 255) |
| 빨강 2 | (170, 80, 50) | (179, 255, 255) |
| 주황 | (10, 100, 100) | (25, 255, 255) |
| 노랑 | (25, 80, 80) | (35, 255, 255) |
| 초록 | (35, 60, 50) | (85, 255, 255) |
| 파랑 | (100, 80, 50) | (130, 255, 255) |
| 보라 | (130, 60, 50) | (160, 255, 255) |
| 흰색 | (0, 0, 200) | (179, 40, 255) |
| 검정 | (0, 0, 0) | (179, 255, 50) |
실전에서는 반드시 카메라 환경에서 직접 튜닝해야 합니다.