Tutorial OpenCV: Detecção de Objetos em Tempo Real Usando MSER no iOS

Publicados: 2022-03-11

Nos últimos anos, o desempenho médio dos telefones celulares aumentou significativamente. Seja por pura potência de CPU ou capacidade de RAM, agora é mais fácil realizar tarefas de computação pesada em hardware móvel. Embora essas tecnologias móveis estejam indo na direção certa, ainda há muito a ser feito nas plataformas móveis, principalmente com o advento da realidade aumentada, realidade virtual e inteligência artificial.

Um grande desafio em visão computacional é detectar objetos de interesse em imagens. O olho e o cérebro humanos fazem um trabalho excepcional, e replicar isso em máquinas ainda é um sonho. Nas últimas décadas, foram desenvolvidas abordagens para imitar isso em máquinas, e isso está melhorando.

Neste tutorial, exploraremos um algoritmo usado na detecção de blobs em imagens. Também usaremos o algoritmo, da biblioteca de código aberto, OpenCV, para implementar um protótipo de aplicativo para iPhone que usa a câmera traseira para adquirir imagens e detectar objetos nelas.

Tutorial OpenCV

OpenCV é uma biblioteca de código aberto que fornece implementações dos principais algoritmos de visão computacional e aprendizado de máquina. Se você deseja implementar um aplicativo para detectar rostos, jogar cartas em uma mesa de pôquer ou até mesmo um aplicativo simples para adicionar efeitos a uma imagem arbitrária, o OpenCV é uma ótima opção.

O OpenCV é escrito em C/C++ e possui bibliotecas wrapper para todas as principais plataformas. Isso o torna especialmente fácil de usar no ambiente iOS. Para usá-lo em um aplicativo iOS Objective-C, baixe o OpenCV iOS Framework no site oficial. Por favor, certifique-se de estar usando a versão 2.4.11 do OpenCV para iOS (que este artigo assume que você está usando), pois a versão mais recente, 3.0, tem algumas mudanças que quebram a compatibilidade na forma como os arquivos de cabeçalho são organizados. Informações detalhadas sobre como instalá-lo estão documentadas em seu site.

MSER

MSER, abreviação de regiões extremas maximamente estáveis, é um dos muitos métodos disponíveis para detecção de blob em imagens. Em palavras simples, o algoritmo identifica conjuntos contíguos de pixels cujas intensidades de pixel de limite externo são maiores (por um determinado limite) do que as intensidades de pixel de limite interno. Tais regiões são ditas maximamente estáveis ​​se não mudam muito em uma quantidade variável de intensidades.

Embora existam vários outros algoritmos de detecção de blob, o MSER foi escolhido aqui porque tem uma complexidade de tempo de execução bastante leve de O(n log(log(n))) onde n é o número total de pixels na imagem. O algoritmo também é robusto para desfoque e escala, o que é vantajoso quando se trata de processar imagens adquiridas por meio de fontes em tempo real, como a câmera de um celular.

Para o propósito deste tutorial, projetaremos o aplicativo para detectar o logotipo da Toptal. O símbolo tem cantos afiados, e isso pode levar a pensar em quão eficazes os algoritmos de detecção de canto podem ser na detecção do logotipo da Toptal. Afinal, esse algoritmo é simples de usar e entender. Embora os métodos baseados em cantos possam ter uma alta taxa de sucesso quando se trata de detectar objetos que são distintamente separados do fundo (como objetos pretos em fundos brancos), seria difícil obter a detecção em tempo real do logotipo da Toptal no mundo real imagens, onde o algoritmo estaria constantemente detectando centenas de cantos.

Estratégia

aprendizado de máquina e opencv

Para cada quadro de imagem que o aplicativo adquire por meio da câmera, ele é convertido primeiro em escala de cinza. As imagens em tons de cinza têm apenas um canal de cor, mas o logotipo estará visível, no entanto. Isso torna mais fácil para o algoritmo lidar com a imagem e reduz significativamente a quantidade de dados que o algoritmo precisa processar para obter pouco ou nenhum ganho extra.

Em seguida, usaremos a implementação do OpenCV o algoritmo para extrair todos os MSERs. Em seguida, cada MSER será normalizado transformando seu retângulo delimitador mínimo em um quadrado. Esta etapa é importante porque o logotipo pode ser adquirido de diferentes ângulos e distâncias e isso aumentará a tolerância à distorção da perspectiva.

Além disso, várias propriedades são calculadas para cada MSER:

  • Número de furos
  • Relação da área do MSER para a área do seu casco convexo
  • Relação da área de MSER para a área de seu retângulo de área mínima
  • Relação entre o comprimento do esqueleto do MSER e a área do MSER
  • Relação da área do MSER para a área de seu maior contorno

aplicativos ios e aprendizado de máquina

