이 기록의 핵심은 Edge TPU용 .tflite 모델, 라벨 파일, 입력 이미지를 같은 실행 명령에 정확히 연결하는 것이며, 아래 설치 명령은 Raspberry Pi 3와 초기 edgetpu API를 전제로 한 당시의 절차입니다. 처음에는 제공된 분류 모델과 고정 이미지로 USB, 런타임, 파일 경로를 검증하고, 그다음 사용자 모델과 웹캠을 한 단계씩 바꾸는 편이 안전합니다. 분류가 성공했다는 사실만으로 웹캠 색상 변환과 detection 후처리까지 맞았다고 볼 수는 없습니다.
Raspberry Pi 3에서 Coral USB 예제를 돌리기 전 확인할 것
어떤 환경을 전제로 하는가
원문에서 사용한 장비와 소프트웨어 조건은 다음과 같습니다.
- Raspberry Pi 3
- Google Coral USB
- TensorFlow
1.7.* edgetpu_api.tar.gz에 포함된python-tflite-source
1
2
3
4
5
6
7
pip install --upgrade "tensorflow==1.7.*"
pip install PILLOW
wget https://storage.googleapis.com/cloud-iot-edge-pretrained-models/edgetpu_api.tar.gz
tar xzf edgetpu_api.tar.gz
cd python-tflite-source
bash ./install.sh
설치가 끝난 뒤에는 이미 꽂혀 있던 Coral USB를 분리했다가 다시 연결했습니다. 이 단계까지는 특정 버전과 배포 파일 이름에 묶여 있으므로, 현재의 모든 Coral 환경에 적용되는 설치법이라고 단정할 수는 없습니다.
가장 작은 분류 예제로 연결을 확인하기
먼저 모델, 라벨, 샘플 이미지를 한 폴더에 받습니다.
1
2
3
4
cd ~/Downloads/
wget https://dl.google.com/coral/canned_models/mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \
https://dl.google.com/coral/canned_models/inat_bird_labels.txt \
https://coral.withgoogle.com/static/docs/images/parrot.jpg
그다음 데모 디렉터리에서 세 경로를 명시합니다.
1
2
3
4
5
cd python-tflite-source/edgetpu/demo
python3 classify_image.py \
--model ~/Downloads/mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \
--label ~/Downloads/inat_bird_labels.txt \
--image ~/Downloads/parrot.jpg
여기서 먼저 볼 것은 정확도 비교가 아니라 입력 세 가지가 서로 맞물리는지입니다. 모델 파일이 Edge TPU용인지, 라벨 경로가 실제 파일을 가리키는지, 이미지가 열리는지를 분리해서 확인하면 다음 웹캠 코드의 문제 범위도 줄어듭니다.
웹캠 탐지 코드가 하는 일
기존 예제는 DetectionEngine으로 모델을 열고, 웹캠 프레임을 BGR에서 RGB로 바꾼 뒤 PIL 이미지로 변환해 추론합니다. 아래는 전체 설치 프로그램이 아니라 추론 루프의 핵심 조각입니다. imutils, OpenCV, 카메라와 명령행 인자가 준비돼 있어야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from edgetpu.detection.engine import DetectionEngine
from imutils.video import VideoStream
from PIL import Image
import argparse
import imutils
import time
import cv2
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", required=True,
help="path to TensorFlow Lite object detection model")
ap.add_argument("-l", "--labels", required=True,
help="path to labels file")
ap.add_argument("-c", "--confidence", type=float, default=0.3,
help="minimum probability to filter weak detections")
args = vars(ap.parse_args())
labels = {}
for row in open(args["labels"]):
classID, label = row.strip().split(maxsplit=1)
labels[int(classID)] = label.strip()
model = DetectionEngine(args["model"])
cam = VideoStream(src=0).start()
time.sleep(2.0)
프레임별 처리는 색상 순서와 좌표계를 맞추는 과정이 중심입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
while True:
frame = cam.read()
frame = imutils.resize(frame, width=500)
output = frame.copy()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
results = model.DetectWithImage(
frame,
threshold=args["confidence"],
keep_aspect_ratio=True,
relative_coord=False,
)
for result in results:
startX, startY, endX, endY = result.bounding_box.flatten().astype("int")
label = labels[result.label_id]
cv2.rectangle(output, (startX, startY), (endX, endY), (0, 255, 0), 2)
text = "{}: {:.2f}%".format(label, result.score * 100)
y = startY - 15 if startY - 15 > 15 else startY + 15
cv2.putText(output, text, (startX, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imshow("Frame", output)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
cam.stop()
기존 실행 예시는 다음 인자를 요구합니다.
1
python detect_image.py --model model_name.tflite --labels labels.txt
파일명이 실제 Python 스크립트와 일치하는지, 분류 모델이 아니라 탐지 모델을 넘겼는지는 사용자가 따로 확인해야 합니다.
그대로 복사하기 전에 볼 한계
이 글은 최신 Coral 소프트웨어 전체 설치 안내가 아닙니다. 특히 tensorflow==1.7.*, edgetpu.detection.engine, 고정 다운로드 URL은 당시 예제의 조건입니다. 코드가 실패했을 때 버전 숫자만 임의로 바꾸면 모델 형식과 Python API가 함께 어긋날 수 있습니다.
따라서 저장해 둘 만한 부분은 다음 판단 순서입니다.
- 공식 샘플 모델, 라벨, 이미지로 USB와 API 연결을 먼저 확인합니다.
- 분류 예제가 성공한 뒤 사용자 모델로 바꿉니다.
- 웹캠 단계에서는 BGR→RGB 변환과 라벨 ID 매핑을 따로 확인합니다.
- 전체 코드가 아니라 핵심 조각이라는 점을 전제로 의존성과 파일명을 점검합니다.
당시 사용자 예제의 출처는 PyImageSearch 글이며, 추가 설치 기록은 Geek’s Voyage 글에 연결되어 있습니다.
실패 지점을 장치, 모델, 입력으로 어떻게 나누나
장치 단계에서는 운영체제가 Coral USB를 인식하는지와 예제 런타임이 장치를 여는지를 봅니다. 이 단계가 실패했다면 라벨 파일이나 카메라 코드를 바꿀 이유가 없습니다. 제공된 모델과 이미지의 가장 작은 예제가 통과해야 모델 변환이나 사용자 코드 문제로 범위를 옮길 수 있습니다.
모델 단계에서는 입력 크기와 label 파일의 대응을 확인합니다. 모델이 기대하는 크기로 이미지를 바꾸고, 출력 class ID가 label의 어느 행을 가리키는지 봅니다. 모델과 라벨이 서로 다른 데이터셋에서 왔다면 추론은 실행돼도 의미 없는 이름이 표시될 수 있습니다.
웹캠 단계에서는 한 frame을 파일로 저장해 색상과 크기를 먼저 확인합니다. OpenCV frame의 색상 순서를 모델 입력에 맞게 바꾸고, box 좌표를 원본 frame 크기로 되돌립니다. 처리 시간이 길면 카메라 읽기, 전처리, 추론, 그리기를 따로 측정해야 어느 부분을 줄일지 판단할 수 있습니다.
함께 읽으면 이해가 이어지는 글
- 라즈베리파이에서 NCS2 추론이 막힐 때: OpenVINO IR 변환 체크리스트 — 라즈베리파이 3와 Neural Compute Stick 2에서 OpenVINO 추론을 준비하는 흐름을 학습, 동결, IR 변환, MYRIAD 실행 단계로 나눕니다. XML/BIN 쌍, input shape, output node…
- NCS2에서 YOLOv3가 실행되지 않을 때: FP16 IR 변환과 입력 Shape 점검 — 라즈베리파이 3와 Neural Compute Stick 2에서 YOLO를 추론하기 위해 weights를 PB와 OpenVINO IR로 바꾸는 흐름을 정리합니다. FP16 지정, 416×416 입력, NHWC, NCHW 변환…
- 라즈베리파이 Google Assistant API 설정 순서: OAuth부터 푸시투토크까지 — 라즈베리파이 Google Assistant 샘플을 위해 마이크, 스피커, 프로젝트와 OAuth 파일, Python 가상환경, push-to-talk 실행을 순서대로 검증합니다.
자주 묻는 질문
일반 TFLite 모델을 Coral USB에서 바로 실행할 수 있나요?
이 글의 예제는 Edge TPU용으로 준비된 .tflite 모델을 전제로 합니다. 파일 확장자만 같다고 호환되는 것은 아니므로 먼저 글에 연결된 샘플 모델로 장치와 API를 확인해야 합니다.
분류 예제가 되면 웹캠 탐지도 바로 되나요?
아닙니다. 분류 성공은 USB, 런타임, 기본 입력 연결을 확인한 것입니다. 웹캠에서는 frame 획득, BGR에서 RGB 변환, detection 좌표와 label ID 해석을 추가로 검증해야 합니다.
이 글의 edgetpu 설치 명령을 현재 그대로 사용해도 되나요?
현재 범용 설치법으로 보면 안 됩니다. Raspberry Pi 3와 초기 Edge TPU API의 기록이므로 당시 흐름을 참고하고, 실제 패키지명과 지원 모델은 사용하는 환경에서 다시 확인해야 합니다.
사용자 모델로 바꾸기 전에 무엇을 기록해야 하나
공식 샘플을 실행할 때 모델 경로, label 경로, 입력 이미지, 예상 class를 한 묶음으로 보관합니다. 실행 명령과 전체 출력도 남깁니다. 이 기준 예제가 반복해서 성공해야 USB 연결과 런타임을 사용자 모델 문제에서 제외할 수 있습니다. 한 번 성공한 뒤 곧바로 여러 파일을 바꾸면 다시 실패했을 때 차이를 찾기 어렵습니다.
사용자 모델을 넣을 때는 입력 tensor의 크기, dtype, 값 범위를 먼저 비교합니다. 샘플 코드가 resize와 normalization을 어디서 하는지 확인하고 모델이 기대하는 방식과 맞춥니다. 출력 tensor도 class score인지 detection box 묶음인지에 따라 후처리가 달라집니다. 분류 예제 코드에 detection 모델만 바꾸는 방식으로는 올바른 결과를 기대할 수 없습니다.
Label 파일은 모델과 같은 학습 class 순서를 가져야 합니다. 첫 줄이 실제 class인지 배경용 항목인지, class ID가 0부터 시작하는지 코드를 통해 확인합니다. 결과 이미지의 이름이 틀릴 때는 모델 재학습보다 raw class ID와 label lookup을 먼저 출력해야 합니다.
웹캠에서는 카메라 frame 하나를 저장해 샘플 이미지 경로로 넣어 봅니다. 이 고정 frame이 맞게 처리된 뒤 실시간 loop로 옮기면 카메라와 추론 문제를 분리할 수 있습니다. BGR→RGB 변환 전후의 한 pixel이나 저장 이미지를 비교하고, 모델 입력의 가로, 세로가 뒤바뀌지 않았는지 확인합니다.
Detection box는 모델 입력 좌표와 원본 frame 좌표 사이의 변환이 필요합니다. Resize 방식이 종횡비를 바꿨는지, 좌표가 정규화된 값인지 확인한 뒤 그립니다. Box가 모두 한쪽에 몰리거나 화면 밖으로 나간다면 USB 속도보다 이 복원식을 먼저 봅니다.
성능은 첫 호출과 반복 호출을 나눠 측정하고 카메라 읽기, 전처리, 추론, 그리기의 시간을 각각 기록합니다. USB accelerator가 빨라도 Python 화면 표시가 병목일 수 있습니다. 필요한 FPS를 만족하지 못하면 어느 단계를 줄일지 이 분해 결과로 판단합니다.
운영 관점에서는 장치가 빠졌거나 모델 로드가 실패했을 때의 동작도 정합니다. 프로그램이 조용히 CPU로 바뀌는지, 오류를 내고 멈추는지, 잘못된 결과를 계속 보여 주는지 확인해야 합니다. 이 실패 경로까지 시험해야 예제 실행을 실제 시스템 준비와 구분할 수 있습니다.
발열이나 전원 상태처럼 긴 실행에서만 드러나는 조건도 짧은 샘플과 분리해 봅니다. 같은 고정 영상을 반복 처리하며 시간과 오류를 기록하고, 장치를 다시 연결했을 때 프로세스 재시작이 필요한지 확인합니다. 한 장 분류 성공은 장시간 카메라 처리의 안정성을 보장하지 않습니다.
사용자 모델의 정확도는 accelerator 유무와 별개로 평가합니다. CPU 원본 경로와 Edge TPU 경로가 같은 전처리, label을 쓸 때 결과가 어떻게 달라지는지 보고, 양자화나 변환 뒤 특히 틀리는 class가 있는지 확인합니다. 속도만 빠르고 필요한 class를 놓치면 배포 목표를 충족하지 못합니다.
카메라 입력이 끊기거나 빈 frame이 들어올 때 추론을 호출하지 않는지도 확인합니다. 이전 frame의 결과를 계속 표시하면 장치는 정상처럼 보여도 화면은 오래된 정보일 수 있습니다. Frame 시각과 처리 성공 여부를 함께 표시하면 이런 실패를 찾기 쉽습니다.
←→ 키와 좌우 스와이프를 지원합니다. 읽던 페이지는 이 기기에 저장됩니다.