CoreML 与Vision
CoreML
-
CoreML是苹果在WWDC2017 新发布的
Framework
,方便了Machine Learning
在苹果自家平台的接接入与使用,同时苹果提供了Python
的coremltools
,方便将各大开源模型训练工具的现有模型转化为MLModel
。
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 接收 request
和 error
对象。
检查 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
如果你正在使用Mac,系统是OS X 10.8或者最新的10.9 Mavericks,恭喜你,系统自带了Python 2.7。如果你的系统版本低于10.8,请自行备份系统并免费升级到最新的10.9,就可以获得Python 2.7。 - 查看python版本
打开terminal
输入
python --version
python查看版本
- 安装pip
输入命令行
sudo easy_install pip
- 安装
coremltools
pip install -U coremltools
使用命令行
pip list
来查看自己安装的python第三方package
pip list
- 转换Caffe Model
这里可以获取一些需要的caffe模型
用来实验转CoreML
Caffe Model
将项目下载到本地以后, 移动到初始项目的目录中,它包含三个文件:class_labels.txt,deploy.prototxt和oxford102.caffemodel。
cd <directory>
terminal
里输入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')
现在这是很短的一行程式码,但其中包含很多事情,接下来让我解释一下这三个档案。
- deploy.prototxt – 描述神经网络的结构。
- oxford102.caffemodel – Caffe格式的数据训练模型。
- class_labels.txt – 包含模型能够识别的全部花类列表。
在上面的说明中,我们将定义一个名为coreml_model的模型,用来当做从Caffe转到Core ML的转换器,它是coremltools.converters.caffe.convert函式的运行结果,这行程式码的最后两个参数是:
image_input_names='data'
class_labels='class_labels.txt'
这两个参数定义了我们想要Core ML模型所接受的输入和输出,让我这样说:电脑只能理解数字。因此,如果不添加这两个参数,我们的Core ML模型将仅接受数字做为输入和输出,而不是图像和字符串做为输入和输出。
现在,你可以按下ENTER并且休息一下,根据你机器的计算能力,转换器运行需要一些时间,当转换器运行完成时,你将会看到一个简单的>>>
。
现在Caffe模型已经被转换,你需要将它保存下来,请输入下列所示的程式码
coreml_model.save('Flowers.mlmodel')
.mlmodel文件将保存在当前文件夹/目录中。
然后将.mlmodel拖入文件中, 可以直接使用