OpenCV | Python | 웹캠연결하기, 트랙바로 명도와 채도 조정하기, 노이즈 제거하기, 라벨링으로 물체 추적하기

박나연·2020년 7월 17일
2

OpenCV

목록 보기
10/40
post-custom-banner

1 웹캠연결하기

사진이 아닌 실시간으로 비추어지는 영상에 대해 처리를 하고 싶다면, 웹캠을 연결하여 카메라 화면으로 보여지는 영상을 가져올 수 있다.


cap = cv.VideoCapture(0)

VideoCapture 함수를 통해 연결되어있는 첫번째 캠과 연결한다.

ret, cam = cap.read()

그리고 캠에 연결한 cap 을 읽어온다.

그 전에 imread(이미지) 함수를 통해 이미지를 불러오고 있었다면, 주석 처리 해준다.

cv.imshow('camera', cam)

'camera' 라는 윈도우 창으로 cam 이 보여지게 하는 것이다.

while 문으로 반복하게 한 것은 카메라에 보여지는 이미지가 연결되어 동영상처럼 보이게 하기 위해서 이다.


import cv2 as cv

cap = cv.VideoCapture(0)

while(True):
    ret, cam = cap.read()

    if(ret) :
        cv.imshow('camera', cam)
        
        
        if cv.waitKey(1) & 0xFF == 27: # esc 키를 누르면 닫음
            break
                     
cap.release()
cv.destroyAllWindows()

그 결과 'camera' 라는 윈도우 창에 웹캠이 연결되어 보여지게 된다.


2 트랙바를 통해 S, V 조절하기

2-1 시작하기

본 블로그의 OpenCV 시리즈 9번 포스팅(사진에서 마우스 클릭하여 특정색 추출하기) 에서 웹캠을 추가하여 동일하게 동작하게 하면,

조명차이로 hsv 값이 다르기 때문에 명확히 오브젝트가 구분되지 않는다.

마우스 클릭으로 특정색 추출하기

동일한 코드에 웹캠만 추가한 것 ! (/ω\)……… (/ω•\)

빨간색을 클릭하였지만, 주변에 색상이 비슷한 피부색까지 함께 추출되며, 또한 노이즈가 발생하는 것을 확인할 수 있었다.


H 값 보다는 S, V 값 때문에 이런 현상이 나타난다.

더 높은 값으로 S, V 값을 조정해야 원하는 물체를 구분해낼수 있다.

정적인 값으로 지정해 놓는다면, 조명과 상황에 따라 바뀌는 색을 추출해 내기에 어려움이 있을 것이다.

따라서 트랙바를 통해 바로바로 적용하여 결과 이미지로 나타나게 해주면 된다.


2-2 본격 트랙바 사용하기

def nothing(x): 
    pass

트랙바를 생성하기 위해서는 사용하지 않는 nothing 함수를 만들어 준다.

cv.namedWindow('img_result')

결과를 화면에 출력해줄 윈도우 창을 만든다.

cv.createTrackbar('threshold', 'img_result', 0, 255, nothing)

트랙바를 원하는 이름('threshold') 으로 원하는 윈도우 창('img_result')에 최소값(0), 최대값(255)을 정하여 만들어 준다.

cv.setTrackbarPos('threshold', 'img_result', 30) # 초기값 30

생성된 윈도우('img_result')의 생성된 트랙바('threshold') 초기값을 설정한다. (30)


이전 포스팅에서 다루었던 코드에서 마우스 클릭하는 이벤트 함수를 그대로 가져왔다.

마우스 클릭으로 특정색 추출하기

mouse_callback 함수에서

threshold = cv.getTrackbarPos('threshold', 'img_result')

를 추가하여 threshold 라는 변수를 getTrackbarPos 라는 함수를 통해 현재 트랙바의 위치를 리턴하도록 한다.

그리고 그 전에, s, v 값을 적절한 값인 30이라고 지정했던 부분을 모두 threshold 로 바꾸어 결과이미지에 적용되도록 한다.

(트랙바의 초기값으로 설정했던 30은 원래대로 바꾸어줘야 한다.)


3 노이즈 제거하기

3-1 morphologyEx

