sábado, 16 de junho de 2018

Amazon Rekognition na prática

Olá pessoal. Muito provável que você já tenha visto essa logo aí antes. A Amazon é uma das empresas que vem se consagrando cada vez mais, no setor de tecnologia, através da sua plataforma de computação em nuvem, Amazon AWS. Sem dúvidas, é uma ótima plataforma, tanto para quem trabalha com infraestrutura, pois oferece vasto serviços nessa área. Mas, também tem atraído vários desenvolvedores, através de alguns serviços. Meu intuito nesse post, é apresentar um pouco, sobre como aplicá-los na prática. O primeiro deles, e o que focaremos aqui no post, é o Amazon Rekognition.
Para ter acesso ao serviço, precisa estar cadastrado no Amazon AWS. Eles oferecem uma conta gratuita durante 12 meses para testar alguns dos serviços da camada gratuita, como este. Como o escopo do post é um pouco grande, não vou entrar em detalhes sobre como criar a conta no Amazon AWS. Essa imagem, resume alguns dos serviços que o Amazon Rekognition traz. Lembrando que este serviço, é focado em Visão Computacional. Há exemplos prontos em Detecção de objetos, Moderação de imagens, Analise facial, Comparação de faces, dentre outras. Tudo isso, da forma mais acessível possível, basta navegar na página e estar utilizando estes recursos. Agora, pense numa situação em que você tenha uma aplicação que tem imagens pelo meio, e gostaria de utilizar alguns destes recursos listados acima, porém não tem conhecimento sobre Visão Computacional, CNN, YOLO, SSD, dentre outras arquiteturas para trabalhar com Visão Computacional, então o Amazon Rekognition pode ser muito útil, e até mesmo que você já tenha conhecimentos nessas arquiteturas, pois, os algoritmos são bem precisos, e abstrai bastante o conhecimento para que você consiga utilizar um destes recursos dentro de uma aplicação. Para conseguir utilizar este serviço dentro de uma aplicação web que queiramos criar, ou uma que já existe, precisamos utilizar o SDK do Rekognition. Neste link está contido uma vasta documentação, para facilitar bastante essa interação via SDK. Vamos ver como isso funciona na prática, através de uma aplicação para comparação de imagens. Nosso objetivo final, é construir uma simples aplicação web que servirá de interface para essa aplicação. Contudo, até lá, temos um certo trabalho pelo caminho, vamos passar por eles agora. Mas antes, uma rápida explicação sobre o recurso de comparação de imagens. Basicamente, teremos imagens de treino, imagens que queremos que o Rekognition faça o reconhecimento, e vamos apresentar uma nova imagem (que pode ser chamada de teste), para que o algoritmo faça as comparações devidas, e informe de acordo com a imagem de teste, quantas pessoas ele identificou na foto que estão nas imagens de treinamento. Já que estamos trabalhando com um serviço do Amazon AWS, vamos utilizar um outro serviço para armazenar as imagens, e assim facilitar o nosso trabalho. O nome do serviço é o S3 (Simple Storage Service) Bucket. Não vou entrar em tantos detalhes sobre esse serviço, mas ele serve para armazenamento de arquivos, sites estáticos, dentre outros.  A primeira tarefa que precisamos, é criar um bucket (semelhante a um repositório). Nesse repositório, armazenaremos as imagens de treinamento que queremos que sejam comparadas, com a imagem que iremos fazer o upload pela aplicação web.

Após isso, estamos prontos para começar o trabalho com o SDK do Rekognition. A primeira coisa que faremos é estarmos seguros de que conseguimos acesso via SDK ao nosso bucket recém criado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import boto3
from botocore.exceptions import ClientError
s3 = boto3.resource('s3')
client = boto3.client('rekognition')

def lista_imagens():
    imagens = []
    bucket = s3.Bucket('faces-imagens')
    for imagem in bucket.objects.all():
        imagens.append(imagem.key)
    return imagens
Ao executar esta função acima, o retorno deve ser uma lista com os nomes das fotos que foram colocadas no bucket criado. Na linha 8, veja que colocamos o nome do nosso bucket, semelhante ao que criamos lá na Amazon AWS. Lembrando que esse mesmo nome não poderá mais ser usado por outro usuário, pois é um nome único. Dando continuidade em nosso trabalho, precisamos agora indexar as imagens, para que o algoritmo passa a reconhecer quem é quem, nas imagens de treinamento. Mas antes, precisamos criar uma coleção, que é um pré-requisito para a indexação das imagens.


