IT/언어

[python/OpenCV] OpenCV를 이용해 이미지의 특정 색 추출 (RGB vs HSV)

개발자 두더지 2021. 11. 15. 23:40
728x90

 OpenCV를 사용해서 특정 범위의 색을 추출하는 방법에 대해서 알아보고자 한다. 예를 들어 (0, 0, 100) ~ (100, 100, 225)와 같이 색 범위를 전달해, 그 내용을 바탕으로 범위 내의 화소만을 추출하고자 한다.

 색의 추출의 경우 이미지의 RGB를 사용하는 방법도 있지만, RGB를 HSV로 변환하여 HSV에서 색을 추출하는 방법도 있다. 이번 포스팅에서 이 두 가지 방법에 대해 알아보고 비교해보고자 한다.

 

HSV : Hue, Saturation, Value


 HSV이란 색상(Hue), 채도(Saturation), 명도(Value) 세 가지 성분을 이용해 색을 표현하는 방법으로, RGB와 같이 빛의 3원색(빨강, 초록, 파랑)과는 다른 세 가지 축으로 색을 표현한다.

1) 색상(Hue) : 색의 종류 (예를 들어, 파란색)

2) 채도(Saturation) : 색의 선명도

3) 명도(Value) : 색의 밝기

 HSV 색공간은 인간이 색을 지각하는 방법과 유사하다고 알려져 있으므로, 예를 들어 조명 조건가 변해도 색상에는 별로 변화를 미치지 못하는 등, 색의 추출이 쉬워지는 경우가 있다. 

 이제는 앞서 말했듯 RGB로 추출하는 방법과 RGB를 HSV로 변환하여 추출하는 2가지 코드살펴보자.

 

 

코드


(1) RGB를 이용한 색 추출

 먼저 첫 번째 방법으로 RGB로 색을 추출하는 방법을 알아보자. 이 방법은 다음과 같이 작성할 수 있다.

import cv2
import numpy as np

image = cv2.imread('./test1.png') # 이미지파일 읽어들이기

# BGR로 색 추출
bgrLower = np.array([102, 255, 255])    # 추출할 색의 하한(BGR)
bgrUpper = np.array([102, 255, 255])    # 추출할 색의 상한(BGR)
img_mask = cv2.inRange(image, bgrLower, bgrUpper) # BGR로 부터 마스크를 작성
result = cv2.bitwise_and(image, image, mask=img_mask) # 원본 이미지와 마스크를 합성

 OpenCV에서는 RGB가 아니라 BGR임에 주의하자! 추출할 색의 상한과 하한을 작성해서 inRange함수로 마스크를 얻어낼 수 있다. bitwise_and로 이 마스크를 원래의 이미지에 적용해 범위 내의 색 이외를 검게 한다.

 

(2) HSV를 이용한 색 추출

 다른 하나는 HSV를 이용한 방법이다. HSV에 대해서는 위에서 이미 설명했으므로, 추가적인 설명은 생략하도록 하겠다. 한편 OpenCV에는 각각의 범위는 다음과 같다.

명칭 값의 범위 참고
색상(H) 0 ~ 180 원래 0~360범위이지만 OpenCV에서는 1/2 범위이다
채도(S) 0 ~ 255 값이 0에 가까우면 하얀색, 255에 가까울수록 H의 색상이 된다.
명도(V) 0 ~ 255 값이 0에 가까우면 검은색, 255에 가까울수록  H의 색상이 된다.

 이 HSV를 사용한 색의 추출 방법은 아래와 같다.

import cv2
import numpy as np

image = cv2.imread('./test1.png') # 이미지 파일 읽어들이기

# HSV로 색 추출
hsvLower = np.array([30, 153, 255])    # 추출할 색의 하한(HSV)
hsvUpper = np.array([30, 153, 255])    # 추출할 색의 상한(HSV)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 이미지를 HSV으로 변환
hsv_mask = cv2.inRange(hsv, hsvLower, hsvUpper)    # HSV에서 마스크를 작성
result = cv2.bitwise_and(image, image, mask=hsv_mask) # 원래 이미지와 마스크를 합성

 방법 1와 비슷하지만, 상한하한이 HSV인 것과 한 번 이미지를 HSV로 변환한 부분이 다르다.

 

 

예시


 아래의 이미지로부터 배경색을 추출해보도록 하겠다.

import numpy as np
import cv2
from time import sleep

# 메인 함수
def main():
    image = cv2.imread('./test1.png') # 파일 읽어들이기

    # BGR로 색추출
    bgrLower = np.array([102, 255, 255])    # 추출할 색의 하한
    bgrUpper = np.array([102, 255, 255])    # 추출할 색의 상한
    bgrResult = bgrExtraction(image, bgrLower, bgrUpper)
    cv2.imshow('BGR_test1', bgrResult)
    sleep(1)

    # HSV로 색추출
    hsvLower = np.array([30, 153, 255])    # 추출할 색의 하한
    hsvUpper = np.array([30, 153, 255])    # 추출할 색의 상한
    hsvResult = hsvExtraction(image, hsvLower, hsvUpper)
    cv2.imshow('HSV_test1', hsvResult)
    sleep(1)

    while True:
        # 키 입력을 1ms기다리고, key가「q」이면 break
        key = cv2.waitKey(1)&0xff
        if key == ord('q'):
            break

    cv2.destroyAllWindows()


# BGR로 특정 색을 추출하는 함수
def bgrExtraction(image, bgrLower, bgrUpper):
    img_mask = cv2.inRange(image, bgrLower, bgrUpper) 
    result = cv2.bitwise_and(image, image, mask=img_mask) 
    return result

# HSV로 특정 색을 추출하는 함수
def hsvExtraction(image, hsvLower, hsvUpper):
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) 
    hsv_mask = cv2.inRange(hsv, hsvLower, hsvUpper) 
    result = cv2.bitwise_and(image, image, mask=hsv_mask)
    return result


if __name__ == '__main__':
    main()

 왼쪽은 HSV 추출 방법으로 추출한 것이고 반대는 BGR 추출 방법으로 추출한 것이다.


참고자료

https://rikoubou.hatenablog.com/entry/2019/02/21/190310

http://tecsingularity.com/opencv/colorextraction/

 

728x90