OpenCV 튜토리얼: iOS에서 MSER을 사용한 실시간 객체 감지
게시 됨: 2022-03-11지난 몇 년 동안 평균 휴대폰 성능이 크게 향상되었습니다. CPU 마력이든 RAM 용량이든 이제 모바일 하드웨어에서 계산량이 많은 작업을 더 쉽게 수행할 수 있습니다. 이러한 모바일 기술이 올바른 방향으로 가고 있지만, 특히 증강 현실, 가상 현실 및 인공 지능의 도래와 함께 모바일 플랫폼에서 해야 할 일이 많이 있습니다.
컴퓨터 비전의 주요 과제는 이미지에서 관심 대상을 감지하는 것입니다. 인간의 눈과 뇌는 탁월한 역할을 하며 이를 기계로 복제하는 것은 여전히 꿈입니다. 최근 수십 년 동안 기계에서 이를 모방하는 접근 방식이 개발되었으며 점점 더 좋아지고 있습니다.
이 자습서에서는 이미지에서 얼룩을 감지하는 데 사용되는 알고리즘을 탐색합니다. 또한 오픈 소스 라이브러리인 OpenCV의 알고리즘을 사용하여 후면 카메라를 사용하여 이미지를 획득하고 그 안의 물체를 감지하는 프로토타입 iPhone 응용 프로그램을 구현합니다.
OpenCV 튜토리얼
OpenCV는 주요 컴퓨터 비전 및 기계 학습 알고리즘의 구현을 제공하는 오픈 소스 라이브러리입니다. 얼굴을 감지하는 응용 프로그램, 포커 테이블에서 카드 놀이를 구현하거나 임의의 이미지에 효과를 추가하는 간단한 응용 프로그램을 구현하려는 경우 OpenCV가 좋은 선택입니다.
OpenCV는 C/C++로 작성되었으며 모든 주요 플랫폼에 대한 래퍼 라이브러리가 있습니다. 이것은 특히 iOS 환경에서 사용하기 쉽게 만듭니다. Objective-C iOS 애플리케이션 내에서 사용하려면 공식 웹사이트에서 OpenCV iOS 프레임워크를 다운로드하십시오. 최신 버전인 3.0에서는 헤더 파일이 구성되는 방식에 호환성을 손상시키는 변경 사항이 있으므로 iOS용 OpenCV 버전 2.4.11(이 기사에서는 사용 중이라고 가정)을 사용하고 있는지 확인하십시오. 설치 방법에 대한 자세한 정보는 웹 사이트에 문서화되어 있습니다.
MSER
MSER(Maximally Stable Extremal Regions)은 이미지 내에서 얼룩 감지에 사용할 수 있는 많은 방법 중 하나입니다. 간단히 말해서, 알고리즘은 외부 경계 픽셀 강도가 내부 경계 픽셀 강도보다 더 높은(주어진 임계값만큼) 인접한 픽셀 세트를 식별합니다. 이러한 영역은 강도의 변화에 따라 크게 변하지 않는 경우 최대로 안정적이라고 합니다.
다른 많은 얼룩 감지 알고리즘이 존재하지만 MSER은 O(n log(log(n)))의 상당히 가벼운 런타임 복잡성을 가지기 때문에 여기에서 선택되었습니다. 여기서 n은 이미지의 총 픽셀 수입니다. 알고리즘은 또한 흐림 및 스케일링에 강하여 휴대폰 카메라와 같은 실시간 소스를 통해 획득한 이미지를 처리할 때 유리합니다.
이 튜토리얼의 목적을 위해 우리는 Toptal의 로고를 감지하는 애플리케이션을 디자인할 것입니다. 심볼은 날카로운 모서리를 가지고 있으며, 이는 모서리 감지 알고리즘이 Toptal의 로고를 감지하는 데 얼마나 효과적인지 생각하게 할 수 있습니다. 결국, 그러한 알고리즘은 사용하기 쉽고 이해하기 쉽습니다. 모서리 기반 방법은 배경과 뚜렷하게 분리된 개체(예: 흰색 배경의 검은색 개체)를 감지하는 경우 성공률이 높을 수 있지만 현실 세계에서 Toptal의 로고를 실시간으로 감지하기는 어렵습니다. 알고리즘이 수백 개의 모서리를 지속적으로 감지하는 이미지.
전략
응용 프로그램이 카메라를 통해 획득한 이미지의 각 프레임에 대해 먼저 회색조로 변환됩니다. 그레이스케일 이미지에는 색상 채널이 하나뿐이지만 로고는 표시됩니다. 이렇게 하면 알고리즘이 이미지를 처리하기가 더 쉬워지고 추가 이득이 거의 또는 전혀 없이 알고리즘이 처리해야 하는 데이터의 양이 크게 줄어듭니다.
다음으로 OpenCV의 구현 알고리즘을 사용하여 모든 MSER을 추출합니다. 다음으로 각 MSER은 최소 경계 직사각형을 정사각형으로 변환하여 정규화됩니다. 이 단계는 로고가 다른 각도와 거리에서 획득될 수 있고 원근 왜곡의 허용치를 증가시킬 수 있기 때문에 중요합니다.
또한 각 MSER에 대해 여러 속성이 계산됩니다.
- 구멍 수
- 볼록 껍질 면적에 대한 MSER 면적의 비율
- 최소 면적 직사각형 면적에 대한 MSER 면적의 비율
- MSER 영역에 대한 MSER 골격의 길이 비율
- 가장 큰 윤곽 영역에 대한 MSER 영역의 비율
이미지에서 Toptal의 로고를 감지하기 위해 모든 MSER의 속성을 이미 학습된 Toptal 로고 속성과 비교합니다. 이 자습서의 목적을 위해 각 속성에 대해 허용되는 최대 차이는 경험적으로 선택되었습니다.
마지막으로 가장 유사한 영역이 결과로 선택됩니다.
iOS 애플리케이션
iOS에서 OpenCV를 사용하는 것은 쉽습니다. 아직 하지 않았다면 iOS 응용 프로그램을 만들고 OpenCV를 사용하도록 Xcode를 설정하는 단계에 대한 간략한 개요가 있습니다.
"SuperCool Logo Detector"라는 새 프로젝트 이름을 만듭니다. 언어로 Objective-C를 선택된 상태로 둡니다.
새 접두사 헤더(.pch) 파일을 추가하고 이름을 PrefixHeader.pch로 지정합니다.
프로젝트 "SuperCool Logo Detector" 빌드 대상으로 이동하고 빌드 설정 탭에서 "접두사 헤더" 설정을 찾으십시오. LLVM 언어 섹션에서 찾거나 검색 기능을 사용할 수 있습니다.
접두사 헤더 설정에 "PrefixHeader.pch" 추가
이 시점에서 iOS 2.4.11용 OpenCV를 설치하지 않았다면 지금 설치하세요.
다운로드한 프레임워크를 프로젝트에 끌어다 놓습니다. 대상 설정에서 "연결된 프레임워크 및 라이브러리"를 확인하십시오. (자동으로 추가되어야 하지만 안전을 위해 더 좋습니다.)
또한 다음 프레임워크를 연결합니다.
- AV재단
- 자산 라이브러리
- 코어미디어
"PrefixHeader.pch"를 열고 다음 3줄을 추가합니다.
#ifdef __cplusplus #include <opencv2/opencv.hpp> #endif”
자동으로 생성된 코드 파일의 확장자를 " .m"에서 " .mm"로 변경합니다. OpenCV는 C++로 작성되었으며 *.mm를 사용하면 Objective-C++를 사용할 것임을 의미합니다.
ViewController.h에서 "opencv2/highgui/cap_ios.h"를 가져오고 CvVideoCameraDelegate 프로토콜을 준수하도록 ViewController를 변경합니다.
#import <opencv2/highgui/cap_ios.h>
Main.storyboard를 열고 초기 뷰 컨트롤러에 UIImageView를 배치합니다.
"imageView"라는 이름의 ViewController.mm에 대한 콘센트를 만듭니다.
"CvVideoCamera *camera;" 변수를 만듭니다. ViewController.h 또는 ViewController.mm에서 후면 카메라에 대한 참조로 초기화합니다.
camera = [[CvVideoCamera alloc] initWithParentView: _imageView]; camera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; camera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480; camera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; camera.defaultFPS = 30; camera.grayscaleMode = NO; camera.delegate = self;
지금 프로젝트를 빌드하면 Xcode는 CvVideoCameraDelegate에서 "processImage" 메서드를 구현하지 않았다고 경고합니다. 지금은 단순함을 위해 카메라에서 이미지를 가져와 간단한 텍스트로 오버레이합니다.
- "viewDidAppear"에 한 줄 추가:
[camera start];
이제 응용 프로그램을 실행하면 카메라에 대한 액세스 권한을 요청합니다. 그러면 카메라에서 비디오가 표시되어야 합니다.
"processImage" 메서드에서 다음 두 줄을 추가합니다.
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));
그 정도입니다. 이제 카메라의 이미지에 "Toptal"이라는 텍스트를 그리는 매우 간단한 응용 프로그램이 생겼습니다. 이제 이 간단한 애플리케이션에서 대상 로고 감지 애플리케이션을 빌드할 수 있습니다. 간결함을 위해 이 기사에서는 애플리케이션이 전반적으로 작동하는 방식을 이해하는 데 중요한 소수의 코드 세그먼트에 대해서만 논의할 것입니다. GitHub의 코드에는 각 세그먼트가 하는 일을 설명하는 상당한 양의 주석이 있습니다.

