目錄:[Swift]Xcode實際操做html
本文將演示機器學習框架的使用,實現對圖片中物體的檢測和識別。swift
首先訪問蘋果開發者網站關於機器學習的網址:數組
https://developer.apple.com/cn/machine-learning/緩存
點擊右側的滾動條,跳轉到模型知識區域。安全
點擊頁面最下方的【Learn about working with models】進入機器學習模型頁面:app
https://developer.apple.com/cn/machine-learning/build-run-models/框架
點擊右側的垂直滾動條,跳轉到模型下載區域。機器學習
蘋果提供了多個已經完成訓練的機器學習模型,ide
選擇【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 }