Detecção de objetos usando OpenCV e Swift

Publicados: 2022-03-11

O Swift está conosco há algum tempo e, por meio de suas iterações, trouxe para nós todos os recursos de uma moderna linguagem de programação orientada a objetos. Isso inclui opcionais, genéricos, tuplas, estruturas que suportam métodos, extensões e protocolos e muito mais. Mas se o seu aplicativo depende de uma biblioteca escrita usando C++, você não pode mais contar com o Swift. Felizmente, o Objective-C++ está aqui para nos ajudar.

Desde a sua introdução, Swift teve grande interoperabilidade com Objective-C e usaremos Objective-C para fazer uma ponte entre Swift e C++. Objective-C++ nada mais é do que Objective-C com a capacidade de link com código C++, e usando isso, neste post do blog, vamos criar um aplicativo simples que reconhecerá o logotipo da Toptal dentro da imagem usando OpenCV. Quando detectarmos esse logotipo, abriremos a página inicial da Toptal.

Conforme declarado na página da web do OpenCV:

"O OpenCV foi projetado para eficiência computacional e com forte foco em aplicativos de tempo real. Escrito em C/C++ otimizado, a biblioteca pode tirar proveito do processamento multi-core."

Esta é uma ótima solução se você deseja desenvolver uma visão computacional rápida e confiável.

Criando o wrapper OpenCV Objective-C++

Neste tutorial, projetaremos o aplicativo que corresponderá a um logotipo da Toptal dentro de uma imagem e abrirá a página da Web da Toptal. Para começar, crie um novo projeto Xcode e configure CocoaPods usando pod init . Adicione o OpenCV ao pod 'OpenCV e execute a pod install no Terminal. Certifique-se de descomentar use_frameworks ! declaração dentro do Podfile.

Agora, quando temos o OpenCV dentro do projeto Xcode, temos que conectá-lo ao Swift. Aqui está um resumo rápido das etapas envolvidas:

Etapa 1: crie uma nova classe OpenCVWrapper do Objective-C . Quando o Xcode pergunta “ Você gostaria de configurar um cabeçalho de ponte Objective-C? ” escolha “ Criar cabeçalho de ponte .” O cabeçalho de ponte é o local onde você importa classes Objective-C e, em seguida, elas ficam visíveis dentro do Swift.

Passo 2: Para usar C++ dentro do Objective-C, temos que mudar a extensão do arquivo de OpenCVWrapper.m para OpenCVWrapper.mm . Você pode fazer isso simplesmente renomeando o arquivo dentro do navegador de projetos do Xcode. Adicionar .mm como uma extensão mudará o tipo de arquivo de Objective-C para Objective-C++.

Etapa 3: Importe o OpenCV para o OpenCVWrapper.mm usando a seguinte importação. É importante escrever a importação dada acima de #import "OpenCVWrapper.h" porque desta forma evitamos um conhecido conflito BOOL. OpenCV contém enum que tem o valor NO que causa um conflito com o valor Objective-C BOOL NO . Se você não precisa de classes que usam essa enumeração, essa é a maneira mais simples. Caso contrário, você precisa desdefinir BOOL antes de importar o OpenCV.

 #ifdef __cplusplus #import <opencv2/opencv.hpp> #import <opencv2/imgcodecs/ios.h> #import <opencv2/videoio/cap_ios.h> #endif

Etapa 4: adicione #import "OpenCVWrapper.h" ao seu cabeçalho de ponte.

Abra OpenCVWrapper.mm e crie uma interface privada onde declararemos as propriedades privadas:

 @interface OpenCVWrapper() <CvVideoCameraDelegate> @property (strong, nonatomic) CvVideoCamera *videoCamera; @property (assign, nonatomic) cv::Mat logoSample; @end

