コンピュータビジョンに基づくジェスチャ認識技術

江湖の人々から蔡溝と呼ばれる無名の大学生
原作者: Jacky Li
電子メール: [email protected]

完成時期:2023.5.2
最終編集日:2023.5.2

手話は、主に難聴者または聴覚障害者によって使用されるコミュニケーションの形式です。このジェスチャーベースの言語により、人々は聴覚の問題による障壁を克服して、考えやアイデアを簡単に表現できるようになります。

この便利なコミュニケーション形式の大きな問題は、世界中の大多数の人々が言語の知識を欠いていることです。他の言語と同様に、手話の学習には多くの時間と労力がかかり、イライラするばかりで、多くの人が学ぶことができません。

ただし、この問題に対する明白な解決策は、機械学習と画像検出の分野にすでに存在しています。手話サインを自動的に分類する予測モデリング技術を実装すると、Zoom ミーティングなどの仮想ミーティングのリアルタイムのキャプションを作成するために使用できます。

これにより、音声ベースの字幕と同期され、聴覚障害者向けの双方向オンラインコミュニケーションシステムが構築されるため、聴覚障害者向けのサービスへのアクセスが大幅に増加します。


多くの手話用の大規模なトレーニング データセットは、人気のあるデータ サイエンス リソースである Kaggle で利用できます。このモデルで使用されているものは「Sign Language MNIST」と呼ばれるもので、パブリック ドメインで自由に利用できるデータセットで、J と Z を除く 24 の ASL 文字のそれぞれの約 1000 個の画像のピクセル情報が含まれています。これらの文字はジェスチャー ベースの記号であるためです。

手話MNIST | ハンドジェスチャー認識タスク用の MNIST のKaggleドロップイン代替品https://www.kaggle.com/datasets/datamunge/sign- language-mnist

トレーニング用のデータを準備する最初のステップは、アルゴリズムが読み取れるように、データセット内のすべてのピクセル データを画像に変換して再形成することです。

import matplotlib.pyplot as plt
import seaborn as sns
from keras.models import Sequential
from keras.layers import Dense, Conv2D , MaxPool2D , Flatten , Dropout , BatchNormalization
from keras.preprocessing.image import ImageDataGenerator
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report,confusion_matrix
import pandas as pd

train_df = pd.read_csv("sign_mnist_train.csv")
test_df = pd.read_csv("sign_mnist_test.csv")

y_train = train_df['label']
y_test = test_df['label']
del train_df['label']
del test_df['label']

from sklearn.preprocessing import LabelBinarizer
label_binarizer = LabelBinarizer()
y_train = label_binarizer.fit_transform(y_train)
y_test = label_binarizer.fit_transform(y_test)

x_train = train_df.values
x_test = test_df.values

x_train = x_train / 255
x_test = x_test / 255

x_train = x_train.reshape(-1,28,28,1)
x_test = x_test.reshape(-1,28,28,1)

上記のコードは、モデルが入力ファイルを理解できるように、すべての MNIST トレーニング画像ファイルを再構成することから始まります。それに加えて、LabelBinarizer 変数はデータセット内のクラスを取得してバイナリに変換します。このプロセスにより、モデルのトレーニングが大幅に高速化されます。

次のステップでは、データへの変更をランダムに実装するデータ ジェネレーターを作成し、トレーニング サンプルの数を増やし、さまざまなインスタンスにノイズと変換を追加して画像をよりリアルにします。

datagen = ImageDataGenerator(
        featurewise_center=False,
        samplewise_center=False, 
        featurewise_std_normalization=False,
        samplewise_std_normalization=False,
        zca_whitening=False,
        rotation_range=10,
        zoom_range = 0.1, 
        width_shift_range=0.1,
        height_shift_range=0.1,
        horizontal_flip=False,
        vertical_flip=False)

datagen.fit(x_train)

画像を処理した後、データで使用される情報のすべてのカテゴリ、つまり 24 の異なる画像グループを認識するために CNN モデルをコンパイルする必要がありました。より少ない画像でクラスのバランスをとるために、データの正規化もデータに追加する必要があります。

