Skip to main content
Balneário Camboriú - SC +55 (47) 99725 1117
Siga-nos:

Neste tutorial, irei apresentar como detectar pontos faciais, quais as regiões importantes a serem utilizadas na detecção e apresentar resultados de experimentos realizados com as bibliotecas OpenCV.

Os pontos faciais podem ser utilizados para diversas finalidades e são uma grande área de estudo científico. As principais áreas e utilidades são: o reconhecimento de expressões e emoções, identificação de rosto cansado e sonolência, identificação de direção do rosto e alinhamento facial,  detecção de piscadas e no auxílio do reconhecimento facial.

Podendo serem utilizados para vários fins, os algoritmos de detecção possuem um único objetivo, detectar pontos faciais.

O que são pontos faciais

Os pontos faciais, em visão computacional, como o próprio nome diz, são pequenas regiões faciais identificadas por uma numeração. Estas regiões, são identificadas por um ponto e uma numeração que pode variar de técnica para técnica utilizada.

Estes pontos têm como objetivo identificar os contornos dos olhos, nariz, boca e os arredores do rosto, iniciando na parte superior, pelas sobrancelhas, terminando na parte inferior no queixo. As orelhas, geralmente, não são identificadas ao coletar os pontos faciais, porém, caso seja necessário detectar estas regiões, os algoritmos disponibilizados pelo OpenCV (utilizados nos experimentos apresentados mais abaixo) estão preparados para serem treinados a fim de detectar estas regiões.

Os algoritmos de detecção facial, comumente, identificam 68 pontos faciais. Na Fig.1 são ilustrados estes pontos faciais e abaixo estão descritos as localizações destes pontos:

  • Contorno do rosto: inicia no ponto 1 e termina no ponto 17;
  • Sobrancelha esquerda: inicia no ponto 18 e termina no ponto 22;
  • Sobrancelha direita: inicia no ponto 23 e termina no ponto 27;
  • Nariz: inicia no ponto 28 e termina no ponto 36;
  • Olho esquerdo: inicia no ponto 37 e termina no ponto 42;
  • Olho direito: inicia no ponto 43 e termina no ponto 46;
  • Contorno externo da boca: inicia no ponto 49 e termina no ponto 60;
  • Contorno interno da boca: inicia no ponto 61 e termina no ponto 68.
Fig 1. Principais pontos faciais utilizados. Autor: Huber, Patrik. (2018)

Identificando pontos faciais

Existem diversas formas de identificar pontos faciais, desde a utilização de morfologia matemática até redes neurais. Para se ter ideia, em uma rápida busca pelos termos “face” “landmark” “detection” só na biblioteca digital IEEE, foram encontrados 846 artigos publicados em revistas e conferências. É muito provável que boa parte destes artigos estejam relacionados a experimentos e muitos deles possuem técnicas semelhantes, porém, ilustra a variedades de trabalhos para tentar identificar pontos faciais.

Apesar de existirem diversos trabalhos com o objetivo de identificar pontos faciais, existe uma grande semelhança em todos eles, os pontos são detectados em imagens com apenas a região do rosto, no máximo um fundo ao redor do rosto, ou seja, para identificar os pontos faciais, antes é preciso coletar apenas o rosto da(s) pessoa(s) na imagem. Em alguns casos também é preciso rotacionar o rosto, a fim de deixar os olhos o mais alinhado possível. Todo este processo é necessário, porque estes algoritmos possuem um único objeto, encontrar os pontos faciais em rostos, e são treinados para identificá-los em rostos verticalmente alinhados ou pouco inclinados.

Com isto, podemos presumir, as seguintes etapas antecessoras à coleta dos pontos faciais na imagem: (1) identificar os rostos na imagem; (2) rotacionar o rosto caso necessário  e (3) identificar os pontos faciais. A imagem da Fig.2 ilustra este processo.

Fig 2. Etapas antecessoras a detecção de pontos faciais.

Algoritmos AAM, LBF e Kazemi

Dos diversos algoritmos existentes, 3 deles estão disponibilizados na biblioteca OpenCV: o algoritmo de detecção por Active Appearance Model (AAM) ou modelo de aparência ativa,  o algoritmo de detecção por Local Binary Features (LBF) ou recursos binários locais e o algoritmo de Kazemi. Estes 3 algoritmos estão prontos para serem treinados e utilizados na detecção de pontos faciais. São esses algoritmos que são testados e apresentados neste post.

O algoritmo AAM foi apresentado pela primeira vez por Edwards, Cootes e Taylor no contexto da análise facial na 3ª Conferência Internacional sobre Reconhecimento de Rostos e Gestos, 1998 [CET1][CET2]. O segundo algoritmo LBF, proposto por Shaoqing Ren et al. realiza a deteção dos pontos por meio de regração [RCWS]. E o terceiro algoritmo de Kazemi,  desenvolvido em 2014 por Vahid Kazemi e Josephine Sullivan realiza a detecção por meio de um conjunto de árvores de regressão [KJ1].

