版本記錄
版本號 | 時(shí)間 |
---|---|
V1.0 | 2019.02.01 星期五 |
前言
Firebase是一家實(shí)時(shí)后端數(shù)據(jù)庫創(chuàng)業(yè)公司顾瞻,它能幫助開發(fā)者很快的寫出Web端和移動(dòng)端的應(yīng)用膊存。自2014年10月Google收購Firebase以來判哥,用戶可以在更方便地使用Firebase的同時(shí)遥倦,結(jié)合Google的云服務(wù)谤绳。Firebase能讓你的App從零到一。也就是說它可以幫助手機(jī)以及網(wǎng)頁應(yīng)用的開發(fā)者輕松構(gòu)建App袒哥。通過Firebase背后負(fù)載的框架就可以簡單地開發(fā)一個(gè)App缩筛,無需服務(wù)器以及基礎(chǔ)設(shè)施。接下來幾篇我們就一起看一下基于Firebase平臺的開發(fā)堡称。
Firebase提供的服務(wù)
首先我們看一下Firebase
目前提供的服務(wù):
這里看一下ML Kit
關(guān)于機(jī)器學(xué)習(xí)的部分還是BETA
瞎抛。
開始
首先看下寫作環(huán)境
Swift 4.2, iOS 12, Xcode 10
在這個(gè)ML Kit
教程中,您將學(xué)習(xí)如何利用Google
的ML Kit
來檢測和識別文本却紧。
幾年前桐臊,有兩種類型的機(jī)器學(xué)習(xí)(ML)開發(fā)人員:高級開發(fā)人員和其他人。底層的ML水平可能很難晓殊,這是很多數(shù)學(xué)断凶,它使用邏輯回歸(logistic regression)
,稀疏性(sparsity)
和神經(jīng)網(wǎng)絡(luò)(neural nets)
等詞語巫俺。但它并不一定要那么難认烁。
您也可以成為ML開發(fā)人員! ML的核心很簡單介汹。有了它砚著,您可以通過訓(xùn)練軟件模型來識別模式而不是硬編碼每種情況和您能想到各種case來解決問題。但是痴昧,開始這可能是令人生畏的稽穆,這是您可以依賴現(xiàn)有工具的地方。
1. Machine Learning and Tooling
就像iOS開發(fā)一樣赶撰,ML
就是工具舌镶。你不會(huì)建立自己的UITableView
柱彻,或者至少你不應(yīng)該,你會(huì)使用一個(gè)框架餐胀,比如UIKit
哟楷。
它與ML的方式相同。 ML擁有蓬勃發(fā)展的工具生態(tài)系統(tǒng)否灾。例如卖擅,Tensorflow
簡化了訓(xùn)練和運(yùn)行模型。 TensorFlow Lite
為iOS
和Android
設(shè)備提供模型支持墨技。
這些工具中的每一個(gè)都需要一些ML的經(jīng)驗(yàn)惩阶。如果您不是ML專家但想要解決特定問題怎么辦?對于這些情況扣汪,有ML Kit
断楷。
ML Kit
ML Kit是一款移動(dòng)SDK,可將Google
的ML
專業(yè)知識帶入您的應(yīng)用崭别。 ML Kit
的API有兩個(gè)主要部分冬筒,用于常見用例和自定義模型(common use cases and custom models)
,無論經(jīng)驗(yàn)如何都易于使用茅主。
當(dāng)前的API支持:
這些用例中的每一個(gè)都帶有一個(gè)預(yù)先訓(xùn)練的模型舞痰,該模型包含在一個(gè)易于使用的API中。 是時(shí)候開始建設(shè)了诀姚!
在本教程中响牛,您將構(gòu)建一個(gè)名為Extractor
的應(yīng)用程序。 你有沒有拍下一張標(biāo)志或海報(bào)的圖片來寫下文字內(nèi)容学搜? 如果一個(gè)應(yīng)用程序可以將文本從標(biāo)志上剝離并保存給您娃善,隨時(shí)可以使用,那就太棒了瑞佩。 例如聚磺,您可以拍攝尋址信封的照片并保存地址。 這正是你要對這個(gè)項(xiàng)目做的炬丸! 做好準(zhǔn)備瘫寝!
首先,打開本教程的項(xiàng)目材料稠炬,該項(xiàng)目使用CocoaPods來管理依賴項(xiàng)焕阿。
Setting Up ML Kit
每個(gè)ML Kit API
都有一組不同的CocoaPods
依賴項(xiàng)。 這很有用首启,因?yàn)槟恍枰墤?yīng)用程序所需的依賴項(xiàng)暮屡。 例如,如果您沒有識別地標(biāo)毅桃,則在您的應(yīng)用中不需要該模型褒纲。 在Extractor
中准夷,您將使用Text Recognition API
。
如果您要將Text Recognition API
添加到您的應(yīng)用程序莺掠,那么您需要將以下行添加到您的Podfile
中衫嵌,但您不必為啟動(dòng)項(xiàng)目執(zhí)行此操作,因?yàn)?code>Podfile中有已經(jīng)寫好 - 您可以檢查彻秆。
pod 'Firebase/Core' => '5.5.0'
pod 'Firebase/MLVision' => '5.5.0'
pod 'Firebase/MLVisionTextModel' => '5.5.0'
您必須打開終端應(yīng)用程序楔绞,切換到項(xiàng)目文件夾并運(yùn)行以下命令來安裝項(xiàng)目中使用的CocoaPods
:
pod install
安裝CocoaPods
后,在Xcode中打開Extractor.xcworkspace
唇兑。
注意:您可能會(huì)注意到項(xiàng)目文件夾包含名為
Extractor.xcodeproj
的項(xiàng)目文件和名為Extractor.xcworkspace
的工作區(qū)文件酒朵,該文件是您在Xcode中打開的文件。 不要打開項(xiàng)目文件幔亥,因?yàn)樗话幾g應(yīng)用程序所需的其他CocoaPods項(xiàng)目耻讽。
該項(xiàng)目包含以下重要文件:
-
ViewController.swift
:此項(xiàng)目中唯一的控制器察纯。 -
+ UIImage.swift
:用于修復(fù)圖像方向的UIImage extension
帕棉。
Setting Up a Firebase Account
首先要?jiǎng)?chuàng)建一個(gè)賬戶,這個(gè)會(huì)在后面單獨(dú)分出來一篇進(jìn)行詳細(xì)介紹饼记。
一般的想法是:
- 1) 創(chuàng)建一個(gè)帳戶香伴。
- 2) 創(chuàng)建一個(gè)項(xiàng)目。
- 3) 將iOS應(yīng)用添加到項(xiàng)目中具则。
- 4) 將
GoogleService-Info.plist
拖到您的項(xiàng)目中即纲。 - 5) 在
AppDelegate
中初始化Firebase
。
這是一個(gè)簡單的過程博肋,但如果您遇到任何障礙低斋,后面我會(huì)單獨(dú)進(jìn)行講解說明。
注意:您需要設(shè)置
Firebase
并為最終和初始項(xiàng)目創(chuàng)建自己的GoogleService-Info.plist
匪凡。
構(gòu)建并運(yùn)行應(yīng)用程序膊畴,您將看到它看起來像這樣:
除了允許您通過右上角的操作按鈕共享硬編碼文本之外,它不會(huì)執(zhí)行任何操作病游。 您將使用ML Kit
將此應(yīng)用程序變?yōu)楝F(xiàn)實(shí)唇跨。
Detecting Basic Text
準(zhǔn)備好第一次文本檢測! 您可以首先向用戶演示如何使用該應(yīng)用程序衬衬。
一個(gè)很好的演示是在應(yīng)用程序首次啟動(dòng)時(shí)掃描示例圖像买猖。 在資源文件夾中包含了一個(gè)名為scanning-text
的圖像,該圖像當(dāng)前是視圖控制器的UIImageView
中顯示的默認(rèn)圖像滋尉。 您將使用它作為示例圖像玉控。
但首先,您需要一個(gè)文本檢測器來檢測圖像中的文本狮惜。
1. Creating a Text Detector
創(chuàng)建名為ScaledElementProcessor.swift
的文件并添加以下代碼:
import Firebase
class ScaledElementProcessor {
}
很好高诺! 你們都完成了既棺! 開個(gè)玩笑。 在類中創(chuàng)建text-detector
屬性:
let vision = Vision.vision()
var textRecognizer: VisionTextRecognizer!
init() {
textRecognizer = vision.onDeviceTextRecognizer()
}
此textRecognizer
是可用于檢測圖像中文本的主要對象懒叛。 您將使用它來識別UIImageView
當(dāng)前顯示的圖像中包含的文本丸冕。 將以下檢測方法添加到類中:
func process(in imageView: UIImageView,
callback: @escaping (_ text: String) -> Void) {
// 1
guard let image = imageView.image else { return }
// 2
let visionImage = VisionImage(image: image)
// 3
textRecognizer.process(visionImage) { result, error in
// 4
guard
error == nil,
let result = result,
!result.text.isEmpty
else {
callback("")
return
}
// 5
callback(result.text)
}
}
花一點(diǎn)時(shí)間來理解這段代碼:
- 1) 在這里,您檢查
imageView
是否實(shí)際包含圖像薛窥。 如果沒有胖烛,只需返回。 但是诅迷,理想情況下佩番,您可以拋出或提供優(yōu)雅的失敗提示。 - 2)
ML Kit
使用特殊的VisionImage
類型罢杉。 它很有用趟畏,因?yàn)樗梢园?code>ML Kit處理圖像的特定元數(shù)據(jù),例如圖像的方向滩租。 - 3)
textRecognizer
有一個(gè)接收VisionImage
的process
方法赋秀,它以傳遞給閉包的參數(shù)的形式返回一個(gè)文本結(jié)果數(shù)組。 - 4) 結(jié)果可能是
nil
律想,在這種情況下猎莲,您將要為回調(diào)返回一個(gè)空字符串。 - 5) 最后技即,觸發(fā)回調(diào)以中繼識別的文本著洼。
2. Using the Text Detector
打開ViewController.swift
,在類主體頂部的outlets
之后而叼,添加一個(gè)ScaledElementProcessor
實(shí)例作為屬性:
let processor = ScaledElementProcessor()
然后身笤,在viewDidLoad()
的底部添加以下代碼,以在UITextView
中顯示檢測到的文本:
processor.process(in: imageView) { text in
self.scannedText = text
}
這個(gè)小block調(diào)用process(in:)
葵陵,傳遞主imageView
并將識別的文本分配給回調(diào)中的scansText
屬性液荸。
運(yùn)行該應(yīng)用程序,您應(yīng)該在圖像正下方看到以下文本:
Your
SCanned
text
will
appear
here
您可能需要滾動(dòng)文本視圖以顯示最后幾行埃难。
注意掃描的“S”
和“C”
是大寫的莹弊。 有時(shí),使用特定字體時(shí)涡尘,可能會(huì)出現(xiàn)錯(cuò)誤的情況忍弛。 這就是為什么文本顯示在UITextView
中的原因,因此用戶可以手動(dòng)編輯以修復(fù)檢測錯(cuò)誤考抄。
3. Understanding the Classes
注意:您不必復(fù)制本節(jié)中的代碼细疚,它只是有助于解釋概念。您將在下一部分中向應(yīng)用添加代碼川梅。
VisionText
您是否注意到ScaledElementProcessor
中的textRecognizer.process(in :)
的回調(diào)在result
參數(shù)中返回了一個(gè)對象而不是普通的文本疯兼?這是VisionText的一個(gè)實(shí)例然遏,它包含許多有用的信息,例如識別的文本吧彪。但是你想做的不僅僅是獲取文本待侵。描繪出每個(gè)識別的文本元素的每個(gè)frame
不是很酷嗎?
ML Kit
以類似于樹的結(jié)構(gòu)提供結(jié)果姨裸。您需要遍歷葉元素以獲取包含已識別文本的frame
的位置和大小秧倾。如果對樹結(jié)構(gòu)的引用讓您感覺到困難,請不要太擔(dān)心傀缩。以下部分應(yīng)闡明正在發(fā)生的事情那先。
VisionTextBlock
使用已識別的文本時(shí),您可以從VisionText
對象開始 - 這是一個(gè)對象(稱為樹)赡艰,它可以包含多個(gè)文本塊(如樹中的分支)售淡。您遍歷每個(gè)分支,這是塊(blocks)
數(shù)組中的VisionTextBlock
對象慷垮,如下所示:
for block in result.blocks {
}
VisionTextElement
VisionTextBlock
只是一個(gè)對象揖闸,包含一系列文本(如樹枝上的葉子),每個(gè)文本都由一個(gè)VisionTextElement
實(shí)例表示换帜。 這個(gè)對象的嵌套允許您查看已識別文本的層次結(jié)構(gòu)楔壤。
循環(huán)遍歷每個(gè)對象如下所示:
for block in result.blocks {
for line in block.lines {
for element in line.elements {
}
}
}
此層次結(jié)構(gòu)中的所有對象都包含文本所在的frame鹤啡。 但是惯驼,每個(gè)對象包含不同級別的粒度(granularity)
。 塊可以包含多個(gè)行递瑰,一行可以包含多個(gè)元素祟牲,并且元素可以包含多個(gè)符號。
在本教程中抖部,您將使用元素(elements)
作為粒度級別。 元素通常對應(yīng)于單詞。 這將允許您繪制每個(gè)單詞并向用戶顯示每個(gè)單詞在圖像中的位置意狠。
最后一個(gè)循環(huán)遍歷文本塊的每一行中的元素嘶卧。 這些元素包含frame
,一個(gè)簡單的CGRect
俯萎。 使用此frame
傲宜,您可以在圖像上的單詞周圍繪制邊框。
Highlighting the Text Frames
1. Detecting Frames
要在圖像上繪制夫啊,您需要使用文本元素的frame創(chuàng)建CAShapeLayer
函卒。 打開ScaledElementProcessor.swift
并將以下struct
添加到文件的頂部:
struct ScaledElement {
let frame: CGRect
let shapeLayer: CALayer
}
這個(gè)struct
是便利實(shí)現(xiàn)。 它可以更輕松地將frame和CAShapeLayer
分組到控制器撇眯。 現(xiàn)在报嵌,您需要一個(gè)輔助方法來從元素的frame創(chuàng)建CAShapeLayer
虱咧。
將以下代碼添加到ScaledElementProcessor
的末尾:
private func createShapeLayer(frame: CGRect) -> CAShapeLayer {
// 1
let bpath = UIBezierPath(rect: frame)
let shapeLayer = CAShapeLayer()
shapeLayer.path = bpath.cgPath
// 2
shapeLayer.strokeColor = Constants.lineColor
shapeLayer.fillColor = Constants.fillColor
shapeLayer.lineWidth = Constants.lineWidth
return shapeLayer
}
// MARK: - private
// 3
private enum Constants {
static let lineWidth: CGFloat = 3.0
static let lineColor = UIColor.yellow.cgColor
static let fillColor = UIColor.clear.cgColor
}
這是代碼的作用:
- 1)
CAShapeLayer
沒有接收CGRect
的初始化程序。 因此锚国,您使用CGRect
構(gòu)造UIBezierPath
并將形狀圖層的path
設(shè)置為UIBezierPath
腕巡。 - 2) 顏色和寬度的可視屬性通過常量枚舉設(shè)置。
- 3) 這個(gè)枚舉有助于保持著色和寬度一致血筑。
現(xiàn)在逸雹,用以下代碼替換process(in:callback :)
:
// 1
func process(
in imageView: UIImageView,
callback: @escaping (_ text: String, _ scaledElements: [ScaledElement]) -> Void
) {
guard let image = imageView.image else { return }
let visionImage = VisionImage(image: image)
textRecognizer.process(visionImage) { result, error in
guard
error == nil,
let result = result,
!result.text.isEmpty
else {
callback("", [])
return
}
// 2
var scaledElements: [ScaledElement] = []
// 3
for block in result.blocks {
for line in block.lines {
for element in line.elements {
// 4
let shapeLayer = self.createShapeLayer(frame: element.frame)
let scaledElement =
ScaledElement(frame: element.frame, shapeLayer: shapeLayer)
// 5
scaledElements.append(scaledElement)
}
}
}
callback(result.text, scaledElements)
}
}
下面進(jìn)行詳細(xì)說明:
- 1) 除了識別的文本之外,回調(diào)現(xiàn)在還會(huì)獲取一系列
ScaledElement
實(shí)例云挟。 - 2)
scaledElements
用作frame
和shape layer
的集合梆砸。 - 3) 正如上面所概述的,代碼使用
for
循環(huán)來獲取每個(gè)元素的frame园欣。 - 4) 最里面的for循環(huán)從元素的
frame
創(chuàng)建shape layer
帖世,然后用于構(gòu)造新的ScaledElement
實(shí)例。 - 5) 將新創(chuàng)建的實(shí)例添加到
scaledElements
沸枯。
2. Drawing
上面的代碼是把你的鉛筆放在一起日矫。 現(xiàn)在,是時(shí)候畫了绑榴! 打開ViewController.swift
哪轿,在viewDidLoad()
中,用以下代碼替換對process(in :)
的調(diào)用:
processor.process(in: imageView) { text, elements in
self.scannedText = text
elements.forEach() { feature in
self.frameSublayer.addSublayer(feature.shapeLayer)
}
}
ViewController
有一個(gè)frameSublayer
屬性翔怎,附加到imageView
窃诉。 在這里,您將每個(gè)元素的shape layer
添加到子圖層赤套,以便iOS將自動(dòng)在圖像上繪制shape
飘痛。
構(gòu)建并運(yùn)行。 看看你的藝術(shù)作品容握!
哦宣脉。 那是什么? 看起來你更像畢加索而不是莫奈剔氏。 這里發(fā)生了什么塑猖? 好吧,現(xiàn)在可能是談?wù)?code>scale的時(shí)候了谈跛。
Understanding Image Scaling
默認(rèn)的scanning-text.png
圖像為654×999 (width x height)
羊苟;但是,UIImageView
具有“Aspect Fit”
的“Content Mode”
币旧,可以在視圖中將圖像縮放到375×369
践险。 ML Kit
接收圖像的實(shí)際大小,并根據(jù)該大小返回元素frame。 然后根據(jù)縮放的大小繪制來自實(shí)際大小的frame巍虫,這會(huì)產(chǎn)生令人困惑的結(jié)果彭则。
在上圖中,請注意縮放大小和實(shí)際大小之間的差異占遥。 您可以看到frame與實(shí)際大小匹配俯抖。 要獲取正確的frame,您需要計(jì)算圖像與視圖的比例瓦胎。
公式相當(dāng)簡單(??):
- 1) 計(jì)算視圖和圖像的分辨率芬萍。
- 2) 通過比較分辨率確定比例。
- 3) 通過將它們乘以scale來計(jì)算高度搔啊,寬度和原點(diǎn)x和y柬祠。
- 4) 使用這些數(shù)據(jù)點(diǎn)創(chuàng)建新的
CGRect
。
如果這聽起來令人困惑负芋,那就沒關(guān)系漫蛔! 當(dāng)你看到代碼時(shí),你會(huì)明白的旧蛾。
Calculating the Scale
打開ScaledElementProcessor.swift
并添加以下方法:
// 1
private func createScaledFrame(
featureFrame: CGRect,
imageSize: CGSize, viewFrame: CGRect)
-> CGRect {
let viewSize = viewFrame.size
// 2
let resolutionView = viewSize.width / viewSize.height
let resolutionImage = imageSize.width / imageSize.height
// 3
var scale: CGFloat
if resolutionView > resolutionImage {
scale = viewSize.height / imageSize.height
} else {
scale = viewSize.width / imageSize.width
}
// 4
let featureWidthScaled = featureFrame.size.width * scale
let featureHeightScaled = featureFrame.size.height * scale
// 5
let imageWidthScaled = imageSize.width * scale
let imageHeightScaled = imageSize.height * scale
let imagePointXScaled = (viewSize.width - imageWidthScaled) / 2
let imagePointYScaled = (viewSize.height - imageHeightScaled) / 2
// 6
let featurePointXScaled = imagePointXScaled + featureFrame.origin.x * scale
let featurePointYScaled = imagePointYScaled + featureFrame.origin.y * scale
// 7
return CGRect(x: featurePointXScaled,
y: featurePointYScaled,
width: featureWidthScaled,
height: featureHeightScaled)
}
這是代碼中發(fā)生的事情:
- 1) 此方法接受
CGRects
的原始圖像大小莽龟,顯示的圖像大小和UIImageView
的frame。 - 2) 圖像和視圖的分辨率分別通過它們的高度和寬度之比來計(jì)算锨天。
- 3) 比例由哪個(gè)分辨率更大來確定毯盈。如果視圖較大,則按高度縮放病袄;否則搂赋,你按寬度縮放。
- 4) 此方法計(jì)算寬度和高度陪拘。frame的寬度和高度乘以比例以計(jì)算縮放的寬度和高度厂镇。
- 5) frame的原點(diǎn)也必須縮放,否則左刽,即使尺寸正確,它也會(huì)偏離錯(cuò)誤的位置酌媒。
- 6) 通過將x和y點(diǎn)scale添加到未縮放的原點(diǎn)乘以
scale
來計(jì)算新原點(diǎn)欠痴。 - 7) 返回縮放的
CGRect
,使用計(jì)算的原點(diǎn)和大小進(jìn)行配置秒咨。
既然你有一個(gè)縮放的CGRect
喇辽,你可以從涂鴉到sgraffito
。
轉(zhuǎn)到ScaledElementProcessor.swift
中的process(in:callback :)
并修改最里面的for
循環(huán)以使用以下代碼:
for element in line.elements {
let frame = self.createScaledFrame(
featureFrame: element.frame,
imageSize: image.size,
viewFrame: imageView.frame)
let shapeLayer = self.createShapeLayer(frame: frame)
let scaledElement = ScaledElement(frame: frame, shapeLayer: shapeLayer)
scaledElements.append(scaledElement)
}
新添加的行創(chuàng)建一個(gè)縮放frame雨席,代碼用于創(chuàng)建正確的位置shape layer
菩咨。
建立并運(yùn)行。 您應(yīng)該看到在正確的位置繪制的frame。 你是一位大師級畫家抽米!
足夠的默認(rèn)照片特占;是時(shí)候使用其他資源進(jìn)行測試了!
Taking Photos with the Camera
該項(xiàng)目已經(jīng)在ViewController.swift
底部的擴(kuò)展中設(shè)置了相機(jī)和庫選取器代碼云茸。 如果您現(xiàn)在嘗試使用它是目,您會(huì)注意到?jīng)]有任何frame匹配。 那是因?yàn)樗栽谑褂妙A(yù)裝圖像中的舊frame标捺! 拍攝或選擇照片時(shí)懊纳,您需要?jiǎng)h除它們并繪制新的。
將以下方法添加到ViewController
:
private func removeFrames() {
guard let sublayers = frameSublayer.sublayers else { return }
for sublayer in sublayers {
sublayer.removeFromSuperlayer()
}
}
此方法使用for
循環(huán)從frame sublayer
中刪除所有子層亡容。 這為您提供了下一張照片的干凈畫布嗤疯。
要合并檢測代碼,請將以下新方法添加到ViewController
:
// 1
private func drawFeatures(
in imageView: UIImageView,
completion: (() -> Void)? = nil
) {
// 2
removeFrames()
processor.process(in: imageView) { text, elements in
elements.forEach() { element in
self.frameSublayer.addSublayer(element.shapeLayer)
}
self.scannedText = text
// 3
completion?()
}
}
這是改變的地方:
- 1) 此方法接受
UIImageView
和回調(diào)闺兢,以便您知道它何時(shí)完成身弊。 - 2) 在處理新圖像之前會(huì)自動(dòng)刪除frame。
- 3) 一切都完成后觸發(fā)完成回調(diào)列敲。
現(xiàn)在阱佛,用以下代碼替換viewDidLoad()
中對processor.process(in:callback :)
的調(diào)用:
drawFeatures(in: imageView)
向下滾動(dòng)到類擴(kuò)展并找到imagePickerController(_:didFinishPickingMediaWithInfo :)
;在imageView.image = pickedImage
之后戴而,將這行代碼添加到if
塊的末尾:
drawFeatures(in: imageView)
拍攝或選擇新照片時(shí)凑术,此代碼可確保刪除舊frame并替換為新照片中的frame。
構(gòu)建并運(yùn)行所意。 如果您使用的是真實(shí)設(shè)備(不是模擬器)淮逊,請拍下印刷文字。 你可能會(huì)看到奇怪的東西:
這里發(fā)生了什么扶踊?
您將在一秒鐘內(nèi)解決圖像方向泄鹏,因?yàn)樯厦媸欠较騿栴}。
Dealing With Image Orientations
此應(yīng)用程序以縱向方向鎖定秧耗。 在設(shè)備旋轉(zhuǎn)時(shí)重繪frame是很棘手的备籽,因此現(xiàn)在更容易限制用戶。
此限制要求用戶拍攝豎屏照片分井。 UICameraPicker
將豎屏照片在幕后旋轉(zhuǎn)90度车猬。 您沒有看到旋轉(zhuǎn),因?yàn)?code>UIImageView會(huì)為您旋轉(zhuǎn)它尺锚。 但是珠闰,detector
得到的是旋轉(zhuǎn)的UIImage
。
這導(dǎo)致一些令人困惑的結(jié)果瘫辩。 ML Kit
允許您在VisionMetadata
對象中指定照片的方向伏嗜。 設(shè)置正確的方向?qū)⒎祷卣_的文本坛悉,但將為旋轉(zhuǎn)的照片繪制frame。
因此承绸,您需要將照片方向固定為始終處于“向上”位置裸影。 該項(xiàng)目包含一個(gè)名為+ UIImage.swift
的擴(kuò)展。 此擴(kuò)展為UIImage
添加了一種方法八酒,可將任何照片的方向更改為向上位置空民。 一旦照片處于正確的方向,一切都將順利進(jìn)行羞迷!
打開ViewController.swift
界轩,在imagePickerController(_:didFinishPickingMediaWithInfo :)
中,用以下內(nèi)容替換imageView.image = pickedImage
:
// 1
let fixedImage = pickedImage.fixOrientation()
// 2
imageView.image = fixedImage
下面詳細(xì)說明:
- 1) 新選擇的圖像
pickedImage
將旋轉(zhuǎn)回向上位置衔瓮。 - 2) 然后浊猾,將旋轉(zhuǎn)的圖像分配給
imageView
。
建立并運(yùn)行热鞍。 再拍那張照片葫慎。 你應(yīng)該在正確的地方看到一切。
Sharing the Text
最后一步不需要您采取任何措施薇宠,該應(yīng)用程序與UIActivityViewController
集成偷办。 看看shareDidTouch()
:
@IBAction func shareDidTouch(_ sender: UIBarButtonItem) {
let vc = UIActivityViewController(
activityItems: [textView.text, imageView.image!],
applicationActivities: [])
present(vc, animated: true, completion: nil)
}
這是一個(gè)簡單的兩步過程。 創(chuàng)建一個(gè)包含掃描文本和圖像的UIActivityViewController
澄港。 然后調(diào)用present()
并讓用戶完成其余的工作椒涯。
在本教程中,您學(xué)習(xí)了:
- 通過構(gòu)建文本檢測照片應(yīng)用程序了解
ML Kit
的基礎(chǔ)知識回梧。 -
ML Kit
文本識別API废岂,圖像比例和方向。
要了解有關(guān)Firebase
和ML Kit
的更多信息狱意,請查看official documentation湖苞。
后記
本篇主要講述了基于ML Kit的iOS圖片中文字的識別,感興趣的給個(gè)贊或者關(guān)注~~~