1
2
3
4
5
6
7
8
9
def cria_colecao():
    collectionId = 'faces'

    # Create a collection
    print('Creating collection:' + collectionId)
    response = client.create_collection(CollectionId=collectionId)
    print('Collection ARN: ' + response['CollectionArn'])
    print('Status code: ' + str(response['StatusCode']))
    print('Done...')
Esse código acima, foi pego da documentação do boto3, é uma função padrão para criar coleções dentro do AWS. Veja que na linha 2, damos o nome a coleção de faces. Você pode alterar esse nome para qualquer outro. Agora que criamos a coleção, podemos indexar as imagens e colocá-las dentro dessa coleção.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def indexa_colecao(imagens):
    for i in imagens:
        print(i)
        response = client.index_faces(
            CollectionId="faces",
            DetectionAttributes=[],
            ExternalImageId=i[:-4],
            Image={
                'S3Object': {
                    'Bucket': 'faces-imagens',
                    'Name': i,
                },
            },

        )
Antes de comentar a função acima, só um detalhe a ser comentado. Antes de executar a função acima, precisamos definir um objeto em python para receber a função lista_imagens() que mostramos acima. Nesse caso, damos o nome desse objeto de imagens. Isso é fundamental, pois note que na função acima, tudo gira em torno de um loop que é feito nesse objeto, que vai percorrer todas as imagens de treino que colocamos no nosso bucket. Note também, que na linha 5, definimos o mesmo nome da coleção que criamos acima. Na linha 7, criamos um slice, para extrair apenas o identificador de uma imagem. Por exemplo, se você faz o upload de uma imagem com nome: Fulano.png, esse slice que começa da direita para a esquerda, contando quatro posições, retornará apenas o nome Fulano.E por último, na linha 10, informamos o nome do bucket em que as imagens se encontram. Após a execução desta função, se você quiser ver o resultado gerado pelo AWS, basta executar o script a seguir pela linha de comando: aws rekognition list-faces --collection-id [nome da coleção]. Lembrando, que para executar esse comando, você deve ter configurado o AWS CLI de acordo com as credenciais da sua conta.





















Esse foi o resultado gerado pelo nosso algoritmo. Agora sim o Rekognition sabe quem é quem em cada imagem. Agora vamos preparar nossa aplicação para fazer a comparação das imagens. Para imagem de teste, temos que fazer a mesma indexação, só que agora, em apenas uma imagem.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def detecta_faces():
    faces_detectadas = client.index_faces(
        CollectionId="faces",
        DetectionAttributes=['DEFAULT'],
        ExternalImageId='TEMP',
        Image={
            'S3Object': {
                'Bucket': 'faces-imagens',
                'Name': '_analise.png',
            },
        },

    )
    return faces_detectadas
Note que o código acima, é bem semelhante ao que fizemos na indexação das imagens de treinamento, com uma diferença que aqui, estamos fazendo a indexação apenas em uma única imagem. Porém, o retorno dessa função é um arquivo JSON que contém muitas informações. Temos um array com o nome de FaceRecords e dentro dele, teremos vários objetos, sendo cada um identificado pelo nome Face, e em cada uma das faces, temos características como, o FaceId, ImageId, Confidence, dentre outros. Nós precisamos apenas que seja retornada os FaceIds, pois é o que utilizamos para fazer a comparação das imagens. Por isso temos a seguinte função:

1
2
3
4
5
def cria_lista_faces_detectadas(faces_detectadas):
    faceId_detectadas = []
    for imagens in range(len(faces_detectadas['FaceRecords'])):
        faceId_detectadas.append(faces_detectadas['FaceRecords'][imagens]['Face']['FaceId'])
    return faceId_detectadas 
 Antes de executar esta função, precisamos criar um objeto com o nome face_detectadas que vai receber o resultado da função detecta_faces(), e a partir dele faremos um loop, como é visto na linha 3, para conseguir capturar a informação do FaceId que vamos precisar para fazer a comparação das imagens. A comparação é feita a partir da seguinte função:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def compara_imagens(faceId_detectadas):
    resultado_comparacao = []
    for ids in faceId_detectadas:
        resultado_comparacao.append(
            client.search_faces(
                CollectionId='faces',
                FaceId=ids,
                FaceMatchThreshold=80,
                MaxFaces=10
            )
        )
    return resultado_comparacao
