Détection d'objets à l'aide d'OpenCV et de Swift

Publié: 2022-03-11

Swift est avec nous depuis un certain temps maintenant, et à travers ses itérations, il nous a apporté toutes les fonctionnalités d'un langage de programmation orienté objet moderne. Ceux-ci incluent les options, les génériques, les tuples, les structures qui prennent en charge les méthodes, les extensions et les protocoles, et bien d'autres. Mais si votre application repose sur une bibliothèque écrite en C++, vous ne pouvez plus compter sur Swift. Heureusement, Objective-C++ est là pour nous aider.

Depuis son introduction, Swift a eu une grande interopérabilité avec Objective-C et nous utiliserons Objective-C pour faire le pont entre Swift et C++. Objective-C++ n'est rien de plus qu'Objective-C avec la possibilité de se lier au code C++, et en utilisant cela, dans ce billet de blog, nous allons créer une application simple qui reconnaîtra le logo Toptal à l'intérieur de l'image en utilisant OpenCV. Lorsque nous détectons ce logo, nous ouvrons la page d'accueil de Toptal.

Comme indiqué sur la page Web OpenCV :

"OpenCV a été conçu pour l'efficacité de calcul et avec un fort accent sur les applications en temps réel. Écrite en C/C++ optimisé, la bibliothèque peut tirer parti du traitement multicœur."

C'est une excellente solution si vous souhaitez développer une vision par ordinateur rapide et fiable.

Création de l'encapsuleur OpenCV Objective-C++

Dans ce didacticiel, nous allons concevoir l'application qui correspondra à un logo Toptal dans une image et ouvrira la page Web Toptal. Pour commencer, créez un nouveau projet Xcode et configurez CocoaPods à l'aide pod init . Ajoutez OpenCV au pod 'OpenCV et exécutez l' pod install dans le terminal. Assurez-vous de décommenter use_frameworks ! déclaration à l'intérieur de Podfile.

Maintenant, lorsque nous avons OpenCV dans le projet Xcode, nous devons le connecter à Swift. Voici un aperçu rapide des étapes impliquées :

Étape 1 : Créer une nouvelle classe Objective-C OpenCVWrapper . Lorsque Xcode vous demande « Souhaitez-vous configurer un en-tête de pont Objective-C ? " choisissez " Créer un en-tête de pontage ". L'en-tête de pontage est l'endroit où vous importez les classes Objective-C, puis elles sont visibles dans Swift.

Étape 2 : Pour utiliser C++ dans Objective-C, nous devons changer l'extension de fichier de OpenCVWrapper.m à OpenCVWrapper.mm . Vous pouvez le faire en renommant simplement le fichier dans le navigateur de projet de Xcode. L'ajout de .mm comme extension changera le type de fichier d'Objective-C en Objective-C++.

Étape 3 : Importez OpenCV dans OpenCVWrapper.mm en utilisant l'importation suivante. Il est important d'écrire l'importation donnée au-dessus de #import "OpenCVWrapper.h" car nous évitons ainsi un conflit BOOL bien connu. OpenCV contient enum qui a la valeur NO , ce qui provoque un conflit avec la valeur Objective-C BOOL NO . Si vous n'avez pas besoin de classes qui utilisent une telle énumération, c'est le moyen le plus simple. Sinon, vous devez indéfinir BOOL avant d'importer OpenCV.

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

Étape 4 : Ajoutez #import "OpenCVWrapper.h" à votre en-tête de pontage.

Ouvrez OpenCVWrapper.mm et créez une interface privée où nous déclarerons des propriétés privées :

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

Afin de créer CvVideoCamera , nous devons lui transmettre UIImageView , et nous le ferons via notre initialiseur de désignateur.

 - (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; }

À l'intérieur, nous configurons CvVideoCamera qui rend la vidéo à l'intérieur du parentView donné et via le délégué nous envoie l'image cv::Mat pour analyse.

processImage: méthode provient du protocole CvVideoCameraDelegate , et à l'intérieur, nous ferons la correspondance des modèles.

 - (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]; } }

Tout d'abord, nous convertissons l'image donnée en image en niveaux de gris car, dans la méthode init, nous avons converti notre modèle de logo Toptal correspondant à l'image en niveaux de gris. La prochaine étape consiste à vérifier les correspondances avec un certain seuil, ce qui nous aidera avec la mise à l'échelle et les angles. Nous devons vérifier les angles car vous pouvez capturer une photo sous différents angles dans lesquels nous voulons toujours détecter le logo. Après avoir atteint le seuil donné, nous invoquerons le délégué et ouvrirons la page Web de Toptal.

N'oubliez pas d'ajouter NSCameraUsageDescription à votre Info.plist sinon, votre application plantera juste après avoir appelé [self.videoCamera start]; .

Maintenant enfin Swift

Jusqu'à présent, nous nous sommes concentrés sur Objective-C++ parce que toute la logique est faite à l'intérieur. Notre code Swift sera assez simple :

  1. Dans ViewController.swift , créez UIImageViewCvVideoCamera rendra le contenu.
  2. Créez une instance d' OpenCVWrapper et transmettez-lui l'instance UIImageView .
  3. Implémentez le protocole OpenCVWrapperDelegate pour ouvrir la page Web de Toptal lorsque nous détectons le logo.
 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 en action

Dans cet article, nous avons montré comment vous pouvez intégrer du code C++ avec Swift et créer des classes wrapper qui sont là pour relier le code C++ avec Swift. Maintenant, lorsque nous détectons le logo Toptal via la caméra, nous ouvrons la page d'accueil de Toptal.

Détection du logo de Toptal avec OpenCV Swift

Pour les futures mises à jour, vous souhaiterez peut-être exécuter la correspondance des modèles de CV dans un fil de discussion en arrière-plan. De cette façon, vous ne bloquerez pas le thread principal et l'interface utilisateur restera réactive. Comme il s'agit d'un exemple simple d'OpenCV, la correspondance des modèles peut ne pas être très efficace, mais le but de cet article était de vous montrer comment vous pouvez commencer à l'utiliser.

Si vous êtes toujours intéressé à apprendre OpenCV et ses utilisations plus complexes dans iOS, je recommande la détection d'objets en temps réel à l'aide de MSER dans iOS , qui vous guide à travers la détection d'images à l'aide de la caméra arrière de l'iPhone.