Swift之Vision 图像识别框架

  • 2017年苹果大大又推出了新机型iPhone 8和iPhone 8Plus, 这还不是重点, 重点是那一款价值9000RMB的iPhone X, 虽说网上吐槽声从未停止过, 但是我觉得还是不错的哈!
  • 软件方面, 苹果大大也推出了iOS 11, 经本人iPhone 7手机亲测, 耗电快外加通知栏改不完的bug
  • 当然了随着iOS 11的推出, 也随之推出了一些新的API,如:ARKitCore MLFileProviderIdentityLookupCore NFCVison 等。
  • 这里我们还要说的就是Apple 在 WWDC 2017 推出的图像识别框架--Vison官方文档
  • Demo地址

一. Vision应用场景

  • Face Detection and Recognition : 人脸检测

    • 支持检测笑脸、侧脸、局部遮挡脸部、戴眼镜和帽子等场景,可以标记出人脸的矩形区域
    • 可以标记出人脸和眼睛、眉毛、鼻子、嘴、牙齿的轮廓,以及人脸的中轴线
  • Image Alignment Analysis: 图像对比分析
  • Barcode Detection: 二维码/条形码检测
    • 用于查找和识别图像中的条码
    • 检测条形码信息
  • Text Detection: 文字检测
    • 查找图像中可见文本的区域
    • 检测文本区域的信息
  • Object Detection and Tracking: 目标跟踪
    • 脸部,矩形和通用模板

二. Vision支持的图片类型

1. Objective-C中

  • CVPixelBufferRef
  • CGImageRef
  • CIImage
  • NSURL
  • NSData

2. Swift中

  • CVPixelBuffer
  • CGImage
  • CIImage
  • URL
  • Data

具体详情可在Vision.frameworkVNImageRequestHandler.h文件中查看

三. Vision之API介绍

  • 使用在vision的时候,我们首先需要明确自己需要什么效果,然后根据想要的效果来选择不同的类
  • 给各种功能的 Request 提供给一个 RequestHandler
  • Handler 持有需要识别的图片信息,并将处理结果分发给每个 Requestcompletion Block
  • 可以从 results 属性中得到 Observation 数组
  • observations数组中的内容根据不同的request请求返回了不同的observation
  • 每种ObservationboundingBoxlandmarks等属性,存储的是识别后物体的坐标,点位等
  • 我们拿到坐标后,就可以进行一些UI绘制。

1. RequestHandler处理请求对象

  • VNImageRequestHandler: 处理与单个图像有关的一个或多个图像分析请求的对象

    • 一般情况下都是用该类处理识别请求
    • 初始化方法支持CVPixelBuffer, CGImage, CIImage, URL, Data
  • VNSequenceRequestHandler: 处理与多个图像序列有关的图像分析请求的对象
    • 目前我在处理物体跟踪的时候使用该类
    • 初始化方法同上

2. VNRequest介绍

  • VNRequest: 图像分析请求的抽象类, 继承于NSObject
  • VNBaseImageRequest: 专注于图像的特定部分的分析请求
  • 具体分析请求类如下:

3. VNObservation检测对象

  • VNObservation: 图像分析结果的抽象类, 继承与NSObject
  • 图像检测结果的相关处理类如下:

四. 实战演练

1. 文本检测

  • 方式一: 识别出具体的每一个字体的位置信息
  • 方式二: 识别一行字体的位置信息
  • 如图效果:

1.1 现将图片转成初始化VNImageRequestHandler对象时, 可接受的的CIImage

//1. 转成ciimage
guard let ciImage = CIImage(image: image) else { return }
复制代码

1.2 创建处理请求的handle

  • 参数一: 图片类型
  • 参数二: 字典类型, 有默认值为[:]
let requestHandle = VNImageRequestHandler(ciImage: ciImage, options: [:])
复制代码

1.3 创建回调闭包

  • 两个参数, 无返回值
  • VNRequest: 是所有请求Request的父类
public typealias VNRequestCompletionHandler = (VNRequest, Error?) -> Swift.Void复制代码
  • 具体代码如下:
//4. 设置回调
let completionHandle: VNRequestCompletionHandler = { request, error inlet observations = request.results//识别出来的对象数组
}复制代码

1.4 创建识别请求

  • 两种初始化方式
//无参数
public convenience init()//闭包参数
public init(completionHandler: Vision.VNRequestCompletionHandler? = nil)复制代码
  • 这里使用带闭包的初始化方式
let baseRequest = VNDetectTextRectanglesRequest(completionHandler: completionHandle)
复制代码
  • 属性设置(是否识别具体的每一个文字)
