私的AI研究会 > OpenVINO11
Open Model Zooと呼ばれる学習済みモデルファイルのアーカイブから Model Optimizer で学習済みモデルの変換を行いディープラーニング推論を実習する。
Caffe の学習済み軽量化モデル「squeezenet1.1」を使って、画像分類をおこなう。
引用したサイト → https://jellyware.jp/aicorex/contents/out_c11_model_optimizer.html
~/work$ python3 $INTEL_OPENVINO_DIR/deployment_tools/tools/model_downloader/downloader.py --name squeezenet1.1
~/work$ python3 $INTEL_OPENVINO_DIR/deployment_tools/tools/model_downloader/converter.py --squeezenet1.1 --precisions FP16
~/work$ ls ./public/squeezenet1.1/FP16/* squeezenet1.1.bin squeezenet1.1.xml
2つのファイルを Raspberry Pi の ~/worlspace/public/squeezenet1.1/FP16/ ディレクトリにコピーする。
wget https://raw.githubusercontent.com/HoldenCaulfieldRye/caffe/master/data/ilsvrc12/synset_words.txt synset_words.txt を Raspberry Pi の ~/worlspace/ にコピーする。日本語訳を synset_words_jp.txt として同じ場所に作成しておく。
vi classification.py # -*- coding: utf-8 -*- import numpy as np labels = np.loadtxt('synset_words.txt', dtype='str', delimiter='\n') print(labels[282]) labels = np.loadtxt('synset_words_jp.txt', dtype='str', delimiter='\n') print(labels[282])
pi@raspberrypi:~/workspace $ python3 classification.py n02123159 tiger cat n02123159 トラ猫
Input Original model Image, name - data, shape - 1,3,227,227, format is B,C,H,W where: B - batch size C - channel H - height W - width Channel order is BGR. Mean values - [104, 117, 123] Converted model Image, name - data, shape - 1,3,227,227, format is B,C,H,W where: B - batch size C - channel H - height W - width Channel order is BGR.
Output Original model Object classifier according to ImageNet classes, name - prob, shape - 1,1000, output data format is B,C where: B - batch size C - Predicted probabilities for each class in [0, 1] range Converted model Object classifier according to ImageNet classes, name - prob, shape - 1,1000, output data format is B,C where: B - batch size C - Predicted probabilities for each class in [0, 1] range
./public/squeezenet1.1/FP16/squeezenet1.1.xml~ ./public/squeezenet1.1/FP16/squeezenet1.1.bin~
synset_words.txt~ synset_words_jp.txt (機械翻訳)~
※ 以下の操作は Raspberry Pi 上で行う。
vi classification1.py # -*- coding: utf-8 -*- import cv2 import numpy as np # モジュール読み込み from openvino.inference_engine import IECore # モデルの読み込み ie = IECore() net = ie.read_network(model='public/squeezenet1.1/FP16/squeezenet1.1.xml', weights='public/squeezenet1.1/FP16/squeezenet1.1.bin') exec_net = ie.load_network(network=net, device_name="MYRIAD") # 入力データと出力データのキーを取得 input_blob = net.input_info['data'].name out_blob = next(iter(net.outputs)) print('input blob: name="{}", output blob: name="{}"'.format(input_blob, out_blob)) # ラベル読み込み labels = np.loadtxt('synset_words.txt', dtype='str', delimiter='\n') # 入力画像読み込み img = cv2.imread('image/cat.jpg') # 入力データフォーマットへ変換 img = cv2.resize(img, (227, 227)) # HeightとWidth変更 img = img.transpose((2, 0, 1)) # HWC > CHW img = np.expand_dims(img, axis=0) # CHW > BCHW # 推論実行 out = exec_net.infer(inputs={input_blob : img}) # 出力から必要なデータのみ取り出し out = out[out_blob] # 不要な次元を削減 out = np.squeeze(out) # 全て表示 print(out) # 出力値が最大のインデックスを得る index_max = np.argmax(out) print(index_max) # 最大の出力値を表示 print(out[index_max]) # 最大の出力値であるラベルを表示 print(labels[index_max])
pi@raspberrypi:~/workspace $ python3 classification1.py input blob: name="data", output blob: name="prob" [0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 5.8174133e-04 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 9.4294548e-05 2.1016598e-04 0.0000000e+00 0.0000000e+00 : : 0.0000000e+00 0.0000000e+00 0.0000000e+00 2.4580956e-04 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 1.0433197e-03] 283 0.47705078 n02123394 Persian cat
vi classification2.py # -*- coding: utf-8 -*- import cv2 import numpy as np # モジュール読み込み from openvino.inference_engine import IECore # モデルの読み込み ie = IECore() net = ie.read_network(model='public/squeezenet1.1/FP16/squeezenet1.1.xml', weights='public/squeezenet1.1/FP16/squeezenet1.1.bin') exec_net = ie.load_network(network=net, device_name="MYRIAD") # 入力データと出力データのキーを取得 input_blob = net.input_info['data'].name out_blob = next(iter(net.outputs)) print('input blob: name="{}", output blob: name="{}"'.format(input_blob, out_blob)) # ラベル読み込み labels = np.loadtxt('synset_words.txt', dtype='str', delimiter='\n') labels_jp = np.loadtxt('synset_words_jp.txt', dtype='str', delimiter='\n') # 入力画像読み込み img = cv2.imread('image/cat.jpg') # 入力データフォーマットへ変換 img = cv2.resize(img, (227, 227)) # HeightとWidth変更 img = img.transpose((2, 0, 1)) # HWC > CHW img = np.expand_dims(img, axis=0) # CHW > BCHW # 推論実行 out = exec_net.infer(inputs={input_blob : img}) # 出力から必要なデータのみ取り出し out = out[out_blob] # 不要な次元を削減 out = np.squeeze(out) # 降順でベスト5のインデックスを抽出 index_order = np.argsort(out)[::-1][:5] # ベスト5のインデックスについてラベルと値を表示 for index in index_order: # print(labels[index], out[index]) print(labels[index],' ',labels_jp[index], out[index])
pi@raspberrypi:~/workspace $ python3 classification2.py input blob: name="data", output blob: name="prob" label="n02123394 Persian cat", label_jp="n02123394 ペルシャ猫", probability="0.47705078125" label="n02123045 tabby, tabby cat", label_jp="n02123045 ぶち、ぶち猫", probability="0.255615234375" label="n02127052 lynx, catamount", label_jp="n02127052 オオヤマネコ、カタマウント", probability="0.037994384765625" label="n04074963 remote control, remote", label_jp="n04074963 リモコン", probability="0.034576416015625" label="n03793489 mouse, computer mouse", label_jp="n03793489 マウス、コンピューターマウス", probability="0.03350830078125"
label | out | 分類結果(日本語) | 確率値(百分率) |
Persian cat | 0.47705078 | ペルシャネコ | 47.7% |
tabby, tabby cat | 0.25561523 | トラネコ | 25.6% |
lynx, catamount | 0.037994385 | オオヤマネコ | 3.8% |
remote control, remote | 0.034576416 | リモコン | 3.6% |
mouse, computer mouse | 0.0335083 | PCのマウス | 3.4% |
USBカメラを使ったリアルタイム推論
vi classification3.py # -*- coding: utf-8 -*- import cv2 import numpy as np # モジュール読み込み from openvino.inference_engine import IECore # モデルの読み込み ie = IECore() net = ie.read_network(model='public/squeezenet1.1/FP16/squeezenet1.1.xml', weights='public/squeezenet1.1/FP16/squeezenet1.1.bin') exec_net = ie.load_network(network=net, device_name="MYRIAD") # 入力データと出力データのキーを取得 input_blob = net.input_info['data'].name out_blob = next(iter(net.outputs)) print('input blob: name="{}", output blob: name="{}"'.format(input_blob, out_blob)) # ラベル読み込み labels = np.loadtxt('synset_words.txt', dtype='str', delimiter='\n') # カメラ準備 cap = cv2.VideoCapture(0) camera_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) camera_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # メインループ while True: # キーが押されたら終了 key = cv2.waitKey(1) if key != -1: break # カメラ画像読み込み ret, frame = cap.read() # 入力データフォーマットへ変換 img = cv2.resize(frame, (227, 227)) # HeightとWidth変更 img = img.transpose((2, 0, 1)) # HWC > CHW img = np.expand_dims(img, axis=0) # CHW > BCHW # 推論実行 out = exec_net.infer(inputs={input_blob : img}) # 出力から必要なデータのみ取り出し out = out[out_blob] # 不要な次元を削減 out = np.squeeze(out) # 降順でベスト3のインデックスを抽出 index_order = np.argsort(out)[::-1][:3] # テキスト表示位置y座標初期値 text_y = camera_height - 80 # ベスト3のインデックスについてラベルと値を表示 for index in index_order: # 左側の文字列10文字は取り除いてラベルを取得 label = labels[index] label = label[10:] # outを百分率にして小数点2桁以下は丸めて、文字列化 value = out[index] * 100 value = round(value, 1) value = str(value) + '% ' # 文字の表示 cv2.putText(frame, value + label, (10, text_y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (240, 180, 0), 2) # テキスト表示位置y座標増加 text_y = text_y + 30 # 画像表示 cv2.imshow('frame', frame) # 終了処理 cap.release() cv2.destroyAllWindows()
input blob: name="data", output blob: name="prob"
vi classification4.py # -*- coding: utf-8 -*- import cv2 import numpy as np import myfunction # モジュール読み込み from openvino.inference_engine import IECore # モデルの読み込み ie = IECore() net = ie.read_network(model='public/squeezenet1.1/FP16/squeezenet1.1.xml', weights='public/squeezenet1.1/FP16/squeezenet1.1.bin') exec_net = ie.load_network(network=net, device_name="MYRIAD") # 入力データと出力データのキーを取得 input_blob = net.input_info['data'].name out_blob = next(iter(net.outputs)) print('input blob: name="{}", output blob: name="{}"'.format(input_blob, out_blob)) # ラベル読み込み labels = np.loadtxt('synset_words_jp.txt', dtype='str', delimiter='\n') # 日本語フォント指定 fontPIL = 'NotoSansCJK-Bold.ttc' # カメラ準備 cap = cv2.VideoCapture(0) camera_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) camera_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # メインループ while True: # キーが押されたら終了 key = cv2.waitKey(1) if key != -1: break # カメラ画像読み込み ret, frame = cap.read() # 入力データフォーマットへ変換 img = cv2.resize(frame, (227, 227)) # HeightとWidth変更 img = img.transpose((2, 0, 1)) # HWC > CHW img = np.expand_dims(img, axis=0) # CHW > BCHW # 推論実行 out = exec_net.infer(inputs={input_blob : img}) # 出力から必要なデータのみ取り出し out = out[out_blob] # 不要な次元を削減 out = np.squeeze(out) # 降順でベスト3のインデックスを抽出 index_order = np.argsort(out)[::-1][:3] # テキスト表示位置y座標初期値 text_y = camera_height - 80 # ベスト3のインデックスについてラベルと値を表示 for index in index_order: # 左側の文字列10文字は取り除いてラベルを取得 label = labels[index] label = label[10:] # outを百分率にして小数点2桁以下は丸めて、文字列化 value = out[index] * 100 value = round(value, 1) value = str(value) + '% ' # 文字の表示 # cv2.putText(frame, value + label, (10, text_y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (240, 180, 0), 2) myfunction.cv2_putText(img = frame, text = value + label, org = (10, text_y), fontFace = fontPIL, fontScale = 18, color = (240, 180, 0), mode = 0) # テキスト表示位置y座標増加 text_y = text_y + 30 # 画像表示 cv2.imshow('frame', frame) # 終了処理 cap.release() cv2.destroyAllWindows()
vi myfunction.py # -*- coding: utf-8 -*- import numpy as np import cv2 from PIL import Image, ImageDraw, ImageFont def cv2_putText(img, text, org, fontFace, fontScale, color, mode=0): # OpenCVで日本語フォントを描写する を関数化する を汎用的にする # https://qiita.com/mo256man/items/b6e17b5a66d1ea13b5e3 # # cv2.putText()にないオリジナル引数「mode」 orgで指定した座標の基準 # 0(デフォ)=cv2.putText()と同じく左下 1=左上 2=中央 # テキスト描写域を取得 fontPIL = ImageFont.truetype(font = fontFace, size = fontScale) dummy_draw = ImageDraw.Draw(Image.new("RGB", (0,0))) text_w, text_h = dummy_draw.textsize(text, font=fontPIL) text_b = int(0.1 * text_h) # バグにより下にはみ出る分の対策 # テキスト描写域の左上座標を取得(元画像の左上を原点とする) x, y = org offset_x = [0, 0, text_w//2] offset_y = [text_h, 0, (text_h+text_b)//2] x0 = x - offset_x[mode] y0 = y - offset_y[mode] img_h, img_w = img.shape[:2] # 画面外なら何もしない if not ((-text_w < x0 < img_w) and (-text_b-text_h < y0 < img_h)) : print ("out of bounds") return img # テキスト描写域の中で元画像がある領域の左上と右下(元画像の左上を原点とする) x1, y1 = max(x0, 0), max(y0, 0) x2, y2 = min(x0+text_w, img_w), min(y0+text_h+text_b, img_h) # テキスト描写域と同サイズの黒画像を作り、それの全部もしくは一部に元画像を貼る text_area = np.full((text_h+text_b,text_w,3), (0,0,0), dtype=np.uint8) text_area[y1-y0:y2-y0, x1-x0:x2-x0] = img[y1:y2, x1:x2] # それをPIL化し、フォントを指定してテキストを描写する(色変換なし) imgPIL = Image.fromarray(text_area) draw = ImageDraw.Draw(imgPIL) draw.text(xy = (0, 0), text = text, fill = color, font = fontPIL) # PIL画像をOpenCV画像に戻す(色変換なし) text_area = np.array(imgPIL, dtype = np.uint8) # 元画像の該当エリアを、文字が描写されたものに更新する img[y1:y2, x1:x2] = text_area[y1-y0:y2-y0, x1-x0:x2-x0] return img def main(): img = np.full((200,400,3), (160,160,160), dtype=np.uint8) imgH, imgW = img.shape[:2] fontPIL = "NotoSerifCJK-Bold.ttc" # フォント指定 size = 30 text = "日本語も\n可能なり" color = (255,0,0) positions = [(-imgW,-imgH), # これは画像外にあり描写されない (0,0), (0,imgH//2), (0,imgH), (imgW//2,0), (imgW//2,imgH//2), (imgW//2,imgH), (imgW,0), (imgW,imgH//2), (imgW,imgH)] for pos in positions: img = cv2.circle(img, pos, 60, (0,0,255), 3) img = cv2_putText(img = img, text = text, org = pos, # 円の中心と同じ座標を指定した fontFace = fontPIL, fontScale = size, color = color, mode = 2) # 今指定した座標は文字描写域の中心だぞ cv2.imshow("", img) cv2.waitKey(0) cv2.destroyAllWindows() if __name__ == "__main__": main()
classification3.py:15: DeprecationWarning: 'inputs' property of IENetwork class is deprecated. To access DataPtrs user need to use 'input_data' property of InputInfoPtr objects which can be accessed by 'input_info' property.
Classification3.py:15:DeprecationWarning:IENetworkクラスの 'inputs'プロパティは非推奨になりました。 DataPtrsにアクセスするには、ユーザーは、「input_info」プロパティでアクセスできるInputInfoPtrオブジェクトの「input_data」プロパティを使用する必要があります。
# 入力データと出力データのキーを取得 input_blob = next(iter(net.inputs))
# 入力データと出力データのキーを取得 input_blob = net.input_info['data'].name