Note que a função para comparação das imagens é bem simples, basta utilizar o método search_faces do rekognition, passando como parâmetros a coleção que criamos, e os FaceId que extraímos da imagem de teste com a imagem anterior. A partir de agora, já temos toda a implementação necessária relacionada ao SDK do Rekognition. O resultado da função acima será uma resposta muito grande, e com algumas informações que também não são interessantes para nós neste momento. Como vamos trabalhar com uma aplicação web para interface, é interessante formatar um pouco melhor essa resposta, além de exportá-la para um arquivo JSON.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def gera_dados_json(resultado_comparacao):
    dados_json = []
    for face_matches in resultado_comparacao:
        if(len(face_matches.get('FaceMatches'))) >= 1:
            perfil = dict(nome = face_matches['FaceMatches'][0]['Face']['ExternalImageId'],
                          faceMatch=round(face_matches['FaceMatches'][0]['Similarity'],2)
            )
            dados_json.append(perfil)
    return dados_json

def exporta_dados(dados_json):
    arquivo =s3.Object('faces-frontend','dados.json')
    arquivo.put(Body=json.dumps(dados_json))
Na primeira função, temos uma formatação melhor dos dados que estarão vindo. Veja que na linha 4, temos uma condição se o FaceMatches for maior ou igual a 1. Isso tem a ver com o fato se vamos ter alguma das imagens de treinamento presentes dentro da imagem de análise. Se tivermos, criamos um dicionário com duas informações: o nome da pessoa, e a similaridade (característica de semelhança entre as fotos). E por fim, pegamos esse resultado e geramos um arquivo JSON, e salvamos num outro Bucket S3. Nesse outro bucket, o qual damos o nome de faces-frontend, vamos armazenar um site estático para nossa aplicação. Como temos um arquivo JSON que será gerado cada vez que houver upload de uma imagem, podemos facilmente trabalhar com o Javascript para estar fazendo uma requisição assíncrona utilizando AJAX e lendo esse arquivo JSON, para construir a interface web. Não vou entrar muito em detalhes aqui sobre o desenvolvimento da aplicação web, pois é uma outra seara. O trabalho com visão computacional com o Rekognition, vai até a comparação das imagens, e note que foi um trabalho um pouco árduo, apesar de utilizarmos o serviço do Rekognition, o que nos ajudou bastante a prosseguir com a aplicação, precisamos de algumas configurações de acordo com a documentação da mesma. Tendo o arquivo JSON, você passa o bastão para um desenvolvedor web que trabalhe com frontend e aí rapidamente você terá uma interface web, o que vai enriquecer ainda mais essa experiência de Visão Computacional. Mostrarei um exemplo aqui de uma interface web simples, para atender a funcionalidade da listagem das imagens em que estavam na imagem de treinamento, e que o algoritmo conseguiu encontrar na imagem de análise.

A imagem de análise que eu fiz o upload, foi a seguinte:
Note então que o Rekognition conseguiu trabalhar muito bem, e reconheceu o Rodrigo e a Samilly, ambos estão presentes na imagem de treino, com a imagem apresentada na tabela dentro da aplicação web. E por último, a última coluna apresenta o grau de similaridade entre a face de treinamento e de análise. Agora é brincar com o exemplo, criar conjunto de imagens de treino, e testar várias imagens de análises, garanto que essa brincadeira é bem legal :)
Por último, algumas considerações. Nessa aplicação temos duas funcionalidades: A primeira é a listagem de faces encontradas depois da comparação de imagens, nós tínhamos esse resultado presente no arquivo JSON, mas ter isso representado visualmente numa aplicação web, é bem melhor não é verdade? A outra funcionalidade que temos é de fazer o upload da imagem de análise diretamente pela aplicação web. E o mais bacana é que após o upload, a função automaticamente é disparada, e o nosso algoritmo começa a trabalhar. What?? Você falou automaticamente? Exatamente, e aí gostaria de falar um pouco sobre este outro serviço da AWS, antes de finalizar esse post. O serviço que possibilita isso é o AWS Lambda. Seu objetivo é bem simples, tirar toda a complexidade da equipe de desenvolvimento ou operações para colocar uma aplicação em produção. Aliás, se fala bastante no termo Servless, em que não precisamos nos preocupar com as configurações de servidor para execução de uma aplicação. Até o presente momento, o AWS Lambda, trabalha com as linguagens: C# (.NET Core 1 e Core 2), Go, Java 8, NodeJS e Python. Como trabalhamos nossos scripts em Python, podemos transportar todo aquele script que trabalhou com as imagens de análise e colocá-lo em função no AWS Lambda. Não precisamos mudar absolutamente nada em nosso script. Apenas adicionar um método main com parâmetros de evento e contexto, que sejam requeridos pelo Lambda, e então adicionar neste método a execução dos demais métodos.

