環境は Raspberry Pi 3B+ です。もちろん、ここでのtensorflow のインストールはRaspberry Pi 環境である必要はありません。ARMアーキテクチャである必要があるだけです。つまり、現在市場に出ているほとんどの組み込みシステムは、この削減された命令セットを使用しています。
コンピュータ側の検出については、SSD(Single Shot MultiBox Detector)シリーズ:コンピュータビジョンのSSD改良版(スムーズなL1ノルムロスとフォーカスロス)「4」をご参照ください。
オペレーティング システムは Linux に属します。主に自分のチップ アーキテクチャがどの種類に属するかを確認するために、自分のハードウェア環境に慣れましょう。
1. Linux ビューのチップ アーキテクチャ
前述したように、ARM アーキテクチャにインストールする必要があり、命令を通じて現在のチップ アーキテクチャを確認する方法。
これらはすべて Linux システム内にあるため、入力されるコマンドは同じであり、次の 3 つのコマンドのいずれかで、それらが属するアーキテクチャを表示できます。
Arch
uname -a
ファイル /bin/bash
X86 の図 (ここに WSL をインストールしました):
これは、X86 の 64 ビット拡張であるX86_64です。これはもともと AMD によって設計されたため、「AMD64」とも呼ばれ、後に Intel によって採用されたため、 「Intel64」
ARM マップと呼ばれます。
ここでのaarch64 は、ARM アーキテクチャに属するARMv8-A アーキテクチャで導入された 64 ビット命令セットです。
Linux システムのレジもありますが、使用するリソースが少ないため、ローエンドのチップを使用するだけで済みます。一般的には「Pentium」で、返されるのは i686 です。
2. 設置環境
tensorflowをインストールします。もちろん、自分の状況に応じてこのバージョンを選択してください。最新バージョンはtensorflow-2.4.0-cp35-none-linux_armv6l.whl
# https://github.com/lhelontra/tensorflow-on-arm/releases
pip install tensorflow-1.8.0-cp27-none-linux_armv7l.whl
2.1、SSDモバイル端末モデル
#http://download.tensorflow.org/models/object_detection/ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz
tar -xzvf ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz 。
2.2. OpenCV ビジョンライブラリのインストール
pip インストール opencv-python
2.3、デモの実行、コマンドライン操作
python opencv_camera.py
# または、.py ファイルが Jupyter にロードされている場合は、その内容がコピーされます。クリックして
%load opencv_camera.pyを実行します
3. 物体認識
次に、実際の操作に移ります。カメラにオブジェクトを検出させ、オブジェクト カテゴリをマークします。ここでは CSI カメラを使用します。USB インターフェイスの場合、コードにはコメントも含まれています。
3.1. 関連ライブラリのインポート
関連するライブラリをインポートすると、 object_detectionなどのソースコードが末尾にアドレスが記載されているので、テストしたい場合はクローンして遊んでみてください。
import numpy as np
import cv2
import os,time
import tensorflow as tf
from object_detection.utils import label_map_util
from object_detection.utils import visualization_utils as vis_utils
import ipywidgets.widgets as widgets
from image_fun import bgr8_to_jpeg
3.2. カメラの初期化
カメラは初期化されており、USB インターフェースのカメラも用意されていますが、無人車両に搭載されているカメラを CSI インターフェースとして借用します。カメラが USB の場合は、ls /dev/video*コマンド、 camera = USBCamera(capture_device=1)
を使用してデバイス インデックスを表示できます。
#from jetcam.usb_camera import USBCamera
from jetcam.csi_camera import CSICamera
from jetcam.utils import bgr8_to_jpeg
#camera = USBCamera(width=320, height=240, capture_fps=30)
camera = CSICamera(width=320, height=240, capture_fps=30)
#将相机设置为running = True,为新帧附加回调
camera.running = True
3.3. JetCam のインストール
もちろん、ここで JetCam がインストールされていない場合は、先にインストールしても問題ありません。JetCam は、NVIDIA Jetson 用の使いやすい Python カメラ インターフェイスです。Jetcam をインストールします。
git clone https://github.com/NVIDIA-AI-IOT/jetcam
cd Jetcam
sudo python3 setup.py install
3.4、画像表示コンポーネント
カメラが初期化された後、Jupyter で新しい画像コンポーネントを作成し、カメラによってキャプチャされたデータを更新します。
image_widget = widgets.Image(format='jpg', width=320, height=240)
display(image_widget)
image_widget.value = bgr8_to_jpeg(camera.value)
実行後、ここに写真が表示されますが、最初の静止フレームです 継続的に更新したい場合は、次の 2 つの更新方法を使用します。
3.5. モデルの初期化
軽量 SSD モデルとタグをロードする
MODEL_NAME = 'ssdlite_mobilenet_v2_coco_2018_05_09'
PATH_TO_CKPT = MODEL_NAME + '/frozen_inference_graph.pb'
PATH_TO_LABELS = os.path.join('data', 'mscoco_label_map.pbtxt')
NUM_CLASSES = 90
IMAGE_SIZE = (12, 8)
fileAlreadyExists = os.path.isfile(PATH_TO_CKPT)
if not fileAlreadyExists:
print('Model does not exsist !')
exit
3.6. 負荷グラフ
これは上でロードされた計算グラフです。これは凍結されたシリアル化グラフです。つまり、トレーニングできません。つまり、予測に使用され、事前トレーニングされたモデルです。
また、ラベルマップも読み込まれており、これらのラベルを分類してidと名前に対応した辞書型を作成します。Graphの使い方は後ほど詳しく紹介します!
print('Loading...')
detection_graph = tf.Graph()
with detection_graph.as_default(): #语句下定义属于计算图detection_graph的张量和操作
od_graph_def = tf.compat.v1.GraphDef()
with tf.io.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
serialized_graph = fid.read()
od_graph_def.ParseFromString(serialized_graph)
tf.import_graph_def(od_graph_def, name='')
label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True)
category_index = label_map_util.create_category_index(categories)
print('Finish Load Graph..')
print(len(category_index),category_index)
print(category_index[1]['name'])#person
ここでのcategory_index は、以下に示すように、{1: {'id': 1, 'name': 'person'}, 2: {'id': 2, 'name': 'bicycle'},...} に似た 80 カテゴリの辞書 です 。
3.7. カメラの検出
リアルタイム検出のためにカメラを呼び出す方法は 2 つあり、1 つはカメラの値を周期的に読み取る、つまり無限ループで処理する方法、もう 1 つはコールバック関数を使用して、カメラの値が変化したときに画像コンポーネントの値を自動的に更新する方法です。
3.7.1、無限ループ
# Main
t_start = time.time()
fps = 0
with detection_graph.as_default():
with tf.compat.v1.Session(graph=detection_graph) as sess:
while True:
frame = camera.value
# ret, frame = cap.read()
# frame = cv2.flip(frame, -1) # Flip camera vertically
# frame = cv2.resize(frame,(320,240))
##############
image_np_expanded = np.expand_dims(frame, axis=0)
image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
num_detections = detection_graph.get_tensor_by_name('num_detections:0')
# print('Running detection..')
(boxes, scores, classes, num) = sess.run(
[detection_boxes, detection_scores, detection_classes, num_detections],
feed_dict={image_tensor: image_np_expanded})
# print('Done. Visualizing..')
vis_utils.visualize_boxes_and_labels_on_image_array(
frame,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
category_index,
use_normalized_coordinates=True,
line_thickness=8)
for i in range(0, 10):
if scores[0][i] >= 0.5:
print(category_index[int(classes[0][i])]['name'])
##############
fps = fps + 1
mfps = fps / (time.time() - t_start)
cv2.putText(frame, "FPS " + str(int(mfps)), (10,10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 2)
image_widget.value = bgr8_to_jpeg(frame)
3.7.2. コールバック関数
更新関数を作成し、カメラの起動時に呼び出すと、カメラの値が変更されたときに更新関数がそれを処理します。基本的にはこの方法がおすすめです!
detection_graph.as_default()
sess = tf.compat.v1.Session(graph=detection_graph)
t_start = time.time()
fps = 0
def update_image(change):
global fps
global sess
frame = change['new']
image_np_expanded = np.expand_dims(frame, axis=0)
image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
num_detections = detection_graph.get_tensor_by_name('num_detections:0')
(boxes, scores, classes, num) = sess.run(
[detection_boxes, detection_scores, detection_classes, num_detections],
feed_dict={image_tensor: image_np_expanded})
# 锚框与标签
vis_utils.visualize_boxes_and_labels_on_image_array(
frame,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
category_index,
use_normalized_coordinates=True,
line_thickness=8)
'''
for i in range(0, 10):
if scores[0][i] >= 0.5:
print(category_index[int(classes[0][i])]['name'])
'''
fps = fps + 1
mfps = fps / (time.time() - t_start)
cv2.putText(frame, "FPS " + str(int(mfps)), (10,10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 2)
image_widget.value = bgr8_to_jpeg(frame)
#摄像头更新函数
camera.observe(update_image, names='value')
#停止更新,单纯的unobserve不能停止摄像头的继续运行,我加了一个stop停止
#camera.unobserve(update_image, names='value')
#camera.stop()
レンダリングは次のとおりです。
もちろん、認識が間違っている場合もあります。たとえば、下の図では、後ろにあるタバコが本として認識されています。これも正常であり、データセットに関連しています。結局のところ、本の表示形状のように見えます。
4、tf.Graph()
ここでは、上記のコードに表示されるtf.Graph関数の関連紹介を示します。これは Tensorflow の計算グラフです。内部にはテンソル用の特別な計算モジュールがあります。通常は G(V,E) として表される複数の計算グラフを作成することもできます。ここで、G はグラフを表し、V はグラフ G の頂点のセット、E はグラフGのエッジのセットです。このような構築は並列処理できるため、パフォーマンスの向上に非常に役立ちます。
Graphは本質的にコレクション配列 (Tensor tensor) と Operation 演算のデータ構造であり、Python 以外の環境でも実行でき、このデータ構造を説明するだけで十分です。したがって、Graph を構築することで、計算プロセスを簡単に表現および最適化することができ、深層学習モデルの学習と推論を実現できます。
グラフが構築されたら、TensorFlow の Session オブジェクトを使用してグラフを実行できます。実行中、セッションは、グラフで定義されたノードとエッジ間の関係に従って、あるノードから別のノードにデータを転送し、各ノードで操作を実行して、最終的に必要な計算結果を取得します。
例を見てみましょう:
g1 = tf.Graph()
g2 = tf.Graph()
with g1.as_default():
a = tf.constant([1,2])
b = tf.constant([3,4])
c = tf.constant([5,6])
result1 = a + b + c
with g2.as_default():
d = tf.constant([11,22,33])
e = tf.constant([33,44,55])
result2 = d * e
2 つの計算グラフを定義した後、それらの下でそれぞれ計算し、セッションの実行を通じて計算結果を実行できます。
with tf.compat.v1.Session(graph=g1) as sess:
print(a)#Tensor("Const_12:0", shape=(2,), dtype=int32)
print(result1)#Tensor("add_8:0", shape=(2,), dtype=int32)
print(sess.run(result1))#[ 9 12]
g1 の計算グラフの下に、計算ノードと計算ノードが表示されます。ここでは加算演算が行われ、次に g2 の乗算演算が表示されます。
with tf.compat.v1.Session(graph=g2) as sess:
print(d)#Tensor("Const_12:0", shape=(3,), dtype=int32)
print(result2)#Tensor("mul:0", shape=(3,), dtype=int32)
print(sess.run(result2))#[ 363 968 1815]
また、これらのテンソル計算グラフがどれに属しているかを検証することもできます。
print(a.graph is g1)#True
print(a.graph is g2)#False
print(d.graph is g1)#False
print(d.graph is g2)#True
4.1、run() でパラメータを取得します
もちろん、この実行関数のフェッチパラメーター値は、上記の合計と積の演算に加えて、単一の要素、リスト、またはタプルにすることもできます。
import tensorflow as tf
sess = tf.compat.v1.Session()
a = tf.constant([10, 20])
b = tf.constant([1.0, 2.0])
v = sess.run(a)
print(type(v),v)#<class 'numpy.ndarray'> [10 20]
v = sess.run([a, b])
print(v)#[array([10, 20], dtype=int32), array([1., 2.], dtype=float32)]
もちろん、辞書にすることもでき、その中の a と b の値は numpy 配列に変換されます
import collections
MyData = collections.namedtuple('MyData', ['a', 'b'])
v = sess.run({'k1': MyData(a, b), 'k2': [b, a]})
print(v)
#{'k1': MyData(a=array([10, 20], dtype=int32), b=array([1., 2.], dtype=float32)), 'k2': [array([1., 2.], dtype=float32), array([10, 20], dtype=int32)]}
4.2、run() の feed_dict パラメータ
この feed_dict パラメータの入力値は辞書型である必要があります。
v = sess.run([a, b],feed_dict={b:[33,44]})
print(v)
#[array([10, 20], dtype=int32), array([33., 44.], dtype=float32)]
より詳細なソースコード: https://github.com/yihangzhao/SSDMobile