Lepton으로 사람 찾기
여기서 다룰 내용은 오픈 소스 컴퓨터 비전 라이브러리 OpenCV를 사용하여 FLIR Lepton 카메라로 기본적인 사람 찾기 알고리즘을 구현하는 방법입니다.
목표
열화상 카메라는 거의 모든 조명 조건에서 포유동물을 찾는 데 탁월한 성능을 발휘합니다. 할 수 있는 것을 탐색하기 위한 연습으로 Lepton의 시야각에 있는 사람을 찾아 OpenCV로 윤곽선을 그려보십시오.
도구
필요한 하드웨어:
- Lepton
- PureThermal 보드
- OpenCV 바인딩이 설치된 Python 2.7 환경. Windows, OSX 또는 Linux 설정 가능.
- 영상을 저장하려는 경우, PIL
설정
특정 플랫폼의 튜토리얼에 따라 Python 환경을 설정하고 OpenCV를 설치합니다. 설정이 끝나면 웹캠 스트림을 보면서 모든 기능이 작동하는지 확인합니다.
import cv2
cv2.namedWindow("preview")
cameraID = 0
vc = cv2.VideoCapture(cameraID)
if vc.isOpened(): # 첫 번째 프레임 가져오기 시도
rval, frame = vc.read()
else:
rval = False
while rval:
cv2.imshow("preview", frame)
rval, frame = vc.read()
key = cv2.waitKey(20)
if key == 27: # exit on ESC
break
이렇게 작성하면 스트림이 표시됩니다. 웹캠이 컴퓨터에 연결되어 있거나 통합되어 있는 경우에는 cameraID의 값을 0 이외의 값으로 변경해야 할 수 있습니다. 이 개발 환경에서 PureThermal 보드의 ID는 1입니다.
접근 방식
OpenCV의 웹캠 캡처 코드는 라디오메트릭 열화상 데이터를 캡처할 수 없어서 인원수 계산에는 이상적인 형식이지만, PureThermal 보드에서 컬러 피드를 캡처할 수 있기 때문에 약간의 전처리로 충분히 만족스러운 윤곽선을 그릴 수 있습니다.
특히, 인간은 기본 색상표에서 매우 밝게 나타나는 경향이 있어서 RGB 이미지를 HSV로 변환하고 V 채널로 보면 현장에서 사람의 체온을 띄는 물체가 어디에 있는지 매우 명확하게 알 수 있습니다.
cv2.imshow("preview", frame) 부분을 다음과 같이 바꿔 보십시오.
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)
frame_v = frame_hsv[:,:,2]
cv2.imshow("preview", frame_v)
이제 인간이 어디에 있는지 확연히 알 수 있고, 컴퓨터 비전으로 할 수 있는 것이 무엇인지 확실해집니다.
OpenCV 입력
OpenCV는 Python과 함께 C++로 작성된 컴퓨터 비전 라이브러리로 인기가 매우 많습니다. 이 라이브러리는 다양한 일반 컴퓨터 비전 작업에 사용되며, 여기서도 윤곽선을 그리는데 사용할 것입니다.
우선 시작할 부분은 Canny Edge Detection인데, 이미지에서 경계선을 식별하는 강력한 알고리즘입니다.
아래 코드를 사용하면 OpenCV로 감지한 경계선을 볼 수 있습니다.
thresh = 50
edges = cv2.Canny(frame_v,thresh,thresh*2, L2gradient=True)
cv2.imshow("preview", edges)
하지만 그렇게 깔끔해 보이지는 않네요. 이 알고리즘이 고주파 노이즈를 너무 많이 감지하여 경계선이 선명하지 않습니다. 약간의 스무딩 처리를 하면 깔끔하게 정리할 수 있습니다. 여기서는 바이래터럴 필터라고 하는 경계선 보존 영상 스무딩 기법을 사용합니다. 가우시안 블러링과 비슷하긴 하지만, 우리가 처음에 원했던 경계선에 미치는 영향은 크지 않습니다.
blurredBrightness = cv2.bilateralFilter(frame_v,9,150,150)
thresh = 70
edges = cv2.Canny(blurredBrightness,thresh,thresh*2, L2gradient=True)
cv2.imshow("preview", edges)
좀 나아졌지만, 아직 개선의 여지가 있습니다. 조명이 사람으로 인식하는 것을 없애 보겠습니다. 좀 까다로운 작업이지만 OpenCV로 처리할 수 있습니다. 먼저, 원본 이미지를 임계 처리하고 픽셀이 따듯한 곳은 모두 1이 입력되고 그렇지 않은 곳은 0이 입력되도록 하여 이진 이미지를 생성합니다. 그런 다음 OpenCV를 사용하여 이 작업으로 생성된 값이 1인 BLOB을 침식시킬 겁니다. 그리고 이러한 BLOB을 다시 이전과 거의 동일한 크기로 팽창시키겠습니다.
_,mask = cv2.threshold(blurredBrightness,200,1,cv2.THRESH_BINARY)
erodeSize = 5
dilateSize = 7
import numpy as np
eroded = cv2.erode(mask, np.ones((erodeSize, erodeSize)))
mask = cv2.dilate(eroded, np.ones((dilateSize, dilateSize)))
침식과 팽창 작업 후에는 이진 이미지의 본질은 그대로 남고 작은 모양들이 모두 제거됩니다. 바로 우리가 원하는 모습입니다. 이렇게 하면 작은 모양의 경계선을 모두 지울 수 있습니다.
그럼 감지된 경계선에 적용했을 때 어떤 모습이 되는지 보겠습니다.
나쁘지 않네요! 그리고 소스 이미지 위에 겹치면 멋진 이미지가 됩니다.
최종 코드는 아래와 같습니다. 상수는 원하는 대로 조정해야 할 수 있으며, OpenCV는 특정 요구 사항에 맞게 결과를 개선하는 데 사용할 수 있는 종합적인 도구를 제공합니다.
import cv2
import numpy as np
cv2.namedWindow("preview")
cameraID = 0
vc = cv2.VideoCapture(cameraID)
if vc.isOpened(): # 첫 번째 프레임 가져오기 시도
rval, frame = vc.read()
else:
rval = False
while rval:
frame_v = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)[:,:,2]
blurredBrightness = cv2.bilateralFilter(frame_v,9,150,150)
thresh = 50
edges = cv2.Canny(blurredBrightness,thresh,thresh*2, L2gradient=True)
_,mask = cv2.threshold(blurredBrightness,200,1,cv2.THRESH_BINARY)
erodeSize = 5
dilateSize = 7
eroded = cv2.erode(mask, np.ones((erodeSize, erodeSize)))
mask = cv2.dilate(eroded, np.ones((dilateSize, dilateSize)))
cv2.imshow("preview", cv2.resize(cv2.cvtColor(mask*edges, cv2.COLOR_GRAY2RGB) | frame, (640, 480), interpolation = cv2.INTER_CUBIC))
rval, frame = vc.read()
key = cv2.waitKey(20)
if key == 27: # exit on ESC
break