Data original: 13/09/2019
Python é uma linguagem que está sendo amplamente utilizada para reconhecimento de padrões em imagens. Com isso, as possibilidades são imensas, como criar um programa que reconhece queimaduras e diz o grau da queimadura, se é de grau I, II, III...
Quem começa a estudar o assunto de reconhecimento de padrões em imagens muitas vezes começa com coisas que estão bem documentadas como reconhecimento facial. Então hoje iremos usar Python para reconhecer rostos em imagens.
Irei usar uma imagem da banda Metallica como teste:

Aqui, iremos precisar de uma lib bastante usada em visão computacional que é a lib OpenCV, que é escrita em C/C++ mas criaram um wrapper da lib para Python, então agora é possível usar OpenCV em códigos Python.
OpenCV faz uso de algoritmos de machine learning para encontrar padrões dentro de uma imagem de acordo com alguma referência. Eu posso pedir para a lib encontrar rostos, ou pernas, ou braços, ou até ensinar a lib a encontrar o que eu quiser, como bananas e maçãs dentro de uma foto. Aqui que as coisas ficam mais interessantes.
O funcionamento desse mecanismo de "match" é bastante complexo e se eu explicar com mais detalhes o post vai ficar imenso. Então vou tentar ser breve mas didático.
Para encontrar rostos ou qualquer outro padrão dentro de uma imagem, a lib usa mecanismo que chamamos de cascatas, ou cascades em inglês. Em resumo, ele realiza uma série de testes, e não um grande teste, e caso o padrão passe no primeiro teste, o segundo é feito e assim sucessivamente. Isso poupa trabalho e poder computacional, evitando que o algoritmo realize uma grande avaliação em todos os locais da imagem, o que com certeza levaria bastante tempo.
Essas cascades estão basicamente dentro de um arquivo que iremos usar para ensinar a lib o que precisamos encontrar. Então imaginem um arquivo (aqui iremos usar um em formato XML) que contém os testes necessários para detecção de rostos em imagens. Irei ensinar a lib que é aquilo que eu quero procurar nas imagens e ela realiza o resto do trabalho pra nós, com alguns ajustes.
O arquivo necessário para ensinar o algoritmo está neste link:
https://raw.githubusercontent.com/shantnu/FaceDetect/master/haarcascade_frontalface_default.xmlBaixando o arquivo acima, haarcascade_frontalface_default.xml, já podemos começar.
Criado um ambiente virtual, basta instalar a lib:
pip install opencv-python
Dentro do código Python, para importar a OpenCV o código é o seguinte:
import cv2
Se deu tudo certo, já podemos começar a criar nosso código. Irei criar uma variável que receberá o classificador da cascata de código necessária para reconhecimento facial, usando a função da OpenCV responsável por classificar nossa cascata de reconhecimento de padrões:
import cv2
faces_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
Aqui eu usei o arquivo XML baixado acima e ensinei meu programa que eu quero procurar rostos dentro de imagens. Ou seja, o que me refiro a cascata ou cascade é simplesmente um compilado de código que contém os dados necessários para reconhecimento facial.
Em seguida, irei abrir a imagem acima, usando outra função de OpenCV que é capaz de abrir imagens. Em seguida, irei mudar as cores da minha imagem para uma escala de cinza, pois muitos programas de reconhecimento de imagens realizam esse procedimento, o que facilita o trabalho:
import cv2
faces_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
img = cv2.imread('Metallica.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
Percebam que eu abri a imagem e em seguida transformei a imagem e escala de cinza. cvtColor é uma função que recebe como parâmetros a imagem e o resultado de cores final que eu quero aplicar na minha imagem. COLOR_BGR2GRAY transforma a imagem em escala de cinza.
Podemos observar o resultado até agora acrescentando o código abaixo:
cv2.imshow('Metallica gray', img_gray)
cv2.waitKey(0)
"imshow" é uma função de OpenCV que mostra a imagem na nossa tela. O primeiro parâmetro é o título da janela que irá abrir, o segundo parâmetro é a imagem propriamente dita. Acima estou pedindo para abrir a imagem em escala de cinza.
cv2.waitKey(0) é somente um comando que diz para a OpenCV que só deve fechar a imagem quando o usuário clicar qualquer tecla no teclado ou fechar a janela.
Rodando o código até agora, nosso programa irá a abrir uma janela e mostrar a seguinte imagem:

Agora podemos continuar nosso código. Basicamente, o que iremos fazer agora é rodar nosso algoritmo de reconhecimento facial na nossa imagem e desenhar um retângulo ao redor de cada rosto. O código para detecção dos rostos é o seguinte:
metallica_faces = faces_cascade.detectMultiScale(
img_gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(60, 60),
flags = cv2.CASCADE_SCALE_IMAGE
)
Percebam que estou usando a variável que recebeu os dados da cascata, faces_cascade, e estou usando uma função dela que é detectMultiScale, que recebe alguns argumentos como parâmetros.
O primeiro argumento é a imagem a ser usada para teste, que é nossa imagem em escala de cinza. Em seguida, scaleFactor é um fator de calibração. Dependendo do número, ou ele detecta rostos demais, onde não há, ou não detecta rostos de acordo com o esperado. O valor de 1,2 serviu bem para nosso propósito.
minNeighbors e minSize são parâmetros usados porque o algoritmo basicamente passa uma "janela" ao redor da imagem, procurando pelos padrões, e minSize é o tamanho dessa janela. minNeighbors é um valor usado para evitar falsos positivos ou falsos negativos. A explicação mais detalhada desse parâmetro é um tanto complicada, então vou deixar para outra oportunidade caso alguém tenha interesse.
Podemos pedir um print de metallica_faces e ver o que o programa nos retorna:
print(metallica_faces)
# [[240 146 73 73]
# [353 83 77 77]
# [104 129 78 78]
# [519 138 73 73]]
O retorno do print acima é um array com 4 valores, cada um corresponde a um rosto encontrado e indica a localização desse rosto dentro de nossa imagem. Aqui já percebemos que nosso programa está rodando corretamente (ou quase, pois não vimos exatamente a localização ainda desses pontos), pois detectou 4 rostos, em teoria.
Agora, para desenhar o retângulo ao redor de cada rosto:
for (a, b, c, d) in metallica_faces:
cv2.rectangle(img, (a, b), (a+c, b+d), (150, 150, 0), 2)
Acima temos um laço for, que irá, para cada rosto detectado, desenhar um retângulo dentro da imagem, partindo do ponto (a, b), que são os dois primeiros valores de cada item do array, e irá desenhar um retângulo até o ponto (a+c, b+d), sendo c e d os dois últimos pontos de cada item do array. (150, 150, 0) é a cor escolhida para o retângulo, usando o formato rgb de cores (
Colors RGB) e 2 é o tamanho da linha do retângulo.
Por fim, irei mostrar a imagem novamente, agora usando a imagem original e não a imagem em escala de cinza:
cv2.imshow('Metallica', img)
cv2.waitKey(0)
O código completo ficou assim:
import cv2
faces_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
img = cv2.imread('Metallica.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
metallica_faces = faces_cascade.detectMultiScale(
img_gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(60, 60),
flags = cv2.CASCADE_SCALE_IMAGE
)
for (a, b, c, d) in metallica_faces:
cv2.rectangle(img, (a, b), (a+c, b+d), (150, 150, 0), 2)
cv2.imshow('Metallica', img)
cv2.waitKey(0)
E rodando o código, o resultado é o seguinte:

Como dito no início, nós mesmos podemos criar nossa cascata para detectar o que quisermos. Aqui, um link para quem tiver mais curiosidade, de um cascata criada para detectar bananas em imagens:
Coding RobinBasicamente o que é feito é treinar o algoritmo com imagens que são de bananas contra imagens que não contém bananas. Quantas mais imagens sem bananas usadas, melhor a detecção do algoritmo.
O código acima pode não detectar perfeitamente rostos em todas as imagens que vocês testarem. Usei outra foto para treino e um dos integrantes da banda estava com a boca aberta como se estivesse gritando e o algoritmo não pegou o rosto dele. Tudo isso pode ser customizável dentro dos parâmetros da função detectMultiScale.
Concluindo, Python vem sendo amplamente usada para esse tipo de projeto e dá pra criar muitas coisas interessantes com essa base vista hoje.