Programming/IoT_Embedded

객체인식 = 라즈베리파이 + Coral EdgeTPU

이세우 2019. 11. 15. 12:42


구글에서 Coral Edge TPU라는 제품을 내 놓은게 좀 됐습니다.



이것을 라즈베리파이에 USB로 연결하면 빠른 병렬 연산이 가능해 지기 때문에 실시간 객체 인식도 가능합니다.

이번 포스트에서 어떻게 하는지 단계적으로 알아보겠습니다.

장치 구매 
먼저 Coral Edge TPU 장치를 구매해야겠죠.
위 링크에서 "BUY" 버튼을 누르면 여러 distributor 목록이 나오는데 국내에서 구매하기에는 Mouse가 적당해 보입니다.


가격은 오늘(2019.11.15) 기준으로 $74.99 이네요.

알리익스프레스에서도 판매를 하긴하는데 제가 알아본 바로는 최저 가격이 $97.50으로 더 비쌉니다.
구매하는 시점에 따라 다를 수 있으니 잘 비교해보세요.


설치
장치가 준비되었으면 라즈베리파이에 필요한 소프트웨어를 설치해야 합니다.
자세한 설명은 제품 홈페이지에 잘 설명되어있습니다. 

설치할 내용은 다음과 같습니다.
  1. Edge TPU Runtime
  2. Tensorflow Lite API
  3. Edge TPU API


Edge TPU Runtime 설치
Edge TPU Runtime은 장치를 인식하고 동작하게 하는데 필요한 소프트웨어 입니다. 
APT를 이용해서 설치할 수 있는데 구글에서 제공하는 저장소를 추가해야 합니다.
다음 명령어를 순차적으로 실행해서 저장소 소스리스트와 키를 추가합니다.

~$ echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list
위 명령어는 /etc/apt/source/list.d 디렉토리에 소스 리스트를 추가합니다.

다음으로 apt-key를 다운로드 받아 추가 합니다.
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -

끝으로 저장소 리스트를 새로 갱신합니다.

sudo apt-get update

이제 실제로 설치를 진행합니다. 이 때 Edge TPU를 표준으로 사용할 것인지 최대 클럭으로 사용할 것인지에 따라 설치 명령어가 달라집니다.

표준 사용인 경우는 아래와 같습니다.

sudo apt-get install libedgetpu1-std

최대 클럭인 경우는 아래와 같습니다.

sudo apt-get install libedgetpu1-max



최대 클럭으로 사용하는 경우 장치가 뜨거워져서 화상을 입을 수 있으니 조심하라고 합니다.
저는 최대 클럭으로 설치했고 실제 동작시켜보니 뜨겁긴 합니다.

Tensorflow Lite API 설치

Coral Edge TPU로 Inference 하는 방법은 3가지입니다.
  • TF Lite C++ API
  • TF Lite Python API
  • Edge TPU Python API

Tensorflow Lite를 이용하는 방법으로 C++언어와 Python 언어를 선택할 수 있는데 이때 동작 장치를 Edge TPU로 지정하다록 코드를 반드시 수정해야 합니다.
Tensorflow Lite를 직접 사용하지 않아도 되게 추상화 시켜놓은 것이 Edge TPU Python API입니다.
이것을 이용하면 훨씬 쉽게 모델에게 추론시킬 수 있고 Transfer learning도 쉽습니다.

Tensorflow Lite 설치에 대한 설명은 아래 링크에 자세히 설명되어 있습니다.

위 링크에 방문해서 사용하는 보드에 맞는 wheel 파일을 다운로드 받아야 합니다. 


라즈베리파이는 ARMv7이고 Rasbian Buster 부터는 Python3.7이 기본 설치되어 있으니까 해당 링크를 복사해서 다운로드 받습니다. 아래 명령어 처럼 될겁니다.

wget https://dl.google.com/coral/python/tflite_runtime-1.14.0-cp37-cp37m-linux_armv7l.whl



다운로드 받은 다음 pip로 설치를 진행합니다.

