코딩 수업 기록/수화 인식 프로젝트

[수화 인식 프로젝트] 3,4주차 수업 _ opencv 에서 webcam 캡쳐하여 모서리 인식하기

백복치 2021. 9. 26. 17:56

webcam에서 사진 찍은 걸 저장하는 과정을 완료했으니,

그 사진을 가지고 와서 인식하는 작업을 해보자!

 

opencv는 참 좋은 환경이다.. 이런 다양한 기능까지 할 수 있게 해주고 

혼자 감격의 눈물 백번은 쏟음.

 

일단 여기까지 해오면서 가장 중요하다고 생각했던 것은

파일 명을 어떻게 지정하느냐, 또 imwrite랑 imread의 이해가 제일 먼저 중요하다.

 

나는 맨땅에 해딩식으로 하면서 익힌 스타일이라 시간이 오래걸렸지만, 

뭐 다음에 할 때는 응용해서 좀 더 숙련됐다고 치지 뭐 ^_____^

 

아무튼 처음에 파일명을 지정할 때 저장되는 시간을 파일명으로 지정했다.

아무 생각없이 월/일 시간/분/초 로 지정을 해줬고

 

imread로 읽을 때도 그 파일명을 불러오기로 했는데 아무리 해도 에러가 났다.

 

아니 샘플 사진은 잘만 가져오더니 왜 내 사진은 못가져오는것인가!! 했는데,

당연한 것이었다. 초까지 설정을 했으니 당연히 바로 가져온다고 해도 오차 범위가 날게 뻔한 것이었다.

 

키를 설정할 것이 아니라 캡쳐 후에 바로 그 사진에 대한 것까지 가져올 수 있도록 하거나,

캡쳐와 사진 불러와 인식하는 기능을 따로 구현하고 싶으면 초단위를 없애면 된다.

 

나는 따로 구현하고 싶어서 key 로 다 나눠놓았기 때문에,

과감히 초단위를 삭제함.. 그랬더니 정상적으로 가져오게 되었다.

 

            img = cv2.imread("D:/captureimage/" + str(now) + ".png", cv2.IMREAD_COLOR)
            img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)에서

img_gray는 출력할 이미지, img는 입력할 이미지, cv2.COLOR_BGR2GRAY는 색상 변환 코드이다.

사진이 저장된 곳에서 사진은 읽어오고 gray로 색을 바꿔주자.

 

그 후에는 이미지의 특징점인 모서리들을 출력해줄것이다.

다양한 방법들이 있었는데, Harris Corner Detection는 조명 변화에 유연한 특징이 있다고 해서 사용하기로 했다.

 

corner = cv2.cornerHarris(img_gray, 2, 3, 0.04)

순서대로

(코너 검출할 그레이 스케일 이미지, 검출할 코너 갯수, 코너로 판단할 문력값, 검출할 코너 사이의 최소거리) 이다.

마지막 최소거리는 이 거리 이내에 있으면 무시하겠다는 의미이다.

 

그 다음으로 변화량 결과의 최대값 10% 이상의 좌표를 구했다.

coord = np.where(corner > 0.1 * corner.max())
coord = np.stack((coord[1], coord[0]), axis=-1)

cv2.cornerHarris() 함수의 변환 결과는 입력 이미지와 크기가 같은 1차원 배열이기 때문에

배열에서 코너값이 코너의 최대값*0.1인 부분보다 큰 값의 색인 위치를 coord라는 변수에 지정한다.

np.where() 함수는 조건에 부합하는 값을 찾는 함수이다.

그리고 난 후 np.stack()이라는 함수를 사용하여 배열을 합쳐 coord에 저장해준다.

np.stack()는 동일한 길이를 가져야한다. 특정 축인 axis를 따라서 합치게 된다.

 

그리고 코너로 인식된 좌표들에 동그라미로 표시를 해주도록 하겠다.

for x, y in coord:
 	cv2.circle(img, (x, y), 5, (0, 0, 255), 1, cv2.LINE_AA)

cv2.circle(img, (x, y), 5, (0, 0, 255), 1, cv2.LINE_AA)에서

img는 그림을 그릴 입력 이미지, (x,y)는 원의 중심 좌표 즉 그림을 그릴 지점, (0,0,255)는 반지름이다.

그리고 1은 BRG형태의 COLOR이다. 

 

corner_norm = cv2.normalize(corner, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)

그리고 그 변화량을 영상으로 표현하기 위해서 0~255로 정규화 해준다.

다 되었다. 표현한 것을 다시 화면에 출력해주면 끝!

 

corner_norm = cv2.cvtColor(corner_norm, cv2.COLOR_GRAY2BGR)
merged = np.hstack((corner_norm, img))
cv2.imshow('Harris Corner', merged)
cv2.waitKey()
cv2.destroyAllWindows()

이렇게 화면에 출력하고 나면 빨간색 동그라미가 표시된 부분들이 코너 부분임을 알 수 있다.

코너에서 픽셀의 변화량이 가장 크기 때문이라고 하는데 너무 신기한 사실이지 않은가..

어떤 사진을 인식하냐에 따라서 오차 범위가 많이 달라지긴 하지만

