Manipulação de imagens em tempo real.
Nesse artigo, simularemos o funcionamento dos filtros do Instagram demonstrando como é possível aplicar filtros em tempo real utilizando apenas Python, OpenCV e uma webcam. Para não estendermos demais esse post, não detalharemos o algoritmo por trás de cada filtro.

Nesse artigo, simularemos o funcionamento dos filtros do Instagram demonstrando como é possível aplicar filtros em tempo real utilizando apenas Python, OpenCV e uma webcam. Para não estendermos demais esse post, não detalharemos o algoritmo por trás de cada filtro. Utilizaremos a classe filters.py
que discutimos neste artigo.
Nosso algoritmo será composto de 3 etapas principais:
- Inicialização: nessa etapa configuramos as dependências e inicializamos o streaming
- Execução: etapa principal do nosso projeto, que será responsável por aplicar o filtro selecionado nos frames do streaming iniciado na etapa 1.
- Finalização: nessa etapa encerramos o streaming e liberamos os processos que foram inicializados na etapa 1.
Após conhecer o fluxo da nossa aplicação.... hora do código.
Hora do Código
Como de costume, começamos importando os pacotes que iremos utilizar, e definimos algumas constantes importantes (para evitar números mágicos no meio do código):
# importar pacotes
import os
from imutils.video import VideoStream
import imutils
import numpy as np
import cv2
import time
from os.path import dirname, join
import os
# importamos nossos filtros
from filters import grayscale, original, sketch, sepia, blur, canny
# constantes
WEBCAM = os.environ.get('WEBCAM', 1)
Lembrando que também precisamos instalar as dependências:
pip install imutils numpy opencv-python
Note que nossa constante WEBCAM
é um dado do tipo inteiro e foi obtida a partir das variáveis de ambiente do sistema operacional. Veremos mais adiante que essa constante será responsável por indicar à nossa aplicação qual webcam deve ser utilizada.
"Quero te ver" - Inicializando o streaming
Nossa proposta é manipular imagens em tempo real, por isso não podemos simplesmente enviar uma imagem como fizemos em artigos anteriores, precisamos iniciar um streaming de vídeo .
Um Streaming de Vídeo nada mais é do que a transmissão de um vídeo (ou uma sequência de imagens - frames) em tempo real, nesse caso, de uma webcam para o computador que está executando nossa aplicação.
Para nos ajudar nessa tarefa utilizaremos a classe VideoStream
da biblioteca imutils
que torna esse processo tão simples quanto executar um comando:
def main():
print('[INFO] starting video stream')
vs = VideoStream(src=WEBCAM).start()
time.sleep(2.0)
filters = {
'0': original,
'1': grayscale,
'2': sketch,
'3': sepia,
'4': blur,
'5': canny,
'6': None,
'7': None
}
print("""Press any of the following keys to:
0: Original Image
1: Grayscale
2: Sketch
3: Sepia
4: Blur
5: Canny
6: Face detection
7: Blur face
q: Quit""")
# initial_filter
selected_filter = '0'
No código acima estamos definindo uma função main
que abrigará todo nosso código.
Em seguida instanciamos e inicializamos o streaming de vídeo utilizando como fonte da transmissão a webcam
, que foi definida anteriormente, para evitar possíveis erros, indicamos ao nosso algoritmo para aguardar 2 segundos antes de prosseguirmos a execução time.sleep(2.0)
.
Como é desejável que o usuário seja capaz de alterar o filtro em tempo de execução, criamos um dicionário filters
que traduzirá o filtro selecionado para a função correspondente.
Para finalizar esse etapa mostramos um texto com os filtros disponíveis e inicializamos o filtro inicial para "0" - imagem original.
"Vamos mudar" - Aplicando filtros no streaming
Com o streaming iniciado podemos agora ler os frames e aplicar as transformações que o usuário selecionar. Para termos o efeito de vídeo, iremos colocar todo o processamento em um loop:
while True:
# ler frames
frame = vs.read()
frame = imutils.resize(frame, width=400)
# pegar filtro selecionado
filter = filters.get(selected_filter)
if filter is not None:
# aplicar filtro no frame
frame = filter(frame)
# exibir frame na tela
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
if key in [ord(k) for k in filters.keys()]:
selected_filter = chr(key)
Dentro do loop fazemos a leitura do frame capturado vs.read()
redimensionamos a imagem mutils.resize(frame, width=400)
, em seguida pegamos o filtro a partir do dicionário que configuramos na etapa anterior filter = filter.get(selected_filter)
e o aplicamos à imagem frame = filter(frame)
, nesse momento nossa variável frame
está com o filtro aplicado.
Por fim utilizamos o OpenCV para exibir a imagem cv2.imshow("Frame", frame)
.
Como todo nosso código está sendo executado em um loop infinito é importante configurarmos uma condição de parada, nesse caso utilizamos o waitKey
do OpenCV.
O waitKey
ficará "escutando" e, quando alguma tecla for pressionada, ela será armazenada na variável key
. Em nosso exemplo realizamos 2 testes:
- O primeiro verifica se a tecla é a letra "q", em caso positivo, interrompe a execução do loop.
- O segundo verifica se a tecla existe no dicionário de filtros, em caso positivo, aplica o filtro selecionado.
"Ao sair, apague a luz" - Finalizando a aplicação
Quando interrompemos o loop, precisamos destruir as janelas abertas pelo imshow
do OpenCV e encerrar o stream:
# destruir as janelas e interromper o stream
cv2.destroyAllWindows()
vs.stop()
Da maneira que construímos, para aplicarmos qualquer outro filtro, basta que ele seja programado - no nosso caso os filtros localizados em filters.py
- e incluído no dicionário filters
.
Bônus - Deep Learning para Detecção de Rostos
Para irmos um pouco além, como etapa bônus, utilizaremos um modelo de Deep Learning pré-treinado e discutido neste artigo.
Iremos implementar a funcionalidade de detecção e censura de rostos da mesma maneira dos filtros, ou seja, o usuário poderá selecioná-los através das teclas.
Como utilizaremos Deep Learning e um modelo já treinado, precisamos, primeiro carregar os arquivos, informamos o nível de confiança desejado (nesse caso, 70% ou 0.7) e carregamos o modelo:
PROTOTXT = join(dirname(__file__), "deploy.prototxt.txt")
MODEL = join(dirname(__file__), "res10_300x300_ssd_iter_140000.caffemodel")
CONFIDENCE_THRESHOLD = 0.7
# carregar o modelo
net = cv2.dnn.readNetFromCaffe(PROTOTXT, MODEL)
Nota: utilizamos as funções join
e dirname(__file__)
, ao invés de simplesmente informar o nome do arquivo, para melhor compatibilidade entre sistemas operacionais.
Com nosso modelo carregado, iremos incluir as funções de detecção e censura de rostos no dicionário (opções 6 e 7) e dentro do nosso loop:
h, w = frame.shape[:2]
blob = cv2.dnn.blobFromImage(frame, 1, (300, 300), (104.0, 177.0, 123.0))
net.setInput(blob)
detections = net.forward()
# iterar ao longo das deteccoes
for i in range(0, detections.shape[2]):
# exemplo de intervalo de confianca
confidence = detections[0, 0, i, 2]
# selecionar apenas intervalos acima do threshold
if confidence > CONFIDENCE_THRESHOLD:
# label da confiança
text = "{:.2f}%".format(confidence * 100)
# calcular o bounding box
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype('int')
if selected_filter == '6':
frame = cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 0), 1)
if selected_filter == '7':
face = frame[startY:endY, startX:endX]
blured = cv2.GaussianBlur(face, (33, 33), 0)
frame[startY:endY, startX:endX] = blured
Primeiramente extraímos o tamanho (altura e largura) do frame que estamos analisando.
Como estamos trabalhando com deep learning, precisamos realizar alguns pré-processamentos na imagen antes de passá-la através da rede. Para nos auxiliar nesse processo, o OpenCV possui a função cv2.dnn.blobFromImage
, que executa os seguintes passos:
- Subtração da média
- Escala
- Opcionalmente troca de canais (alterar R[ed] com B[lue])
Mais detalhes sobre esse recurso podem ser encontrados aqui.
Para a subtração da média utilizamos os valores descritos no benchmark do próprio OpenCV.
Definimos, então, o blob
gerado como input da rede neural net.setInput(blob)
e a executamos net.forward()
esse processo resultará nas detecções realizadas. Como a rede irá retornar mais de uma detecção e seus respectivos graus de confiança, iremos iterar sobre elas procurando pelo primeiro resultado que esteja acima do limiar de 70% que definimos anteriormente.
Quando encontrado, iremos extrair os pontos que delimitam o rosto detectado formando uma "caixa" (bounding box):
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype('int')
Com os pontos da bounding box - que delimitam a área de rosto detectado -, podemos aplicar os filtros desejados.
Em nosso exemplo nosso filtro 6 (detecção de rosto) irá exibir um retângulo verde ao redor do rosto frame = cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 0), 1)
. Enquanto que nosso filtro 7 (censura) irá aplicar o GaussianBlur
para embaçar e censurar o rosto.
face = frame[startY:endY, startX:endX]
blured = cv2.GaussianBlur(face, (33, 33), 0)
frame[startY:endY, startX:endX] = blured
Demo

A versão final do projeto pode ser conferida no link abaixo
Projeto
Referências