model = Sequential()
model.add(Conv2D(75 , (3,3) , strides = 1 , padding = 'same' , activation = 'relu' , input_shape = (28,28,1)))
model.add(BatchNormalization())
model.add(MaxPool2D((2,2) , strides = 2 , padding = 'same'))
model.add(Conv2D(50 , (3,3) , strides = 1 , padding = 'same' , activation = 'relu'))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(MaxPool2D((2,2) , strides = 2 , padding = 'same'))
model.add(Conv2D(25 , (3,3) , strides = 1 , padding = 'same' , activation = 'relu'))
model.add(BatchNormalization())
model.add(MaxPool2D((2,2) , strides = 2 , padding = 'same'))
model.add(Flatten())
model.add(Dense(units = 512 , activation = 'relu'))
model.add(Dropout(0.3))
model.add(Dense(units = 24 , activation = 'softmax'))

アルゴリズムは変数 (Conv2D モデルなど) を追加することによって初期化され、24 個の機能に凝縮されることに注意してください。また、CNN がより効率的にデータを処理できるようにするために、バッチ技術も使用しています。

最後に、損失関数と指標を定義し、モデルをデータに適合させます。

model.compile(optimizer = 'adam' , loss = 'categorical_crossentropy' , metrics = ['accuracy'])
model.summary()

history = model.fit(datagen.flow(x_train,y_train, batch_size = 128) ,epochs = 20 , validation_data = (x_test, y_test))

model.save('smnist.h5')

このコードには解凍する必要があることがたくさんあります。セクションに分けて見てみましょう。

ライン1:

model.compile 関数は多くのパラメータを受け入れますが、そのうちの 3 つがコードに示されています。オプティマイザーと損失パラメーターは、次の行の epoch ステートメントと連携して、データの計算方法を段階的に変更することでモデル内のエラーの量を効果的に削減します。

それに加えて、最適化するメトリクスは精度関数であり、これにより、設定されたエポック数の後にモデルが達成可能な最大の精度を持つことが保証されます。

4行目:

ここで実行される関数は、設計されたモデルを、コードの最初のビットで開発された画像データ内のデータと照合します。また、画像検出の精度を向上させるためにモデルが必要とするエポック数または反復数も定義します。モデルにテストの側面を導入するために、検証セットもここで呼び出されます。モデルはこのデータを使用して精度を計算します。

5行目:

コード ビット内のすべてのステートメントの中で、model.save 関数はおそらくこのコードの最も重要な部分です。これにより、モデルの実装時に何時間もの時間を節約できます。

開発されたモデルは、約 95% のトレーニング精度で手話記号を正確に検出および分類します。


現在、2 つの一般的なリアルタイム ビデオ処理ライブラリ、Mediapipe と OpenCV を使用して、Web カメラ入力を取得し、以前に開発したモデルをライブ ビデオ ストリーム上で実行できるようになりました。

まず、プログラムに必要なパッケージをインポートする必要があります。

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 
import tensorflow as tf
import cv2
import mediapipe as mp
from keras.models import load_model
import numpy as np
import time

 

起動時に実行する OS コマンドは、Mediapipe によって使用される Tensorflow ライブラリが不要な警告を発するのを防ぐだけです。これにより、プログラムによって提供される今後の出力がより明確になり、理解しやすくなります。

コードのメインの while ループを開始する前に、まず保存されたモデルや OpenCV カメラの情報などのいくつかの変数を定義する必要があります。

model = load_model('smnist.h5')

mphands = mp.solutions.hands
hands = mphands.Hands()
mp_drawing = mp.solutions.drawing_utils

cap = cv2.VideoCapture(0)
_, frame = cap.read()
h, w, c = frame.shape

analysisframe = ''
letterpred = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y']

ここで設定された各変数は、4 つのカテゴリのいずれかに分類されます。冒頭のクラスは、このペーパーの最初の部分でトレーニングしたモデルに直接関連しています。

