import cv2
import numpy as np

Digital Image

디지털 이미지는 2차원 행렬의 형태로 표현이 된다. 행과 열로 이루어진 격자를 pixel라고 하며, pixel로 이루어진 이미지를 bitmap image라고 한다. 각 pixel의 위치는 영상좌표와 행렬 위치, 2가지로 표현할 수 있다. 좌상단 꼭짓점을 중심으로 영상좌표는 (y, x)로 표현하고, 행렬위치는 (row, column)로 표현한다. OpenCV 에서는 영상좌표와 행렬 위치, 2가지 형태가 사용되기 때문에 유의해야 한다.

Binary Image

Binary Image 는 픽셀당 1bit로 표현하는 이미지를 의미한다. 오직 흰색(1)과 검은색(0)으로만 표현이 된다. Binary Image를 만드는 방법에는 두 가지가 있다. 첫 번째는 원하는 포인트에 점을 지어 만드는 방법이고, 두 번째는 기존의 흑백 영상으로부터 임계값(threshold)을 두어 만드는 방법이 있다. 이 때 임계값은 0~255 사이 중 하나를 지정하게 되는데 기본적으로 임계값을 128로 두어 127보다 작은 픽셀 값은 0으로, 큰 픽셀값은 1로 만드는 방법이다. 이렇게 만든 Binary Image는 Image Segmentation, Object Detection 등 다양한 영상처리 기법의 한 과정으로 응용된다.

Grayscale Image

Grayscale Image 는 픽셀당 8bit(1Byte), 즉 256 단계로 명암(빛의 세기)를 표현할 수 있는 이미지이다. 이 픽셀의 밝기 값을 어느 범위에서 표현할지에 따라 흑백 영상의 모습이 약간씩 달라진다. 가장 작은 범위로 표현한 영상이 Binary Image(0과 1로만 밝기를 표현)라고 할 수 있으며 가장 큰 값으로 표현한 범위가 8bit로 표현한 Grayscale Image라고 할 수 있다. ★ 대부분의 영상처리 알고리즘은 바로 이 흑백 이미지에서 처리 된다. 심지어 딥러닝을 통한 얼굴 인식 등에도 컬러 영상이 아닌 흑백 영상이 학습 데이터로써 이용된다. 컬러 이미지는 표현방법이 약간 복잡하기 때문에 영상 처리를 적용하기 까다롭고 복잡하기 떄문이다.

Color Image

Color Image는 보통 3개의 채널로 표현된다. 이 때, 3개의 채널은 각각 Red, Green, Blue를 뜻하며 채널 별로 Grayscale Image처럼 0~255 사이의 값으로 표현된다. 이 세 가지의 기본 컬러를 조합하여 만든 색을 True Color라고 부르며 여러 이미지나 그래픽 관련 프로그램들은 이 RGB 채널에 투명도(Transparent)를 나타내는 Alpha 채널을 더하여 RGBA로 4개의 채널을 사용하기도 한다. 이 Color Image는 다양한 컬러 모델로 표현할 수 있는데 가장 대표적인 형태로는 RGB와 HSV, YCbCr모델 등이 있다.

### RGB 모델
RGB 모델은 빛의 삼원색인 빨간색(Red), 초록색(Green), 파란색(Blue)를 기본 색으로 사용한다.

### HSV 모델
HSV 모델은 색상(Hue), 채도(Saturation, 선명도), 명도(Value, 밝기)로 표현한다.

 


Color Space 색공간 변환

openCV에는 총 150여 가지의 변환 방법이 있다. 아래는 변환 방법을 확인하는 코드이다. flag 명들이 직관적이라 한 눈에 알아볼 수 있지만 코드에 대한 자세한 설명은 OpenCV의 reference를 참고하면 된다.
cv::ColorConversionCodes 참고

flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
print(flags)

엄청나게 많다

cv2.cvtColor(src, code [, dst [, dstCn]]) → dst

cv2Color함수는 색공간 변환을 위해 사용하는 함수이다. 첫번째 인자 src에는 색공간을 변환할 이미지를 집어넣고, 두번째 인자 code에는 색공간 변환 방법을 지정해주면 된다.

색공간 변환방법에는 앞서 설명한 cv::ColorConversionCodes 를 참고하면 된다. 앞서 언급했듯이 대부분의 영상처리는 Grayscale Image에서 이루어지므로, BGR로 이루어진 컬러이미지를 Grayscale로 변환하는 cv2.COLOR_BGR2GRAY 코드와 BGR을 HSV로 변환하는 cv2.BGR2HSV를 가장 많이 사용한다. 아래 코드는 BGR를 HSV로 변환하는 코드이다.