이렇게 프로그램을 실행하면 자잘한 노이즈가 발생하는 것을 확인할 수 있다. 점으로 보이는 노이즈와 검은 구멍들을 메우기위해 morphologyEX 함수를 이용한다.

kernel = np.ones((11,11), np.uint8)

우선 바이너리 이미지(img_mask)에서 특정픽셀을 0으로 만드는 것이다. () 안의 숫자의 크기에 따라 그 양이 달라진다. (11) 이 커질 수록 더 세밀한 제거가 이루어 진다.

img_mask = cv.morphologyEx(img_mask, cv.MORPH_OPEN, kernel)

이미지 상의 점으로 보이는 노이즈들을 제거하는 것이다.

img_mask = cv.morphologyEx(img_mask, cv.MORPH_CLOSE, kernel)

다음은 검은 구멍들을 메우는 것이다.

(Opening 과 Closing 에서 모두 커널 크기를 조절하면 메우고, 제거하는 정도가 달라진다.)


3-2 결과

깔끔하게 색상이 나타나 구분되는 것을 확인할 수 있다.


4 라벨링으로 오브젝트 추적하기

4-1 connectedComponentsWithStats

OpenCV 에 포함된함수로 쉽게 라벨링 할 수 있다.

numOfLabels, img_label, stats, centroids = cv.connectedComponentsWithStats(img_mask)

각각 다중 값을 반환하도록 하는 것이다.

retval : 라벨을 붙일 이미지 = numOfLabels, img_mask
label : 라벨이 지정된 객체, 이미지 = img_label
stats : 각 라벨에 대한 통계, 갯수 ?
centroids : 라벨의 중심

다중값 반환 Python
위 사진을 보면 이해가 쉽게 가능하다.!


4-2 정리

for idx, centroid in enumerate(centroids):

enumerate 는 리스트의 순서와 리스트 값을 불러오는 기능을 한다.
여기서는

numOfLabels, img_label, stats, centroids = cv.connectedComponentsWithStats(img_mask)

에서 centroids 의 리스트 값을 불러온다. idx 는 centroids 의 인덱스, centroid 는 리스트 값을 의미한다.


if stats[idx][0] == 0 and stats[idx][1] == 0:
	continue
        
if np.any(np.isnan(centroid)):
	continue

라벨이 붙여진 것이 0 이거나 없어도 계속 한다는 의미인 것같다. 확실하지 않음.

x, y, width, height, area = stats[idx]

라벨이 붙은 것들의 좌표(x, y), 폭(width), 높이(height), 넓이(area)를 변수로 선언한다.

centerX, centerY = int(centroid[0]), int(centroid[1])

중심좌표를 정의한다.

if area > 50:

넓이가 50 이상일때, 즉 너무 작은 물체는 검출하지 않는다.

cv.circle(img_color, (centerX, centerY), 10, (0, 0, 255), 10)

라벨로 지정된 물체의 중심에 작은 원을 그린다.

cv.rectangle(img_color, (x, y), (x+width, y+height), (0,0,255))

라벨로 지정된 물체의 외곽에 사각형을 그린다.


4-3 결과

분홍색 큐브를 따라가며 라벨링이 된 것을 확인할 수 있다.


5 사용 코드

import cv2 as cv
import numpy as np


hsv = 0
lower_blue1 = 0
upper_blue1 = 0
lower_blue2 = 0
upper_blue2 = 0
lower_blue3 = 0
upper_blue3 = 0


def nothing(x): # 트랙바 생성시 필요함
    pass