1
2
3
4
5
6
7
def main(event, context):
    faces_detectadas = detecta_faces()
    print(faces_detectadas)
    faceId_detectadas = cria_lista_faces_detectadas(faces_detectadas)
    resultado_comparacao = compara_imagens(faceId_detectadas)
    dados_json = gera_dados_json(resultado_comparacao)
    exporta_dados(dados_json)
Feito isso, basta copiar todo o script, ir até o Amazon AWS, criar uma função no AWS Lambda e transpor o código para lá.


Note que no Handler da função, definimos o nome do arquivo python, que damos o nome de faceanalise, juntamente com o método main, que é o que vai executar os demais métodos. Um recurso sensacional que o AWS Lambda tem, são triggers que podem ser associadas à função, para definir quando a mesma pode ser executada. Pensando bem, em nossa aplicação, qual seria o melhor momento para informar ao AWS Lambda a executar esse script? Lembra que na aplicação web adicionamos uma funcionalidade para permitir adicionar a imagem de análise diretamente ao Bucket S3? Que tal, informar para o Lambda que a nossa aplicação deve ser executada sempre que houver um upload da imagem de análise?

Foi exatamente isso que fizemos. Veja acima a disposição da arquitetura da aplicação no AWS Lambda. Ao lado direito, temos os serviços que são utilizados. O Amazon CloudWatch por padrão, já é utilizado em conjunto com o AWS Lambda. Mas veja que na arquitetura também temos o Rekognition para a comparação das imagens e temos o Amazon S3 para armazenamento de objetos. E do lado esquerdo, temos uma trigger que associamos ao S3, com a seguinte claúsula: Após o upload da imagem de análise, execute esta aplicação. Impressionante não é mesmo? Muito se tem falado sobre a evolução das plataformas de computação em nuvem, cada vez mais, elas tem investido em assuntos como Big Data, Data Science e Inteligência Artificial. Dedicar tempo estudando sobre Cloud Computing com certeza é uma ótima oportunidade não apenas para fazer coisas legais, mas estar preparado para resolver problemas do dia a dia de forma eficiente. Até a próxima!

quinta-feira, 3 de maio de 2018

Reconhecimento Facial na prática

Bem provável que você já tenha ouvido falar bastante deste termo. Não a toa, é hoje uma das aplicações muito utilizadas na área de visão computacional. Podendo ser aplicado em diversos eixos como sistemas de segurança, sistemas para, entrenenimento, etc. No entanto, trata-se de uma atividade não tão trivial quando estamos falando dos computadores, pois o mesmo não entende uma imagem como nós seres humanos, através de nossa visão compreendemos, para o computador, a imagem nada mais é do que um conjunto de pixels, onde se deve realizar cálculos matemáticos para extrair alguma informação, ou realizar algum processamento em uma imagem. Neste artigo vamos nos restringir a um case sobre reconhecimento facial utilizando alguns recursos da biblioteca DLib. Trata-se de uma biblioteca recente, que já contém ótimos exemplos de reconhecimento facial, detecção de objetos, etc. A biblioteca está disponível nas linguagem C++ e Python. Antes de comentar sobre o nosso projeto, vamos falar um pouco sobre dois algoritmos de reconhecimento facial muito popular, disponíveis no OpenCV, apesar de não ser o que estaremos utilizando no projeto, é interessante falarmos um pouco sobre eles, para fins de contextualização.
  • Algoritmo Eigenfaces: Esse algoritmo utiliza a técnica PCA (Principal Component Analysis - Análise dos Componentes Principais) para realizar a redução de dimensionalidade, com o intuito de passar informações mais discrimantes da face, para a avaliação do algoritmo no reconhecimento das faces. Por utilizar o PCA, estaremos trabalhando nesse algoritmo, com aprendizagem não-supervisionada, ou seja, não precisaremos informar os dados de saída, nesse caso, os labels para cada imagem de treinamento que nosso algoritmo for submetido.
  • Algoritmo Fisherfaces: Esse algoritmo trabalha o seu aprendizado de forma supervisionada, precisamos apresentar os labels ou classes das imagens de treinamento para o algoritmo. A extração de características é realizada separadamente, ou seja, a iluminação de uma face, não afetará as demais faces.
