darknetのモデルデータをopenVINOのモデルデータに変換し、tinyYOLOで画像認識を行う
openVINOの2019 R3.1 がリリー(2019.10.29現在、ubuntu用のみ)スされ、YOLOのサンプルプログラムが用意されていたので、tinyYOLOを実行してみた。
pythonはpyenv環境で3.7.4を使用
以下は openVINO フルパッケージをubuntuにインストール にしたがって、openVINO フルパッケージ(2019 R3.1) をインストールしたubuntuでの作業。
上記参考サイトの手順に従って、darknetのtinyYOLOモデルデータをopenVINOのモデルデータに変換する。
cd /work/NCS2
git clone https://github.com/mystic123/tensorflow-yolo-v3.git
cd tensorflow-yolo-v3/
git checkout ed60b90
wget https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names
wget https://pjreddie.com/media/files/yolov3-tiny.weights
python convert_weights_pb.py --class_names coco.names --data_format NHWC --weights_file yolov3-tiny.weights --tiny
mv frozen_darknet_yolov3_model.pb yolo_v3_tiny.pb
models_dir=/work/NCS2/openvino_models/FP16
python /opt/intel/openvino/deployment_tools/model_optimizer/mo_tf.py \
--input_model yolo_v3_tiny.pb \
--tensorflow_use_custom_operations_config /opt/intel/openvino/deployment_tools/model_optimizer/extensions/front/tf/yolo_v3_tiny.json \
--output_dir ${models_dir} \
--data_type FP16 \
--batch 1
/work/NCS2/openvino_models/FP16ディレクトリに yolo_v3_tiny.bin yolo_v3_tiny.mapping yolo_v3_tiny.xml の3つが出来る
[!NOTE] FP32で計算する場合はこちら
NCStick使用時はFP16のみサポートなので、FP16で作っておくと使い回しできて楽。
そんなに認識精度が変わるわけでもなさそうだし。models_dir=/work/NCS2/openvino_models/FP32 python /opt/intel/openvino/deployment_tools/model_optimizer/mo_tf.py \ --input_model yolo_v3_tiny.pb \ --tensorflow_use_custom_operations_config /opt/intel/openvino/deployment_tools/model_optimizer/extensions/front/tf/yolo_v3_tiny.json \ --output_dir ${models_dir} \ --data_type FP32 \ --batch 1
pbファイルにはラベルデータが入っているはずだが、この後の変換でラベルデータは欠落するらしい。
後のプログラムのためにファイル名変更しておく
cp coco.names ${models_dir}/yolo_v3_tiny.labels
cd ..
cp -r /opt/intel/openvino/deployment_tools/open_model_zoo/demos/python_demos/object_detection_demo_yolov3_async .
cd object_detection_demo_yolov3_async/
コピったソースそのままでも大丈夫だが、ちょっと修正しておく。
--- object_detection_demo_yolov3_async.py.org 2019-10-29 05:08:34.982999999 +0900
+++ object_detection_demo_yolov3_async.py 2019-10-29 11:20:46.112091500 +0900
@@ -210,7 +210,8 @@
else:
labels_map = None
- input_stream = 0 if args.input == "cam" else args.input
+ # input_stream = 0 if args.input == "cam" else args.input
+ input_stream = 0 if args.input == "cam" else os.path.abspath(args.input)
is_async_mode = True
cap = cv2.VideoCapture(input_stream)
@@ -234,6 +235,8 @@
next_request_id = 1
render_time = 0
parsing_time = 0
+ frame_time = 0
+ prev_time = time()
# ----------------------------------------------- 6. Doing inference -----------------------------------------------
log.info("Starting inference...")
@@ -263,6 +266,8 @@
# Start inference
start_time = time()
+ frame_time = start_time - prev_time # 1フレームの処理時間
+ prev_time = start_time
exec_net.start_async(request_id=request_id, inputs={input_blob: in_frame})
det_time = time() - start_time
@@ -303,8 +308,9 @@
# Validation bbox of detected object
if obj['xmax'] > origin_im_size[1] or obj['ymax'] > origin_im_size[0] or obj['xmin'] < 0 or obj['ymin'] < 0:
continue
- color = (int(min(obj['class_id'] * 12.5, 255)),
- min(obj['class_id'] * 7, 255), min(obj['class_id'] * 5, 255))
+ # color = (int(min(obj['class_id'] * 12.5, 255)),
+ # min(obj['class_id'] * 7, 255), min(obj['class_id'] * 5, 255))
+ color = (255, 128, 128)
det_label = labels_map[obj['class_id']] if labels_map and len(labels_map) >= obj['class_id'] else \
str(obj['class_id'])
@@ -322,16 +328,17 @@
# Draw performance stats over frame
inf_time_message = "Inference time: N\A for async mode" if is_async_mode else \
"Inference time: {:.3f} ms".format(det_time * 1e3)
+ frame_time_message = "Frame time: {:.3f} ms".format(frame_time * 1e3)
render_time_message = "OpenCV rendering time: {:.3f} ms".format(render_time * 1e3)
async_mode_message = "Async mode is on. Processing request {}".format(cur_request_id) if is_async_mode else \
"Async mode is off. Processing request {}".format(cur_request_id)
parsing_message = "YOLO parsing time is {:.3f}".format(parsing_time * 1e3)
- cv2.putText(frame, inf_time_message, (15, 15), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
- cv2.putText(frame, render_time_message, (15, 45), cv2.FONT_HERSHEY_COMPLEX, 0.5, (10, 10, 200), 1)
- cv2.putText(frame, async_mode_message, (10, int(origin_im_size[0] - 20)), cv2.FONT_HERSHEY_COMPLEX, 0.5,
- (10, 10, 200), 1)
- cv2.putText(frame, parsing_message, (15, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (10, 10, 200), 1)
+ cv2.putText(frame, inf_time_message, (15, 15), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
+ cv2.putText(frame, parsing_message, (15, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, ( 10, 10, 200), 1)
+ cv2.putText(frame, render_time_message, (15, 45), cv2.FONT_HERSHEY_COMPLEX, 0.5, ( 10, 10, 200), 1)
+ cv2.putText(frame, frame_time_message, (10, int(origin_im_size[0] - 35)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
+ cv2.putText(frame, async_mode_message, (10, int(origin_im_size[0] - 20)), cv2.FONT_HERSHEY_COMPLEX, 0.5, ( 10, 10, 200), 1)
start_time = time()
cv2.imshow("DetectionResults", frame)
上のパッチ内容をa.patchとして保存したとして、以下のコマンドを実行
patch object_detection_demo_yolov3_async.py a.patch
実行コマンドは以下。
dataディレクトリに認識用の画像データを用意してある(以下同じ)。
models_dir=/work/NCS2/openvino_models/FP16
python object_detection_demo_yolov3_async.py \
--model ${models_dir}/yolo_v3_tiny.xml \
--cpu_extension /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_avx2.so \
--labels ${models_dir}/yolo_v3_tiny.labels \
--input ../../data/000004.jpg
実行コマンドは以下。
入力ファイルをmp4に変えるだけ。
カメラからの入力を使用する場合は--input cam
とするらしいが、カメラないので未確認。
models_dir=/work/NCS2/openvino_models/FP16
python object_detection_demo_yolov3_async.py \
--model ${models_dir}/yolo_v3_tiny.xml \
--cpu_extension /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_avx2.so \
--labels ${models_dir}/yolo_v3_tiny.labels \
--input ../../data/testvideo3.mp4
以下は Intel NCStick2用動作環境の構築 にしたがって環境構築したRaspberryPiを使用。
RaspberryPi用はopenVINO 2019R3のまま(2019.10.29現在、R3.1はリリースされていない)だけど、問題なし。
ubuntuで作成した object_detection_demo_yolov3_async ディレクトリをまるごとRaspberryPiにコピーする。
実行コマンドは以下。 ubuntuの実行コマンドと比べて、以下の変更がある。
--device MYRIAD
を追加--cpu_extension
を削除python object_detection_demo_yolov3_async.py \
--device MYRIAD \
--model ${models_dir}/yolo_v3_tiny.xml \
--labels ${models_dir}/yolo_v3_tiny.labels \
--input ../../data/000004.jpg
実行コマンドは以下。
こちらも入力ファイルをmp4に変えるだけ。
python object_detection_demo_yolov3_async.py \
--device MYRIAD \
--model ${models_dir}/yolo_v3_tiny.xml \
--labels ${models_dir}/yolo_v3_tiny.labels \
--input ../../data/testvideo3.mp4
openCVではMP4ファイルを保存することができる。
object_detection_demo_yolov3_async.py に以下の変更を加えることで、認識結果をMP4ファイルに保存することができる。
以下の修正ファイルは簡易的に保存する処理を追加したため、保存ファイル名は決め打ち。
汎用的にするなら、オプションで指定できるようにしてもいいかもね。
ただし、実際に保存するタイミングとMP4ファイルのタイムインデックスが一致するわけではないので、 処理時の見た目と保存ファイルを再生したときの見た目は異なるので注意が必要。
--- object_detection_demo_yolov3_async.py 2019-10-29 11:20:46.112091500 +0900
+++ record.py 2019-10-29 11:35:37.296005608 +0900
@@ -218,6 +218,18 @@
number_input_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
number_input_frames = 1 if number_input_frames != -1 and number_input_frames < 0 else number_input_frames
+ # =====================================================================================
+ # 幅と高さを取得
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
+ size = (width, height)
+ # フレームレート(1フレームの時間単位はミリ秒)の取得
+ frame_rate = int(cap.get(cv2.CAP_PROP_FPS))
+ # フォーマット
+ fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
+ writer = cv2.VideoWriter('./outtest.mp4', fmt, frame_rate, size)
+ # =====================================================================================
+
wait_key_code = 1
# Number of frames in picture is 1 and this will be read in cycle. Sync mode is default value for this case
@@ -342,6 +354,9 @@
start_time = time()
cv2.imshow("DetectionResults", frame)
+ # =====================================================================================
+ writer.write(frame)
+ # =====================================================================================
render_time = time() - start_time
if is_async_mode:
@@ -359,6 +374,10 @@
is_async_mode = not is_async_mode
log.info("Switched to {} mode".format("async" if is_async_mode else "sync"))
+ # =====================================================================================
+ writer.release()
+ # =====================================================================================
+
cv2.destroyAllWindows()