Detectando objetos com métodos clássicos — OpenCV (cascades)

Arnaldo Gualberto
Ensina.AI
Published in
8 min readSep 21, 2018

Nos últimos anos, detectores de objetos baseados em Deep Learning vêm superando por uma larga margem detectores que utilizavam técnicas clássicas, como métodos em cascata (cascade). Porém, entender como os métodos clássicos funcionavam é um exercício interessante para ver seus defeitos e qualidades, e como eles inspiraram a criação dos métodos mais avançados. Nesse primeiro artigo da série (depois veremos o detector da Dlib), eu pretendo:

  • apresentar os detectores baseados em cascades;
  • mostrar suas vantagens e desvantagens;
  • dizer quando eles são mais indicados;
  • como treinar seu próprio detector de objetos; e
  • recomendações de parâmetros de treinamento

O que são cascades?

Os detectores baseados em cascade (cascata) são chamados assim pois treinam uma árvore de decisão em que cada nível analisa um conjunto de atributos diferentes e avalia se esses atributos representam ou não o objeto de interesse.

Cada nível dessa árvore é chamado de estágio. Cada estágio é composto por um mais classificadores fracos (weak classifiers). Esses classificadores fracos são, em geral, treinados com algoritmos baseados em métodos de boosting, como o Adaboost, até que eles atinjam uma taxa mínima de verdadeiros positivos e, ao mesmo tempo, uma taxa máxima de falsos positivos. Um detalhe importante é que os exemplos incorretamente classificados são automaticamente escolhidos para a próxima iteração do algoritmos, só que com pesos maiores. Ou seja, na próxima iteração, o algoritmo deve dar preferência a acertar os exemplos que ele errou anteriormente.

E como cascades funcionam na prática? Na prática, o sistema usa o esquema de janela deslizante sobre a imagem. Cada janela correspondente é classificado pelo cascade como positivo ou negativo. Outro detalhe importante é: para que um objeto seja considerado positivo, ele deve passar por todos os estágios. Isso é importante para permitir o algoritmo economizar tempo, já que se um único estágio dizer que o objeto pertence a classe negativa, o algoritmo já passa para próxima janela e recomeça o processo. Na prática, todo esse processo descrito nesse parágrafo é feito automaticamente pelo framework que você está utilizando. Você só precisa se preocupar em treinar o seu detector.

Agora que sabemos como os cascades funcionam, falta somente entender como eles extraem as features. A OpenCV disponibiliza dois tipos de features para treinamento de cascades: Haar e LBP. Atributos Haar são extraídos subtraindo diferentes pixels da imagem de acordo com as “máscaras” mostradas na figura abaixo. Lembrando que você tem a opção de escolher os tipos de máscaras com a flag -mode (ver seção de treinamento).

Filtros de Haar: Os pixels das regiões brancas são subtraídos dos pixels da região preta correspondente. Por exemplo, a máscara 1(a) apenas subtrai o valor de um pixel do seu vizinho á direita. A máscara 2(c) subtrai o pixel central dos pixels logo acima e abaixo. A máscara 3(a) subtrai um pixel central dos seus 8-vizinhos.
Exemplos de atributos Haar calculadas sobre uma imagem.

Por outro lado, atributos LBP são calculados da seguinte forma. Para cada pixel da imagem, compare o pixel central com sua vizinhança de 8-vizinhos, binarizando-os. Depois, transforme o código binário correspondente em um valor decimal. A figura abaixo exemplifica esse processo.

Exemplo de código LBP calculado para um pixel de uma imagem 16x16.

Treinamento com a OpenCV

A primeira coisa a se fazer quando se quer treinar um detector é gabaritar o objeto de interesse nas imagens que você tem. Vale salientar que cada cascade da OpenCV só é capaz de detectar um tipo de objeto de interesse por vez, ou seja, cada cascade é responsável pode detectar uma classe. Se você tem vários tipos de objetos (por exemplo, cachorros e gatos), você terá de treinar cascades diferentes para cada um. Também é importante destacar que você terá que gabaritar exemplos negativos, ou seja, marcar rects que não são o objeto de interesse. Independente da ferramenta que você utilizar para gabaritar os objetos, você terá que gerar um arquivo de gabarito com o seguinte formato:

gabarito.data: cada linha contém o caminho relativo para imagem, a quantidade de retângulos naquela imagem e as coordenadas (x, y, width, height) de cada retângulo

Com esse arquivo em mão, a gente vai precisar usar agora dois executáveis disponibilizados pela opencv: opencv_createsamples.exe e opencv_traincascade.exe. O primeiro vai gerar um arquivo binário com as amostra positivas redimensionadas para um tamanho especificado, enquanto o segundo vai utilizar esse arquivo para treinar o detector.

Portanto, copie esses executáveis para o local do seu arquivo de gabarito, abra o terminal nessa pasta e digite:

opencv_createsamples.exe -vec VEC_FILE.vec -w 20 -h 20 -info gabarito.data -num 2500

Você pode conferir a lista completa de parâmetros neste link.

Esse comando vai criar um arquivo .vec na pasta que você está. Esse arquivo contém todas as rects que você gabaritou em formato binário redimensionadas para o tamanho fornecido pelos parâmetros -w e -h. Agora, é hora de treinar o detector. Ainda com o terminal aberto, digite:

opencv_traincascade -data OUTPUT_FOLDER -vec VEC_FILE.vec -bg negatives.data -minHitRate 0.999 -maxFalseAlarmRate 0.5 -numPos 2000 -numNeg 4000 -w 20 -h 20 -mode ALL -featureType Haar -maxWeakCount 1000