Os autores do algoritmo AAM relatam, na  European Conference, que a abordagem deste método pode ser utilizada para propósitos gerais (não só na detecção de pontos faciais) em visão computacional. [CET3][CET4].

Código e Experimentos

A seguir, são apresentados partes do código criado para realizar os experimentos de detecção de pontos faciais. Caso queira replicar estes experimentos, basta baixar o código fonte disponível no github, link na seção “Source” no final deste post.

Neste primeiro bloco, são inicializados todos os detectores do sistema, o detector facial, o detector de olhos e os algoritmos de detecção de pontos faciais. Para iniciar todos estes detectores é muito simples, basta instanciar a classe cv::CascadeClassifier e informar o modelo treinado, para os detectores faciais e de olhos, e chamar a função create das classes FacemarkLBF, FacemarkAMM e FacemarkKazemi para iniciar os detectores de pontos faciais.

...
cv::Ptr< cv::CascadeClassifier > faceDetector;
cv::Ptr< cv::CascadeClassifier > eyeDetector;
cv::Ptr< cv::face::Facemark > facemark;
main() {
...
  //Inicia o detector de rostos por haarcascade
  faceDetector = iniciarDetectorFacial();
  //Inicia o detector de olhos por haarcascade
  eyeDetector = iniciarDetectorOlhos();
  //Inicia o detector de pontos faciais
  facemark = iniciarDetectorPontosFacialLBF();
  //facemark = iniciarDetectorPontosFacialAAM();
  //facemark = iniciarDetectorPontosFacialKazemi();
...
}
cv::Ptr iniciarDetectorFacial() {
 cv::Ptr faceDetector = new CascadeClassifier;
 faceDetector->load("../../extra/haarcascade_frontalface_alt2.xml");
 return faceDetector;
}
cv::Ptr initSimpleEyeDetector() {
  cv::Ptr faceDetector = new CascadeClassifier;
  faceDetector->load("../../extra/haarcascade_eye.xml");
  return faceDetector;
}
cv::Ptr iniciarDetectorPontosFacialLBF() {
  cv::Ptr facemark = cv::face::FacemarkLBF::create();
  facemark->loadModel("../../extra/lbfmodel.yaml");
  return facemark;
}
cv::Ptr iniciarDetectorPontosFacialKazemi() {
  cv::Ptr facemark = cv::face::FacemarkKazemi::create();
  facemark->loadModel("../../extra/kazemi_model.dat");
  return facemark;
}
cv::Ptr iniciarDetectorPontosFacialAAM()
{
  cv::Ptr facemark = cv::face::FacemarkAAM::create();
  facemark->loadModel("../../extra/aam_model.yaml");
  return facemark;
}

Observe que todos os detectores utilizam a função “load” e “loadModel” para carregar os modelos de dados treinados. Estes detectores não funcionam caso não seja informada a base de dados treinada. Junto com o código fonte, deste projeto, encontra-se a base de dados utilizada nos experimentos. Esta base de dados é extremamente simples e não é útil para projetos mais robustos e aplicações finais.

Note, que foi utilizado o detector facial e de olhos por Haarcascade, porém, nada impede de serem utilizados outros detectores faciais e olhos para este tipo de projeto, como técnica de pré-processamento. 

No próximo bloco, abaixo, é capturado o vídeo para detecção dos pontos faciais, alterando a resolução da imagem para 320 pixels, a fim de facilitar o processamento dos detectores. Para capturar a câmera do vídeo com o OpenCV, basta instanciar a classe “cv::VideoCapturecap” e coletar a imagem do vídeo com o operador “>>” conforme trecho de código “cap>>img”.

main() {
  ...
  //Inicia captura dos vídeos
  cv::VideoCapturecap cap(0);
  if (!cap.isOpened()) {
    std::cout<<"Video Capture Fail"<<std::endl; return1; } cv::Mat img; cap>>img;
    return 1;
  }
  //Calcula nova dimensão da imagem para 320 pixels
  auto showSize = cv::Size(320, ((float)320 / img.cols) * img.rows);
  for (;;)
  {
    //Coleta a imagem da camera
    cap>>img;
    //Reescala a imagem para uma largura de 320 pixels
    cv::resize(img, img, showSize, 0, 0, cv::INTER_LINEAR_EXACT);
    coletarPontosFaciais(img);

    cv::imshow("Origem", img);
    cv::waitKey(5);
  }
}

E no último trecho de código, é apresentado como coletar os pontos faciais com o OpenCV.

