Apple 為開發者社群做了很多事情的同時,亦有另一間公司費盡苦心創造好的工具和服務給開發者,這間公司就是 Google。近年,Google 致力推出並不斷改進他們的服務,像是 Google Cloud、Firebase、TensorFlow 等等,讓 iOS 與 Android 開發者的力量更強大。
在 Google I/O 2018 開發者大會上,Google 發表了一款全新的開發工具 ML Kit 給開發者。Google 在人工智能競爭上一直領先,這次讓開發者獲取 ML Kit 的模型,也代表著 Google 給予開發者很強大的力量。
![Google ML Kit-logo](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-logo.png)
有了 ML Kit,你只需要很少程式碼就可以執行各種機器學習任務。CoreML 與 ML Kit 最大的分別是,在 CoreML 你必須加入自己的模型,但在 ML Kit 你可以選擇 Google 所提供的模型或是執行自己的模型。在本次教學中,我們會選擇 Google 使用的模型,因為加入自己的模型必須使用 TensorFlow、並對 Python 有相當程度的瞭解。
編者提醒:經過 WWDC 18 的發表後,你現在可以藉由 Xcode 10 的 Playgrounds 使用 CreateML 來創建自己的 ML 模型。
另一點差異是,若你的模型很大,你必須有能力將 ML 模型放到 Firebase 之中,並讓你的 App 呼叫伺服器。在 CoreML,你只需要在設備上執行機器學習就可以了。而下列就是 ML Kit 的相關應用:
- 條碼掃描
- 人臉偵測
- 圖像標籤
- 文字辨識
- 地標辨識
- 智慧回覆(即將推出)
在本教學中,我將會展示如何在 Firebase 上創建一個新專案、使用 Cocoapods 下載必要的套件、並將 ML Kit 整合到我們的 App 之中!讓我們開始吧!
創建一個 Firebase 專案
第一步,先到 Firebase 控制台,然後登入你的 Google 帳戶,完成登入後你應該會看到以下的歡迎畫面。
![Firebase Console](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-1.png)
按下 Add Project 新增專案,並為專案命名。就這次的情況而言,讓我們將專案取名為 ML Kit Introduction
。讓專案 ID 保持不變,接著依照情況轉換 Country/region。最後按下 Create Project 按鈕,這應該一分鐘就可以完成。
備註:Firebase 可以創建的專案數量設有上限,所以請謹慎建立專案。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-2.png)
完成後,你的頁面看起來應該像這樣:
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-3.png)
這是你的專案總覽頁面,你可以在這控制台操作各種不同的 Firebase 功能。恭喜你,你已經建立了第一個 Firebase 專案!先不要動這個頁面,讓我們來看看 iOS 專案。從這裡下載初始專案。
簡單看看初始專案
打開初始專案,你會看到大部分的 UI 都為你設計好了。建置並執行程式,你會看到一個 UITableView
,有不同的 ML Kit 選項並導引到不同的頁面。
![iOS App Storyboard](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-4.png)
如果你點選 Choose Image 按鈕,一個 UIImagePickerView
會彈出,選擇一張圖片就可以改變空佔位符。然而,你選擇了圖片之後卻什麼事都沒發生。到此,我們就需要整合 ML Kit,並在圖片上執行機器學習任務了。
連結 Firebase 到 App
回到你專案的 Firebase 控制台,按下寫著 “Add Firebase to your iOS App” 的按鈕。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-5.png)
然後,應該會有一個窗口彈出,指示你如何連結 Firebase。第一件要做的事,就是連結到你的 iOS Bundle ID,你可以在 Xcode 的專案概覽頁面的 General 頁籤中找到這個 ID。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-6.png)
將 ID 填入欄位,然後按下 Register App
。你不需要填寫選填的欄位,因為這個 App 不會上架到 App Store。然後,你會被引導到第二步:下載 GoogleService-Info.plist
,這是一個將會加到專案的重要檔案,按 Download
來下載檔案。
如同 Firebase 網站所示,將檔案拖到側邊欄。確認 Copy items if needed
的方格有被勾選。加入了所需的檔案後,你就可以點選 Next
來進行第三步。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-7.png)
使用 Cocoapods 安裝 Firebase 函式庫
下一步我們將介紹 Cocoapods 的概念。Cocoapods 基本上是一個讓你可以簡單地將套件加入專案之中的方法;然而,當中只要有微小的錯誤,就可能導致災難性的結果。首先,關閉所有 Xcode 視窗並退出應用程式。
備註:請確認你是否已經在裝置上安裝 Cocoapods,如果還沒有安裝,這裡有一篇關於 Cocoapods 的教學可以參考,快將它加到你的 Mac 吧。
在你的 Mac 打開終端機,接著輸入以下指令:
cd
提示: 要得到 Xcode 專案的路徑,點選 Xcode 專案所在的資料夾,然後按 CMD+C,移到終端機並輸入 cd 然後貼上。
現在我們就在目錄中。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-9.png)
要創建一個 pod 其實相當簡單,輸入以下指令:
pod init
等待幾秒鐘之後你的終端機看起來應該像這樣,多加了一行簡單的程式碼。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-10.png)
現在,讓我們加入所有需要的套件到 Podfile。輸入指令到終端機,並等待 Xcode 將它開啟:
open -a Xcode podfile
![](https://www.appcoda.com.tw/content/images/2022/05/image-20.png)
在 # Pods for ML Kit Starter Project
下面輸入下列程式碼:
pod 'Firebase/Core'
pod 'Firebase/MLVision'
pod 'Firebase/MLVisionTextModel'
pod 'Firebase/MLVisionFaceModel'
pod 'Firebase/MLVisionBarcodeModel'
pod 'Firebase/MLVision'
pod 'Firebase/MLVisionLabelModel'
你的 Podfile 看起來應該像這樣。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-12.png)
現在,只剩下一件事要做,就是回到終端機並輸入:
pod install
這將會花上幾分鐘的時間。與此同時,Xcode 正在下載我們將用到的套件。完成以後,再次回到專案資料夾,你會發現一個新的檔案:一個 .xcworkspace 檔。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-14.png)
這是很多開發者會搞砸的地方:你不應該再次打開 .xcodeproj 檔!如果你在這裡打開檔案並編輯內容,兩個檔案就無法同步,這樣的話你就必須要創建一個新的專案重頭再來。從現在開始,你應該保持 .xcworkspace 檔案打開。
回到 Firebase 的網頁,我們已經完成了第三步,點擊 Next 按鈕來進入第四步。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-15.png)
現在它會要求我們打開工作區,並加入幾行程式碼到我們的 AppDelegate.swift
檔。打開 .xcworkspace 檔(再次提醒,不是 .xcodeproj 檔,這一點非常重要),然後切換到 AppDelegate.swift
檔。
當我們在 AppDelegate.swift
檢視頁面之中,唯一需要做的就是加入兩行程式碼。
import UIKit
import Firebase
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
return true
}
我們這裡做的是加入 Firebase 套件,並根據之前的 GoogleService-Info.plist
檔來設置它。你可能會得到一個錯誤訊息,顯示它無法建置 Firebase
模組,不過只要按下 CMD+SHFT+K 來清除專案,再按 CMD+B 即可建置它。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-16.png)
如果錯誤依然存在,切換到專案的 Build Settings
頁籤,搜尋 Bitcode
。你會在 Build Options
下面看到 Enable Bitcode
的選項,將它設定為 No 並再次建置,就應該會成功了。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-17.png)
在 Firebase 控制台按 Next
按鈕來到第五步。現在,你所需要做的就是在裝置上執行 App,第五步就會自動完成了!你應該會被重新導向到專案的概覽頁面,你現在可以在那裡看到一些數據項目。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-18.png)
恭喜你,你已經完成了本教學中最具挑戰性的部分!現在就只需要在 Swift 中加入 ML Kit 程式碼。這是完美的休息時間,從這裡開始,我們就只是在熟悉的程式碼中漫遊!
條碼掃描
第一個實作的功能就是條碼掃描,要把它加到 App 中真的非常簡單。切換到 BarcodeViewController
,你會看到當 Choose Image
按鈕按下時出現的程式碼。為了獲得所有 ML Kit 協定,我們需要加入 Firebase。
import UIKit
import Firebase
接著我們需要定義一些在條碼掃描功能中會使用到的變數。
let options = VisionBarcodeDetectorOptions(formats: .all)
lazy var vision = Vision.vision()
options
變數會告訴 BarcodeDetector
要辨識的條碼類型,ML Kit 可以辨識大部分常見的條碼格式,像是 Codabar、Code 39、Code 93、UPC-A、UPC-E、Aztec、PDF417、QR Code 等。針對我們的目的,我們會要求檢測器辨識所有類型的格式。vision
變數回傳 Firebase Vision 服務的實例,我們就是透過這個變數來執行大部分的計算。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-19.png)
接下來,我們需要處理辨識的邏輯,我們將會在 imagePickerController:didFinishPickingMediaWithInfo
函數內實現這部分,這個函數會在我們選取圖像後執行。現在,函數只會將 imageView
設定成我們所選取的圖像。在 imageView.image = pickedImage
底下加入下列程式碼。
// 1
let barcodeDetector = vision.barcodeDetector(options: options)
let visionImage = VisionImage(image: pickedImage)
//2
barcodeDetector.detect(in: visionImage) { (barcodes, error) in
//3
guard error == nil, let barcodes = barcodes, !barcodes.isEmpty else {
self.dismiss(animated: true, completion: nil)
self.resultView.text = "No Barcode Detected"
return
}
//4
for barcode in barcodes {
let rawValue = barcode.rawValue!
let valueType = barcode.valueType
//5
switch valueType {
case .URL:
self.resultView.text = "URL: \(rawValue)"
case .phone:
self.resultView.text = "Phone number: \(rawValue)"
default:
self.resultView.text = rawValue
}
}
}
讓我們簡單看看程式碼所做的事。雖然程式碼看起來很多但其實很簡單,而且這將會是本教學其餘部分的基本格式。
- 我們做的第一件事是定義兩個變數,第一個是
barcodeDetector
,一個 Firebase Vision 服務的條碼掃描物件,我們將其設定為辨識所有條碼格式。接著定義一張稱為visionImage
的圖像,跟我們所選取的圖像一致。 - 呼叫
barcodeDetector
的detect
方法,並對我們的visionImage
執行此方法。我們定義了兩個物件:barcodes
以及error
。 - 首先,我們來處理錯誤。如果發生了錯誤或是無法辨識到條碼,就要關閉 Image Picker View Controller,並設定
resultView
為 “No Barcode Detected”。然後return
這個函數讓剩餘的部分無法被執行。 - 如果有條碼被辨識到,我們就使用一個 for-loop 來對每一個辨識到的條碼執行相同程式碼。我們定義了兩個常數:一個
rawValue
以及一個valueType
。條碼的 Raw Value 包含了其保存的數據,可能是一些文字、數字或圖像等;而條碼的 Value type 就說明了訊息的類型:電子郵件、聯絡人或連結等。 - 現在我們可以簡單印出原始數值,但這並不會提供很好的使用者體驗;相反地,我們將依據 Value type 來提供客制化訊息。我們確認條碼類型後,並將
resultView
的文字設定為相對應的類型。舉例來說,如果是 URL 的話,我們將resultView
的文字設為 “URL: “,然後顯示該 URL。
建置並執行 App,它應該運作得非常快!最酷的是,由於 resultView
是一個 UITextView,你可以跟它互動、並選擇任何被偵測到的數據如數字、連結和電子郵件等。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-21.png)
臉部偵測
接下來,我們來看一下臉部偵測的應用。與以往只在圖像上框出臉部範圍不同,讓我們進一步看看要怎麼辨認人物是否正在微笑、眼睛是否有睜開等。
就像剛剛一樣,我們需要從定義一些常數開始。
import UIKit
import Firebase
.
.
.
let options = VisionFaceDetectorOptions()
lazy var vision = Vision.vision()
與先前的程式碼唯一不同的地方,是我們呼叫了 FaceDetectorOptions 的預設值,然後在類別的 viewDidLoad
之中設置了這些選項。
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
// Do any additional setup after loading the view.
options.modeType = .accurate
options.landmarkType = .all
options.classificationType = .all
options.minFaceSize = CGFloat(0.1)
}
我們在viewDidLoad
之中定義了偵測器的細節。首先,我們選擇了使用的模式,模式有 accurate 與 fast 兩種。因為這只是一個範例 App,我們將選擇 accurate 模式。但是如果在某些速度較重要的情況下,調用 options.modeType = .fast 可能是更明智的選擇。
下一步我們要求偵測器找到所有的標誌及分類的情況。這兩點有什麼不同?標誌是指臉部的特定部位,像是右臉頰、左臉頰、鼻子底部、眉毛和其他部位等;而分類會是偵測某些事件,就目前的情況而言,ML Kit Vision 只能夠偵測左或右眼是否已經睜開,以及人物是否在微笑。針對我們的目的,要處理的就只有微笑和睜開眼與否的分類。
最後一個需要設定的選項是臉部最小尺寸,當我們輸入 options.minFaceSize = CGFloat(0.1)
就代表想偵測臉部的最小尺寸。這尺寸是用臉部寬度和圖片寬度的比例來表示,如果數值是 0.1,就代表我們指示偵測器搜尋最小尺寸不低於圖像寬度 10% 的臉部。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-22.png)
接著,讓我們來處理一下 imagePickerController:didFinishPickingMediaWithInfo
方法中的邏輯,在 imageView.image = pickedImage
一行下輸入下列程式碼:
let faceDetector = vision.faceDetector(options: options)
let visionImage = VisionImage(image: pickedImage)
self.resultView.text = ""
這簡單地以剛剛定義的選項設定 ML Kit Vision 服務為臉部偵測器,我們也定義了 visionImage
為所選擇的圖片。因為這部分的程式可能會重複執行,所以我們會想清除 resultView
,第三行程式碼就是用來清除它的。接下來,呼叫 faceDetector
的偵測函數。
//1
faceDetector.detect(in: visionImage) { (faces, error) in
//2
guard error == nil, let faces = faces, !faces.isEmpty else {
self.dismiss(animated: true, completion: nil)
self.resultView.text = "No Face Detected"
return
}
//3
self.resultView.text = self.resultView.text + "I see \(faces.count) face(s).\n\n"
for face in faces {
//4
if face.hasLeftEyeOpenProbability {
if face.leftEyeOpenProbability < 0.4 {
self.resultView.text = self.resultView.text + "The left eye is not open!\n"
} else {
self.resultView.text = self.resultView.text + "The left eye is open!\n"
}
}
if face.hasRightEyeOpenProbability {
if face.rightEyeOpenProbability < 0.4 {
self.resultView.text = self.resultView.text + "The right eye is not open!\n"
} else {
self.resultView.text = self.resultView.text + "The right eye is open!\n"
}
}
//5
if face.hasSmilingProbability {
if face.smilingProbability < 0.3 {
self.resultView.text = self.resultView.text + "This person is not smiling.\n\n"
} else {
self.resultView.text = self.resultView.text + "This person is smiling.\n\n"
}
}
}
}
這與剛剛的條碼偵測函數應該非常相似,讓我們來看看程式碼所做的事:
- 呼叫
visionImage
的偵測函數來找尋faces
和errors
。 - 若有錯誤情況或未有偵測到臉部,就將
resultView
文字設為 "No Face Detected" 並回傳方法。 - 若成功偵測到臉部,我們所印的
resultView
第一句文字敘述就會顯示偵測到多少張臉。教學內的字串中,你會常常看到許多\n
符號,代表換行的意思。 - 更進一步觀察細節,如果
face
的左眼有可能睜開了,我們就檢查該機率實際為多少。就這個情況而言,如果機率小於 0.4,我就認定左眼是閉合的,右眼也是如此。當然你可以設置為任何想要的值。 - 同樣地,我會確認微笑的機率,如果機率小於 0.3,這個人很可能就沒有在微笑;反之就是有在微笑。
備註:這些數值是依據個人感覺選擇的,因為微笑比睜開眼睛較難被察覺,所以我降低了它對應的機率值,這樣它就更有可能做出正確的推論。
建置並執行你的程式,看看運作的結果。你可以任意調整的機率值來玩玩看會有什麼結果!
![](https://www.appcoda.com.tw/content/images/2022/05/image-21.png)
圖像標籤
接著,我們會開始進行圖像標籤的部分,這比臉部偵測簡單得多。實際上,在圖像標籤和文字辨識上,你可以有兩種選擇,一是在你的設備上完成所有機器學習的任務(這是 Apple 喜歡的方式,因為所有數據都屬於用戶,這樣可以就離線運作,又不會對 Firebase 做任何呼叫);二是你可以使用 Google 的 Cloud Vision。使用 Cloud Vision 的好處是模型會自動更新而且更準確,因為比起在設備上,Cloud Vision 在雲端上很容易就可以有更大更準確的模型規模。不過,針對我們的目的,我們延續剛剛所做的一切,只實現設備上的版本就好。
看看你能不能獨自完成它吧,這跟前兩個的情況非常類似,真的不行的話也沒關係。以下就是我們要做的事!
import UIKit
import Firebase
...
lazy var vision = Vision.vision()
跟剛剛不同的是,我們的標籤偵測器將使用預設設定,因此只需要定義一個常數。與前文一樣,在 imagePickerController:didFinishPickingMediaWithInfo
方法之中,imageView.image = pickedImage
這行底下輸入下列程式碼:
//1
let labelDetector = vision.labelDetector()
let visionImage = VisionImage(image: pickedImage)
self.resultView.text = ""
//2
labelDetector.detect(in: visionImage) { (labels, error) in
//3
guard error == nil, let labels = labels, !labels.isEmpty else {
self.resultView.text = "Could not label this image"
self.dismiss(animated: true, completion: nil)
return
}
//4
for label in labels {
self.resultView.text = self.resultView.text + "\(label.label) - \(label.confidence * 100.0)%\n"
}
}
這看起來應該非常熟悉,讓我簡單解釋一下:
- 我們定義
labelDetector
來告訴 ML Kit 的 Vision 服務,以偵測圖像的標籤。定義visionImage
為我們所選的圖像。若要重複使用此函數,清除resultView
。 - 我們呼叫了
visionImage
的detect
函數,並找尋labels
和errors
。 - 如果 ML Kit 發生錯誤,或是無法標籤圖像,就回傳函數,並告訴使用者我們無法標籤圖像。
- 如果運作正常,那就將
resultView
的文字設定為圖像的標籤,以及 ML Kit 標籤這張圖時有多少信心。
很簡單對吧?建構並執行你的程式碼!看一下標籤有多準確(或瘋狂)?
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-26.png)
文字辨識
我們幾乎完成了!光學字元辨識 (Optical Character Recognition,OCR) 過去兩年成為了行動裝置 App 最熱門的的術語。有了 ML Kit,要將文字辨識功能實現在你的程式之中就更加容易了,讓我們看看怎麼做吧!
此外,文字辨識功能可以連接到 Google Cloud,並可以通過雲端模式呼叫模型,就像圖片標籤功能一樣,但我們現在將使用設備上的 API 來實作。
import UIKit
import Firebase
...
lazy var vision = Vision.vision()
var textDetector: VisionTextDetector?
跟剛剛一樣,我們呼叫 Vision 服務並定義一個 textDetector
,我們可以在 viewDidLoad
方法中設定 textDetector
變數為 vision
的文字偵測器。
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
textDetector = vision.textDetector()
}
接著,我們只需要處理 imagePickerController:didFinishPickingMediaWithInfo
中的部分。如同往常一樣,我們在 imageView.image = pickedImage
這行下面輸入處理相關邏輯的程式碼。
//1
let visionImage = VisionImage(image: pickedImage)
textDetector?.detect(in: visionImage, completion: { (features, error) in
//2
guard error == nil, let features = features, !features.isEmpty else {
self.resultView.text = "Could not recognize any text"
self.dismiss(animated: true, completion: nil)
return
}
//3
self.resultView.text = "Detected Text Has \(features.count) Blocks:\n\n"
for block in features {
//4
self.resultView.text = self.resultView.text + "\(block.text)\n\n"
}
})
- 我們設定
visionImage
為我們選擇的圖像,並為圖像執行detect
函數來找尋features
和errors
。 - 如果發生錯誤或是沒有偵測到文字,就告訴使用者 "Could not recognize any text" 然後回傳函數。
- 我們給使用者的第一個資訊,是總共有幾段文字被偵測到。
- 最後,我們將
resultView
的文字設為每個區塊的文字,並在\n\n
之間留出空格(新的兩行)。
測試一下!嘗試使用不同的字體和顏色,我的結果顯示,它的功能對於印刷文字的辨識完美無瑕,但辨識手寫文字的確很困難。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-28.png)
地標辨識
地標辨識可以像其他 4 個類型一樣如法炮製。不幸的是,ML Kit 目前不支援在設備上進行地標辨識。 若要執行地標辨識,你需要將專案項目更改為 "Blaze",並激活 Google Cloud Vision API。
![](https://www.appcoda.com/wp-content/uploads/2018/06/mlkit-29.png)
不過,這已經超出了本教學涵蓋的範圍,如果你想要自我挑戰的話,可以參考這份文件來實作,程式碼也包含在最後的專案內!
結論
這篇教學內容真的很豐富,請隨時回來重新查閱不熟悉的部分。如果有任何文章未有解答的問題,歡迎在下面發表評論!藉由 ML Kit,你可以看到在 App 中實現智慧機器學習功能是多麼容易,可以創造的 App 範圍很廣,以下是一些你可以嘗試的想法:
- 辨識文字並向視障使用者朗讀內容的 App。
- 類似 Try Not to Smile by Roland Horvath 的人臉追蹤 App
- 條碼偵測器
- 以標籤來搜尋圖片
這些想法是無盡的,取決於你的想像力,以及想如何幫助使用者。你可以在這裡下載最終的專案。如果你想了解更多有關 ML Kit API 的資料,可以查看他們的文件。希望你在本教學中學到新的東西,並留言和我分享你的進度!
你可以到 GitHub 下載完整專案。