def hsv():
    blue = np.uint8([[[255, 0, 0]]])
    green = np.uint8([[[0, 255, 0]]])
    red = np.uint8([[[0, 0, 255]]])
    
    hsv_blue = cv2.cvtColor(blue, cv2.COLOR_BGR2HSV)
    hsv_green = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
    hsv_red = cv2.cvtColor(red, cv2.COLOR_BGR2HSV)
    
    print('HSV for Blue', hsv_blue)
    print('HSV for Green', hsv_green)
    print('HSV for Red', hsv_red)
hsv()

BGR 색공간을 HSV로 변환할 때 값이 어떻게 변화하는지 잘 알 수 있다.
Blue (255, 0, 0) → (120, 255, 255)
Green (0, 255, 0) → (60, 255, 255)
Red (0, 0, 255) → (0, 255, 255)
BGR 색 맵핑을 어떻게 하느냐에 따라서 HSV 값이 변할 수 있다.

 


Color Space Tracking 색공간 추적하기

def tracking():
    try:
        print("카메라 구동")
        cap = cv2.VideoCapture('KakaoTalk_20200724_175615914.mp4')
    except:
        print("카메라 구동 실패")
        return
    
    while True:
        ret, frame = cap.read()
        
        if ret:
            hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
            
            lower_blue = np.array([110, 100, 100])
            upper_blue = np.array([150, 255, 255])
            lower_green = np.array([50, 100, 100])
            upper_green = np.array([90, 255, 255])
            lower_red = np.array([-10, 100, 100])
            upper_red = np.array([30, 255, 255])
            
            mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)
            mask_green = cv2.inRange(hsv, lower_green, upper_green)
            mask_red = cv2.inRange(hsv, lower_red, upper_red)
            
            res1 = cv2.bitwise_and(frame, frame, mask = mask_blue)
            res2 = cv2.bitwise_and(frame, frame, mask = mask_green)
            res3 = cv2.bitwise_and(frame, frame, mask = mask_red)
            
            cv2.imshow('Origianl', frame)
            cv2.imshow('Blue', res1)
            cv2.imshow('Green', res2)
            cv2.imshow('Red', res3)
            
            k = cv2.waitKey(0) & 0xFF
            if k == 27:
                break
                
        else:
            print("비디오 종료")
            break
    
    cap.release()
    cv2.destroyAllWindows()

 

tracking()

 

대부분의 함수들에 대해서는 우리가 이미 일부분 알고있는 것들이다. 간단히 설명하자면 VideoCapture(0)을 통해 웹캠 구현시 생성되는 객체를 cap에 초기화 한 후, 프레임 단위로 이미지를 읽어들여 frame에 저장한다. 그리고 색공간이 BGR로 되어 있는 이 frame을 HSV 형태로 변환 시켜 준다. 그 다음 내가 원하는 부분의 색깔만 masking 되도록 범위를 지정해준다. lower과 upper로 구분하여 최솟값과 최댓값을 지정해준다. 이 때, inRange 함수를 사용한다.

cv2.inRange(src, lowerb, upperb [, dst]) → dst

첫번째 인자에는 이미지, 두번째와 세번째에는 각각 하한선과 상한선을 나타낸다. 이 함수는 하한선과 상한선으로 범위를 지정한 뒤, 이 이미지에서 각각의 픽셀 값을 확인하여 지정한 범위에 값이 존재하면 그 값을 그대로, 그렇지 않은 부분은 0(검은색)으로 채워서 결과값을 반환한다.

mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)
mask_green = cv2.inRange(hsv, lower_green, upper_green)
mask_red = cv2.inRange(hsv, lower_red, upper_red)
res1 = cv2.bitwise_and(frame, frame, mask = mask_blue)
res2 = cv2.bitwise_and(frame, frame, mask = mask_green)
res3 = cv2.bitwise_and(frame, frame, mask = mask_red)

이 코드를 통해 각각의 프레임 이미지에 blue, green, red색상별로 masking 한 후, 비트연산을 통해 mask로 씌워진 부분만 이미지로 출력이 되도록 조정한다. 여기서 lowerb, upperb의 범위를 조정함에 따라서 범위를 좁혀 masking을 까다롭게 할지, 범위를 넓혀 masking을 좀 널널하게 할지 조절할 수가 있다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기