目录:[Swift]Xcode实际操作
本文将演示机器学习框架的使用,实现对图片中物体的检测和识别。
首先访问苹果开发者网站关于机器学习的网址:
https://developer.apple.com/cn/machine-learning/
点击右侧的滚动条,跳转到模型知识区域。
点击页面最下方的【Learn about working with models】进入机器学习模型页面:
https://developer.apple.com/cn/machine-learning/build-run-models/
点击右侧的垂直滚动条,跳转到模型下载区域。
苹果提供了多个已经完成训练的机器学习模型,
选择【ResNet 50】:从 1000 种类别对象 (如树木、动物、食物、汽车及人物等) 中检测出图像中的主体。
点击下方的【Core】进行下载,https://docs-assets.developer.apple.com/coreml/models/Resnet50.mlmodel
模型下载后将模型拖动到项目中【DemoApp】
在弹出的选项设置窗口中,保持默认的参数设置,然后点击完成【Finish】按钮,确认模型的导入。
在左侧的模型框架区,选择查看模型文件。
从右侧的属性面板可以看出模型的类型、体积、作者、版权声明、描述信息。
从底部的参数可以看出,该模型拥有一个输入参数,和两个输出参数。
在资源文件中导入一张鸟类的图片,将使用机器学习迅雷模型,检测图片中出现的鸟类的名称。
在项目导航区,打开视图控制器的代码文件【ViewController.swift】
1 import UIKit
2 //导入机器学习框架
3 import CoreML
4
5 class ViewController: UIViewController {
6
7 override func viewDidLoad() {
8 super.viewDidLoad()
9 // Do any additional setup after loading the view, typically from a nib.
10
11 //加载项目中指定名称的图片资源
12 let image = UIImage(named: "sample")
13
14 //机器学习模型只可以识别像素缓存格式的图像,
15 //需要将图片的格式进行转换,
16 //首先定义图片格式转换后的宽度和高度
17 let width : CGFloat = 224.0
18 let height : CGFloat = 224.0
19
20 //然后获得一个基于位图的上下文,并设置其为当前的上下文。
21 UIGraphicsBeginImageContext(CGSize(width: width, height: height))
22 //将从项目中加载的图像,绘制在上下文的指定区域
23 image?.draw(in:CGRect(x: 0, y: 0, width: width, height: height))
24 //接着从上下文中获得格式转换后的图像
25 let newImage = UIGraphicsGetImageFromCurrentImageContext()
26 //完成图像的格式转换后,关闭当前的上下文。
27 UIGraphicsEndImageContext()
28
29 //添加一个版本兼容性的判断语句
30 if #available(iOS 11.0, *)
31 {
32 //初始化机器学习模型的对象
33 let resnet50 = Resnet50()
34
35 //通过调用机器学习模型的对象的预测方法,对图像中的物体进行识别。
36 //需要注意的是,传入的图片需要是CVPixelBuffer格式,
37 //这里使用一个方法将图片进行格式转换,此方法在下方实现
38 guard let output = try? resnet50.prediction(image:pixelBufferFromImage(image: newImage!)) else
39 {
40 fatalError("Unexpected error.")
41 }
42
43 //在控制台输出识别的结果
44 print(output.classLabel)
45 }
46 }
47
48 //添加一个方法,用来实现图片格式的转换
49 func pixelBufferFromImage(image: UIImage) -> CVPixelBuffer
50 {
51 //初始化一个上下文对象,它将被用来渲染CIImage图像
52 let ciContext = CIContext(options: nil)
53 //通过UIImage对象,初始化一个CIImage对象。
54 let ciImage = CIImage(image: image)
55 //通过上下文对象,将CIImage对象,转换为CGImage类型。
56 //其中extend属性表示该对象在上下文中的区域。
57 let cgImage = ciContext.createCGImage(ciImage!, from: ciImage!.extent)
58
59 //创建一个非安全的可变指针,并给指针分配相应的内存。
60 let umPointer = UnsafeMutablePointer<UnsafeRawPointer>.allocate(capacity: 1)
61 //初始化一个CFNumber格式的数字,该类型位于Core Foundation框架
62 let cfNum = CFNumberCreate(kCFAllocatorDefault, .intType, umPointer)
63 //初始化一个数组对象,它将作为后面的字典对象的值
64 let values: [CFTypeRef] = [kCFBooleanTrue, kCFBooleanTrue, cfNum!]
65 //初始化两个非安全可变指针,作为字典的键和值
66 //键
67 let keysPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 1)
68 //值
69 let valuesPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 1)
70
71 //初始化一个字符串数组,包含三个键
72 //1.像素缓存图像兼容性
73 //2.像素缓存位图上下文兼容性
74 //3..像素缓存每行的字节数
75 let keys: [CFString] = [kCVPixelBufferCGImageCompatibilityKey,//1.像素缓存图像兼容性
76 kCVPixelBufferCGBitmapContextCompatibilityKey,//2.像素缓存位图上下文兼容性
77 kCVPixelBufferBytesPerRowAlignmentKey]//3..像素缓存每行的字节数
78 //使用上文创建的两个数字,
79 //对键、值两个非安全可变指针进行初始化
80 keysPointer.initialize(to: keys)
81 valuesPointer.initialize(to: values)
82
83 //通过键、值两个指针,以及默认的内存分配方式和键的数量等参数,初始化一个字典对象。
84 //该字典对象将作为配置选项,被用来创建像素缓存
85 let options = CFDictionaryCreate(kCFAllocatorDefault, keysPointer, valuesPointer, keys.count, nil, nil)
86 //新建一个图像缓存变量
87 var pxbuffer: CVPixelBuffer?
88 //然后对输入的缓存变量进行初始化。
89 //参数依次标注
90 var status = CVPixelBufferCreate(kCFAllocatorDefault, //1.内存分配方式
91 cgImage!.width,//2.图像宽度
92 cgImage!.height,//3.图像高度
93 kCVPixelFormatType_32BGRA, //4.像素格式类型
94 options, //5.配置参数
95 &pxbuffer)//6.像素缓存的内存地址
96 //接着锁定像素缓冲区的基址,在使用CPU访问像素数据之前,必须调用该函数
97 status = CVPixelBufferLockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
98
99 //获得像素缓冲区的基址
100 let bufferAddress = CVPixelBufferGetBaseAddress(pxbuffer!)
101 //然后创建一个基于设备的RGB颜色空间。
102 //当在输出设备上显示时,依赖于设备的颜色空间中的颜色,
103 //不会被变换或以其他方式被修改
104 let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
105 //获得像素缓冲区每行的字节数
106 let bytesperrow = CVPixelBufferGetBytesPerRow(pxbuffer!)
107 //通过上文创建的参数,初始化一个二维绘图环境
108 let context = CGContext(data: bufferAddress,
109 width: cgImage!.width,
110 height: cgImage!.height,
111 bitsPerComponent: 8,
112 bytesPerRow: bytesperrow,
113 space: rgbColorSpace,
114 bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue
115 | CGBitmapInfo.byteOrder32Little.rawValue)
116 //重置二维绘图环境的旋转角度为0
117 context?.concatenate(CGAffineTransform(rotationAngle: 0))
118 //由于当前的二维绘图环境的坐标系统,和设备屏幕的坐标系统不同,
119 //所以在此对二维绘图环境进行上下文翻转
120 context?.concatenate(__CGAffineTransformMake( 1, 0, 0, -1, 0, CGFloat(cgImage!.height) ))
121
122 //将图像绘制在二维绘图环境中,并指定图像的显示区域。
123 context?.draw(cgImage!,
124 in: CGRect(x:0, y:0,
125 width:CGFloat(cgImage!.width),
126 height:CGFloat(cgImage!.height)))
127 //最后解锁橡树缓冲区的基地址。
128 //在使用CPU访问像素数据之后,必须调用该函数。
129 status = CVPixelBufferUnlockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
130
131 //返回处理完成的像素缓存
132 return pxbuffer!
133 }
134
135 override func didReceiveMemoryWarning() {
136 super.didReceiveMemoryWarning()
137 // Dispose of any resources that can be recreated.
138 }
139 }