Este artículo es compartido por la comunidad de Huawei Cloud " Reconocimiento dinámico de gestos por video CNN-VIT [jugando con Huawei Cloud] ", autor: HouYanSong.
Reconocimiento dinámico de gestos por vídeo CNN-VIT
El desarrollo de la inteligencia artificial está cambiando cada día que pasa y también ha afectado profundamente el desarrollo del campo de la interacción persona-computadora. Los gestos, como forma natural y rápida de interacción, son muy utilizados en campos como la conducción inteligente y la realidad virtual. La tarea del reconocimiento de gestos es que cuando el operador realiza un determinado gesto, la computadora pueda determinar de forma rápida y precisa el tipo de gesto. Este artículo utilizará ModelArts para desarrollar y entrenar un modelo de algoritmo de reconocimiento de gestos dinámicos de video para detectar categorías de gestos dinámicos como deslizar hacia arriba, deslizar hacia abajo, deslizar hacia la izquierda, deslizar hacia la derecha, abrir, cerrar, etc., para lograr una función similar al aire. Gestos en teléfonos móviles Huawei.
Introducción al algoritmo
El algoritmo de reconocimiento de gestos dinámicos de video CNN-VIT primero utiliza la red previamente entrenada InceptionResNetV2 para extraer características de clips de acción de video cuadro por cuadro y luego ingresa el codificador Transformer para su clasificación. Usamos el conjunto de datos de muestra de reconocimiento dinámico de gestos para probar el algoritmo, que contiene un total de 108 videos. El conjunto de datos contiene videos de 7 gestos, incluidos gestos no válidos, deslizar hacia arriba, deslizar hacia abajo, deslizar hacia la izquierda, deslizar hacia la derecha, abrir, cerrar,. etc. El proceso de operación específico es el siguiente:
Primero, decodificamos el archivo de video capturado para extraer fotogramas clave, los guardamos cada 4 fotogramas y luego realizamos el recorte central y el preprocesamiento de la imagen.
def cargar_video(nombre_archivo): tapa = cv2.VideoCapture(nombre_archivo) # Extraer cada pocos fotogramas intervalo_cuadro = 4 marcos = [] contar = 0 mientras que Verdadero: derecha, marco = cap.read() si no se retira: romper # Guardar cada cuadro frame_interval si cuenta % frame_interval == 0: #Cultivo central marco = crop_center_square(marco) # Zoom marco = cv2.resize(marco, (IMG_SIZE, IMG_SIZE)) # BGR -> RGB [0,1,2] -> [2,1,0] cuadro = cuadro[:, :, [2, 1, 0]] marcos.append(marco) contar += 1 devolver np.array (marcos)
Luego creamos un extractor de características de imagen y utilizamos el modelo previamente entrenado InceptionResNetV2 para extraer características de imagen.
def get_feature_extractor(): feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2( pesos = 'imagenet', incluir_top = Falso, agrupación = 'promedio', forma_entrada = (IMG_SIZE, IMG_SIZE, 3) ) preprocess_input = keras.applications.inception_resnet_v2.preprocess_input entradas = keras.Input((IMG_SIZE, IMG_SIZE, 3)) preprocesado = preprocess_input(entradas) salidas = feature_extractor (preprocesado) modelo = keras.Model(entradas, salidas, nombre = 'feature_extractor') modelo de devolución
Luego extraiga el vector de características del video. Si el video tiene menos de 40 fotogramas, cree una matriz de todos 0 para rellenar:
def load_data(vídeos, etiquetas): características_video = [] para vídeo en tqdm (vídeos): fotogramas = cargar_video(vídeo) recuentos = len(cuadros) # Si el número de fotogramas es menor que MAX_SEQUENCE_LENGTH si cuenta < MAX_SEQUENCE_LENGTH: # relleno diferencia = MAX_SEQUENCE_LENGTH - recuentos #Crea una matriz numerosa de todos los 0 relleno = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Concatenación de matrices marcos = np.concatenate((marcos, relleno)) # Obtener el cuadro MAX_SEQUENCE_LENGTH anterior cuadros = cuadros[:MAX_SEQUENCE_LENGTH, :] # Extraer características en lotes video_feature = feature_extractor.predict(fotogramas) video_features.append(video_feature) devolver np.array (video_features), np.array (etiquetas)
Finalmente, crea el modelo VIT con el siguiente código:
#Codificación de posición clase PositionalEmbedding(capas.Capa): def __init__(self, longitud_secuencia, salida_dim): super().__init__() #Construir una lista a partir de 0~MAX_SEQUENCE_LENGTH self.posiciones = tf.range(0, límite=MAX_SEQUENCE_LENGTH) self.positional_embedding = capas.Embedding(input_dim=seq_length, output_dim=output_dim) def llamada(yo,x): #Codificación de posición posiciones_embedding = self.positional_embedding(self.posiciones) # Agregar entradas devolver x + posiciones_incrustación # Codificador clase TransformerEncoder (capas.Capa): def __init__(self, num_heads, embed_dim): super().__init__() self.p_embedding = Incrustación posicional (MAX_SEQUENCE_LENGTH, NUM_FEATURES) self.attention = capas.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.1) self.layernorm = capas.LayerNormalization() def llamada(yo,x): # incrustación posicional positional_embedding = self.p_embedding(x) # auto atención atención_fuera = self.atención( consulta = positional_embedding, valor = incrustación_posicional, clave = incrustación_posicional, máscara_atención = Ninguna ) # norma de capa con conexión residual salida = self.layernorm(incrustación_posicional + salida_atención) salida de retorno def video_cls_model(clase_vocab): #Número de categorías núm_clases = len(clase_vocab) # Definir modelo modelo =keras.Sequential([ capas.InputLayer(input_shape=(MAX_SEQUENCE_LENGTH, NUM_FEATURES)), Codificador de transformador(2, NUM_FEATURES), capas.GlobalMaxPooling1D(), capas.Abandono(0.1), capas.Dense(classes_num, activación="softmax") ]) # compilar modelo model.compile(optimizador = keras.optimizadores.Adam(1e-5), pérdida = keras.losses.SparseCategoricalCrossentropy(from_logits=False), métricas = ['precisión'] ) modelo de devolución
Entrenamiento modelo
Para una experiencia completa, puedes hacer clic en Ejecutar en ModelArts para ejecutar el Notebook que publiqué con un solo clic :
La precisión final del modelo en todo el conjunto de datos alcanzó el 87%, lo que significa que el entrenamiento en un conjunto de datos pequeño logró resultados relativamente buenos.
razonamiento en vídeo
Primero cargue el modelo VIT y obtenga la etiqueta de índice de categoría de video:
importar aleatoriamente #Cargar modelo modelo = tf.keras.models.load_model('modelo_guardado') # Etiquetas de categoría label_to_name = {0:'Gesto no válido', 1:'Deslizar hacia arriba', 2:'Deslizar hacia abajo', 3:'Deslizar hacia la izquierda', 4:'Deslizar hacia la derecha', 5:'Abrir', 6:'Cerrar', 7: 'acercar', 8: 'alejar'}
Luego use el extractor de funciones de imagen InceptionResNetV2 para extraer funciones de video:
# Obtener funciones de video def getVideoFeat(cuadros): recuento_cuadros = len(cuadros) # Si el número de fotogramas es menor que MAX_SEQUENCE_LENGTH si frame_count < MAX_SEQUENCE_LENGTH: # relleno diferencia = MAX_SEQUENCE_LENGTH - frame_count #Crea una matriz numerosa de todos los 0 relleno = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Concatenación de matrices marcos = np.concatenate((marcos, relleno)) # Obtener el cuadro MAX_SEQ_LENGTH anterior cuadros = cuadros[:MAX_SEQUENCE_LENGTH,:] # Calcular características de video N, 1536 video_feat = feature_extractor.predict(fotogramas) devolver video_feat
Finalmente, el vector de características de la secuencia de video se ingresa en el codificador Transformer para su predicción:
#Predicción de vídeo def pruebaVideo(): archivo_prueba = muestra.aleatoria(vídeos, 1)[0] etiqueta = test_file.split('_')[-2] print('Nombre de archivo: {}'.format(test_file) ) print('Categoría real:{}'.format(label_to_name.get(int(label))) ) # Lee cada fotograma del vídeo. cuadros = cargar_video (archivo_prueba) #Seleccione el cuadro anterior MAX_SEQUENCE_LENGTH para mostrar cuadros = cuadros[:MAX_SEQUENCE_LENGTH].astype(np.uint8) # Guardar como GIF imageio.mimsave('animación.gif', fotogramas, duración=10) # Obtener funciones hazaña = getVideoFeat(fotogramas) # Inferencia del modelo problema = model.predict(tf.expand_dims(hazaña, eje=0))[0] print('Categoría prevista: ') para i en np.argsort(prob)[::-1][:5]: print('{}: {}%'.format(label_to_name[i], round(prob[i]*100, 2))) visualización de retorno (Imagen (open ('animation.gif', 'rb').read()))
Resultados de la predicción del modelo:
Nombre del archivo:hand_gesture/woman_014_0_7.mp4 Clase real: gesto no válido Categoría de pronóstico: Gestos no válidos: 99,82% Disminución: 0,12% Cierre: 0,04% Deslizar hacia la izquierda: 0,01% Abierto: 0,01%
Haga clic para seguir y conocer las nuevas tecnologías de Huawei Cloud lo antes posible ~
Decidí renunciar al software industrial de código abierto. Eventos importantes: se lanzó OGG 1.0, Huawei contribuyó con todo el código fuente y se lanzó oficialmente Ubuntu 24.04. El equipo de la Fundación Google Python fue despedido por la "montaña de código de mierda" . ". Se lanzó oficialmente Fedora Linux 40. Una conocida compañía de juegos lanzó Nuevas regulaciones: los obsequios de boda de los empleados no deben exceder los 100.000 yuanes. China Unicom lanza la primera versión china Llama3 8B del mundo del modelo de código abierto. Pinduoduo es sentenciado a compensar 5 millones de yuanes por competencia desleal. Método de entrada en la nube nacional: solo Huawei no tiene problemas de seguridad para cargar datos en la nube.