pip3 install tflite_runtime-1.14.0-cp37-cp37m-linux_armv7l.whl



TF Lite로 모델 실행해 보기

구글에서 예제를 미리 만들어서 Github에 올려 두었는데 이걸 이용해서 한번 돌려 보겠습니다.

디렉토리를 만들고 예제를 다운로드 받습니다.

mkdir coral && cd coral

git clone https://github.com/google-coral/tflite.git

모델파일과 label 등 예제에 필요한 파일으 다운로드 해야 하는데 이것도 예제로 미리 만들어 놨기 때문에 해당 디렉토리에 가서 명령어만 실행하면 됩니다. 이 명령을 실행하는 동안 NumPy, PIL 등의 종속 라이브러리도 함께 설치됩니다.

cd tflite/python/examples/classification

bash install_requirements.sh


이제 예제를 실행해 봅니다.

python3 classify_image.py \
--model models/mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \
--labels models/inat_bird_labels.txt \
--input images/parrot.jpg


실행 결과는 아래와 같이 나오면 정상입니다.

INFO: Initialized TensorFlow Lite runtime.
----INFERENCE TIME----
Note: The first inference on Edge TPU is slow because it includes loading the model into Edge TPU memory.
11.8ms
3.0ms
2.8ms
2.9ms
2.9ms
-------RESULTS--------
Ara macao (Scarlet Macaw): 0.76562

예제에 사용한 이미지는 앵무새 이미지인데 그 결과로 "Ara macao"라고 나오는 걸 볼 수 있습니다.
"Ara macao"는 우리 말로 "금강앵무"라고 하네요.


Edge TPU API 설치

이제 TF Lite를 쉽게 쓸 수 있게 도와주는 Edge TPU API를 설치합니다. 
저장소 리스트는 이미 추가 되어 있기 때문에 아래의 apt  명령어로 설치만 하면 됩니다.

apt install python3-edgetpu


OpenCV 설치

실시간 카메라 영상을 얻기 위해서 OpenCV를 사용할 예정입니다. 그래서 OpenCV를 설치해야 합니다.
설치 명령어는 아래와 같습니다.

sudo pip3 install opencv-contrib-python==4.1.0.25

라즈베리파이에 맞게 미리 빌드된 OpenCV는  https://www.piwheels.org/ 에서 배포하고 있는데요,
오늘 기준으로 OpenCV의 경우 최신 파일을 받으면 import 하는 구문에서 오류가 발생합니다. 우회하는 방법이 있기는 하지만 굳이 고생할 필요 없으니 바로 이전 버전을 지정해서 설치합니다.

그 다음 piwheels.org에서 배포하는 OpenCV 바이너리의 문제가 종속라이브러리를 설치해주지 않는 건데요. 
아래 명령어로 종속 라이브러리를 모두 설치해 줘야 사용이 가능합니다.

sudo apt install -y  libqt4-test libhdf5-dev libatlas-base-dev libjasper-dev  libqtgui4


객체 인식 프로그램 작성

edge TPU API로 프로그램을 작성하는 것은 무처 쉽습니다. 과정을 요약하면 아래와 같습니다.

우선 import 구문을 작성합니다.

from edgetpu.detection.engine import DetectionEngine

모델 파일 경로를 지정해서 객체를 생성합니다.

engine = DetectionEngine(model)

그 다음 이미지를 전달하고 인식을 요청하면 후보들을 반환합니다.
이 때 이미지는 PIL.Image 형태만 지원합니다.

candidates = engine.detect_with_image(img)

후보들을 순회하면서 객체의 id와 좌표를 얻습니다.

for obj in candidates:
    obj.label_id
    obj.bounding_box
    obj.score

이렇게 얻은 좌표에 사각형을 표시하고 label.txt 파일의  id와 같은 행에 기재된 이름을 표시하면 됩니다.

관련 API 문서는 아래에서 자세히 볼 수 있습니다.


아래는 전체 코드 입니다. 파일이름은 "coral_object_detect.py" 로 저장한것으로 하겠습니다.