コードの 2 番目と 3 番目のセクションでは、Mediapipe と OpenCV の実行と開始に必要な変数を定義します。最後のカテゴリは主に、検出されたフレームを分析し、画像モデルによって提供されるデータを相互参照するための辞書を作成するために使用されます。

プログラムの次の部分は、プログラムの大部分が実行されるメインの while True ループです。

while True:
    _, frame = cap.read()

    k = cv2.waitKey(1)
    if k%256 == 27:
        # ESC pressed
        print("Escape hit, closing...")
        break

    framergb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    result = hands.process(framergb)
    hand_landmarks = result.multi_hand_landmarks
    if hand_landmarks:
        for handLMs in hand_landmarks:
            x_max = 0
            y_max = 0
            x_min = w
            y_min = h
            for lm in handLMs.landmark:
                x, y = int(lm.x * w), int(lm.y * h)
                if x > x_max:
                    x_max = x
                if x < x_min:
                    x_min = x
                if y > y_max:
                    y_max = y
                if y < y_min:
                    y_min = y
            y_min -= 20
            y_max += 20
            x_min -= 20
            x_max += 20
            cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2)
            mp_drawing.draw_landmarks(frame, handLMs, mphands.HAND_CONNECTIONS)
    cv2.imshow("Frame", frame)

cap.release()
cv2.destroyAllWindows()

プログラムのこの部分はカメラから入力を取得し、インポートされた画像処理ライブラリを使用してデバイスの入力をコンピューターに表示します。コードのこの部分は、カメラから一般的な情報を取得し、それを新しいウィンドウに表示することに重点を置いています。ただし、Mediapipe ライブラリを使用すると、指や手のひらなどの手の主要なランドマークを検出し、手の周囲に境界ボックスを作成できます。

境界ボックスの概念は、あらゆる形式の画像分類と分析の重要なコンポーネントです。このボックスにより、モデルは機能に必要な画像の部分に直接焦点を当てることができます。これがないと、アルゴリズムが間違った場所でパターンを見つけ、間違った結果が生じる可能性があります。

たとえば、トレーニング中に境界ボックスが欠落していると、モデルが時計や椅子などの画像の特徴をラベルに関連付けることがあります。これにより、プログラムが画像内の時計に気づき、時計が存在するという事実のみに基づいて表示する手話文字を決定する可能性があります。

ほとんど終わった!プログラムの最後から 2 番目の部分は、プロンプトに従って 1 つのフレームをキャプチャし、境界ボックスの寸法に合わせてトリミングすることです。

while True:
    _, frame = cap.read()
    
    k = cv2.waitKey(1)
    if k%256 == 27:
        # ESC pressed
        print("Escape hit, closing...")
        break
    elif k%256 == 32:
        # SPACE pressed
        # SPACE pressed
        analysisframe = frame
        showframe = analysisframe
        cv2.imshow("Frame", showframe)
        framergbanalysis = cv2.cvtColor(analysisframe, cv2.COLOR_BGR2RGB)
        resultanalysis = hands.process(framergbanalysis)
        hand_landmarksanalysis = resultanalysis.multi_hand_landmarks
        if hand_landmarksanalysis:
            for handLMsanalysis in hand_landmarksanalysis:
                x_max = 0
                y_max = 0
                x_min = w
                y_min = h
                for lmanalysis in handLMsanalysis.landmark:
                    x, y = int(lmanalysis.x * w), int(lmanalysis.y * h)
                    if x > x_max:
                        x_max = x
                    if x < x_min:
                        x_min = x
                    if y > y_max:
                        y_max = y
                    if y < y_min:
                        y_min = y
                y_min -= 20
                y_max += 20
                x_min -= 20
                x_max += 20 

        analysisframe = cv2.cvtColor(analysisframe, cv2.COLOR_BGR2GRAY)
        analysisframe = analysisframe[y_min:y_max, x_min:x_max]
        analysisframe = cv2.resize(analysisframe,(28,28))


        nlist = []
        rows,cols = analysisframe.shape
        for i in range(rows):
            for j in range(cols):
                k = analysisframe[i,j]
                nlist.append(k)
        
        datan = pd.DataFrame(nlist).T
        colname = []
        for val in range(784):
            colname.append(val)
        datan.columns = colname

        pixeldata = datan.values
        pixeldata = pixeldata / 255
        pixeldata = pixeldata.reshape(-1,28,28,1)