Para detectar o logotipo da Toptal em uma imagem, as propriedades de todos os MSERs são comparadas com as propriedades do logotipo da Toptal já aprendidas. Para o propósito deste tutorial, as diferenças máximas permitidas para cada propriedade foram escolhidas empiricamente.

Por fim, a região mais semelhante é escolhida como resultado.

Aplicativo iOS

Usar o OpenCV do iOS é fácil. Se você ainda não fez isso, aqui está um resumo rápido das etapas envolvidas na configuração do Xcode para criar um aplicativo iOS e usar o OpenCV nele:

  1. Crie um novo nome de projeto “SuperCool Logo Detector”. Como idioma, deixe Objective-C selecionado.

  2. Adicione um novo arquivo de cabeçalho de prefixo (.pch) e nomeie-o como PrefixHeader.pch

  3. Entre no projeto “SuperCool Logo Detector” Build Target e na guia Build Settings, encontre a configuração “Prefix Headers”. Você pode encontrá-lo na seção Idioma do LLVM ou usar o recurso de pesquisa.

  4. Adicione “PrefixHeader.pch” à configuração de cabeçalhos de prefixo

  5. Neste ponto, se você não instalou o OpenCV para iOS 2.4.11, faça-o agora.

  6. Arraste e solte a estrutura baixada no projeto. Verifique “Estruturas e bibliotecas vinculadas” em suas configurações de destino. (Deve ser adicionado automaticamente, mas é melhor estar seguro.)

  7. Além disso, vincule as seguintes estruturas:

    • AVFoundation
    • Biblioteca de Ativos
    • CoreMedia
  8. Abra “PrefixHeader.pch” e adicione as 3 linhas a seguir:

     #ifdef __cplusplus #include <opencv2/opencv.hpp> #endif”
  9. Altere as extensões dos arquivos de código criados automaticamente de “ .m” para “ .mm”. OpenCV é escrito em C++ e com *.mm você está dizendo que usará Objective-C++.

  10. Importe “opencv2/highgui/cap_ios.h” em ViewController.he altere ViewController para estar em conformidade com o protocolo CvVideoCameraDelegate:

     #import <opencv2/highgui/cap_ios.h>
  11. Abra Main.storyboard e coloque um UIImageView no controlador de exibição inicial.

  12. Faça uma saída para ViewController.mm chamada “imageView”

  13. Crie uma variável “CvVideoCamera *camera;” em ViewController.h ou ViewController.mm e inicialize-o com uma referência à câmera traseira:

     camera = [[CvVideoCamera alloc] initWithParentView: _imageView]; camera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; camera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480; camera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; camera.defaultFPS = 30; camera.grayscaleMode = NO; camera.delegate = self;
  14. Se você construir o projeto agora, o Xcode avisará que você não implementou o método “processImage” do CvVideoCameraDelegate. Por enquanto, e por uma questão de simplicidade, vamos apenas adquirir as imagens da câmera e sobrepô-las com um texto simples:

    • Adicione uma única linha a “viewDidAppear”:
     [camera start];
    • Agora, se você executar o aplicativo, ele solicitará permissão para acessar a câmera. E então você deve ver o vídeo da câmera.

    • No método “processImage” adicione as duas linhas a seguir:

     const char* str = [@"Toptal" cStringUsingEncoding: NSUTF8StringEncoding]; cv::putText(image, str, cv::Point(100, 100), CV_FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(0, 0, 255));

É bem isso. Agora você tem um aplicativo muito simples que desenha o texto “Toptal” nas imagens da câmera. Agora podemos construir nosso aplicativo de detecção de logotipo de destino a partir deste mais simples. Para resumir, neste artigo discutiremos apenas alguns segmentos de código que são críticos para entender como o aplicativo funciona em geral. O código no GitHub tem uma quantidade razoável de comentários para explicar o que cada segmento faz.

Como o aplicativo tem apenas um objetivo, detectar o logotipo da Toptal, assim que é iniciado, os recursos do MSER são extraídos da imagem do modelo fornecido e os valores são armazenados na memória:

 cv::Mat logo = [ImageUtils cvMatFromUIImage: templateImage]; //get gray image cv::Mat gray; cvtColor(logo, gray, CV_BGRA2GRAY); //mser with maximum area is std::vector<cv::Point> maxMser = [ImageUtils maxMser: &gray]; //get 4 vertices of the maxMSER minrect cv::RotatedRect rect = cv::minAreaRect(maxMser); cv::Point2f points[4]; rect.points(points); //normalize image cv::Mat M = [GeometryUtil getPerspectiveMatrix: points toSize: rect.size]; cv::Mat normalizedImage = [GeometryUtil normalizeImage: &gray withTranformationMatrix: &M withSize: rect.size.width]; //get maxMser from normalized image std::vector<cv::Point> normalizedMser = [ImageUtils maxMser: &normalizedImage]; //remember the template self.logoTemplate = [[MSERManager sharedInstance] extractFeature: &normalizedMser]; //store the feature [self storeTemplate];