import numpy as np
import cv2
import time
from PIL import Image
from edgetpu.detection.engine import DetectionEngine

model = "mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite"
label_path = "coco_labels.txt"
# creating DetectionEngine with model
engine = DetectionEngine(model)

labels = {}
box_colors = {}
prevTime = 0

# reading label file
with open(label_path, 'r'as f:
    lines = f.readlines()
    for line in lines:    # ex) '87 teddy bear'
        id, name = line.strip().split(maxsplit=1)   # ex) '87', 'teddy bear'
        labels[int(id)] = name
print(f"Model loaded({model}) \nTrained object({len(labels)}):\n{labels.values()}")
print("Quit to ESC.")

cap = cv2.VideoCapture(-1)
while True:
    ret, frame = cap.read()
    if not ret:
        print("cannot read frame.")
        break
    img = frame[:, :, ::-1].copy()  # BGR to RGB
    img = Image.fromarray(img)  # NumPy ndarray to PIL.Image

    # threshold=0.5: mininum confidence , top_k=5 : maximum number of detected object 
    candidates = engine.detect_with_image(img, threshold=0.5, top_k=5, keep_aspect_ratio=True, relative_coord=False, )
    if candidates:
        for obj in candidates:
            if obj.label_id in box_colors:
                box_color = box_colors[obj.label_id] # the same color for the same object
            else :
                box_color = [int(j) for j in np.random.randint(0,2553)] # random color for new object
                box_colors[obj.label_id] = box_color

            # drawing bounding-box
            box_left, box_top, box_right, box_bottom = tuple(map(int, obj.bounding_box.ravel()))
            cv2.rectangle(frame, (box_left, box_top), (box_right, box_bottom), box_color, 2)

            # drawing label box
            accuracy = int(obj.score * 100)
            label_text = labels[obj.label_id] + " (" + str(accuracy) + "%)" 
            (txt_w, txt_h), base = cv2.getTextSize(label_text, cv2.FONT_HERSHEY_PLAIN, 23)
            cv2.rectangle(frame, (box_left - 1, box_top - txt_h), (box_left + txt_w, box_top + txt_h), box_color, -1)
            cv2.putText(frame, label_text, (box_left, box_top+base), cv2.FONT_HERSHEY_PLAIN, 2, (255,255,255), 2)

    # calculating and drawing fps            
    currTime = time.time()
    fps = 1/ (currTime -  prevTime)
    prevTime = currTime
    cv2.putText(frame, "fps:%.1f"%fps, (10,30), cv2.FONT_HERSHEY_PLAIN, 2, (0,255,0), 2)
    cv2.imshow('Object Detecting', frame)
    if cv2.waitKey(1)&0xFF == 27:
        break  
cap.release()



모델 다운로드 
실행을 위해서는 모델파일과 각 객체에 대한 id와 이름을 갖는 label파일이 필요합니다.
이 예제에서는 mobileNet SSD를 Coco 데이타셋으로 훈련한 모델을 사용합니다.
아래 명령어로 모델과 label을 각각 다운로드 받을 수 있습니다.
그 밖에 다른 모델은 아래 링크에서 확인 할 수 있습니다.

wget https://github.com/google-coral/edgetpu/raw/master/test_data/mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite
wget https://dl.google.com/coral/canned_models/coco_labels.txt


라즈베리파이에서 실행하기
이제 라즈베리파이에 카메라와 Coral Edge TPU를 연결하고 실행해 봅니다.

python3 coral_object_detect.py

라즈베리파이 3에서 실행한 결과입니다.

FPS가 약 9~10 정도가 나옵니다.

아래 사진은 라즈베리파이 4에서 실행한 모습입니다.


새로나온 라즈베리파이 4에서 실행해 보니 26~27  FPS가 나옵니다. 역시 돈 값을 하는 군요.
라즈베리파이 4가 더 빠른 이유는 USB 3.0 포트로 연결해서 그런것 같습니다.


실험 한 결과는 아래 동영상으로도 보실 수 있습니다. 


이상입니다.