ARKit
、Core ML
、FileProvider
、IdentityLookup
、Core NFC
、Vison
等。Vison
官方文档Face Detection and Recognition
: 人脸检测 Image Alignment Analysis
: 图像对比分析Barcode Detection
: 二维码/条形码检测 Text Detection
: 文字检测 Object Detection and Tracking
: 目标跟踪 CVPixelBufferRef
CGImageRef
CIImage
NSURL
NSData
CVPixelBuffer
CGImage
CIImage
URL
Data
具体详情可在
Vision.framework
的VNImageRequestHandler.h
文件中查看
vision
的时候,我们首先需要明确自己需要什么效果,然后根据想要的效果来选择不同的类Request
提供给一个 RequestHandler
Handler
持有需要识别的图片信息,并将处理结果分发给每个 Request
的 completion Block
中results
属性中得到 Observation
数组observations
数组中的内容根据不同的request请求返回了不同的observation
Observation
有boundingBox
,landmarks
等属性,存储的是识别后物体的坐标,点位等RequestHandler
处理请求对象VNImageRequestHandler
: 处理与单个图像有关的一个或多个图像分析请求的对象 CVPixelBuffer
, CGImage
, CIImage
, URL
, Data
VNSequenceRequestHandler
: 处理与多个图像序列有关的图像分析请求的对象 VNRequest
: 图像分析请求的抽象类, 继承于NSObject
VNBaseImageRequest
: 专注于图像的特定部分的分析请求VNObservation
检测对象VNObservation
: 图像分析结果的抽象类, 继承与NSObject
VNImageRequestHandler
对象时, 可接受的的CIImage
//1. 转成ciimage
guard let ciImage = CIImage(image: image) else { return }
let requestHandle = VNImageRequestHandler(ciImage: ciImage, options: [:])
VNRequest
: 是所有请求Request的父类public typealias VNRequestCompletionHandler = (VNRequest, Error?) -> Swift.Void
//4. 设置回调
let completionHandle: VNRequestCompletionHandler = { request, error in
let observations = request.results
//识别出来的对象数组
}
//无参数
public convenience init()
//闭包参数
public init(completionHandler: Vision.VNRequestCompletionHandler? = nil)
let baseRequest = VNDetectTextRectanglesRequest(completionHandler: completionHandle)
// 设置识别具体文字
baseRequest.setValue(true, forKey: "reportCharacterBoxes")
open func perform(_ requests: [VNRequest]) throws
//6. 发送请求
DispatchQueue.global().async {
do{
try requestHandle.perform([baseRequest])
}catch{
print("Throws:\(error)")
}
}
Observations
对象results
是[Any]?
类型boundingBox
属性可以获取到对应的文本区域的尺寸boundingBox
得到的是相对iamge的比例尺寸, 都是小于1的//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.boundingBox
smallRects.append(convertRect(boundBox, image))
}
}
坐标转换
/// image坐标转换
fileprivate func convertRect(_ rectangleRect: CGRect, _ image: UIImage) -> CGRect {
//此处是将Image的实际尺寸转化成imageView的尺寸
let imageSize = image.scaleImage()
let w = rectangleRect.width * imageSize.width
let h = rectangleRect.height * imageSize.height
let x = rectangleRect.minX * imageSize.width
//该Y坐标与UIView的Y坐标是相反的
let y = (1 - rectangleRect.minY) * imageSize.height - h
return CGRect(x: x, y: y, width: w, height: h)
}
//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 in
let observations = request.results
self.handleImageObservable(type: type, image: image, observations, completeBack)
}
//5. 创建识别请求
switch type {
case .rectangle:
baseRequest = VNDetectRectanglesRequest(completionHandler: completionHandle)
case .staticFace:
baseRequest = VNDetectFaceRectanglesRequest(completionHandler: completionHandle)
default:
break
}
/// 矩形检测
fileprivate func rectangleDectect(_ observations: [Any]?, image: UIImage, _ complecHandle: JunDetectHandle){
//1. 获取识别到的VNRectangleObservation
guard let boxArr = observations as? [VNRectangleObservation] else { return }
//2. 创建rect数组
var bigRects = [CGRect]()
//3. 遍历识别结果
for boxObj in boxArr {
// 3.1
bigRects.append(convertRect(boxObj.boundingBox, image))
}
//4. 回调结果
complecHandle(bigRects, [])
}
observation
转成VNFaceObservation
guard let boxArr = observations as? [VNFaceObservation] else { return }
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.hashValue
let version = code.symbolVersion
let mask = code.maskPattern
let data = code.errorCorrectedPayload
let dataStr = String(data: data, encoding: .utf8)
print("这是二维码信息--", level, "---", version, "----", mask, "---", dataStr ?? "")
}
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 }
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()
由于真机不好录制gif图(尝试了一下, 效果不是很好, 放弃了), 想看效果的朋友下载源码真机运行吧
request
的初始化这里就不做介绍了, 说一下handle
的初始化方法 CVPixelBuffer
: 扫描实时输出的对象//1. 创建处理请求
let faceHandle = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
DispatchQueue.global().async {
do{
try faceHandle.perform([baseRequest])
}catch{
print("Throws:\(error)")
}
}
/// 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) + 40
let imageHeight = image.size.height / image.size.width * imageWidth
return 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...pointCount {
//2.1 获取当前位置并转化到合适尺寸
let point = landmark.normalizedPoints[i]
let rectWidth = viewRect.width * faceRect.width
let rectHeight = viewRect.height * faceRect.height
let rectY = viewRect.height - (point.y * rectHeight + faceRect.minY * viewRect.height)
var rectX = point.x * rectWidth + faceRect.minX * viewRect.width
if position == .front{
rectX = viewRect.width + (point.x - 1) * rectWidth
}
XArray.append(rectX)
YArray.append(rectY)
}
return (XArray, YArray)
}
CGRect
, 添加眼镜效果即可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, 文中如有解释不全, 或者错误的地方, 还请不吝赐教