O aplicativo possui apenas uma tela com botão Iniciar/Parar, e todas as informações necessárias, como FPS e número de MSERs detectados, são desenhadas automaticamente na imagem. Desde que o aplicativo não seja interrompido, para cada quadro de imagem na câmera, o seguinte método processImage é invocado:

 -(void)processImage:(cv::Mat &)image { cv::Mat gray; cvtColor(image, gray, CV_BGRA2GRAY); std::vector<std::vector<cv::Point>> msers; [[MSERManager sharedInstance] detectRegions: gray intoVector: msers]; if (msers.size() == 0) { return; }; std::vector<cv::Point> *bestMser = nil; double bestPoint = 10.0; std::for_each(msers.begin(), msers.end(), [&] (std::vector<cv::Point> &mser) { MSERFeature *feature = [[MSERManager sharedInstance] extractFeature: &mser]; if(feature != nil) { if([[MLManager sharedInstance] isToptalLogo: feature] ) { double tmp = [[MLManager sharedInstance] distance: feature ]; if ( bestPoint > tmp ) { bestPoint = tmp; bestMser = &mser; } } } }); if (bestMser) { NSLog(@"minDist: %f", bestPoint); cv::Rect bound = cv::boundingRect(*bestMser); cv::rectangle(image, bound, GREEN, 3); } else { cv::rectangle(image, cv::Rect(0, 0, W, H), RED, 3); } // Omitted debug code [FPS draw: image]; }

Esse método, em essência, cria uma cópia em escala de cinza da imagem original. Ele identifica todos os MSERs e extrai seus recursos relevantes, pontua cada MSER por semelhança com o modelo e escolhe o melhor. Finalmente, ele desenha um limite verde ao redor do melhor MSER e sobrepõe a imagem com meta-informações.

Abaixo estão as definições de algumas classes importantes e seus métodos neste aplicativo. Seus propósitos são descritos nos comentários.

GeometryUtil.h

 /* This static class provides perspective transformation function */ @interface GeometryUtil : NSObject /* Return perspective transformation matrix for given points to square with origin [0,0] and with size (size.width, size.width) */ + (cv::Mat) getPerspectiveMatrix: (cv::Point2f[]) points toSize: (cv::Size2f) size; /* Returns new perspecivly transformed image with given size */ + (cv::Mat) normalizeImage: (cv::Mat *) image withTranformationMatrix: (cv::Mat *) M withSize: (float) size; @end

MSERManager.h

 /* Singelton class providing function related to msers */ @interface MSERManager : NSObject + (MSERManager *) sharedInstance; /* Extracts all msers into provided vector */ - (void) detectRegions: (cv::Mat &) gray intoVector: (std::vector<std::vector<cv::Point>> &) vector; /* Extracts feature from the mser. For some MSERs feature can be NULL !!! */ - (MSERFeature *) extractFeature: (std::vector<cv::Point> *) mser; @end

MLManager.h

 /* This singleton class wraps object recognition function */ @interface MLManager : NSObject + (MLManager *) sharedInstance; /* Stores feature from the biggest MSER in the templateImage */ - (void) learn: (UIImage *) templateImage; /* Sum of the differences between logo feature and given feature */ - (double) distance: (MSERFeature *) feature; /* Returns true if the given feature is similar to the one learned from the template */ - (BOOL) isToptalLogo: (MSERFeature *) feature; @end

Depois que tudo estiver conectado, com este aplicativo, você poderá usar a câmera do seu dispositivo iOS para detectar o logotipo da Toptal de diferentes ângulos e orientações.

Detectando uma imagem (o logotipo da Toptal) verticalmente.

Detectar uma imagem (o logotipo da Toptal) diagonalmente em uma camisa.

Os aplicativos de realidade aumentada começam com a compreensão das imagens, e é assim que você pode fazer isso.
Tweet

Conclusão

Neste artigo mostramos como é fácil detectar objetos simples de uma imagem usando OpenCV. Todo o código está disponível no GitHub. Sinta-se à vontade para bifurcar e enviar solicitações push, pois as contribuições são bem-vindas.

Como é verdade para qualquer problema de aprendizado de máquina, a taxa de sucesso da detecção de logotipo neste aplicativo pode ser aumentada usando um conjunto diferente de recursos e um método diferente para classificação de objetos. No entanto, espero que este artigo ajude você a começar com a detecção de objetos usando MSER e aplicações de técnicas de visão computacional, em geral.

Leitura adicional

  • J. Matas, O. Chum, M. Urban e T. Pajdla. “Estéreo de linha de base amplo robusto de regiões extremas extremamente estáveis.”
  • Neumann, Lucas; Matas, Jiri (2011). “Um método para localização e reconhecimento de texto em imagens do mundo real”
Relacionado: Um Tutorial Introdutório de Programação de Robôs