Se temos algoritmos já consagrados para trabalhar com reconhecimento facial, qual o propósito desse artigo trazendo mais um algoritmo? Por que nesse caso, estaremos falando de CNN (Convolution Neural Network, ou Rede Neural Convolucional), é o que se tem de mais avançado quando estamos tratando de visão computacional. Apenas a título de conhecimento, o CNN é uma rede neural profunda com algumas peculiaridades, como: a convolução e o pooling (agrupamento).

 Perceba que temos uma imagem de entrada e como saída teremos um conjunto de classes para que a nossa rede vai realize a associação, geralmente essa camada, geralmente utiliza a função de ativação softmax que calcula as probabilidades entre as diferentes classes. Mas em comparação com uma rede neural profunda, não temos nenhuma diferença, a diferença ocorre pela existência da convolução, onde será aplicado um filtro numa imagem que estará percorrendo-a a fim de encontrar as características da imagem. A sacada é que após a convolução, ocorre o subsampling, também chamado de pooling, isso acontece por que a CNN requer muito esforço computacional, quanto maior a imagem, mais filtros serão aplicado à imagem, para otimizar o tempo de processamento de uma CNN, podemos utilizar o pooling, como o max pooling, que vai realizar um agrupamento dos maiores valores de um filtro que foi utilizado na convolução. É perceptível pela imagem, que à medida que aplicamos convolução e subsampling, a profundidade do conjunto de dados aumentará, ao mesmo tempo que estaremos reduzindo a largura e altura. Feito essa explicação, vamos comentar um pouco sobre o projeto que estaremos mostrando aqui nesse artigo. A sequência de passos será o seguinte:
  1. Detecção de faces: O primeiro passo para reconhecer faces, é que consigamos inicialmente detectá-las. Para isso, vamos utilizar uma função disponível na biblioteca do DLib.
  2. Detecção de pontos faciais: Antes do reconhecimento facial, precisamos utilizar técnicas que permitam realizar a detecção das regiões de interesse em uma imagem, como estaremos trabalhando com detecção de faces, um exemplo de região de interesse pode ser detectar pontos faciais.
  3. Utilização de CNN: Por último, faremos o treinamento de um modelo de CNN pré-treinado para treinar nossas fotos a fim de estarmos aptos para realizar o reconhecimento facial.

Nas etapas 2 e 3, estaremos alguns modelos já disponibilizados na internet, para facilitar o processo de reconhecimento facial. Esses modelos, poderão ser encontrados nesse repositório do github. Vamos utilizar como exemplo nesse projeto, algumas imagens da série Silicon Valley. Vamos então à primeira etapa, utilizaremos a seguinte imagem: 
Posteriormente, utilizaremos essa mesma imagem, como imagem de teste para verificar se ela irá reconhecer a face do Richard, esse personagem ao meio. Como comentamos, vamos utilizar DLib, nosso código ficará assim:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import cv2
import dlib

imagem = cv2.imread("fotos/equipe.8.jpg")
detector = dlib.get_frontal_face_detector()
facesDetectadas = detector(imagem, 3)
print(facesDetectadas)
print("Faces detectadas: ", len(facesDetectadas))
for face in facesDetectadas:
    e, t, d, b = (int(face.left()), int(face.top()), int(face.right()), int(face.bottom()))
    cv2.rectangle(imagem, (e, t), (d, b), (0, 255, 255), 2)

cv2.imshow("Detector hog", imagem)
cv2.waitKey(0)
cv2.destroyAllWindows()