단일 배경과 명확한 코너가 있는 사진이라면 확실한 결과를 가져올 수 있을 것 같다.

 

나는 손가락을 인식시켜서 수화앱을 만드는 것이 목적이라 앞으로도

좀 더 많은 수정이 필요할 것 같지만 이렇게 결과값이 나오는 것만 해도 아주 만족스럽다... 헿

 

<전체코드>

# python -m pip install opencv-python 필요
import cv2
import time
import datetime

import numpy as np

video = cv2.VideoCapture(0)  # WebCam의 경우 0 또는 1
# 비디오 파일의 경우 '경로/파일명.확장자'

prev_time = 0  # 이전 프레임 재생 시간 값 0으로 초기화
FPS = 100
a = 0

while True:

    ret, frame = video.read()  # ret = frame 읽기가 성공하면 while문의 true 값 반환, frame = 배열 형식의 영상 프레임 값을 반환
    frame = cv2.flip(frame, 1)
    current_time = time.time() - prev_time  # 현재 시간 - 이전 프레임 재생 시간

    # ret이 true의 값을 가진다면 영상을 재생
    if (ret is True) and (current_time > 1. / FPS):  # 비교
        prev_time = time.time()
        cv2.imshow("VideoCapture", frame)  # imshow(윈도우 창의 이름, cv2.VideoCapture()의 리턴값)

        now = datetime.datetime.now().strftime("%m-%d (%H;%M)")

        key = cv2.waitKey(30) & 0xff

        if key == 27:  # esc 아스키 코드 번호 27
            break
        elif key == 32:  # space 아스키 코드 번호 32
            print("캡쳐완료" + ' ' + now)
            cv2.imwrite("D:/captureimage/" + str(now) + ".png", frame)
            # window에서 imwrite 사용 시 한글 포함 경로를 저장하지 못함, 또한 파일명에 사용할 수 없는 특수기호 포함 시
            # 실행에는 문제없지만 파일 저장은 안되기 때문에 주의하기
        elif key == 24:  # control-x
            print("최근 파일 색상 검출")
            img_color = cv2.imread("D:/captureimage/" + str(now) + ".png")

            print('shape: ', img_color.shape)
            height, width = img_color.shape[:2]

            img_hsv = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)  # cvtColor 함수를 이용하여 hsv 색공간으로 변환

            lower_blue = (120 - 10, 30, 30)  # hsv 이미지에서 바이너리 이미지로 생성 , 적당한 값 30
            upper_blue = (120 + 10, 255, 255)
            img_mask = cv2.inRange(img_hsv, lower_blue, upper_blue)

            # lower_blue = np.array([120 - 10, 30, 30]) # hsv 이미지에서 바이너리 이미지로 생성 , 적당한 값 30
            # upper_blue = np.array([120 + 10, 255, 255])
            # img_mask = cv2.inRange(img_hsv, lower_blue, upper_blue)  # 범위내의 픽셀들은 흰색, 나머지 검은색

            img_result = cv2.bitwise_and(img_color, img_color,
                                         mask=img_mask)  # 바이너리 이미지를 마스크로 사용하여 원본이미지에서 범위값에 해당하는 영상부분을 획득

            cv2.imshow('img_origin', img_color)  # 원본
            cv2.imshow('img_mask', img_mask)  # 색상 추출 파일
            cv2.imshow('img_color', img_result)  # 하얀색으로 변환환

        elif key == 107: # 키보드 k 입력
            print("최근 파일 모서리 검출")
            img = cv2.imread("D:/captureimage/" + str(now) + ".png", cv2.IMREAD_COLOR)
            img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

            # 모서리 검출
            corner = cv2.cornerHarris(img_gray, 2, 3, 0.04)
            # (코너 검출할 그레이 스케일 이미지, 검출할 코너 갯수, 코너로 판단할 문력값, 검출할 코너 사이의 최소거리, 이 거리 이내에 있으면 무시)

            # 10% 지표구하기
            coord = np.where(corner > 0.1 * corner.max())
            coord = np.stack((coord[1], coord[0]), axis=-1)

            for x, y in coord:
                cv2.circle(img, (x, y), 5, (0, 0, 255), 1, cv2.LINE_AA)

            corner_norm = cv2.normalize(corner, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)

            corner_norm = cv2.cvtColor(corner_norm, cv2.COLOR_GRAY2BGR)
            merged = np.hstack((corner_norm, img))
            cv2.imshow('Harris Corner', merged)
            cv2.waitKey()
            cv2.destroyAllWindows()

 

참고 (아주 설명을 잘해주셨다! 짱이다!)

https://bkshin.tistory.com/entry/OpenCV-26-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%ED%82%A4-%ED%8F%AC%EC%9D%B8%ED%8A%B8

 

OpenCV - 26. 이미지의 특징점(Keypoints)과 특징점 검출기(Keypoints detector)

이번 포스팅에서는 이미지의 특징점과 특징점 검출기 대해 알아보겠습니다. 이번 포스팅 역시 '파이썬으로 만드는 OpenCV 프로젝트(이세우 저)'를 정리한 것임을 밝힙니다. 코드: github.com/BaekKyunShi

bkshin.tistory.com

 

<결과값>