1. 程式人生 > >[Xcode10 實際操作]七、檔案與資料-(20)CoreML機器學習框架:檢測和識別圖片中的物體

[Xcode10 實際操作]七、檔案與資料-(20)CoreML機器學習框架:檢測和識別圖片中的物體

本文將演示機器學習框架的使用,實現對圖片中物體的檢測和識別。

首先訪問蘋果開發者網站關於機器學習的網址:

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 }