Esse exemplo utiliza as bibliotecas do Opencv e do DLib. Na linha 4, apresentamos a imagem que estaremos realizando a detecção facial. Na linha 5, utilizamos um dos recursos disponíveis do DLib que é o detector de faces, através do método get_frontal_face_detector(). Existem outros algoritmos para detectar faces como o haarcascades que é baseado no Adaboost. Já esse algoritmo do DLib, é baseado no HOG (Histograma de Gradientes Orientados). A sua caixa delimitadora (bounding box) é construída utilizando os parâmetros left, top, right e bottom. Continuando no código, na linha 6, chamamos o detector passando como parâmetro a imagem que queremos detectar e o tamanha da escala. E nas linhas 7 a linha 15, são procedimentos padrões, inclusive bem similares aos que é feito com o algoritmo haarcascade, percorremos pixel a pixel e desenhamos um retângulo sob a face detectada em cada foto. A atenção especial aqui, é lembrar que o HOG trabalha com os parâmetros left, top, right e bottom. Executando o código, teremos o seguinte resultado:
Legal, temos o resultado esperado. Vamos avançar agora à 2º etapa, agora que temos a bounding box, vamos extrair a região de interesse. Para isso, utilizaremos um dos recursos presente no código do Github que eu coloquei o link aqui no artigo. Esse recurso é um preditor específico para trabalhar com pontos faciais, seu objetivo, é detectar 68 pontos faciais. Vamos então utilizá-lo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import dlib
import cv2
import numpy as np
 
def imprimeLinhas(imagem, pontosFaciais):
    p68 = [[0, 16, False], # linha do queixo
           [17, 21, False], # sombrancelha direita
           [22, 26, False], # sombancelha esquerda
           [27, 30, False], # ponte nasal
           [30, 35, True], # nariz inferior
           [36, 41, True], # olho esquerdo
           [42, 47, True], # olho direito
           [48, 59, True], # labio externo
           [60, 67, True]] # labio interno
    for k in range(0, len(p68)):
        pontos = []
        for i in range(p68[k][0], p68[k][1] + 1):
            ponto = [pontosFaciais.part(i).x, pontosFaciais.part(i).y]
            pontos.append(ponto)
        pontos = np.array(pontos, dtype=np.int32)
        cv2.polylines(imagem, [pontos], p68[k][2], (255, 0, 0), 2)

imagem = cv2.imread("fotos/equipe.8.jpg")

detectorFace = dlib.get_frontal_face_detector()
detectorPontos = dlib.shape_predictor("recursos/shape_predictor_68_face_landmarks.dat")
facesDetectadas = detectorFace(imagem, 2)
for face in facesDetectadas:
    pontos = detectorPontos(imagem, face)
    imprimeLinhas(imagem, pontos)

cv2.imshow("Pontos faciais", imagem)
cv2.waitKey(0)
cv2.destroyAllWindows()
Nesse exemplo, acrescentamos a biblioteca do numpy, pois precisamos trabalhar com arrays para conseguirmos desenhar a região de interesse em cada face. Criamos a função imprimeLinhas que será responsável por desenhar os pontos faciais à imagem. Após isso, na linha 23, informamos a imagem que estaremos utilizando, na linha 25 fazemos o processo da detecção de faces, precisamos delimitar aonde que estaremos aplicando a nossa região de interesse. Na linha 26, apilcamos o preditor com 68 pontos faciais, esse preditor pode ser encontrado no link do github. Por fim, faremos um loop pelas faces detectadas, e em cima delas, criaremos a nossa região de interesse, e por último, chamamos a função imprimeLinhas para desenhar esses pontos na tela. O resultado será:

Ótimo! Veja que essa segunda etapa foi concluída como esperávamos. Agora, partimos para a terceira etapa. Para isso, iremos utilizar o personagem Richard, que é o personagem do meio nesta foto, vamos treinar o nosso algoritmo com três fotos dele, e depois faremos testes nas imagens que contém toda a sua equipe, para ver se o nosso algoritmo reconhece o Richard. Antes de começar os trabalhos, faço aqui uma ressalva, caso você prefira utilizar algoritmos como Fisherfaces ou Eigenfaces, saiba que seu algoritmo precisará de muito mais fotos do que apenas três. Vamos usar as seguintes fotos no treinamento:


