CoreML 与Vision

2018-07-03  本文已影响60人  Roct

本文环境为Swift4和iOS11.4 参考文章1, 参考文章2

CoreML

Vision

Vision是一个新的,强大的,易于使用的框架,是苹果于WWDC 2017上针对CoreML使用所提出的新Framework,能快速有效的用于面部检测、面部特征点、文字、矩形、条形码和物体。

开始

下载起始项目。它已包含了用于显示图片的用户界面,并允许用户从照片库中选择另一张图片。这样你就可以专注于实现 App 的机器学习和视觉方面。

构建并运行该项目;可以看到一张城市夜景图,以及一个按钮:

示例图片

从“照片” App 的照片库中选择另一张图片。此起始项目的 Info.plist 已经有 Privacy – Photo Library Usage Description,所以你会被提示允许使用。

图片和按钮之间的空隙有一个 label,将会在此显示模型对图片场景的分类。

将 Core ML 模型集成到你的 App

本教程使用 Places205-GoogLeNet 模型,可以从苹果的“机器学习”页面下载。下载第一个。还在这个页面,注意一下其它三个模型,它们都用于在图片中检测物体——树、动物、人等等。

为你的项目添加模型

下载 GoogLeNetPlaces.mlmodel 后,把它从 Finder 拖到项目导航器的 Resources 组里:

选择该文件,然后等一会儿。Xcode 生成了模型类后会显示一个箭头:

点击箭头,查看生成的类:

Xcode 已生成了输入和输出类以及主类 GoogLeNetPlaces,主类有一个 model 属性和两个 prediction 方法。

Vision 框架会把 GoogLeNetPlacesOutput 属性转换为自己的 results 类型,并管理对 prediction 方法的调用,所以在所有生成的代码中,我们只会使用 model 属性。

在 Vision Model 中包装 Core ML Model

终于,要开始写代码了!打开 ViewController.swift,并在 import UIKit 下面 import 两个框架:

import CoreML
import Vision

下一步,在 IBActions 扩展下方添加如下扩展:

// MARK: - Methods
extension ViewController {

  func detectScene(image: CIImage) {
    answerLabel.text = "detecting scene..."

    // 从生成的类中加载 ML 模型
    guard let model = try? VNCoreMLModel(for: GoogLeNetPlaces().model) else {
      fatalError("can't load Places ML model")
    }
  }
}

我们上面的代码做了这些事:

首先,给用户显示一条消息,让他们知道正在发生什么事情。

GoogLeNetPlaces 的指定初始化方法会抛出一个 error,所以创建时必须用 try

VNCoreMLModel 只是用于 Vision 请求的 Core ML 模型的容器。

标准的 Vision 工作流程是创建模型,创建一或多个请求,然后创建并运行请求处理程序。我们刚刚已经创建了模型,所以下一步是创建请求。

detectScene(image:) 的末尾添加如下几行:

// 创建一个带有 completion handler 的 Vision 请求
let request = VNCoreMLRequest(model: model) { [weak self] request, error in
  guard let results = request.results as? [VNClassificationObservation],
    let topResult = results.first else {
      fatalError("unexpected result type from VNCoreMLRequest")
  }

  // 在主线程上更新 UI
  let article = (self?.vowels.contains(topResult.identifier.first!))! ? "an" : "a"
  DispatchQueue.main.async { [weak self] in
    self?.answerLabel.text = "\(Int(topResult.confidence * 100))% it's \(article) \(topResult.identifier)"
  }
}

VNCoreMLRequest 是一个图像分析请求,它使用 Core ML 模型来完成工作。它的 completion handler 接收 requesterror 对象。

检查 request.results 是否是 VNClassificationObservation 对象数组,当 Core ML 模型是分类器,而不是预测器或图像处理器时,Vision 框架就会返回这个。而 GoogLeNetPlaces 是一个分类器,因为它仅预测一个特征:图像的场景分类。

VNClassificationObservation 有两个属性:identifier - 一个 String,以及 confidence - 介于0和1之间的数字,这个数字是是分类正确的概率。使用对象检测模型时,你可能只会看到那些 confidence 大于某个阈值的对象,例如 30% 的阈值。

然后取第一个结果,它会具有最高的 confidence 值,然后根据 identifier 的首字母把不定冠词设置为“a”或“an”。最后,dispatch 回到主线程来更新 label。你很快会明白分类工作为什么不在主线程,因为它会很慢。