Observações:

  • o arquivo VEC_FILE.vec deve ser o mesmo especificado no opencv_createsamples.exe. Mesma coisa para os parâmetros -w e -h.
  • o -numPos deve ser ligeiramente menor que a quantidade de rects disponíveis no VEC_FILE.vec. Aconselho a colocar 10% menor. Mesma coisa para o -numNeg.
  • Se você quiser utilizar atributos LBP, utilize -featureType LBP e omita o parâmetro -mode.

Cada estágio no treinamento demora mais que o anterior! Porém, é possível recomeçar o treinamento do ponto onde parou caso dê algum problema. Então, fique tranquilo e vá tomar um café!

Como avaliar o seu Detector?

Em geral, detectores são avaliados por Precision e Recall. Como eu já disse em outro post, o recall analisa “quantos objetos do seu banco foram detectados?” Enquanto a precisão responde “quantas detecções são realmente o objeto de interesse?”. Você também pode conferir essas métricas visualmente na figura abaixo:

Porém, avaliar o detector por duas métricas distintas, torna mais difícil escolher qual o melhor detector. Para isso, existe a F-measure, também conhecida como f1, que relaciona precisão e recall:

Dicas para o Treinamento

  • Se possível, tenha ao menos 2500 ROIs positivas e 5000 imagens negativas, no mínimo; Tenha certeza que nas imagens negativas não aparecem o objeto que se deseja detectar.
  • ROIs extraídas do domínio da aplicação geram melhores resultados que ROIs de bancos de imagens da internet.
  • Tente extrair ROIs maiores que 20x20, ou pelo menos, com 300–400 pixels. As proporções das ROIs extraídas também devem ser bastante similares. Exemplo de ROIs similares: {20x20, 50x50, 65x64, …}, {20x40, 30x60, 45x85, …}. Exemplo de ROIs com proporções variantes: {20x20, 20x30, 40x20, …}.
  • O tamanho das ROI positivas influencia na métrica de avaliação do detector. Lembre-se disso quando for marcar as ROIs. A OpenCV, em geral, acha objetos maiores que as ROIs demarcadas, podendo prejudicar o coeficiente de Jaccard.
  • Treine ao menos 15 estágios e avalie a performance a partir de um certo estágio. Verifique a curva Precisão x Recall no banco de teste para cada estágio e decida se mais estágios serão suficientes. Lembre-se que todo estágio de treinamento demora mais que o anterior.
Curvas de precisão, recall e f-measure para cada estágio
  • O treinamento deve parar quando as curvas Precisão e Recall se cruzam no banco de teste; (ou quando umas das curvas parar de crescer ou começar a diminuir, caso não venham a se cruzar). Portanto, escolha um dos cascades que apresentam as melhoras taxas de F-measure.

Lembrando que isso é apenas uma recomendação baseada na minha experiência com os detectores. Se você acha que um determinado estágio tem bons resultados, escolha-o!

  • Detectores Haar convergem mais rápido que LBP, ou seja, necessitam de menos estágios para obter resultados de Precisão e Recall próximos aos detectores LBP. Mesmo assim, o treinamento é mais demorado.
  • Detectores LBP são mais rápidos para treinar e detectar que detectores Haar.

Recomendações de parâmetros

opencv_createsamples.exe

  • num: defina esse parâmetro igual ao número de ROIs presentes nas imagens
  • w, h: aconselha-se definir wxh, pelo menos, 20x20. É altamente importante que a proporção entre -w e -h seja a mesma (ou bastante parecida) com a proporção das ROIs extraídas. Se as ROIs do banco têm proporções muito diferentes (por exemplo, ±0.5, ±0.7 e/ou ±1.0) é melhor treinar diferentes detectores para um mesmo objeto (um para cada proporção). Os valores de w e h também deve ser menores ou iguais ao tamanho da menor ROI do banco, porém pelo menos 20x20.

opencv_traincascade.exe

  • numPos: deve sempre ser menor que o parâmetro -num utilizado no opencv_createsamples.exe, pois o número de imagens utilizada pelo opencv_traincascade.exe pode aumentar com os estágios. Se possível, observe a taxa de imagens consumidas pelo treinamento a cada estágio. Ou então, defina como 90–95% de -num.
  • numNeg: aconselha-se definir esse parâmetro como o dobro de -numPos.
  • numStages: inicialmente, treine um detector para 15 estágios e siga as orientações definidas em “Recomendações de Treinamento”
  • minHitRate: 0.999 é um valor ideal.
  • maxFalseAlarmRate: 0.5 é um valor recomendado.

Uma boa taxa de acceptanceRatio é aproximadamente 0.0002.

Considerações Finais

Se você nunca teve experiência com detector antes, treinar um detector com a OpenCV é uma boa experiência de aprendizagem. O processo todo é bem simples e seu detector treinado pode facilmente rodar em tempo real. Obviamente, se você precisa de altas taxas de precisão, eu aconselho que você dê preferência a um detector baseado em Deep Learning, mas lembre-se que ele será mais lento.

Umas das vantagens dos detectores da OpenCV em relação a outros métodos clássicos é que eles são relativamente bons para objetos pequenos e mais robustos a variação de pose e forma. Porém, necessitam de um grande número de imagens positivas e negativas gabaritadas e apresentam menor precisão e recall.

Por fim, se você quer ver alguns detectores que eu já treinei com a OpenCV em ação, dá uma olhada nos vídeos abaixo:

--

--