// 设置识别具体文字
baseRequest.setValue(true, forKey: "reportCharacterBoxes")
复制代码
  • 不设置该属性, 识别出来的是一行文字

1.5 发送请求

    open func perform(_ requests: [VNRequest]) throws
复制代码
  • 该方法会抛出一个异常错误
  • 在连续不断(摄像头扫描)发送请求过程中, 必须在子线程执行该方法, 否则会造成线程堵塞
//6. 发送请求
DispatchQueue.global().async {do{try requestHandle.perform([baseRequest])}catch{print("Throws:\(error)")}
}复制代码

1.6 处理识别的Observations对象

  • 识别出来的results[Any]?类型
  • 根据boundingBox属性可以获取到对应的文本区域的尺寸
  • 需要注意的是:
    • boundingBox得到的是相对iamge的比例尺寸, 都是小于1的
    • Y轴坐标于UIView坐标系是相反的
//1. 获取识别到的VNTextObservation
guard let boxArr = observations as? [VNTextObservation] else { return }//2. 创建rect数组
var bigRects = [CGRect](), smallRects = [CGRect]()//3. 遍历识别结果
for boxObj in boxArr {// 3.1尺寸转换//获取一行文本的区域位置bigRects.append(convertRect(boxObj.boundingBox, image))//2. 获取guard let rectangleArr = boxObj.characterBoxes else { continue }for rectangle in rectangleArr{//3. 得到每一个字体的的尺寸let boundBox = rectangle.boundingBoxsmallRects.append(convertRect(boundBox, image))}
}复制代码

坐标转换

/// image坐标转换
fileprivate func convertRect(_ rectangleRect: CGRect, _ image: UIImage) -> CGRect {
//此处是将Image的实际尺寸转化成imageView的尺寸let imageSize = image.scaleImage()let w = rectangleRect.width * imageSize.widthlet h = rectangleRect.height * imageSize.heightlet x = rectangleRect.minX * imageSize.width//该Y坐标与UIView的Y坐标是相反的let y = (1 - rectangleRect.minY) * imageSize.height - hreturn CGRect(x: x, y: y, width: w, height: h)
}复制代码

2. 矩形识别和静态人脸识别

  • 识别图像中的矩形
  • 静态人脸识别
  • 主要核心代码
//1. 转成ciimage
guard let ciImage = CIImage(image: image) else { return }//2. 创建处理request
let requestHandle = VNImageRequestHandler(ciImage: ciImage, options: [:])//3. 创建baseRequest
//大多数识别请求request都继承自VNImageBasedRequest
var baseRequest = VNImageBasedRequest()//4. 设置回调
let completionHandle: VNRequestCompletionHandler = { request, error inlet observations = request.resultsself.handleImageObservable(type: type, image: image, observations, completeBack)
}//5. 创建识别请求
switch type {
case .rectangle:baseRequest = VNDetectRectanglesRequest(completionHandler: completionHandle)
case .staticFace:baseRequest = VNDetectFaceRectanglesRequest(completionHandler: completionHandle)
default:break
}复制代码
  • 处理识别的observation
    /// 矩形检测fileprivate func rectangleDectect(_ observations: [Any]?, image: UIImage, _ complecHandle: JunDetectHandle){//1. 获取识别到的VNRectangleObservationguard let boxArr = observations as? [VNRectangleObservation] else { return }//2. 创建rect数组var bigRects = [CGRect]()//3. 遍历识别结果for boxObj in boxArr {// 3.1bigRects.append(convertRect(boxObj.boundingBox, image))}//4. 回调结果complecHandle(bigRects, [])}复制代码
  • 静态人脸识别需要将observation转成VNFaceObservation
guard let boxArr = observations as? [VNFaceObservation] else { return }
复制代码

3. 条码识别

  • 这里请求的步骤与矩形识别相同, 这里不再赘述
  • 需要注意的是,在初始化request的时候需要设一个置可识别的条码类型参数
  • 这里先看一下VNDetectBarcodesRequest的两个参数
//支持的可识别的条码类型(需要直接用class调用)
open class var supportedSymbologies: [VNBarcodeSymbology] { get }//设置可识别的条码类型
open var symbologies: [VNBarcodeSymbology]
复制代码
  • 此处设置可识别到的条码类型为, 该请求支持是别的所有类型, 如下
  • 注意supportedSymbologies参数的调用方法
let request = VNDetectBarcodesRequest(completionHandler: completionHandle)
request.symbologies = VNDetectBarcodesRequest.supportedSymbologies
复制代码
  • 条码识别不但能识别条码的位置信息, 还可以识别出条码的相关信息, 这里以二维码为例
  • 这里需要将识别的observations转成[VNBarcodeObservation]
  • VNBarcodeObservation有三个属性