このコードは、プログラムの最後の部分と非常によく似ています。これは主に、境界ボックスの生成に含まれるプロセスが両方の部分で同じであるためです。

ただし、コードのこの分析部分では、画像の周囲にビジュアル オブジェクトを作成するのではなく、OpenCV の画像変形関数を使用して、境界ボックスの寸法に合わせて画像のサイズを変更します。

さらに、NumPy と OpenCV を使用して、モデルがトレーニングされた画像と同じ特性を持つように画像を変更します。

また、パンダを使用して、保存された画像のピクセル データを使用してデータフレームを作成するため、モデルを作成したのと同じ方法でデータを正規化できます。

最後に、処理された画像上でトレーニングされたモデルを実行し、情報出力を処理する必要があります。

prediction = model.predict(pixeldata)
predarray = np.array(prediction[0])
letter_prediction_dict = {letterpred[i]: predarray[i] for i in range(len(letterpred))}
predarrayordered = sorted(predarray, reverse=True)
high1 = predarrayordered[0]
high2 = predarrayordered[1]
high3 = predarrayordered[2]
for key,value in letter_prediction_dict.items():
    if value==high1:
        print("Predicted Character 1: ", key)
        print('Confidence 1: ', 100*value)
    elif value==high2:
        print("Predicted Character 2: ", key)
        print('Confidence 2: ', 100*value)
    elif value==high3:
        print("Predicted Character 3: ", key)
        print('Confidence 3: ', 100*value)
time.sleep(5)

 

コードのこの部分には多くの情報が含まれています。コードのこの部分を 1 つずつ分析していきます。

最初の 2 行は、手の画像が Keras の異なるクラスであるという予測確率をプロットします。データは 2 つのテンソルとして表され、最初のテンソルには確率情報が含まれます。テンソルは本質的には配列と同様に、特徴ベクトルのコレクションです。このモデルによって生成されるテンソルは 1 次元であるため、線形代数ライブラリ NumPy とともに使用して、情報をより Python 的な形式に解析できます。

ここから、変数 Letterpred の下に以前に作成したクラスのリストを使用して、テンソルの値とキーを照合する辞書を作成します。これにより、各文字の確率を対応するクラスと一致させることができます。

このステップの後、リスト内包表記を使用して値を最高から最低まで並べ替えます。このようにして、リストの最初のいくつかの項目を取得し、表示されている手話画像に最も近い 3 つの文字をそれらに割り当てることができます。

最後に、for ループを使用して辞書内のすべてのキー:値のペアを反復処理し、最大値を対応するキーと照合し、各文字の確率を出力します。

示されているように、モデルはカメラから表示されるキャラクターを正確に予測します。このプログラムは、特徴の予測に加えて、CNN Keras モデルの分類の信頼性も示します。


開発したモデルはさまざまな方法で実装できますが、主な用途は、Facetime などのビデオ通話用の字幕デバイスです。このようなアプリケーションを作成するには、表示されるシンボルを予測しながらモデルをフレームごとに実行する必要があります。

Keras画像解析モデルを利用することで、手話から英語へのシンプルかつ簡単なコミュニケーションを可能にするプログラムです。

作者が言いたいことがある

コードが必要な場合は、ブロガーと個人的にチャットしてください。ブロガーから返信があります。
ブロガーの発言が有益だと感じたら、クリックして応援してください。今後もそのような問題を更新し続けます...

おすすめ

転載: blog.csdn.net/weixin_62075168/article/details/130461699