Leve você para desenvolver um modelo de reconhecimento de gestos dinâmico de vídeo

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

RC.gif

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:

Apresentação 1_edit_569379046802172.png

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 :

Captura de tela 28/04/2024 133611_edit_572368136181552.pngA 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.
{{o.nome}}
{{m.nome}}

Acho que você gosta

Origin my.oschina.net/u/4526289/blog/11062248
Recomendado
Clasificación