Cet article est partagé par la communauté Huawei Cloud « Reconnaissance dynamique des gestes vidéo CNN-VIT [Jouer avec Huawei Cloud] », auteur : HouYanSong.
Reconnaissance gestuelle dynamique vidéo CNN-VIT
Le développement de l’intelligence artificielle évolue chaque jour et a également profondément affecté le développement du domaine de l’interaction homme-machine. Les gestes, en tant que moyen d'interaction naturel et rapide, sont largement utilisés dans des domaines tels que la conduite intelligente et la réalité virtuelle. La tâche de la reconnaissance gestuelle est que lorsque l'opérateur effectue un certain geste, l'ordinateur peut déterminer rapidement et avec précision le type de geste. Cet article utilisera ModelArts pour développer et entraîner un modèle d'algorithme de reconnaissance de gestes dynamiques vidéo afin de détecter les catégories de gestes dynamiques telles que le balayage vers le haut, le balayage vers le bas, le balayage vers la gauche, le balayage vers la droite, l'ouverture, la fermeture, etc., pour obtenir une fonction similaire à l'air. gestes sur les téléphones mobiles Huawei.
Introduction à l'algorithme
L'algorithme de reconnaissance dynamique des gestes vidéo CNN-VIT utilise d'abord le réseau pré-entraîné InceptionResNetV2 pour extraire les caractéristiques des clips d'action vidéo image par image, puis entre dans l'encodeur du transformateur pour la classification. Nous utilisons l'échantillon de données de reconnaissance dynamique des gestes pour tester l'algorithme, qui contient un total de 108 vidéos. L'ensemble de données contient des vidéos de 7 gestes, y compris des gestes invalides, glisser vers le haut, glisser vers le bas, glisser vers la gauche, glisser vers la droite, ouvrir, fermer, etc. Le processus de fonctionnement spécifique comme suit :
Tout d'abord, nous décodons le fichier vidéo capturé pour extraire les images clés, les enregistrons toutes les 4 images, puis effectuons un recadrage central et un prétraitement de l'image. Le code est le suivant :
def load_video(nom_fichier) : cap = cv2.VideoCapture (nom_fichier) # Extraire toutes les quelques images frame_interval = 4 cadres = [] compte = 0 tandis que Vrai : à droite, frame = cap.read() sinon ret : casser # Enregistrez chaque image frame_interval si compte % frame_interval == 0 : #Recadrage central cadre = crop_center_square(cadre) # Zoom cadre = cv2.resize(cadre, (IMG_SIZE, IMG_SIZE)) # BGR -> RVB [0,1,2] -> [2,1,0] cadre = cadre[:, :, [2, 1, 0]] frames.append (cadre) compter += 1 retourner np.array (images)
Ensuite, nous créons un extracteur de caractéristiques d'image et utilisons le modèle pré-entraîné InceptionResNetV2 pour extraire les caractéristiques de l'image. Le code est le suivant :
def get_feature_extractor() : feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2( poids = 'imagenet', include_top = Faux, mise en commun = 'moyenne', input_shape = (IMG_SIZE, IMG_SIZE, 3) ) preprocess_input = keras.applications.inception_resnet_v2.preprocess_input entrées = keras.Input((IMG_SIZE, IMG_SIZE, 3)) prétraité = preprocess_input (entrées) sorties = feature_extractor (prétraité) model = keras.Model (entrées, sorties, nom = 'feature_extractor') modèle de retour
Extrayez ensuite le vecteur de caractéristiques vidéo. Si la vidéo contient moins de 40 images, créez un tableau composé uniquement de 0 pour le remplissage :
def load_data (vidéos, étiquettes) : vidéo_features = [] pour la vidéo dans tqdm(vidéos) : frames = load_video(vidéo) comptes = len (images) # Si le nombre d'images est inférieur à MAX_SEQUENCE_LENGTH si compte < MAX_SEQUENCE_LENGTH : # remplisseur diff = MAX_SEQUENCE_LENGTH - compte #Créer un tableau numpy de tous les 0 remplissage = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Concaténation de tableaux frames = np.concatenate((frames, padding)) # Récupère la trame MAX_SEQUENCE_LENGTH précédente frames = frames[:MAX_SEQUENCE_LENGTH, :] # Extraire les fonctionnalités par lots video_feature = feature_extractor.predict (images) video_features.append(video_feature) retourner np.array (video_features), np.array (étiquettes)
Enfin, créez le modèle VIT avec le code suivant :
#Position encodage classe PositionalEmbedding (layers.Layer): def __init__(self, seq_length, output_dim) : super().__init__() #Construire une liste de 0~MAX_SEQUENCE_LENGTH self.positions = tf.range(0, limite=MAX_SEQUENCE_LENGTH) self.positional_embedding = layer.Embedding(input_dim=seq_length, output_dim=output_dim) appel def (soi, x): #Position encodage positions_embedding = self.positional_embedding(self.positions) # Ajouter des entrées retourner x + positions_embedding # Encodeur classe TransformerEncoder (layers.Layer) : def __init__(self, num_heads, embed_dim) : super().__init__() self.p_embedding = PositionalEmbedding (MAX_SEQUENCE_LENGTH, NUM_FEATURES) self.attention = layer.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.1) self.layernorm = layer.LayerNormalization() appel def (soi, x): # intégration positionnelle positional_embedding = self.p_embedding(x) # attention personnelle attention_out = soi.attention( requête = positional_embedding, valeur = positional_embedding, clé = positional_embedding, attention_mask = Aucun ) # norme de couche avec connexion résiduelle sortie = self.layernorm (positional_embedding + attention_out) sortie de retour def video_cls_model(class_vocab) : #Nombre de catégories classes_num = len(class_vocab) # Définir le modèle modèle =keras.Sequential([ layer.InputLayer(input_shape=(MAX_SEQUENCE_LENGTH, NUM_FEATURES)), TransformateurEncoder(2, NUM_FEATURES), couches.GlobalMaxPooling1D(), couches.Dropout(0.1), layer.Dense(classes_num, activation="softmax") ]) # Compiler le modèle model.compile (optimiseur = keras.optimizers.Adam (1e-5), perte = keras.losses.SparseCategoricalCrossentropy(from_logits=False), métriques = ['précision'] ) modèle de retour
Formation sur modèle
Pour une expérience complète, vous pouvez cliquer sur Exécuter dans ModelArts pour exécuter le Notebook que j'ai publié en un clic :
La précision finale du modèle sur l'ensemble des données a atteint 87 %, ce qui signifie que la formation sur un petit ensemble de données a donné des résultats relativement bons.
raisonnement vidéo
Chargez d’abord le modèle VIT et obtenez la balise d’index de catégorie vidéo :
importer au hasard #Charger le modèle modèle = tf.keras.models.load_model('saved_model') # Balises de catégorie label_to_name = {0 : « Geste invalide », 1 : « Faites glisser vers le haut », 2 : 7 : 'zoom avant', 8 : 'zoom arrière'}
Utilisez ensuite l'extracteur de fonctionnalités d'image InceptionResNetV2 pour extraire les fonctionnalités vidéo :
# Obtenez des fonctionnalités vidéo def getVideoFeat (images) : frames_count = len (images) # Si le nombre d'images est inférieur à MAX_SEQUENCE_LENGTH si frames_count < MAX_SEQUENCE_LENGTH : # remplisseur diff = MAX_SEQUENCE_LENGTH - frames_count #Créer un tableau numpy de tous les 0 remplissage = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Concaténation de tableaux frames = np.concatenate((frames, padding)) # Récupère la trame MAX_SEQ_LENGTH précédente frames = frames[:MAX_SEQUENCE_LENGTH,:] # Calculer les fonctionnalités vidéo N, 1536 video_feat = feature_extractor.predict (images) retourner video_feat
Enfin, le vecteur caractéristique de la séquence vidéo est entré dans le Transformer Encoder pour la prédiction :
#Prédiction vidéo def testVideo() : test_file = random.sample(vidéos, 1)[0] étiquette = test_file.split('_')[-2] print('Nom du fichier : {}'.format(fichier_test) ) print('Catégorie réelle :{}'.format(label_to_name.get(int(label))) ) # Lisez chaque image de la vidéo frames = load_video(test_file) #Sélectionnez l'image précédente MAX_SEQUENCE_LENGTH à afficher frames = frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8) # Enregistrer au format GIF imageio.mimsave('animation.gif', frames, durée=10) # Obtenez des fonctionnalités feat = getVideoFeat (images) # Inférence de modèle prob = model.predict(tf.expand_dims(feat, axis=0))(0] print('Catégorie prévue : ') pour moi dans np.argsort(prob)[::-1][:5] : print('{} : {}%'.format(label_to_name[i], round(prob[i]*100, 2))) return display(Image(open('animation.gif', 'rb').read()))
Résultats de prédiction du modèle :
Nom du fichier : hand_gesture/woman_014_0_7.mp4 Classe réelle : geste invalide Catégorie de prévision : Gestes invalides : 99,82% Baisse : 0,12 % Clôture : 0,04 % Balayez vers la gauche : 0,01 % Ouvert : 0,01%
Cliquez pour suivre et découvrir les nouvelles technologies de Huawei Cloud dès que possible~
J'ai décidé d'abandonner les logiciels industriels open source. OGG 1.0 est sorti, Huawei a contribué à tout le code source. Ubuntu 24.04 LTS a été officiellement publié. L'équipe de Google Python Foundation a été tuée par la "montagne de merde de code" . ". Fedora Linux 40 a été officiellement lancé. Une société de jeux bien connue a publié de nouvelles réglementations : les cadeaux de mariage des employés ne doivent pas dépasser 100 000 yuans. China Unicom lance la première version chinoise Llama3 8B au monde du modèle open source. Pinduoduo est condamné à compenser 5 millions de yuans pour concurrence déloyale Méthode de saisie dans le cloud domestique - seul Huawei n'a aucun problème de sécurité de téléchargement de données dans le cloud.