北京時(shí)間2017.6.6日凌晨1點(diǎn),新一屆的WWDC召開蛉艾,蘋果在大會(huì)上發(fā)布了iOS11的beta版含末,伴隨著iOS 11的發(fā)布,也隨之推出了一些新的API绍昂,如:ARKit 、Core ML偿荷、FileProvider窘游、IdentityLookup 、Core NFC跳纳、Vison 等忍饰。
本篇文章主要簡(jiǎn)單介紹下其中的 Vision API 的使用(Vision更強(qiáng)大的地方是可以結(jié)合Core ML模型實(shí)現(xiàn)更強(qiáng)大的功能,本篇文章就不詳細(xì)展開了)
Vison 與 Core ML 的關(guān)系
Vision 是 Apple 在 WWDC 2017 推出的圖像識(shí)別框架寺庄。
Core ML 是 Apple 在 WWDC 2017 推出的機(jī)器學(xué)習(xí)框架艾蓝。
Core ML
根據(jù)這張圖就可以看出,Core ML的作用就是將一個(gè)Core ML模型斗塘,轉(zhuǎn)換成我們的App工程可以直接使用的對(duì)象,就是可以看做是一個(gè)模型的轉(zhuǎn)換器赢织。
Vision在這里的角色,就是相當(dāng)于一個(gè)用于識(shí)別Core ML模型的一個(gè)角色.
Vision
根據(jù)官方文檔看逛拱,Vision 本身就有Face Detection and Recognition(人臉檢測(cè)識(shí)別)敌厘、Machine Learning Image Analysis(機(jī)器學(xué)習(xí)圖片分析)、Barcode Detection(條形碼檢測(cè))朽合、Text Detection(文本檢測(cè))俱两。。曹步。宪彩。。等等這些功能讲婚。
所以可以這樣理解:
Vision庫里本身就已經(jīng)自帶了很多訓(xùn)練好的Core ML模型尿孔,這些模型是針對(duì)上面提到的人臉識(shí)別、條形碼檢測(cè)等等功能筹麸,如果你要實(shí)現(xiàn)的功能剛好是Vision庫本身就能實(shí)現(xiàn)的活合,那么你直接使用Vision庫自帶的一些類和方法就行,但是如果想要更強(qiáng)大的功能物赶,那么還是需要結(jié)合其它Core ML模型白指。
Vision 與 Core ML 總結(jié)
Core ML可以看做一個(gè)模型的轉(zhuǎn)換器,可以將一個(gè) ML Model 格式的模型文件自動(dòng)生成一些類和方法酵紫,可以直接使用這些類去做分析告嘲,讓我們更簡(jiǎn)單的在app中使用訓(xùn)練好的模型错维。
Vision本身就是能對(duì)圖片做分析,他自帶了針對(duì)很多檢測(cè)的功能橄唬,相當(dāng)于內(nèi)置了一些Model赋焕,另外Vision也能使用一個(gè)你設(shè)置好的其它的Core ML Model來對(duì)圖進(jìn)行分析。
Vision就是建立在Core ML層之上的仰楚,使用Vision其實(shí)還是用到了Core ML隆判,只是沒有顯式地直接寫Core ML的代碼而已。
Vison 的應(yīng)用場(chǎng)景
圖像配準(zhǔn)
-
矩形檢測(cè)
-
二維碼/條形碼檢測(cè)
-
目標(biāo)跟蹤:臉部僧界,矩形和通用模板
-
文字檢測(cè):監(jiān)測(cè)文字外框蜜氨,和文字識(shí)別
-
人臉檢測(cè):支持檢測(cè)笑臉、側(cè)臉捎泻、局部遮擋臉部飒炎、戴眼鏡和帽子等場(chǎng)景,可以標(biāo)記出人臉的矩形區(qū)域
-
人臉特征點(diǎn):可以標(biāo)記出人臉和眼睛笆豁、眉毛郎汪、鼻子、嘴闯狱、牙齒的輪廓煞赢,以及人臉的中軸線
Vison 的設(shè)計(jì)理念
蘋果最擅長(zhǎng)的,把復(fù)雜的事情簡(jiǎn)單化哄孤,Vision的設(shè)計(jì)理念也正是如此照筑。
對(duì)于使用者我們抽象的來說,我們只需要:提出問題-->經(jīng)過機(jī)器-->得到結(jié)果瘦陈。
開發(fā)者不需要是計(jì)算機(jī)視覺專家凝危,開發(fā)者只需要得到結(jié)果即可,一切復(fù)雜的事情交給Vision晨逝。
Vison 的性能對(duì)比
Vision 與 iOS 上其他幾種帶人臉檢測(cè)功能框架的對(duì)比:
根據(jù)官方提供的資料可以看出來蛾默,Vision 和 Core Image、AV Capture 在精確度捉貌,耗時(shí)支鸡,耗電量來看基本都是Best、Fast趁窃、Good牧挣。
Vision 支持的圖片類型
Vision 支持多種圖片類型,如:
CIImage
NSURL
NSData
CGImageRef
CVPixelBufferRef
Vison 的使用 與結(jié)構(gòu)圖
Vision使用中的角色有:
Request醒陆,RequestHandler瀑构,results和results中的Observation數(shù)組。
Request類型:
有很多種统求,比如圖中列出的 人臉識(shí)別检碗、特征識(shí)別、文本識(shí)別码邻、二維碼識(shí)別等折剃。
結(jié)果圖
使用概述:
我們?cè)谑褂眠^程中是給各種功能的 Request 提供給一個(gè) RequestHandler,Handler 持有需要識(shí)別的圖片信息像屋,并將處理結(jié)果分發(fā)給每個(gè) Request 的 completion Block 中怕犁。可以從 results 屬性中得到 Observation 數(shù)組己莺。
observations數(shù)組中的內(nèi)容根據(jù)不同的request請(qǐng)求返回了不同的observation奏甫,如:VNFaceObservation、VNTextObservation凌受、VNBarcodeObservation阵子、VNHorizonObservation,不同的Observation都繼承于VNDetectedObjectObservation胜蛉,而VNDetectedObjectObservation則是繼承于VNObservation挠进。每種Observation有boundingBox,landmarks等屬性誊册,存儲(chǔ)的是識(shí)別后物體的坐標(biāo)领突,點(diǎn)位等,我們拿到坐標(biāo)后案怯,就可以進(jìn)行一些UI繪制君旦。
具體人臉識(shí)別使用示例:
1,創(chuàng)建處理圖片處理對(duì)應(yīng)的RequestHandler對(duì)象嘲碱。
// 轉(zhuǎn)換CIImage
CIImage *convertImage = [[CIImage alloc]initWithImage:image];
// 創(chuàng)建處理requestHandler
VNImageRequestHandler *detectRequestHandler = [[VNImageRequestHandler alloc]initWithCIImage:convertImage options:@{}];
2金砍, 創(chuàng)建回調(diào)Handler。(用于識(shí)別成功后進(jìn)行回調(diào)執(zhí)行的一個(gè)Block)
// 設(shè)置回調(diào)
CompletionHandler completionHandler = ^(VNRequest *request, NSError * _Nullable error) {
NSArray *observations = request.results;
};
3麦锯, 創(chuàng)建對(duì)應(yīng)的識(shí)別 Request 請(qǐng)求捞魁,指定 Complete Handler
VNImageBasedRequest *detectRequest = [[VNDetectFaceRectanglesRequest alloc]initWithCompletionHandler: completionHandler];
4,發(fā)送識(shí)別請(qǐng)求离咐,并在回調(diào)中處理回調(diào)接受的數(shù)據(jù)
[detectRequestHandler performRequests:@[detectRequest] error:nil];
代碼整合:
總的來說一共經(jīng)過這幾步之后基本的人臉識(shí)別就實(shí)現(xiàn)了谱俭。
// 轉(zhuǎn)換CIImage
CIImage *convertImage = [[CIImage alloc]initWithImage:image];
// 創(chuàng)建處理requestHandler
VNImageRequestHandler *detectRequestHandler = [[VNImageRequestHandler alloc]initWithCIImage:convertImage options:@{}];
// 設(shè)置回調(diào)
CompletionHandler completionHandler = ^(VNRequest *request, NSError * _Nullable error) {
NSArray *observations = request.results;
[self handleImageWithType:type image:image observations:observations complete:complete];
};
// 創(chuàng)建BaseRequest
VNImageBasedRequest *detectRequest = [[VNDetectFaceRectanglesRequest alloc]initWithCompletionHandler:completionHandler];
// 發(fā)送識(shí)別請(qǐng)求
[detectRequestHandler performRequests:@[detectRequest] error:nil];
VNFaceObservation 介紹:
VNFaceObservation里面,我們能拿到的有用信息就是boundingBox宵蛀。
/// 處理人臉識(shí)別回調(diào)
+ (void)faceRectangles:(NSArray *)observations image:(UIImage *_Nullable)image complete:(detectImageHandler _Nullable )complete{
NSMutableArray *tempArray = @[].mutableCopy;
for (VNFaceObservation *observation in observations) {
CGRect faceRect = [self convertRect:observation.boundingBox imageSize:image.size];
}
boundingBox直接是CGRect類型昆著,但是boundingBox返回的是x,y,w,h的比例,需要進(jìn)行轉(zhuǎn)換术陶。
/// 轉(zhuǎn)換Rect
+ (CGRect)convertRect:(CGRect)oldRect imageSize:(CGSize)imageSize{
CGFloat w = oldRect.size.width * imageSize.width;
CGFloat h = oldRect.size.height * imageSize.height;
CGFloat x = oldRect.origin.x * imageSize.width;
CGFloat y = imageSize.height - (oldRect.origin.y * imageSize.height) - h;
return CGRectMake(x, y, w, h);
}
關(guān)于Y值為何不是直接oldRect.origin.y * imageSize.height出來凑懂,是因?yàn)檫@個(gè)時(shí)候直接算出來的臉部是MAX Y值而不是min Y值,所以需要進(jìn)行轉(zhuǎn)換一下梧宫。
特征識(shí)別介紹:
VNDetectFaceLandmarksRequest 特征識(shí)別請(qǐng)求返回的也是VNFaceObservation接谨,但是這個(gè)時(shí)候VNFaceObservation 對(duì)象的 landmarks 屬性就會(huì)有值摆碉,這個(gè)屬性里面存儲(chǔ)了人物面部特征的點(diǎn)。
如:
// 臉部輪廊
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nonnull faceContour;
// 左眼脓豪,右眼
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable leftEye;
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable rightEye;
// 鼻子巷帝,鼻嵴
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable nose;
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable noseCrest;
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable medianLine;
// 外唇,內(nèi)唇
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable outerLips;
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable innerLips;
// 左眉毛扫夜,右眉毛
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable leftEyebrow;
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable rightEyebrow;
// 左瞳,右瞳
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable leftPupil;
@property (nonatomic, strong) VNFaceLandmarkRegion2D * _Nullable rightPupil;
每個(gè)特征對(duì)象里面都有一個(gè)pointCount屬性楞泼,通過特征對(duì)象的normalizedPoints方法,可以取出來特征里面的每一個(gè)點(diǎn)笤闯,我們拿到點(diǎn)進(jìn)行轉(zhuǎn)換后堕阔,相應(yīng)的UI繪制或其他操作。
例如:
UIImage *sourceImage = image;
// 遍歷所有特征
for (VNFaceLandmarkRegion2D *landmarks2D in pointArray) {
CGPoint points[landmarks2D.pointCount];
// 轉(zhuǎn)換特征的所有點(diǎn)
for (int i=0; i<landmarks2D.pointCount; i++) {
CGPoint point = landmarks2D.normalizedPoints[i];
CGFloat rectWidth = sourceImage.size.width * observation.boundingBox.size.width;
CGFloat rectHeight = sourceImage.size.height * observation.boundingBox.size.height;
CGPoint p = CGPointMake(point.x * rectWidth + observation.boundingBox.origin.x * sourceImage.size.width, observation.boundingBox.origin.y * sourceImage.size.height + point.y * rectHeight);
points[i] = p;
}
UIGraphicsBeginImageContextWithOptions(sourceImage.size, false, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor greenColor] set];
CGContextSetLineWidth(context, 2);
// 設(shè)置翻轉(zhuǎn)
CGContextTranslateCTM(context, 0, sourceImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 設(shè)置線類型
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
// 設(shè)置抗鋸齒
CGContextSetShouldAntialias(context, true);
CGContextSetAllowsAntialiasing(context, true);
// 繪制
CGRect rect = CGRectMake(0, 0, sourceImage.size.width, sourceImage.size.height);
CGContextDrawImage(context, rect, sourceImage.CGImage);
CGContextAddLines(context, points, landmarks2D.pointCount);
CGContextDrawPath(context, kCGPathStroke);
// 結(jié)束繪制
sourceImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Vision Demo演示:
以上是簡(jiǎn)單列舉了一些代碼颗味,具體更詳細(xì)的可參考官方文檔或Demo代碼(后面有Demo 下載鏈接)
下面GIF演示一下Vision Demo 超陆,此Demo比較簡(jiǎn)單,演示了基本的一些Vision的使用
圖像識(shí)別:
人臉識(shí)別浦马、特征識(shí)別侥猬、文字識(shí)別
動(dòng)態(tài)識(shí)別:
動(dòng)態(tài)監(jiān)測(cè)人臉,動(dòng)態(tài)進(jìn)行添加
Demo下載地址
https://github.com/bigsen/Vision_Demo
https://github.com/bigsen/Vision_Track
https://github.com/NilStack/HelloVision
https://github.com/jeffreybergier/Blog-Getting-Started-with-Vision
參考資料:
http://www.reibang.com/p/174b7b67acc9
http://www.reibang.com/p/e371099f12bd
https://github.com/NilStack/HelloVision
https://developer.apple.com/documentation/vision
https://github.com/jeffreybergier/Blog-Getting-Started-with-Vision
https://tech.iheart.com/iheart-wwdc-familiar-faces-1093fe751d9e
http://yulingtianxia.com/blog/2017/06/19/Core-ML-and-Vision-Framework-on-iOS-11/