//条码类型: qr, code128....等等
open var symbology: VNBarcodeSymbology { get }//条码的相关信息
open var barcodeDescriptor: CIBarcodeDescriptor? { get }//如果是二维码, 则是二维码的网址链接
open var payloadStringValue: String? { get }
复制代码
  • 如上述图片识别出来的payloadStringValue参数则是小编的简书地址
  • 下面是以上述图片的二维码为例处理的CIBarcodeDescriptor对象
  • 有兴趣的可以仔细研究研究
    /// 二维码信息处理fileprivate func qrCodeHandle(barCode: CIBarcodeDescriptor?){//1. 转成对应的条码对象guard let code = barCode as? CIQRCodeDescriptor else { return }//2. 解读条码信息let level = code.errorCorrectionLevel.hashValuelet version = code.symbolVersionlet mask = code.maskPatternlet data = code.errorCorrectedPayloadlet dataStr = String(data: data, encoding: .utf8)print("这是二维码信息--", level, "---", version, "----", mask, "---", dataStr ?? "")}复制代码

4. 人脸特征识别

  • 可识别出人脸的轮廓, 眼睛, 鼻子, 嘴巴等具体位置
  • VNFaceLandmarks2D介绍
    /// 脸部轮廓var faceContour: VNFaceLandmarkRegion2D?/// 左眼, 右眼var leftEye: VNFaceLandmarkRegion2D?var rightEye: VNFaceLandmarkRegion2D?/// 左睫毛, 右睫毛var leftEyebrow: VNFaceLandmarkRegion2D?var rightEyebrow: VNFaceLandmarkRegion2D?/// 左眼瞳, 右眼瞳var leftPupil: VNFaceLandmarkRegion2D?var rightPupil: VNFaceLandmarkRegion2D?/// 鼻子, 鼻嵴, 正中线var nose: VNFaceLandmarkRegion2D?var noseCrest: VNFaceLandmarkRegion2D?var medianLine: VNFaceLandmarkRegion2D?/// 外唇, 内唇var outerLips: VNFaceLandmarkRegion2D?var innerLips: VNFaceLandmarkRegion2D?
复制代码
//某一部位所有的像素点
@nonobjc public var normalizedPoints: [CGPoint] { get }//某一部位的所有像素点的个数
open var pointCount: Int { get }
复制代码
  • 将所有的像素点坐标转换成image对应的尺寸坐标
  • 使用图像上下文, 对应部位画线
  • 在UIView中重写func draw(_ rect: CGRect)方法
//5.1 获取当前上下文
let content = UIGraphicsGetCurrentContext()//5.2 设置填充颜色(setStroke设置描边颜色)
UIColor.green.set()//5.3 设置宽度
content?.setLineWidth(2)//5.4. 设置线的类型(连接处)
content?.setLineJoin(.round)
content?.setLineCap(.round)//5.5. 设置抗锯齿效果
content?.setShouldAntialias(true)
content?.setAllowsAntialiasing(true)//5.6 开始绘制
content?.addLines(between: pointArr)
content?.drawPath(using: .stroke)//5.7 结束绘制
content?.strokePath()
复制代码

5. 动态人脸识别和实时动态添加

由于真机不好录制gif图(尝试了一下, 效果不是很好, 放弃了), 想看效果的朋友下载源码真机运行吧

  • 这里提供一张可供扫描的图片

  • request的初始化这里就不做介绍了, 说一下handle的初始化方法

    • CVPixelBuffer: 扫描实时输出的对象
//1. 创建处理请求
let faceHandle = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])复制代码
  • 主要强调一点, 相机扫描, 获取实时图像的过程, 必须在子线程执行, 否在会堵塞线程, 整个app失去响应, 亲自踩过的坑
DispatchQueue.global().async {do{try faceHandle.perform([baseRequest])}catch{print("Throws:\(error)")}
}
复制代码

