Swift之Vision 图像识别框架

2017-11-29  本文已影响1062人  TitanCoder

Swift之Vision 图像识别框架

一. Vision应用场景

二. Vision支持的图片类型

1. Objective-C中

2. Swift中

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

三. Vision之API介绍

1. RequestHandler处理请求对象

2. VNRequest介绍

VNImageBasedRequest.png

3. VNObservation检测对象

VNObservation.png

四. 实战演练

1. 文本检测

WechatIMG3.jpeg WechatIMG5.jpeg

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 创建回调闭包

public typealias VNRequestCompletionHandler = (VNRequest, Error?) -> Swift.Void

//4. 设置回调
let completionHandle: VNRequestCompletionHandler = { request, error in
    let 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对象

//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)
}

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

1511935758595.jpg 1511936019734.jpg
//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, [])
    }

guard let boxArr = observations as? [VNFaceObservation] else { return }

3. 条码识别

1511936988374.jpg
//支持的可识别的条码类型(需要直接用class调用)
open class var supportedSymbologies: [VNBarcodeSymbology] { get }

//设置可识别的条码类型
open var symbologies: [VNBarcodeSymbology]
let request = VNDetectBarcodesRequest(completionHandler: completionHandle)
request.symbologies = VNDetectBarcodesRequest.supportedSymbologies
//条码类型: qr, code128....等等
open var symbology: VNBarcodeSymbology { get }

//条码的相关信息
open var barcodeDescriptor: CIBarcodeDescriptor? { get }

//如果是二维码, 则是二维码的网址链接    
open var payloadStringValue: String? { get }
    /// 二维码信息处理
    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 ?? "")
    }

4. 人脸特征识别

1511944652200.jpg
    /// 脸部轮廓
    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 }
//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图(尝试了一下, 效果不是很好, 放弃了), 想看效果的朋友下载源码真机运行吧

//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..<landmark.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)
    }

6. 物体跟踪

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
//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中的所有使用的情况


GitHub--Demo地址


其他相关文章

上一篇 下一篇

猜你喜欢

热点阅读