现在,做第三步:创建并运行请求处理程序。

把下面几行添加到 detectScene(image:) 的末尾:

// 在主线程上运行 Core ML GoogLeNetPlaces 分类器
let handler = VNImageRequestHandler(ciImage: image)
DispatchQueue.global(qos: .userInteractive).async {
  do {
    try handler.perform([request])
  } catch {
    print(error)
  }
}

VNImageRequestHandler 是标准的 Vision 框架请求处理程序;不特定于 Core ML 模型。给它 image 作为 detectScene(image:) 的参数。然后调用它的 perform 方法来运行处理程序,传入请求数组。在这个例子里,我们只有一个请求。

perform 方法会抛出 error,所以用 try-catch 将其包住。

使用模型来分类场景

哇,刚刚写了好多代码!但现在只需要在两个地方调用 detectScene(image:) 就好了。

把下面几行添加到 viewDidLoad() 的末端和 imagePickerController(_:didFinishPickingMediaWithInfo:) 的末端:

guard let ciImage = CIImage(image: image) else {
  fatalError("couldn't convert UIImage to CIImage")
}

detectScene(image: ciImage)

现在构建并运行。不需要多久就可以看见分类:

哈哈,是的,图片里有 skyscrapers(摩天大楼)。还有一列火车。

如果仔细看一下



可以注意到图片的大小会有要求, 如果大小不一样可能会影响到图片识别的效果
所以我们需要对代码进行一些改进

extension ViewController: UIImagePickerControllerDelegate {
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        dismiss(animated: true, completion: nil)
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        picker.dismiss(animated: true)
        UIGraphicsBeginImageContextWithOptions(CGSize(width: 224, height: 224), true, 2.0)
    image.draw(in: CGRect(x: 0, y: 0, width: 299, height: 299))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    
    let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
    var pixelBuffer : CVPixelBuffer?
    let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(newImage.size.width), Int(newImage.size.height), kCVPixelFormatType_32ARGB, attrs, &pixelBuffer)
    guard (status == kCVReturnSuccess) else {
      return
    }
    
    CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
    let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer!)
    
    let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
    let context = CGContext(data: pixelData, width: Int(newImage.size.width), height: Int(newImage.size.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer!), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) //3
    
    context?.translateBy(x: 0, y: newImage.size.height)
    context?.scaleBy(x: 1.0, y: -1.0)
    
    UIGraphicsPushContext(context!)
    newImage.draw(in: CGRect(x: 0, y: 0, width: newImage.size.width, height: newImage.size.height))
    UIGraphicsPopContext()
    CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
    scene.image = newImage
    guard let prediction = try? Resnet50().prediction(image: pixelBuffer!) else {
      return
    }
    answerLabel.text = prediction.classLabel

    }
}

直接在图片选择的delegate里, 对图片进行处理, 将宽高设置为模型要求的大小

如何将你自己的模型转换成CoreML

苹果官方文档
下面将进行傻瓜式教学

python --version
python查看版本
sudo easy_install pip
pip install  -U  coremltools

使用命令行

pip list

来查看自己安装的python第三方package


pip list
cd  <directory>

terminal里输入python进入python编码

python编码
第一步是import Core ML tools
import  coremltools

下一步

coreml_model  =  coremltools.converters.caffe.convert(('oxford102.caffemodel',  'deploy.prototxt'),  image_input_names='data',  class_labels='class_labels.txt')

现在这是很短的一行程式码,但其中包含很多事情,接下来让我解释一下这三个档案。

image_input_names='data'
class_labels='class_labels.txt'
这两个参数定义了我们想要Core ML模型所接受的输入和输出,让我这样说:电脑只能理解数字。因此,如果不添加这两个参数,我们的Core ML模型将仅接受数字做为输入和输出,而不是图像和字符串做为输入和输出。

现在,你可以按下ENTER并且休息一下,根据你机器的计算能力,转换器运行需要一些时间,当转换器运行完成时,你将会看到一个简单的>>>

现在Caffe模型已经被转换,你需要将它保存下来,请输入下列所示的程式码

coreml_model.save('Flowers.mlmodel')

.mlmodel文件将保存在当前文件夹/目录中。

然后将.mlmodel拖入文件中, 可以直接使用

CoreML官方文档

上一篇下一篇

猜你喜欢

热点阅读