Обнаружение объектов с использованием OpenCV и Swift

Опубликовано: 2022-03-11

Swift уже давно с нами, и благодаря своим итерациям он принес нам все возможности современного объектно-ориентированного языка программирования. К ним относятся необязательные параметры, дженерики, кортежи, структуры, поддерживающие методы, расширения и протоколы, и многое другое. Но если ваше приложение опирается на библиотеку, написанную с использованием C++, вы больше не можете рассчитывать на Swift. К счастью, Objective-C++ здесь, чтобы помочь нам.

С момента своего появления у Swift была отличная совместимость с Objective-C, и мы будем использовать Objective-C для соединения Swift с C++. Objective-C++ — это не что иное, как Objective-C с возможностью связывания с кодом C++, и, используя это, в этом сообщении блога мы создадим простое приложение, которое будет распознавать логотип Toptal внутри изображения с помощью OpenCV. Когда мы обнаружим этот логотип, мы откроем домашнюю страницу Toptal.

Как указано на веб-странице OpenCV:

«OpenCV был разработан для повышения эффективности вычислений и уделения особого внимания приложениям реального времени. Написанная на оптимизированном языке C/C++, библиотека может использовать преимущества многоядерной обработки».

Это отличное решение, если вам нужна быстрая разработка и надежное компьютерное зрение.

Создание оболочки OpenCV Objective-C++

В этом уроке мы разработаем приложение, которое будет соответствовать логотипу Toptal внутри изображения и открывать веб-страницу Toptal. Для начала создайте новый проект Xcode и настройте CocoaPods с помощью pod init . Добавьте OpenCV в модуль Podfile pod 'OpenCV и запустите pod install в терминале. Обязательно раскомментируйте use_frameworks ! заявление внутри Podfile.

Теперь, когда у нас есть OpenCV внутри проекта Xcode, мы должны подключить его к Swift. Вот краткое описание необходимых шагов:

Шаг 1: Создайте новый класс OpenCVWrapper для Objective-C. Когда Xcode спрашивает вас: « Хотите ли вы настроить соединительный заголовок Objective-C? » выберите « Создать соединительный заголовок ». Связующий заголовок — это место, куда вы импортируете классы Objective-C, после чего они видны внутри Swift.

Шаг 2: Чтобы использовать C++ внутри Objective-C, мы должны изменить расширение файла с OpenCVWrapper.m на OpenCVWrapper.mm . Вы можете сделать это, просто переименовав файл в навигаторе проекта Xcode. Добавление .mm в качестве расширения изменит тип файла с Objective-C на Objective-C++.

Шаг 3: Импортируйте OpenCV в OpenCVWrapper.mm используя следующий импорт. Важно написать данный импорт выше #import "OpenCVWrapper.h" потому что таким образом мы избегаем известного конфликта BOOL. OpenCV содержит перечисление со значением NO , которое вызывает конфликт со значением Objective-C BOOL NO . Если вам не нужны классы, использующие такое перечисление, то это самый простой способ. В противном случае вам придется отменить определение BOOL перед импортом OpenCV.

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

Шаг 4: Добавьте #import "OpenCVWrapper.h" в заголовок моста.

Откройте OpenCVWrapper.mm и создайте приватный интерфейс, в котором мы объявим приватные свойства:

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

Чтобы создать CvVideoCamera , мы должны передать UIImageView , и мы сделаем это через наш инициализатор указателя.

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

Внутри него мы настраиваем CvVideoCamera , которая рендерит видео внутри данного parentView и через делегат отправляет нам изображение cv::Mat для анализа.

processImage: метод из протокола CvVideoCameraDelegate , и внутри него мы будем выполнять сопоставление с шаблоном.

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

Во-первых, мы преобразуем данное изображение в изображение в градациях серого, потому что внутри метода init мы преобразовали наш шаблон логотипа Toptal, соответствующий изображению, в градации серого. Следующим шагом будет проверка совпадений с определенным порогом, который поможет нам с масштабированием и углами. Мы должны проверить углы, потому что вы можете сделать снимок под разными углами, в которых мы все еще хотим обнаружить логотип. После достижения заданного порога мы вызовем делегата и откроем веб-страницу Toptal.

Не забудьте добавить NSCameraUsageDescription в свой Info.plist, иначе ваше приложение вылетит сразу после вызова [self.videoCamera start]; .

Теперь, наконец, Свифт

До сих пор мы фокусировались на Objective-C++, потому что вся логика выполняется внутри него. Наш код Swift будет довольно простым:

  1. Внутри ViewController.swift создайте UIImageView , где CvVideoCamera будет отображать контент.
  2. Создайте экземпляр OpenCVWrapper и передайте ему экземпляр UIImageView .
  3. Реализовать протокол OpenCVWrapperDelegate , чтобы открывать веб-страницу Toptal при обнаружении логотипа.
 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 в действии

В этой статье мы показали, как вы можете интегрировать код C++ со Swift и создавать классы-оболочки, предназначенные для соединения кода C++ со Swift. Теперь, когда мы обнаруживаем логотип Toptal через камеру, мы открываем домашнюю страницу Toptal.

Обнаружение логотипа Toptal с помощью OpenCV Swift

Для будущих обновлений вы можете запустить сопоставление шаблонов резюме в фоновом потоке. Таким образом, вы не будете блокировать основной поток, а пользовательский интерфейс останется отзывчивым. Поскольку это простой пример OpenCV, сопоставление с шаблоном может быть не очень успешным, но цель этой статьи состояла в том, чтобы показать вам, как вы можете начать его использовать.

Если вы все еще заинтересованы в изучении OpenCV и его более сложном использовании в iOS, я рекомендую Обнаружение объектов в реальном времени с использованием MSER в iOS , которое проведет вас через обнаружение изображений с помощью задней камеры iPhone.