扫描结果处理

  • 动态人脸识别和静态人脸识别不同的地方就是, 动态实时刷新, 更新UI, 所以处理结果的方法相同
  • 动态添加: 这里处理方式是添加一个眼镜效果
  • 这里需要获取到两只眼睛的位置和宽度
    • 先获取到左右眼的所有的像素点和像素点的个数
    • 遍历所有的像素点, 转换成合适的坐标
    • 将左右眼的所有的point, 分别获取X和Y坐标放到不同的数组
    • 将数组有小到大排序, 得到X的最大和最小的差值, Y的最大和最小的差值
    • 具体代码如下
    /// H偶去转换后的尺寸坐标fileprivate func getEyePoint(faceModel: FaceFeatureModel, position: AVCaptureDevice.Position) -> CGRect{//1. 获取左右眼guard let leftEye = faceModel.leftEye else { return CGRect.zero }guard let rightEye = faceModel.rightEye else { return CGRect.zero }//2. 位置数组let leftPoint = conventPoint(landmark: leftEye, faceRect: faceModel.faceObservation.boundingBox, position: position)let rightPoint = conventPoint(landmark: rightEye, faceRect: faceModel.faceObservation.boundingBox, position: position)//3. 排序let pointXs = (leftPoint.0 + rightPoint.0).sorted()let pointYs = (leftPoint.1 + rightPoint.1).sorted()//4. 添加眼睛let image = UIImage(named: "eyes")!let imageWidth = (pointXs.last ?? 0.0) - (pointXs.first ?? 0) + 40let imageHeight = image.size.height / image.size.width * imageWidthreturn CGRect(x: (pointXs.first ?? 0) - 20, y: (pointYs.first ?? 0) - 5, width: imageWidth, height: imageHeight)}复制代码
  • 每一只眼睛的坐标处理
    /// 坐标转换fileprivate func conventPoint(landmark: VNFaceLandmarkRegion2D, faceRect: CGRect, position: AVCaptureDevice.Position) -> ([CGFloat], [CGFloat]){//1. 定义var XArray = [CGFloat](), YArray = [CGFloat]()let viewRect = previewLayer.frame//2. 遍历for i in 0..<landmark.pointCount {//2.1 获取当前位置并转化到合适尺寸let point = landmark.normalizedPoints[i]let rectWidth = viewRect.width * faceRect.widthlet rectHeight = viewRect.height * faceRect.heightlet rectY = viewRect.height - (point.y * rectHeight + faceRect.minY * viewRect.height)var rectX = point.x * rectWidth + faceRect.minX * viewRect.widthif position == .front{rectX = viewRect.width + (point.x - 1) * rectWidth}XArray.append(rectX)YArray.append(rectY)}return (XArray, YArray)}复制代码
  • 最后获取到该CGRect, 添加眼镜效果即可

6. 物体跟踪

  • 简介

    • 我们在屏幕上点击某物体, 然后Vision就会根据点击的物体, 实时跟踪该物体
    • 当你移动手机或者物体时, 识别的对象和红框的位置是统一的
  • 这里我们出的的对象是VNDetectedObjectObservation
  • 定义一个观察属性
fileprivate var lastObservation: VNDetectedObjectObservation?
复制代码
  • 创建一个处理多个图像序列的请求
//处理与多个图像序列的请求handle
let sequenceHandle = VNSequenceRequestHandler()
复制代码
  • 创建跟踪识别请求
//4. 创建跟踪识别请求
let trackRequest = VNTrackObjectRequest(detectedObjectObservation: lastObservation, completionHandler: completionHandle)
//将精度设置为高
trackRequest.trackingLevel = .accurate复制代码
  • 当用户点击屏幕时,我们想要找出用户点击的位置,
  • 根据点击的位置, 获取到一个新的物体对象
//2. 转换坐标
let convertRect = visionTool.convertRect(viewRect: redView.frame, layerRect: previewLayer.frame)//3. 根据点击的位置获取新的对象
let newObservation = VNDetectedObjectObservation(boundingBox: convertRect)
lastObservation = newObservation
复制代码
  • 获取到扫描的结果, 如果是一个VNDetectedObjectObservation对象, 重新赋值
//1. 获取一个实际的结果
guard let newObservation = observations?.first as? VNDetectedObjectObservation else { return }//2. 重新赋值
self.lastObservation = newObservation
复制代码
  • 根据获取到的新值, 获取物体的坐标位置
  • 转换坐标, 改变红色框的位置
//4. 坐标转换
let newRect = newObservation.boundingBox
let convertRect = visionTool.convertRect(newRect, self.previewLayer.frame)
self.redView.frame = convertRect
复制代码

以上就是iOS 11的新框架Vision在Swift中的所有使用的情况

  • 文中所列的内容可能有点空洞, 也稍微有点乱
  • 小编也是刚接触Vision, 文中如有解释不全, 或者错误的地方, 还请不吝赐教

GitHub--Demo地址

  • 注意:
  • 这里只是列出了主要的核心代码,具体的代码逻辑请参考demo
  • 文中相关介绍有的地方如果有不是很详细或者有更好建议的,欢迎联系小编
  • 如果方便的话, 还望star一下