...
void coletarPontosFaciais(cv::Mat imagemOriginal)
{
    cv::Mat imagemOriginalCinza;
    std::vector< cv::Rect > rostosDetectados;
    std::vector< std::vector< cv::Point2f > > pontosFaciais;
    cv::Mat imagemComPontosFaciais;
    {
        //Converte em tons de cinza e equaliza a imagem
        //Detecção por haarcascade funcionam bem com imagens equalizadas
        cvtColor(imagemOriginal, imagemOriginalCinza, cv::COLOR_BGR2GRAY);
        equalizeHist(imagemOriginalCinza, imagemOriginalCinza);
        //Detecta os rostos na imagem
        faceDetector->detectMultiScale(imagemOriginal, rostosDetectados);
    }
    if (rostosDetectados.size() != 0)
    {
        imagemComPontosFaciais = imagemOriginal.clone();
        //Demarca rosto na imagem original
        for (auto &&rostoDetec : rostosDetectados)
        {
            demarcarRostoDetectado(imagemOriginal, rostoDetec);
        }
        if (requerDetecaoDosOlhos) {
            //Detecta os pontos faciais com código personalizado para o algorítmo AAM
            facemarkAAMFit(static_cast< cv::face::FacemarkAAM* >(facemark.get()), eyeDetector,
                fitEmTonsDeCinza
                    ? imagemOriginalCinza
                    : imagemOriginal,
                rostosDetectados, 
                pontosFaciais);
        } else {
            //Detecta os pontos faciais
            pontosDetectados = facemark->fit(fitEmTonsDeCinza
                              ? imagemOriginalCinza
                              : imagemOriginal,
                          rostosDetectados, pontosFaciais);
        }
        if (pontosDetectados)
        {
            demarcarPontosFaciais(imagemComPontosFaciais, rostosDetectados, pontosFaciais);
            imshow("Pontos faciais", imagemComPontosFaciais);
        }
    }
    escreverQtdPiscadas(imagemOriginal, piscadas);
}

Observe que, a primeira etapa realizada é a detecção dos rostos através da função “faceDetector->detectMultiScale“, onde é informada uma imagem de entrada, e como resposta desta função, é devolvido um vetor de retângulos, com as regiões dos rostos encontrados. Mais abaixo, na função “facemark->fit”,  é chamada a função que detecta os pontos faciais, esta função deve receber a imagem que contém os rostos e o vetor de retângulos, com as posições dos rostos encontrados. Por fim, a função fit, devolve todos os pontos faciais encontrados, de todos os rostos informados à ela.

Neste mesmo bloco de códigos, mencionado acima, encontrasse a função “facemarkAAMFit“, ela possui um código personalizado para realizar a coleta dos pontos faciais, com o algoritmo AAM. Esta personalização coleta informações de rotação e translação da imagem, a fim de rotacionar e ajustar a posição do rosto, etapa necessária para o algoritmo AMM. Esta função, está disponível no código fonte do github e foi copiada do próprio tutorial da OpenCV

As funções demarcarRostoDetectado e demarcarPontosFaciais, não são funções do OpenCV e foram criadas apenas para demarcarem as regiões do rosto e os pontos faciais detectados.  Estas funções, também estão disponíveis no código fonte do projeto.

Como resultado deste projeto, foi realizado o experimento ilustrado pela Fig 3.  Observe que o detector facial (etapa de pré-processamento) encontrou uma segunda face, que na verdade eram 3 potes. Por consequência desta falha, na detecção facial, o detector de pontos também tentou identificar os pontos faciais.

Fig 3 .Resultados obtidos com este experimento.

Espero que esse conteúdo os ajudem na tarefa de identificação de pontos faciais. 
Caso tenham dúvidas e sugestões de melhorias no post, deixe seu comentário abaixo.

Source:

https://github.com/visaocomputacionalexemplos/experimentos_faciais/tree/main/detectar_pontos_faciais_opencv

Referencias:

Huber, Patrik. (2018). Real-time 3D morphable shape model fitting to monocular in-the-wild videos.

[CET1] Edwards, G. J.; Taylor, C. J.; Cootes, T. F. (1998). “Interpreting face images using active appearance models”. Proceedings Third IEEE International Conference on Automatic Face and Gesture Recognition. p. 300[CET2] Cootes, T.F.; Edwards, G.J.; Taylor, C.J. Active Appearance Models. Wolfson Image Analysis Unit,. Department of Medical Biophysics,[CET3] Cootes, T. F.; Edwards, G. J.; Taylor, C. J. (1998). “Active appearance models”. Computer Vision — ECCV’98. Lecture Notes in Computer Science. 1407. p. 484.[CET4] Cootes, T. F.; Edwards, G. J.; Taylor, C. J. (2001). “Active appearance models”. IEEE Transactions on Pattern Analysis and Machine Intelligence. 23 (6): 681.  [KJ1] V. Kazemi and J. Sullivan, “One millisecond face alignment with an ensemble of regression trees,” 2014 IEEE Conference on Computer Vision and Pattern Recognition, 2014, pp. 1867-1874, doi: 10.1109/CVPR.2014.241. [RCWS] S. Ren, X. Cao, Y. Wei and J. Sun, “Face Alignment at 3000 FPS via Regressing Local Binary Features,” 2014 IEEE Conference on Computer Vision and Pattern Recognition, 2014, pp. 1685-1692, doi: 10.1109/CVPR.2014.218.

Nenhum comentário ainda!

Seu endereço de e-mail não será publicado