Nosso código de treinamento das imagens será:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import os
import glob
import _pickle as cPickle
import dlib
import cv2
import numpy as np

detectorFace = dlib.get_frontal_face_detector()
detectorPontos = dlib.shape_predictor("recursos/shape_predictor_68_face_landmarks.dat")
reconhecimentoFacial = dlib.face_recognition_model_v1("recursos/dlib_face_recognition_resnet_model_v1.dat")

indice = {}
idx = 0
descritoresFaciais = None

for arquivo in glob.glob(os.path.join("fotos/treinamento", "*.jpg")):
    imagem = cv2.imread(arquivo)
    facesDetectadas = detectorFace(imagem, 1)
    
    for face in facesDetectadas:
        pontosFaciais = detectorPontos(imagem, face)
        descritorFacial = reconhecimentoFacial.compute_face_descriptor(imagem, pontosFaciais)
        listaDescritorFacial = [df for df in descritorFacial]
        npArrayDescritorFacial = np.asarray(listaDescritorFacial, dtype=np.float64)
        npArrayDescritorFacial = npArrayDescritorFacial[np.newaxis, :]
        if descritoresFaciais is None:
            descritoresFaciais = npArrayDescritorFacial
        else:
            descritoresFaciais = np.concatenate((descritoresFaciais, npArrayDescritorFacial), axis=0)

        indice[idx] = arquivo
        idx += 1

np.save("recursos/descritores_r.npy", descritoresFaciais)
with open("recursos/indices_r.pickle", 'wb') as f:
    cPickle.dump(indice, f)
A execução desse script irá culminar na criação de dois arquivos: o descritores_r.npy e o indices_r.pickle. Como pode ser visto da linha 34 a linha 36. No início do script, na linha 08 e 09, vemos a primeira e segunda etapa sendo feita, para que consigamos realizar o reconhecimento facial. Para isso, utilizaremos um modelo de CNN treinado (está disponível no link do Github), a vantagem é que podemos utilizar várias imagens faciais agora, sem ter a preocupação de ter de retreinar o modelo, pois o mesmo custaria um esforço computacional. Criamos um loop para que sejam utilizadas todas as fotos que estiverem do diretório fotos/treinamento, e que sejam do formato jpg. Após isso, aplicaremos outro loop pelas imagens em que a face foi detectada, a partir daí aplicamos a detecção de pontos faciais, e vamos aplicar também nosso reconhecedor facial, através do método compute_face_descriptor(). Após isso, precisamos fazer algumas conversões para que os valores encontrados, fique em um formato de array do numpy, o que vai facilitar a comparação com novas imagens que passarmos para testes. E por último, fazemos a iteração de cada índice relacionado às imagens que passamos de treinamento. Por fim, vamos colocar o nosso algorito a prova e ver se ele foi capaz de reconhecer o Richard em algumas fotos que ele está junto com sua equipe, mas antes, vamos ao código que realiza o teste de reconhecimento facial:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import os
import glob
import _pickle as cPickle
import dlib
import cv2
import numpy as np
detectorFace = dlib.get_frontal_face_detector()
detectorPontos = dlib.shape_predictor("recursos/shape_predictor_68_face_landmarks.dat")
reconhecimentoFacial = dlib.face_recognition_model_v1("recursos/dlib_face_recognition_resnet_model_v1.dat")
indices = np.load("recursos/indices_r.pickle")
descritoresFaciais = np.load("recursos/descritores_r.npy")
limiar = 0.5

for arquivo in glob.glob(os.path.join("fotos", "*.jpg")):
    imagem = cv2.imread(arquivo)
    facesDetectadas = detectorFace(imagem, 2)
    for face in facesDetectadas:
        e, t, d, b = (int(face.left()), int(face.top()), int(face.right()), int(face.bottom()))
        pontosFaciais = detectorPontos(imagem, face)
        descritorFacial = reconhecimentoFacial.compute_face_descriptor(imagem, pontosFaciais)
        listaDescritorFacial = [fd for fd in descritorFacial]
        npArrayDescritorFacial = np.asarray(listaDescritorFacial, dtype=np.float64)
        npArrayDescritorFacial = npArrayDescritorFacial[np.newaxis, :]

        distancias = np.linalg.norm(npArrayDescritorFacial - descritoresFaciais, axis=1)
        print("Distâncias: {}".format(distancias))
        minimo = np.argmin(distancias)
        print(minimo)
        distanciaMinima = distancias[minimo]
        print(distanciaMinima)

        if distanciaMinima <= limiar:
            nome = os.path.split(indices[minimo])[1].split(".")[0]
        else:
            nome = ' '

        cv2.rectangle(imagem, (e, t), (d, b), (0, 255, 255), 2)
        texto = "{} {:.4f}".format(nome, distanciaMinima)
        cv2.putText(imagem, texto, (d, t), cv2.FONT_HERSHEY_COMPLEX_SMALL, 0.5, (0, 255, 255))

    cv2.imshow("Reconhecimento facial", imagem)
    cv2.waitKey(0)
