簡書更新停留在17年?????
-
需求分析:
1、尋寶活動,商家藏寶并將場景拍照上傳至服務(wù)器,用戶根據(jù)線索到達(dá)指定地點,打開app進(jìn)行實時掃描,如掃描到的圖片與服務(wù)器的圖片匹配成功,則視為中獎;
2、OpenCV中用于圖片對比及識別的算法較多,具體原理及優(yōu)缺點本文不做介紹,demo中用到的是特征點對比中的ORB算法;
3、本次需求中用到兩個算法:特征點-ORB和直方圖;
經(jīng)反復(fù)測試發(fā)現(xiàn)特征點對比較為適合棱角分明的場景,比如桌子/電腦等;而直方圖對比較為適合復(fù)雜且棱角較少的場景,比如辦公室的盆栽類;(ps:測試分為室內(nèi)測試及室外測試,且測試次數(shù)及測試量都不太大,結(jié)論僅限于參考)
-
OpenCV集成
1、OpenCV下載:官網(wǎng)地址
2、demo中OpenCV版本為3.4.3;
3、添加依賴庫:

-
功能開發(fā):
1、拍照上傳:
1)將.m文件后綴改為.mm;
2)導(dǎo)入頭文件(務(wù)必放在所有頭文件之上)
#import <opencv2/opencv.hpp>
#import <opencv2/imgproc/types_c.h>
#import <opencv2/videoio/cap_ios.h>
#import <opencv2/imgcodecs/ios.h>
3)打開相機(jī)具體實現(xiàn):
@interface CaptureView()<CvVideoCameraDelegate>
@property (strong, nonatomic) UIImageView *CvImageV;
@property (nonatomic,retain) CvVideoCamera *videoCamera;
@end
@implementation CaptureView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
[self setupCvVideoCamera];
}
return self;
}
// MARK: - init
-(void)setupCvVideoCamera{
self.CvImageV = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.videoCamera = [[CvVideoCamera alloc]initWithParentView:self.CvImageV];
self.videoCamera.delegate = self;
self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPresetiFrame960x540;
self.videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
self.videoCamera.grayscaleMode = NO;
self.videoCamera.defaultFPS = 30;
[self addSubview:self.CvImageV];
}
// MARK: - CvVideoCameraDelegate
- (void)processImage:(cv::Mat &)image{
// 將Mat轉(zhuǎn)換為Xcode的UIImageView顯示
UIImage *currentImage = MatToUIImage(image);
}
// MARK: - event and response
// 開啟
- (void)start{
[self.videoCamera start];
}
// 暫停
- (void)stop{
[self.videoCamera stop];
}
4)問題:opencv返回的圖片矩陣為BGR,需要轉(zhuǎn)成RGB來上傳,具體代碼見demo
2、圖片對比:
1)將.m文件后綴改為.mm;
2)導(dǎo)入頭文件(務(wù)必放在所有頭文件之上)
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#include "opencv2/core/core.hpp"
#include <iostream>
#include <vector>
3)實現(xiàn)代碼:
/**
@param boxImage 模板圖片
@param senceImage 實時圖片
@return 對比結(jié)果(見圖)
*/
-(UIImage *)similarlyMatchWithBox:(UIImage *)boxImage andSence:(UIImage *)senceImage{
if (boxImage && senceImage) {
cv::Mat sence,box;
box = [self cvMatFromUIImage:boxImage];
sence = [self cvMatFromUIImage:senceImage];
cvtColor(box, box, CV_RGBA2RGB);
cvtColor(sence, sence, CV_RGBA2RGB);
vector<KeyPoint> keyPoints_obj, keyPoints_sence;
Ptr<ORB> detector = ORB::create();
detector->detect(box, keyPoints_obj);
detector->detect(sence, keyPoints_sence);
Mat description_box,description_sence;
detector->compute(box, keyPoints_obj, description_box);
detector->compute(sence, keyPoints_sence, description_sence);
vector<DMatch> matches;
Ptr<DescriptorMatcher> matcher = DescriptorMatcher ::create(cv::BFMatcher::BRUTEFORCE);
// 處理拍的區(qū)域為黑色,比如手機(jī)平放在桌面上,會閃退
if (description_sence.cols <= 0) {
return nil;
}
if (description_sence.rows <= 0) {
return nil;
}
matcher->match(description_box, description_sence, matches);
// 發(fā)現(xiàn)匹配
vector<DMatch> good_matches;
for (unsigned int i = 0; i < matches.size(); i++) {
//distance 值根據(jù)實際開發(fā)來調(diào)試
if (matches[i].distance <= 320) {
good_matches.push_back(matches[i]);
}
}
Mat imgMatches;
drawMatches(box,keyPoints_obj,sence,keyPoints_sence,good_matches,imgMatches,Scalar::all(-1),Scalar::all(-1),vector<char>(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
UIImage *m = [self UIImageFromCVMat:imgMatches];
return m;
}
return nil;
}
4)具體代碼見demo中ImageCompared文件;
5)問題:
① 相機(jī)拍到黑色時會崩潰:可通過description_sence.cols <= 0 && description_sence.rows <= 0來判斷;這個坑我和安卓都爬了好久才爬出來;(悄悄告訴你我先爬出來的,美滋滋)
② 存儲在服務(wù)器的模板圖可多上傳幾張,一張圖片匹配成功率會很低:可設(shè)置至少上傳X張,且在上傳的時候也進(jìn)行匹配,匹配率達(dá)到某個值則可上傳,否則要求商家重拍,防止上傳的圖片不是同一個場景;
③ 內(nèi)存問題:實時掃描且進(jìn)行相似度對比消耗內(nèi)存非常大,很快就會內(nèi)存溢出導(dǎo)致閃退,我的解決方法是設(shè)置為每隔X秒進(jìn)行一次匹配,時間可自己調(diào)試然后給定;
6)匹配結(jié)果圖:

- 最后:OpenCV里的學(xué)問太多了,這次需求甲方爸爸趕得急是一方面,之前沒了解過也是真的,熬了幾個通宵才搞定,大概算是一只腳邁進(jìn)去了,如果二期需求有時間排的話可能另一只腳也就能邁進(jìn)去了,如果沒有的話就此打住。