def mouse_callback(event, x, y, flags, param):
    global hsv, lower_blue1, upper_blue1, lower_blue2, upper_blue2, lower_blue3, upper_blue3, threshold

    # 마우스 왼쪽 버튼 누를시 위치에 있는 픽셀값을 읽어와서 HSV로 변환 (x, y 값으로 저장)
    if event == cv.EVENT_LBUTTONDOWN:
        print(img_color[y, x])
        color = img_color[y, x]

        one_pixel = np.uint8([[color]])
        hsv = cv.cvtColor(one_pixel, cv.COLOR_BGR2HSV)
        hsv = hsv[0][0]
        
        threshold = cv.getTrackbarPos('threshold', 'img_result')

        
        # HSV 색공간에서 마우스 클릭으로 얻은 픽셀값과 유사한 픽셀값의 범위를 정함
        if hsv[0] < 10:
            print("case1")
            lower_blue1 = np.array([hsv[0]-10+180, threshold, threshold]) # 색상만 조절
            upper_blue1 = np.array([180, 255, 255])
            lower_blue2 = np.array([0, threshold, threshold])
            upper_blue2 = np.array([hsv[0], 255, 255])
            lower_blue3 = np.array([hsv[0], threshold, threshold])
            upper_blue3 = np.array([hsv[0]+10, 255, 255])
            #     print(i-10+180, 180, 0, i)
            #     print(i, i+10)

        elif hsv[0] > 170:
            print("case2")
            lower_blue1 = np.array([hsv[0], threshold, threshold])
            upper_blue1 = np.array([180, 255, 255])
            lower_blue2 = np.array([0, threshold, threshold])
            upper_blue2 = np.array([hsv[0]+10-180, 255, 255])
            lower_blue3 = np.array([hsv[0]-10, threshold, threshold])
            upper_blue3 = np.array([hsv[0], 255, 255])
            #     print(i, 180, 0, i+10-180)
            #     print(i-10, i)
        else:
            print("case3")
            lower_blue1 = np.array([hsv[0], threshold, threshold])
            upper_blue1 = np.array([hsv[0]+10, 255, 255])
            lower_blue2 = np.array([hsv[0]-10, threshold, threshold])
            upper_blue2 = np.array([hsv[0], 255, 255])
            lower_blue3 = np.array([hsv[0]-10, threshold, threshold])
            upper_blue3 = np.array([hsv[0], 255, 255])
            #     print(i, i+10)
            #     print(i-10, i)

        print(hsv[0])
        print("@1", lower_blue1, "~", upper_blue1)
        print("@2", lower_blue2, "~", upper_blue2)
        print("@3", lower_blue3, "~", upper_blue3)


cv.namedWindow('img_color')
cv.setMouseCallback('img_color', mouse_callback)

cv.namedWindow('img_result')
cv.createTrackbar('threshold', 'img_result', 0, 255, nothing)
cv.setTrackbarPos('threshold', 'img_result', 30) # 초기값 30



cap = cv.VideoCapture(0)



while(True):
    #img_color = cv.imread('hsv.png')
    ret, img_color = cap.read()
    height, width = img_color.shape[:2]
    img_color = cv.resize(img_color, (width, height), interpolation=cv.INTER_AREA)

    img_hsv = cv.cvtColor(img_color, cv.COLOR_BGR2HSV)

    img_mask1 = cv.inRange(img_hsv, lower_blue1, upper_blue1)
    img_mask2 = cv.inRange(img_hsv, lower_blue2, upper_blue2)
    img_mask3 = cv.inRange(img_hsv, lower_blue3, upper_blue3)
    img_mask = img_mask1 | img_mask2 | img_mask3
    
    kernel = np.ones((11,11), np.uint8) 
    img_mask = cv.morphologyEx(img_mask, cv.MORPH_OPEN, kernel) # 점으로 보이는 노이즈를 제거
    img_mask = cv.morphologyEx(img_mask, cv.MORPH_CLOSE, kernel) # 검은 구멍 메움

    img_result = cv.bitwise_and(img_color, img_color, mask=img_mask)
    
    numOfLabels, img_label, stats, centroids = cv.connectedComponentsWithStats(img_mask)
    # 쉽게 라벨링 할 수 있음
    
    for idx, centroid in enumerate(centroids):
        if stats[idx][0] == 0 and stats[idx][1] == 0:
            continue
        
        if np.any(np.isnan(centroid)):
            continue
        
        x, y, width, height, area = stats[idx]
        centerX, centerY = int(centroid[0]), int(centroid[1])
        
        if area > 50:
            cv.circle(img_color, (centerX, centerY), 10, (0, 0, 255), 10)
            cv.rectangle(img_color, (x, y), (x+width, y+height), (0,0,255))
        

    cv.imshow('img_color', img_color)
    cv.imshow('img_mask', img_mask)
    cv.imshow('img_result', img_result)


    if cv.waitKey(1) & 0xFF == 27:
        break


cv.destroyAllWindows()

참조 영상

profile
Data Science / Computer Vision
post-custom-banner

0개의 댓글