Para criar CvVideoCamera , temos que passar UIImageView para ele, e faremos isso através do nosso inicializador de designador.

 - (instancetype)initWithParentView:(UIImageView *)parentView delegate:(id<OpenCVWrapperDelegate>)delegate { if (self = [super init]) { self.delegate = delegate; parentView.contentMode = UIViewContentModeScaleAspectFill; self.videoCamera = [[CvVideoCamera alloc] initWithParentView:parentView]; self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPresetHigh; self.videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; self.videoCamera.defaultFPS = 30; self.videoCamera.grayscaleMode = [NSNumber numberWithInt:0].boolValue; self.videoCamera.delegate = self; // Convert UIImage to Mat and store greyscale version UIImage *templateImage = [UIImage imageNamed:@"toptal"]; cv::Mat templateMat; UIImageToMat(templateImage, templateMat); cv::Mat grayscaleMat; cv::cvtColor(templateMat, grayscaleMat, CV_RGB2GRAY); self.logoSample = grayscaleMat; [self.videoCamera start]; } return self; }

Dentro dele, configuramos o CvVideoCamera que renderiza o vídeo dentro do parentView fornecido e através do delegado nos envia a imagem cv::Mat para análise.

processImage: método é do protocolo CvVideoCameraDelegate , e dentro dele faremos a correspondência de templates.

 - (void)processImage:(cv::Mat&)image { cv::Mat gimg; // Convert incoming img to greyscale to match template cv::cvtColor(image, gimg, CV_BGR2GRAY); // Get matching cv::Mat res(image.rows-self.logoSample.rows+1, self.logoSample.cols-self.logoSample.cols+1, CV_32FC1); cv::matchTemplate(gimg, self.logoSample, res, CV_TM_CCOEFF_NORMED); cv::threshold(res, res, 0.5, 1., CV_THRESH_TOZERO); double minval, maxval, threshold = 0.9; cv::Point minloc, maxloc; cv::minMaxLoc(res, &minval, &maxval, &minloc, &maxloc); // Call delegate if match is good enough if (maxval >= threshold) { // Draw a rectangle for confirmation cv::rectangle(image, maxloc, cv::Point(maxloc.x + self.logoSample.cols, maxloc.y + self.logoSample.rows), CV_RGB(0,255,0), 2); cv::floodFill(res, maxloc, cv::Scalar(0), 0, cv::Scalar(.1), cv::Scalar(1.)); [self.delegate openCVWrapperDidMatchImage:self]; } }

Primeiro, convertemos a imagem fornecida em imagem em tons de cinza porque dentro do método init convertemos nossa imagem correspondente ao modelo de logotipo Toptal em tons de cinza. O próximo passo é verificar correspondências com um determinado limite que nos ajudará com a escala e os ângulos. Temos que verificar os ângulos porque você pode capturar uma foto em vários ângulos nos quais ainda queremos detectar o logotipo. Depois de atingir o limite determinado, invocaremos o delegado e abriremos a página da Web da Toptal.

Não se esqueça de adicionar NSCameraUsageDescription ao seu Info.plist, caso contrário, seu aplicativo irá travar logo após chamar [self.videoCamera start]; .

Agora finalmente Swift

Até agora estávamos focando em Objective-C++ porque toda a lógica é feita dentro dele. Nosso código Swift será bastante simples:

  1. Dentro de ViewController.swift , crie UIImageView onde CvVideoCamera irá renderizar o conteúdo.
  2. Crie uma instância do OpenCVWrapper e passe a instância UIImageView para ela.
  3. Implemente o protocolo OpenCVWrapperDelegate para abrir a página web da Toptal quando detectamos o logotipo.
 class ViewController: UIViewController { var wrapper: OpenCVWrapper! @IBOutlet var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. wrapper = OpenCVWrapper.init(parentView: imageView, delegate: self) } } extension ViewController: OpenCVWrapperDelegate { //MARK: - OpenCVWrapperDelegate func openCVWrapperDidMatchImage(_ wrapper: OpenCVWrapper) { UIApplication.shared.open(URL.init(string: "https://toptal.com")!, options: [:], completionHandler: nil) } }

OpenCV Swift em ação

Neste artigo, mostramos como você pode integrar o código C++ com o Swift e criar classes wrapper que estão aqui para fazer a ponte do código C++ com o Swift. Agora, quando detectamos o logotipo da Toptal pela câmera, abrimos a página inicial da Toptal.

Detectando o logotipo da Toptal com o OpenCV Swift

Para atualizações futuras, você pode querer executar a correspondência de modelo de CV em um thread em segundo plano. Dessa forma, você não bloqueará o thread principal e a interface do usuário permanecerá responsiva. Como este é um exemplo simples de OpenCV, a correspondência de modelos pode não ser muito bem-sucedida, mas o objetivo deste artigo foi mostrar como você pode começar a usá-lo.

Se você ainda estiver interessado em aprender OpenCV e seus usos mais complexos no iOS, recomendo Real-time Object Detection Using MSER in iOS , que o orienta pela detecção de imagem usando a câmera traseira do iPhone.