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!

5 comentários:

  1. Parabéns!

    Luís Carlos (seu colega da DSA)

    ResponderExcluir
  2. Ótima Solução, Rodrigo. Parabéns.

    Vladimir Alencar (Colega DSA)

    ps. Você pagou algum $ para rodar essa aplicação?

    ResponderExcluir
    Respostas
    1. Não Vladimir. Todos os serviços que utilizei estão dentro do limite pré-estabelecido da camada gratuita, logo não paguei nada.

      Excluir
    2. Obrigado Rodrigo. Ano passado me registrei nos serviços "Gratuitos" da Amazon, e apenas olhei alguns dashboards. Depois de 15 dias veio a conta de aproximadamente 600 dólares!!! Tive de passar um email explicando que não tinha usado NADA da plataforma. Três dias depois, eles estornaram o valor. Mas fiquei de orelha em pé....

      Excluir
  3. parabéns Rodrigo!! muito legal !
    Luiz seu colega da DSA

    ResponderExcluir

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,...