Detecção de objetos usando OpenCV e Swift
Publicados: 2022-03-11O 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:
- Dentro de
ViewController.swift
, crieUIImageView
ondeCvVideoCamera
irá renderizar o conteúdo. - Crie uma instância do
OpenCVWrapper
e passe a instânciaUIImageView
para ela. - 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.
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.