애플리케이션은 단 하나의 목적을 가지고 있기 때문에 Toptal의 로고를 감지하는 것은 실행되자마자 주어진 템플릿 이미지에서 MSER 기능이 추출되고 값이 메모리에 저장됩니다.
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];
응용 프로그램에는 시작/중지 버튼이 있는 화면이 하나만 있으며 FPS 및 감지된 MSER 수와 같은 필요한 모든 정보가 이미지에 자동으로 그려집니다. 응용 프로그램이 중지되지 않는 한 카메라의 모든 이미지 프레임에 대해 다음 processImage 메서드가 호출됩니다.
-(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]; }
본질적으로 이 방법은 원본 이미지의 회색조 복사본을 만듭니다. 모든 MSER을 식별하고 관련 기능을 추출하고 템플릿과의 유사성에 대해 각 MSER에 점수를 매기고 가장 좋은 것을 선택합니다. 마지막으로 최상의 MSER 주위에 녹색 경계를 그리고 메타 정보로 이미지를 오버레이합니다.
다음은 이 응용 프로그램에서 몇 가지 중요한 클래스와 해당 메서드의 정의입니다. 그들의 목적은 주석에 설명되어 있습니다.
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
모든 것이 함께 연결되면 이 애플리케이션을 사용하여 iOS 장치의 카메라를 사용하여 다양한 각도와 방향에서 Toptal의 로고를 감지할 수 있어야 합니다.
결론
이 기사에서는 OpenCV를 사용하여 이미지에서 간단한 개체를 감지하는 것이 얼마나 쉬운지 보여주었습니다. 전체 코드는 GitHub에서 사용할 수 있습니다. 기여를 환영하므로 자유롭게 포크하고 푸시 요청을 보내십시오.
모든 기계 학습 문제와 마찬가지로 이 응용 프로그램에서 로고 감지의 성공률은 다른 기능 집합과 객체 분류를 위한 다른 방법을 사용하여 증가할 수 있습니다. 그러나 이 기사가 일반적으로 MSER을 사용한 물체 감지와 컴퓨터 비전 기술의 응용을 시작하는 데 도움이 되기를 바랍니다.
추가 읽기
- J. Matas, O. Chum, M. Urban 및 T. Pajdla. "최대로 안정적인 극단 영역의 강력한 와이드 베이스라인 스테레오."
- 노이만, 루카스; 마타스, 지리(2011). "실제 이미지에서 텍스트 현지화 및 인식 방법"