其他相关文章

  • Swift之二维码的生成、识别和扫描
  • iOS黑科技之(CoreImage)静态人脸识别(一)
  • iOS黑科技之(AVFoundation)动态人脸识别(二)

Swift之Vision 图像识别框架相关推荐

  1. swift 加载gif 框架图片

    swift 加载gif 框架图片 SwiftGifOrigin 以下代码 轻松搞定 let imgView = UIImageView(frame: CGRect(x: 50, y: 100, wid ...

  2. Swift版iOS游戏框架Sprite Kit基础教程下册

    Swift版iOS游戏框架Sprite Kit基础教程下册 试读下载地址:http://pan.baidu.com/s/1qWBdV0C  介绍:本教程是国内唯一的Swift版的Spritekit教程 ...

  3. Perfect:Swift 语言服务器端软件框架简介

    Perfect:Swift 语言服务器端软件框架 Perfect 开源项目 参与 Perfect 开发 Slack 在线协同 Perfect:Swift 语言服务器端软件框架 Perfect是一组完整 ...

  4. 服务器框架语言,Perfect:Swift 语言服务器端软件框架简介

    Perfect:Swift 语言服务器端软件框架 Perfect:Swift 语言服务器端软件框架 Perfect是一组完整.强大的工具箱.软件框架体系和Web应用服务器,可以在Linux.iOS和m ...

  5. swift离散型网络框架利用extension加泛型

    OC见过离散型网络框架,项目用过后觉得不错不同业务扩展起来挺方便.但是最近这个项目中用的是集约型的,里面逻辑代码感觉比较冗余.但是迭代很久的项目又不能轻易动.刚好组员对Swift和组件化感兴趣.所以就 ...

  6. iOS swift Alamofire+HandyJSON网络框架封装

    iOS swift Alamofire+HandyJSON网络框架封装 我们在学习Objective_C时使用的网络框架是AFNetworking+MJExtension,而在swift中Alamof ...

  7. 基于Sikuli GUI图像识别框架的PC客户端自动化测试实践

    一.GUI图像识别框架元祖:Sikuli Sikuli 创始于 2009 年,是麻省理工学院用户界面设计小组的一个开源研究项目.2012 年由 RaiMan 接管开发和 支持并将其命名为 Sikuli ...

  8. Swift之SDWebImage第三方框架

    在学习Swift过程中,最害怕的使用了OC的第三方框架 好不容易配置成功了,却出现了意外的Bug [UIImageView setImageWithURL:]: unrecognized select ...

  9. swift之网络请求框架Alamofire

    原码解读:https://www.cnblogs.com/machao/p/6640248.html?utm_source=tuicool&utm_medium=referral 官网:htt ...

最新文章

  1. HTML5地理定位用法
  2. loadrunner 录制 odbc 迭代出现lrd_db_option: ERROR, return-code=LRDE2009错误
  3. 2020:编程语言大盘点
  4. boost::leaf::capture用法的测试程序
  5. Crossing River(信息学奥赛一本通-T1232)
  6. HTML:调用静态页面html 的几种方法
  7. 【codevs1230】元素查找,弱弱的二分查找
  8. 微博拟全球发售1100万股 发售价不超388港元
  9. java的setbounds_java Swing组件setBounds()简单用法实例分析
  10. 黑马程序员——关于static关键字
  11. Javascript知识汇总------获取构造函数constructor名称和一些字符串处理方法
  12. CentOS Linux操作系统
  13. I2C电路故障排除---边沿时间与杜邦线
  14. FlowJo For Mac(流式细胞分析器工具)
  15. 35枚不同风格的设计师个人网站欣赏
  16. 正则表达式 “.*“匹配任意字符
  17. 为什么价值增殖过程不外是超过一定点而延长了的价值形成过程
  18. PAT 1157 Anniversary
  19. 【沙发管家】夏普电视怎么装软件?怎么才能看免费电影,电视直播?
  20. Centos7 xfs_trans_read_buf_map metadata i/o error

热门文章

  1. flask https启动
  2. IndexError: list index out of range的解决办法
  3. Python Numpy多维数组.sum(axis=0/1/2...) 详解
  4. 3.Git的基本操作
  5. Python常用函数与技巧总结(二)
  6. 【tensorboard】从tensorboard上看出模型是否存在过拟合
  7. 学硕计算机306分调剂,考研学硕可调剂专硕 需符合两个条件
  8. java boolean 按位或_布尔运算符与按位运算符
  9. 7-6 统计字符串中数字字符的个数_洛谷 || 标题统计(C语言)
  10. wait notify的使用