Este artigo é compartilhado pela comunidade Huawei Cloud " CNN-VIT Video Dynamic Gesture Recognition [Brincando com Huawei Cloud] ", autor: HouYanSong.
Reconhecimento dinâmico de gestos de vídeo CNN-VIT
O desenvolvimento da inteligência artificial está mudando a cada dia que passa e também afetou profundamente o desenvolvimento do campo da interação humano-computador. Os gestos, como forma de interação natural e rápida, são amplamente utilizados em áreas como direção inteligente e realidade virtual. A tarefa do reconhecimento de gestos é que quando o operador faz um determinado gesto, o computador pode determinar com rapidez e precisão o tipo de gesto. Este artigo usará ModelArts para desenvolver e treinar um modelo de algoritmo de reconhecimento de gesto dinâmico de vídeo para detectar categorias de gestos dinâmicos, como deslizar para cima, deslizar para baixo, deslizar para a esquerda, deslizar para a direita, abrir, fechar, etc., para obter uma função semelhante ao ar gestos em telefones celulares Huawei.
Introdução ao algoritmo
O algoritmo de reconhecimento de gesto dinâmico de vídeo CNN-VIT primeiro usa a rede pré-treinada InceptionResNetV2 para extrair recursos do clipe de ação de vídeo quadro a quadro e, em seguida, insere o Transformer Encoder para classificação. Usamos o conjunto de dados de amostra de reconhecimento dinâmico de gestos para testar o algoritmo, que contém um total de 108 vídeos. O conjunto de dados contém vídeos de 7 gestos, incluindo gestos inválidos, deslizar para cima, deslizar para baixo, deslizar para a esquerda, deslizar para a direita, abrir, fechar, etc. O processo de operação específico da seguinte forma:
Primeiro, decodificamos o arquivo de vídeo capturado para extrair os quadros-chave, salvamos-os a cada 4 quadros e, em seguida, realizamos o corte central e o pré-processamento da imagem.
def load_video(nome_do_arquivo): cap = cv2.VideoCapture(nome_do_arquivo) # Extraia a cada poucos quadros intervalo_quadro = 4 quadros = [] contagem = 0 enquanto Verdadeiro: certo, quadro = cap.read() se não ret: quebrar # Salva cada quadro frame_interval se contagem% frame_interval == 0: #Corte central quadro = crop_center_square(quadro) # Ampliação quadro = cv2.resize(quadro, (IMG_SIZE, IMG_SIZE)) # BGR -> RGB [0,1,2] -> [2,1,0] quadro = quadro[:, :, [2, 1, 0]] quadros.append(quadro) contar += 1 retornar np.array(quadros)
Em seguida, criamos um extrator de recursos de imagem e usamos o modelo pré-treinado InceptionResNetV2 para extrair recursos de imagem.
def get_feature_extractor(): feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2( pesos = 'imagenet', include_top = Falso, pool = 'média', forma_de_entrada = (IMG_SIZE, IMG_SIZE, 3) ) pré-processo_input=keras.applications.inception_resnet_v2.preprocess_input entradas = keras.Input((IMG_SIZE, IMG_SIZE, 3)) pré-processado = pré-processo_entrada(entradas) saídas = feature_extractor (pré-processado) modelo = keras.Model(entradas, saídas, nome = 'feature_extractor') modelo de retorno
Em seguida, extraia o vetor de recursos do vídeo. Se o vídeo tiver menos de 40 quadros, crie uma matriz de todos os 0s para preenchimento:
def load_data(vídeos, rótulos): recursos_de_vídeo = [] para vídeo em tqdm (vídeos): frames = load_video(vídeo) contagens = len(quadros) # Se o número de frames for menor que MAX_SEQUENCE_LENGTH se contagens < MAX_SEQUENCE_LENGTH: #preenchimento diff = MAX_SEQUENCE_LENGTH - contagens #Crie um array numpy de todos os 0s preenchimento = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Concatenação de array frames = np.concatenate((frames, preenchimento)) # Obtém o quadro MAX_SEQUENCE_LENGTH anterior quadros = quadros[:MAX_SEQUENCE_LENGTH,:] # Extraia recursos em lotes video_feature = feature_extractor.predict(quadros) video_features.append(video_feature) retornar np.array(video_features), np.array(rótulos)
Por fim, crie o modelo VIT com o seguinte código:
#Codificação de posição classe PositionalEmbedding (camadas. Camada): def __init__(self, comprimento_seq, saída_dim): super().__init__() #Construa uma lista de 0~MAX_SEQUENCE_LENGTH self.positions = tf.range(0, limite=MAX_SEQUENCE_LENGTH) self.positional_embedding = camadas.Embedding(input_dim=seq_length, output_dim=output_dim) def chamada(self,x): #Codificação de posição posições_embedding = self.positional_embedding(self.positions) # Adicionar entradas retornar x + posições_embedding # Codificador classe TransformerEncoder (camadas.Layer): def __init__(self, num_heads, embed_dim): super().__init__() self.p_embedding = PositionalEmbedding (MAX_SEQUENCE_LENGTH, NUM_FEATURES) self.attention = camadas.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.1) self.layernorm = camadas.LayerNormalization() def chamada(self,x): # incorporação posicional posicional_embedding = self.p_embedding (x) #autoatenção atenção_out = self.atenção( consulta = positional_embedding, valor = posicional_embedding, chave = posicional_embedding, atenção_mask = Nenhum ) # norma de camada com conexão residual saída = self.layernorm (posicional_embedding + atenção_out) saída de retorno def video_cls_model(class_vocab): #Número de categorias núm_classes = len(vocab_classe) #Definir modelo modelo =keras.Sequential([ camadas.InputLayer(input_shape=(MAX_SEQUENCE_LENGTH, NUM_FEATURES)), TransformerEncoder(2, NUM_FEATURES), camadas.GlobalMaxPooling1D(), camadas.Dropout (0,1), camadas.Dense (classes_num, ativação = "softmax") ]) # Modelo de compilação model.compile (otimizador = keras.optimizers.Adam (1e-5), perda = keras.losses.SparseCategoricalCrossentropy(from_logits=False), métricas = ['precisão'] ) modelo de retorno
Treinamento de modelo
Para uma experiência completa, você pode clicar em Executar no ModelArts para executar o Notebook que publiquei com um clique :
A precisão final do modelo em todo o conjunto de dados atingiu 87%, o que significa que o treinamento em um pequeno conjunto de dados alcançou resultados relativamente bons.
raciocínio de vídeo
Primeiro carregue o modelo VIT e obtenha a tag de índice da categoria de vídeo:
importar aleatoriamente #Carregar modelo modelo = tf.keras.models.load_model('saved_model') # Tags de categoria label_to_name = {0:'Gesto inválido', 1:'Deslizar para cima', 2:'Deslizar para baixo', 3:'Deslizar para a esquerda', 4:'Deslizar para a direita', 5:'Abrir', 6:'Fechar', 7:'aumentar o zoom', 8:'diminuir o zoom'}
Em seguida, use o extrator de recursos de imagem InceptionResNetV2 para extrair recursos de vídeo:
# Obtenha recursos de vídeo def getVideoFeat(quadros): contagem_quadros = len(quadros) # Se o número de frames for menor que MAX_SEQUENCE_LENGTH se contagem_de quadros < MAX_SEQUENCE_LENGTH: #preenchimento diff = MAX_SEQUENCE_LENGTH - contagem_de quadros #Crie um array numpy de todos os 0s preenchimento = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # Concatenação de array frames = np.concatenate((frames, preenchimento)) # Obtém o quadro MAX_SEQ_LENGTH anterior quadros = quadros[:MAX_SEQUENCE_LENGTH,:] # Calcular recursos de vídeo N, 1536 video_feat = feature_extractor.predict (quadros) retornar video_feat
Finalmente, o vetor de recursos da sequência de vídeo é inserido no Transformer Encoder para previsão:
#Previsão de vídeo def testVídeo(): test_file = random.sample(vídeos, 1)[0] rótulo = test_file.split('_')[-2] print('Nome do arquivo: {}'.format(test_file) ) print('Categoria real:{}'.format(label_to_name.get(int(label))) ) # Leia cada quadro do vídeo frames = load_video(test_file) #Selecione o quadro anterior MAX_SEQUENCE_LENGTH para exibir quadros = quadros[:MAX_SEQUENCE_LENGTH].astype(np.uint8) # Salvar como GIF imageio.mimsave('animation.gif', frames, duração=10) # Obtenha recursos façanha = getVideoFeat(quadros) # Inferência de modelo problema = model.predict(tf.expand_dims(feat, eixo=0))[0] print('Categoria prevista: ') para i em np.argsort(prob)[::-1][:5]: print('{}: {}%'.format(label_to_name[i], round(prob[i]*100, 2))) retornar display(Image(open('animation.gif', 'rb').read()))
Resultados da previsão do modelo:
Nome do arquivo:hand_gesture/woman_014_0_7.mp4 Classe real: gesto inválido Categoria de previsão: Gestos inválidos: 99,82% Declínio: 0,12% Fechamento: 0,04% Deslize para a esquerda: 0,01% Aberto: 0,01%
Clique para seguir e conhecer as novas tecnologias da Huawei Cloud o mais rápido possível~
Decidi desistir do software industrial de código aberto . Grandes eventos - OGG 1.0 foi lançado, a Huawei contribuiu com todo o código-fonte do Ubuntu 24.04 LTS foi oficialmente demitido . ". O Fedora Linux 40 foi lançado oficialmente. Uma conhecida empresa de jogos lançou novos regulamentos: os presentes de casamento dos funcionários não devem exceder 100.000 yuans. A China Unicom lança a primeira versão chinesa Llama3 8B do mundo do modelo de código aberto. Pinduoduo é condenado a compensar 5 milhões de yuans por concorrência desleal Método de entrada na nuvem doméstica - apenas a Huawei não tem problemas de segurança de upload de dados na nuvem.