Dieser Artikel wurde von der Huawei Cloud Community „ CNN-VIT Video Dynamic Gesture Recognition [Playing with Huawei Cloud] “ geteilt, Autor: HouYanSong.
Dynamische Gestenerkennung für CNN-VIT-Videos
Die Entwicklung der künstlichen Intelligenz verändert sich von Tag zu Tag und hat auch die Entwicklung des Bereichs der Mensch-Computer-Interaktion tiefgreifend beeinflusst. Gesten als natürliche und schnelle Art der Interaktion werden häufig in Bereichen wie intelligentem Fahren und virtueller Realität eingesetzt. Die Aufgabe der Gestenerkennung besteht darin, dass der Computer schnell und genau die Art der Geste bestimmen kann, wenn der Bediener eine bestimmte Geste ausführt. In diesem Artikel wird ModelArts verwendet, um ein Algorithmusmodell für die dynamische Video-Gestenerkennung zu entwickeln und zu trainieren, um dynamische Gestenkategorien wie Wischen nach oben, Wischen nach unten, Wischen nach links, Wischen nach rechts, Öffnen, Schließen usw. zu erkennen und eine Funktion zu erreichen, die der Luft ähnelt Gesten auf Huawei-Handys.
Einführung in den Algorithmus
Der dynamische Video-Gestenerkennungsalgorithmus von CNN-VIT verwendet zunächst das vorab trainierte Netzwerk InceptionResNetV2, um Bild für Bild Video-Action-Clip-Funktionen zu extrahieren, und gibt dann den Transformer Encoder zur Klassifizierung ein. Wir verwenden den Beispieldatensatz für die dynamische Gestenerkennung, um den Algorithmus zu testen, der insgesamt 108 Videos enthält. Der Datensatz enthält Videos von 7 Gesten, einschließlich ungültiger Gesten, Wischen nach oben, Wischen nach unten, Wischen nach links, Wischen nach rechts, Öffnen, Schließen, usw. Der spezifische Betriebsablauf ist wie folgt:
Zuerst dekodieren wir die aufgenommene Videodatei, um Schlüsselbilder zu extrahieren, speichern sie alle 4 Bilder und führen dann einen zentrierten Zuschnitt und eine Vorverarbeitung des Bildes durch. Der Code lautet wie folgt:
def Load_Video(Dateiname): cap = cv2.VideoCapture(file_name) # Alle paar Frames extrahieren Frame_Interval = 4 Frames = [] Anzahl = 0 während True: rechts, Frame = cap.read() wenn nicht ret: brechen # Speichern Sie jeden Frame_Intervall-Frame wenn count % frame_interval == 0: #Mittelzuschnitt Frame = Crop_center_square(Frame) # Zoomen Frame = cv2.resize(Frame, (IMG_SIZE, IMG_SIZE)) # BGR -> RGB [0,1,2] -> [2,1,0] Frame = Frame[:, :, [2, 1, 0]] Frames.append(Frame) zählen += 1 np.array(frames) zurückgeben
Dann erstellen wir einen Bildmerkmalsextraktor und verwenden das vorab trainierte Modell InceptionResNetV2, um Bildmerkmale zu extrahieren. Der Code lautet wie folgt:
def get_feature_extractor(): feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2( Gewichte = 'imagenet', include_top = Falsch, pooling = 'avg', input_shape = (IMG_SIZE, IMG_SIZE, 3) ) preprocess_input = keras.applications.inception_resnet_v2.preprocess_input Eingaben = keras.Input((IMG_SIZE, IMG_SIZE, 3)) preprocessed = preprocess_input(Eingaben) Ausgänge = feature_extractor (vorverarbeitet) model = keras.Model(Eingaben, Ausgaben, Name = 'feature_extractor') Rückgabemodell
Extrahieren Sie dann den Video-Feature-Vektor. Wenn das Video weniger als 40 Frames hat, erstellen Sie ein Array mit nur Nullen zum Auffüllen:
def load_data(videos, labels): video_features = [] für Video in tqdm(videos): Frames = Load_Video(Video) counts = len(frames) # Wenn die Anzahl der Frames kleiner als MAX_SEQUENCE_LENGTH ist wenn counts < MAX_SEQUENCE_LENGTH: # Füller diff = MAX_SEQUENCE_LENGTH – zählt #Erstellen Sie ein Numpy-Array aller Nullen padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Array-Verkettung Frames = np.concatenate((Frames, Padding)) # Holen Sie sich den vorherigen MAX_SEQUENCE_LENGTH-Frame Frames = Frames[:MAX_SEQUENCE_LENGTH, :] # Features stapelweise extrahieren video_feature = feature_extractor.predict(frames) video_features.append(video_feature) return np.array(video_features), np.array(labels)
Erstellen Sie abschließend das VIT-Modell mit dem folgenden Code:
#Positionskodierung Klasse PositionalEmbedding(layers.Layer): def __init__(self, seq_length, output_dim): super().__init__() #Erstellen Sie eine Liste von 0~MAX_SEQUENCE_LENGTH self.positions = tf.range(0, limit=MAX_SEQUENCE_LENGTH) self.positional_embedding = Layers.Embedding(input_dim=seq_length, output_dim=output_dim) def call(self,x): #Positionskodierung positions_embedding = self.positional_embedding(self.positions) # Eingaben hinzufügen gib x + positions_embedding zurück # Encoder Klasse TransformerEncoder(layers.Layer): def __init__(self, num_heads, embed_dim): super().__init__() self.p_embedding = PositionalEmbedding(MAX_SEQUENCE_LENGTH, NUM_FEATURES) self.attention = Layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.1) self.layernorm = Schichten.LayerNormalization() def call(self,x): # positionelle Einbettung positional_embedding = self.p_embedding(x) # Selbstaufmerksamkeit Attention_out = self.attention( query = positional_embedding, value = positional_embedding, key = positional_embedding, Attention_mask = Keine ) # Ebenennorm mit Restverbindung Ausgabe = self.layernorm(positional_embedding + Attention_out) Ausgabe zurückgeben def video_cls_model(class_vocab): #Anzahl der Kategorien class_num = len(class_vocab) # Modell definieren Modell =keras.Sequential([ Schichten.InputLayer(input_shape=(MAX_SEQUENCE_LENGTH, NUM_FEATURES)), TransformerEncoder(2, NUM_FEATURES), Schichten.GlobalMaxPooling1D(), Schichten.Dropout(0.1), Schichten.Dense(classes_num, Aktivierung="softmax") ]) # Modell kompilieren model.compile(optimizer = keras.optimizers.Adam(1e-5), loss = keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics = ['accuracy'] ) Rückgabemodell
Modelltraining
Für ein umfassendes Erlebnis können Sie in ModelArts auf „Ausführen“ klicken, um das von mir veröffentlichte Notebook mit einem Klick auszuführen :
Die endgültige Genauigkeit des Modells für den gesamten Datensatz erreichte 87 %, was bedeutet, dass beim Training mit einem kleinen Datensatz relativ gute Ergebnisse erzielt wurden.
Video-Argumentation
Laden Sie zunächst das VIT-Modell und rufen Sie das Videokategorie-Index-Tag ab:
Zufällig importieren #Modell laden model = tf.keras.models.load_model('saved_model') # Kategorie-Tags label_to_name = {0:'Ungültige Geste', 1:'Nach oben wischen', 2:'Nach unten schieben', 3:'Nach links schieben', 4:'Nach rechts schieben', 5:'Öffnen', 6:'Schließen', 7:'vergrößern', 8:'verkleinern'}
Verwenden Sie dann den Bildfeature-Extraktor InceptionResNetV2, um Videofeatures zu extrahieren:
# Holen Sie sich Videofunktionen def getVideoFeat(frames): frames_count = len(frames) # Wenn die Anzahl der Frames kleiner als MAX_SEQUENCE_LENGTH ist Wenn Frames_Count < MAX_SEQUENCE_LENGTH: # Füller diff = MAX_SEQUENCE_LENGTH – Frames_Count #Erstellen Sie ein Numpy-Array aller Nullen padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Array-Verkettung Frames = np.concatenate((Frames, Padding)) # Holen Sie sich den vorherigen MAX_SEQ_LENGTH-Frame Frames = Frames[:MAX_SEQUENCE_LENGTH,:] # Berechnen Sie die Videofunktionen N, 1536 video_feat = feature_extractor.predict(frames) return video_feat
Schließlich wird der Merkmalsvektor der Videosequenz zur Vorhersage in den Transformer Encoder eingegeben:
#Videovorhersage def testVideo(): test_file = random.sample(videos, 1)[0] label = test_file.split('_')[-2] print('Dateiname: {}'.format(test_file) ) print('Echte Kategorie:{}'.format(label_to_name.get(int(label))) ) # Lesen Sie jedes Bild des Videos Frames = Load_Video (Testdatei) #Wählen Sie den vorherigen Frame MAX_SEQUENCE_LENGTH zur Anzeige aus Frames = Frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8) # Als GIF speichern imageio.mimsave('animation.gif', Frames, Dauer=10) # Holen Sie sich Funktionen feat = getVideoFeat(frames) # Modellinferenz prob = model.predict(tf.expand_dims(feat, axis=0))[0] print('Vorhergesagte Kategorie: ') für i in np.argsort(prob)[::-1][:5]: print('{}: {}%'.format(label_to_name[i], Round(prob[i]*100, 2))) return display(Image(open('animation.gif', 'rb').read()))
Ergebnisse der Modellvorhersage:
Dateiname:hand_gesture/woman_014_0_7.mp4 Echte Klasse: Ungültige Geste Prognosekategorie: Ungültige Gesten: 99,82 % Rückgang: 0,12 % Schlusskurs: 0,04 % Nach links wischen: 0,01 % Offen: 0,01 %
Ich beschloss , auf Open-Source -Industriesoftware zu verzichten – OGG 1.0 wurde veröffentlicht, das Team von Ubuntu 24.04 LTS wurde offiziell entlassen ". Fedora Linux 40 wurde offiziell veröffentlicht. Ein bekanntes Spieleunternehmen veröffentlichte neue Vorschriften: Hochzeitsgeschenke von Mitarbeitern dürfen 100.000 Yuan nicht überschreiten. China Unicom veröffentlicht die weltweit erste chinesische Llama3 8B-Version des Open-Source-Modells. Pinduoduo wird zur Entschädigung verurteilt 5 Millionen Yuan für unlauteren Wettbewerb. Inländische Cloud-Eingabemethode – nur Huawei hat keine Sicherheitsprobleme beim Hochladen von Cloud-Daten