![見出し画像](https://assets.st-note.com/production/uploads/images/44823986/rectangle_large_type_2_5ecdcab0def8e7b26a5c8761595e09bb.png?width=1200)
OpenCV4Python08:OpenCV(numpy.ndarray)とTensorFlowで画像分類
【0】はじめに
今回はOpenCVで読み込んだ画像データをTensorFlowに渡して簡単な画像分類をしてみる。
【1】numpy.ndarrayとtf.Tensorオブジェクト互換性
OpenCVで読み込んだ画像データの実態は「numpy.ndarrayオブジェクト」である。
これに対しTensorFlowは主に「tf.Tensorオブジェクト」を使ってディープラーニングの演算を行う。
■「tf.Tensorオブジェクト」は「numpy.ndarray」と互換性がある
具体的には、
・numpy.ndarrayをTensorflowの関数に渡す
→ 内部で自動的に「numpy.ndarray→tf.Tensorオブジェクト」に変換。
・tf.TensorオブジェクトをNumPyの関数に渡す
→ 内部で自動的に「tfTensorオブジェクト→numpy.ndarray」に変換。
【2】TFHubにある学習済みMNISTモデルを使う(Kerasを使用しない場合)
「ニューラルネットワークを定義して、学習させて、モデルをつくって…」というのは面倒くさいので「TensorFlow HubにあるMNISTの学習済みモデル」を使用する。
■モデルをロードする(Kerasを使わない場合)
Kerasを使わなず、TensorFlow単独でやる場合は「tensorflow_hub.load()」で読み込めばいい。
【例1】
import tensorflow_hub as hub
# モデルのロード
model = hub.load('https://tfhub.dev/tensorflow/tfgan/eval/mnist/logits/1')
あとはOpenCVで読み込んだ画像データを、このモデルが受け付ける入力データ構造にして渡せばいい。
※モデルが受け付ける入力データ構造について
TensorFlowHubの各モデルのページに説明があればいいのだが、何も書いていない場合もある(今回のMNISTのモデルも特に記述がない)。そういった時には、以下のような感じでsaved_model_cliコマンドをつかって情報を取得してみる。
import tensorflow_hub as hub
# モデルのロード
model = hub.load('https://tfhub.dev/tensorflow/tfgan/eval/mnist/logits/1')
# モデルのダウンロードされている場所(一時的に保存されている場所)を確認
print(hub.resolve('https://tfhub.dev/tensorflow/tfgan/eval/mnist/logits/1'))
出力例:
/tmp/tfhub_modules/977b25dcd7e9d44e13a42af999d5a3f512493960
出力されるパスをコピーして、以下のようにコマンドプロンプトを実行する
$ saved_model_cli show --all --dir /tmp/tfhub_modules/977b25dcd7e9d44e13a42af999d5a3f512493960
実行例:
ということでこのモデルが受け付ける入力データ構造は「(None,28,28,1):n枚x28x28x1」で、データの型は「tf.float32」ということが分かる。
【3】OpenCVで画像を読み込んでデータ形状を合わせてモデルに画像分類させる
今回は以下手書きで書いた数字の2(画像サイズは28x28)を使う。
【例2】:画像の読み込みと前処理(白黒反転)
import cv2 as cv
import numpy as np
... ...
img = cv.imread('2.png',cv.IMREAD_GRAYSCALE)
img = cv.bitwise_not(img) #ピクセル値の反転
MNISTの画像はグレースケール画像。さらに背景黒、数字部分が白なので読み込んだら白黒反転させる。白黒反転させるには「cv.bitwise_not()」でビット反転させればよい。
次に「numpy.ndarray(画像データ)」を受け付ける入力データ構造に変換する。
形状は(None,28,28,1)なのでreshapeで変換する。
データは型がtf.float32なので「ピクセルの値は0~1.0の範囲をとる32ビット浮動小数点データ」にする必要がある。
【例3】:「numpy.ndarray(画像データ)」の変換
# スケール変換 ピクセル値を0~1.0の範囲にする
x = img/255.0
predict_x = x.reshape([1,28,28,1]) #予測用にndarrayの形状変換
print(predict_x.shape)
# (1, 28, 28, 1)
# 32ビット浮動小数点にキャスト
predict_x = predict_x.astype(np.float32)
「numpy.ndarrayのキャスト」は「numpy.ndarray.astype」でできる。
これでMNISTのモデルに渡す画像データもできたので、あとはモデルに投げ込んで画像分類させればよい。
【例4】:モデルに画像データを渡して分類させる
result = model(predict_x)
print(result)
print(result.numpy().argmax())
実行結果例:numpy.ndarray[2]の値が一番高い→数字の「2」と判定
<tf.Tensor: shape=(1, 10), dtype=float32, numpy=
array([[-0.5670906 , 1.8182844 , 7.146054 , -0.41182184, -2.2591584 ,
-3.935476 , -3.7045004 , -1.1462402 , 4.241019 , -0.6560709 ]],
dtype=float32)>
2
【全体コード】:Kerasなし版
import tensorflow_hub as hub
import cv2 as cv
import numpy as np
# TFHubからモデルをロード
model = hub.load('https://tfhub.dev/tensorflow/tfgan/eval/mnist/logits/1')
# 入力画像をロード(グレイスケール画像で読み込み)
img = cv.imread('2.png',cv.IMREAD_GRAYSCALE)
# 入力データ用に画像データに前処理実施
img = cv.bitwise_not(img) # ピクセル値の反転
x = img/255.0 # スケール変換
predict_x = x.reshape([1,28,28,1]) # 形状変換
predict_x = predict_x.astype(np.float32) # キャスト
#print(predict_x.shape)
# モデルに渡して画像分類させる
result = model(predict_x)
print(result)
print(result.numpy().argmax())
※Kerasなしの場合、画像分類の結果は「tf.Tensorオブジェクト」として返ってくる。「argmax()」を使いたいので「tf.Tensor.numpy()」で「numpy.ndarray」を取得している。
【4】Kerasを使う場合のTFHubモデルのロードの仕方
Kerasを使う場合は、「hub.KerasLayer()」でモデルをロードする。
【例5】:Kerasを使う場合のTFHubモデルのロード
import tensorflow_hub as hub
mylayer = hub.KerasLayer('https://tfhub.dev/tensorflow/tfgan/eval/mnist/logits/1')
後はtf.keras.Sequetial()でレイヤとして積んでビルドすればよい。
import tensorflow as tf
mymodel = tf.keras.Sequential([mylayer,])
# モデルのビルド、入力形状はここで指定
mymodel.build((None,)+(28,28)+(1,))
print(mymodel.get_config())
print('-----')
print(mymodel.summary())
出力結果:入力データの構造とデータ型、Sequentialモデルの構造確認
{'layers': [{'class_name': 'InputLayer',
'config': {'batch_input_shape': (None, 28, 28, 1),
'dtype': 'float32',
'name': 'keras_layer_input',
'ragged': False,
'sparse': False}},
{'class_name': 'KerasLayer',
'config': {'dtype': 'float32',
'handle': 'https://tfhub.dev/tensorflow/tfgan/eval/mnist/logits/1',
'name': 'keras_layer',
'trainable': False}}],
'name': 'sequential'}
-----
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
keras_layer (KerasLayer) (None, 10) 0
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
_________________________________________________________________
あとはこのKerasで作った「mymodel」に対し、OpenCVで読み込んだ画像を入力受付できるようにすればよい。
【全体コード】:Kerasあり版
import tensorflow_hub as hub
import tensorflow as tf
import cv2 as cv
import numpy as np
# Kerasを使う場合のTFHubモデルのロード
mylayer = hub.KerasLayer('https://tfhub.dev/tensorflow/tfgan/eval/mnist/logits/1')
# Sequentialモデルの構築
mymodel = tf.keras.Sequential([mylayer,])
mymodel.build((None,)+(28,28)+(1,))
#(None, 28, 28, 1)のバッチインプットサイズを設定している n枚の28x28x1チャネル
#print(mymodel.get_config())
#print(mymodel.summary())
# 入力画像をロード(グレイスケール画像で読み込み)
img = cv.imread('2.png',cv.IMREAD_GRAYSCALE)
# 入力データ用に画像データに前処理実施
img = cv.bitwise_not(img) # ピクセル値の反転
x = img/255.0 # スケール変換
predict_x = x.reshape([1,28,28,1]) # 形状変換
predict_x = predict_x.astype(np.float32) # キャスト
#print(predict_x.shape)
result = mymodel.predict(predict_x)
print(result)
print(result.argmax())
実行結果:array[2]の値が一番高い→数字の「2」と判定
array([[-0.5670906 , 1.8182844 , 7.146054 , -0.41182184, -2.2591584 ,
-3.935476 , -3.7045004 , -1.1462402 , 4.241019 , -0.6560709 ]],
dtype=float32)
2
▲Kerasの場合は、分類結果は「arrayオブジェクト」として返ってくるのでsのまま「argmax()」を使える。
■補足:TensorFlowの画像読み込みライブラリ
TensorFlow自体にも画像を読み込むライブラリはある。(tf.image.decode_image、tf.image.decode_jpeg、tf.image.decode_png等々)。
今回はあくまでOpenCVーTensorFlow間の連携、となった場合の一例としてあげたもの。特に制約がないならTensorFlowが搭載している上記の画像読み込みライブラリを使えば十分。
いいなと思ったら応援しよう!
![fz5050](https://assets.st-note.com/production/uploads/images/33869673/profile_56684e35407d563dbeb38e0a193976a0.png?width=600&crop=1:1,smart)