Face detection com DNN no OpenCV

Simplicidade passa longe dessa área de visão computacional e inteligência artificial. Não é só a questão de programar, serão suas mãos a configurar o sistema; instalar driver de placa de vídeo, suporte a CUDA, compilação do OpenCV etc.

Esse artigo só foi possível graças à ajuda dada pelo Leonardo Lontra, um especialista em IA e CV em sistemas embarcados, que tem participação no código do próprio OpenCV.  Enfim, quer experimentar algo novo e incrível? Então prepare seu Linux para receber configurações!

Instalar o docker

O primeiro passo é criar um container docker. Dessa vez não é só instalar uma imagem pronta como o OpenFace. Vamos pegar um sistema “crú”, então lapidá-lo. Para isso, será necessário ter o docker instalado. Para fazê-lo, sugiro que siga o tutorial da Digital Ocean, caso não o tenha feito ainda.

Depois de instalado, siga as operações como root para simplificar as coisas:

sudo su
service docker start

Baixar imagem Debian Docker

Para baixar a imagem oficial do Debian é bastante simples. Digite agora no terminal (seja ele qual for; konsole, gtkterm, xterm):

docker pull debian

Em pouco tem uma imagem mínima estará disponível no sistema. Daí você pode criar seu container (já descrevi nesse arquivo de CUDA):

docker run -p 9000:9000 -p 8000:8000 --name DobitAoByte --hostname OpenCV -t -i bamos/openface /bin/bash

Se não conectar diretamente ao console, verifique se a imagem está rodando:

docker ps

Se estiver rodando, aparecerá o nome no último campo:

dnn no opencv

Basta conectar-se ao container:

docker attach DobitoByte

E dê Enter 2 vezes.

Mude o nível de diretório para o home do root:

cd

Instale uns pacotes:

apt-get update
apt-get install git wget

 

Agora, clone o repositório do Lontra:

git clone https://github.com/lhelontra/build_opencv.git

Instalando CUDA

Se você ainda não configurou no computador nativo, nem criar o container será possível. Faça esse procedimento primeiro no sistema nativo, se for o caso. Depois, faça no container.

Adicione o repositório (isso é para Debian):

deb http://httpredir.debian.org/debian/ stretch main contrib non-free

E faça a instalação dos pacotes necessários:

apt-get update
apt-get install linux-headers-* nvidia-driver

apt-get install nvidia-cuda-dev nvidia-cuda-toolkit  nvidia-driver

Vai demorar um bocado até tudo estar pronto. Se houver problemas pelo caminho, solucione-os. O próximo passo é modificar o arquivo de configuração para compilar o OpenCV4 (tudo isso já no container):

apt-get install vim vim-common vim-scripts
cd build_opencv
vi configs/default_cuda.conf

Se não souber usar o vi, recomendo que utilize outra coisa; nano, pico, midnight-commander. Vá até a parte gui support e deixe-a assim:

# gui support
FLAGS+=" -D WITH_OPENGL=OFF"
FLAGS+=" -D WITH_GTK=ON"
FLAGS+=" -D WITH_QT=OFF"

Antes de iniciar a compilação, já precavenha-se do iminente erro relacionado ao v4l:

sudo su
mkdir /usr/include/ffmpeg
cd /usr/include/ffmpeg
ln -sf /usr/include/x86_64-linux-gnu/libavcodec/*.h ./
ln -sf /usr/include/x86_64-linux-gnu/libavformat/*.h ./
ln -sf /usr/include/x86_64-linux-gnu/libswscale/*.h ./


sudo apt-get install python-software-properties
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo add-apt-repository ppa:jonathonf/gcc-8.0
sudo apt-get update
sudo apt-get install gcc-8 g++8

Agora iniciando o processo de compilação:

./build_opencv.sh -c configs/default_cuda.conf --build

O que precisar confirmar com Y, não deixe de fazê-lo. Quando for perguntada a versão do GTK, escolha 3.

As dependências serão baixadas e instaladas automaticamente. O processo leverá um tempo gigantesco, mas deve funcionar no final.

Instalei alguns pacotes mais, você pode preceder a compilação com essa instalação:

sudo apt-get install qt5-default qttools5-dev-tools
sudo apt-get update
sudo apt-get upgrade

sudo apt-get install wget unzip

sudo apt-get install build-essential cmake pkg-config

sudo apt-get install libjpeg8-dev libtiff5-dev libjasper-dev libpng12-dev

sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get install libxvidcore-dev libx264-dev

sudo apt-get install libgtk-3-dev

sudo apt-get install libatlas-base-dev gfortran

sudo apt-get install python2.7-dev python3.5-dev

Quando finalizado, será exibido algo como:

dnn no opencvCopie o pacote para /root.

 cp /root/new/build_opencv/sources/opencv-4.0.0/build/opencv_4.0.0-1_amd64.deb /root/

Em outra aba de terminal, copie o pacote de dentro do container para o sistema nativo:

 docker cp gtx1050:/root/opencv_4.0.0-1_amd64.deb .

Instale o pacote:

dpkg -i opencv_4.0.0-1_amd64.deb

Depois, copie a lib cv2.so para o novo path:

cp  /usr/local/lib/pyhon2.7/dist-packages/cv2.so    /usr/local/python/cv2/python-2.7/

E faça o teste de importação da lib (ainda vai ter coisa pra resolver, mas ao menos haverá pistas):

python -c "import cv2"

Tive um erro com a versão da libcudart.

Vá ao site da Nvidia e baixe o pacote. Depois:

apt-get install gnupg-curl
dpkg -i cuda-repo-ubuntu1604_10.0.130-1_amd64.deb
apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub
apt-get update
apt-get install cuda

Por fim, faça um reboot.

Python não consegue importar o módulo cv2

Ainda com probemas? Depois do reboot, voltei a ter o problema, mas a biblioteca estava lá onde deveria estar. Bem, eu tenho uma gambiarra na manga que funcionou:

import sys
sys.path.append("/usr/local/lib/pyhon2.7/dist-packages/")
import cv2

Models

Agora, pegue os arquivos pb pb.txt  do arquivo opencv_face_detector.

Coloque-os no mesmo nível do script Python que fará o face detection. Eu chamei de facednn.py:

#!/usr/bin/env python
# # -*- coding: UTF-8 -*-
# Author: Djames Suhanko <djames.suhanko@gmail.com>
# Created: Qui 20 Dez 2018 21:12:35 -02
# Last Update: Qui 20 Dez 2018 21:12:35 -02
# File: <name>
# Notes: 
from __future__ import division
import time
import sys

sys.path.append("/usr/local/lib/pyhon2.7/dist-packages/")

import cv2

def detectFaceOpenCVDnn(net, frame):
    frameOpencvDnn = frame.copy()
    frameHeight = frameOpencvDnn.shape[0]
    frameWidth = frameOpencvDnn.shape[1]
    blob = cv2.dnn.blobFromImage(frameOpencvDnn, 1.0, (300, 300), [104, 117, 123], False, False)

    net.setInput(blob)
    detections = net.forward()
    bboxes = []
    for i in range(detections.shape[2]):
        confidence = detections[0, 0, i, 2]
        if confidence > conf_threshold:
            x1 = int(detections[0, 0, i, 3] * frameWidth)
            y1 = int(detections[0, 0, i, 4] * frameHeight)
            x2 = int(detections[0, 0, i, 5] * frameWidth)
            y2 = int(detections[0, 0, i, 6] * frameHeight)
            bboxes.append([x1, y1, x2, y2])
            cv2.rectangle(frameOpencvDnn, (x1, y1), (x2, y2), (0, 255, 0), int(round(frameHeight/150)), 8)
    return frameOpencvDnn, bboxes

if __name__ == "__main__" :

    # OpenCV DNN supports 2 networks.
    # 1. FP16 version of the original caffe implementation ( 5.4 MB )
    # 2. 8 bit Quantized version using Tensorflow ( 2.7 MB )
    DNN = "TF"
    if DNN == "CAFFE":
        modelFile = "res10_300x300_ssd_iter_140000_fp16.caffemodel"
        configFile = "deploy.prototxt"
        net = cv2.dnn.readNetFromCaffe(configFile, modelFile)
    else:
        modelFile = "opencv_face_detector_uint8.pb"
        configFile = "opencv_face_detector.pbtxt"
        net = cv2.dnn.readNetFromTensorflow(modelFile, configFile)

    conf_threshold = 0.7

    source = 0
    if len(sys.argv) > 1:
        source = sys.argv[1]

    cap = cv2.VideoCapture(0)
    hasFrame, frame = cap.read()

    vid_writer = cv2.VideoWriter('output-dnn-{}.avi'.format(str(source).split(".")[0]),cv2.VideoWriter_fourcc('M','J','P','G'), 15, (frame.shape[1],frame.shape[0]))

    frame_count = 0
    tt_opencvDnn = 0
    while(1):
        hasFrame, frame = cap.read()
        if not hasFrame:
            break
        frame_count += 1

        t = time.time()
        outOpencvDnn, bboxes = detectFaceOpenCVDnn(net,frame)
        tt_opencvDnn += time.time() - t
        fpsOpencvDnn = frame_count / tt_opencvDnn
        label = "OpenCV DNN ; FPS : {:.2f}".format(fpsOpencvDnn)
        cv2.putText(outOpencvDnn, label, (10,50), cv2.FONT_HERSHEY_SIMPLEX, 1.4, (0, 0, 255), 3, cv2.LINE_AA)

        cv2.imshow("Face Detection Comparison", outOpencvDnn)

        vid_writer.write(outOpencvDnn)
        if frame_count == 1:
            tt_opencvDnn = 0
        
        k = cv2.waitKey(10)
        if k == 27:
            break
    cv2.destroyAllWindows()
    vid_writer.release()

Depois, execute-o:

python facednn.py

E o resultado será algo como a imagem de destaque, mas provavelmente com um susto menor.

Meus agradecimentos ao meu mestre Leonardo Lontra, que sempre me orienta em ambos, visão computacional e inteligência artificial, além de ajudar a resolver os problemas que aparecem no trajeto. Esse código de exemplo também é uma cortesia dele. Não deixem de visitar seu repositório, lá está cheio de tesouros.

Vídeo

Fiz um vídeo curto só para mostrar a robustez da detecção, tire as crianças da sala para não assustá-las com minhas caretas. Quem usa o haarcascade com frontal face sabe que se deslocar o rosto ou inclinar a cabeça, já era a detecção, impressione-se!

Deixe seu like no vídeo, inscreva-se no canal e clique no sininho para receber notificações. Até a próxima!