热烈欢迎,请直接点击!!!

进入博主App Store主页,下载使用各个作品!!!

注:博主将坚持每月上线一个新app!!!

目录:[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 }