cv2.destroyAllWindows()

Perceba que esse código é bem similar ao anterior. Porém, temos algumas especificidades aqui, a primeira delas, é exportar os arquivos que gravamos no treinamento, que foram os índices e descritores. Definimos um valor de limiar também, por que agora, vamos comparar os valores de cada imagem de teste, com os valores dos descritores faciais que foram gerados pela imagem de treinamento. Nesse caso, a distância entre esses valores, deve ser menor ou igual ao que definimos no limiar, para que então, possamos certificar de que a face é de Richard. E por último, vamos ao que interessa. Vamos ver se como ficou nosso teste de reconhecimento facial nas imagens.
Veja que nessa primeira foto, apesar de Richard está com a face meio virada, ainda assim o algoritmo o reconheceu com aproximadamente 0.48 de distância. Lembre-se, como definimos o limiar de 0.50, os valores acima, não serão considerados como Richard. Vamos para próxima foto:
Essa segunda foto, também nosso algoritmo conseguiu reconhecer o Richard com um pouco mais de precisão, 0.42. Note que o reconhecimento se dá pelo texto que é anexado ao lado da face, e por fins de conhecimento, deixamos visível  a bounding box (caixa delimitadora) de detecção de faces, nas demais pessoas também. Vamos a próxima foto:
Note que nessa terceira imagem, também o Richard foi reconhecido corretamente. Até agora três imagens que submetemos a teste, as três, obtivemos resultado desejado. Vamos a quarta foto:
 Veja que nessa foto também nosso algoritmo conseguiu reconhecer o Richard e com uma precisão muito boa, inclusive.
 Outra foto, e tivemos sucesso também no reconhecimento facial de Richard, perceba que tivemos uma melhor precisão nessa imagem.
Achei essa foto bem legal por dois motivos: Ela reconheceu o Richard através de sua face pessoal, e também através da imagem que está no poster ao fundo. E a outra é por ter detectado a face que está na camisa do Erlich.
Nem tudo são mar de rosas na  vida. Veja que nessa imagem, o nosso algoritmo sequer detectou a face de Richard, logo ele não poderia reconhecê-lo. Essa imagem, apresenta uma dificuldade muito alta, pois a sua face está totalmente invertida.
Outra imagem que conseguimos obter êxito no reconhecimento de Richard, mesmo pelo fato de que sua face esteja um pouco curvada para baixo.
E por último, essa imagem nos mostra que o algoritmo conseguiu detectar a face de Richard, porém a distância entre os descritores faciais de treinamento e a imagem de teste foi maior que nosso limiar (0.5), nesse caso basta aumentar um pouco o nosso limiar, e teremos também o reconhecimento facial de Richard nessa imagem.Concluí-se então que tivemos uma ótima experiência com o algoritmo utilizado. De 9 imagens de teste, 7 conseguimos reconhecer o nosso alvo. E ainda vai um acréscimo, utilizamos apenas 3 imagens para treinamento, repito, apenas 3. Quando temos um dataset, é muito comum divirmos nossos dados em 75/25, sendo 75% treinamento e 25% teste, aqui fizemos o contrário, apenas para fins de avaliação mesmo, dedicamos mais imagens para teste do que para treinamento, e concluímos que nosso algoritmo se saiu muito bem. Espero que o artigo tenha sido útil e que de alguma forma possa contribuir positivamente para o conhecimento dos que vierem lê-lo. Até a próxima!

Amazon Rekognition na prática

Olá pessoal. Muito provável que você já tenha visto essa logo aí antes. A Amazon é uma das empresas que vem se consagrando cada vez mais,...