构建您的首个 Android 或 iOS 计算机视觉应用

1. 准备工作

在此 Codelab 中,您将探索如何构建用于处理核心计算机视觉用例的应用,以检测图像的主要内容。这通常称为图像分类或图像标注。

前提条件

此 Codelab 是图像分类入门在线课程的一部分。该指南是专为机器学习领域经验丰富的开发者撰写的。

构建内容

  • 能够对花卉图片进行分类的 Android 应用
  • (可选)能够对花卉图片进行分类的 iOS 应用

所需条件

  • Android Studio(请访问 https://developer.android.com/studio,适用于学习本 Codelab 的 Android 部分)
  • Codelab 中适用于 iOS 部分的 Xcode(可在 Apple App Store 中获取)

2. 开始

计算机视觉是机器学习的一个更广泛领域,该领域旨在寻找新方法,让机器能够处理并从图片内容中提取信息。在计算机仅存储图片的实际数据(如构成图片的像素值)之前,计算机视觉允许计算机解析图片内容,并获取图片所含信息。

例如,在计算机视觉领域,除构成图片的像素外,还可以将猫的图片标记为包含猫。此外,还有其他更细化的计算机视觉领域,例如对象检测,在此领域中,计算机可以定位图片中的多个项并为其生成边界框。

在此 Codelab 中,您将探索如何构建用于处理核心用例的应用,以检测图片的主要内容。这通常称为图像分类或图像标注

为使应用尽可能简单,它将使用与资源捆绑在一起的图片作为资源,并向您显示这些资源的分类。将来的扩展程序可以使用图片选择器或直接从相机中提取图片。

首先,您将使用 Android Studio 逐步完成在 Android 上构建应用。(在 iOS 上,请跳到第 7 步执行等效操作。)

  1. 打开 Android Studio,转到“File”菜单,然后选择“Create New Project”。
  2. 系统会要求您选择项目模板。选择“Empty Activity”。

859b1875e37c321a.png

  1. 点击 Next。系统会要求您配置您的项目。为其指定您想用的任意名称和软件包名称,而此 Codelab 中的示例代码使用的是项目名称 ImageClassifierStep1 和软件包名称 com.google.imageclassifierstep1。

ee3b6a81bad87b3.png

  1. 选择您的首选语言(Kotlin 或 Java)。本 Codelab 使用的是 Kotlin,因此,如果您想要严格按照本文中的说明操作,可能需要选择 Kotlin。
  2. 准备就绪后,点击“Finish”。Android Studio 会为您创建应用。完成所有设置可能需要一些时间。

3.导入机器学习套件的 Image Labelling 库

机器学习套件 (https://developers.google.cn/ml-kit) 为开发者提供了多种解决方案,满足机器学习方面的常见场景,并使其易于实施和跨平台工作。机器学习套件提供一个一体化的库,供您在此应用中使用,名为 Image Labeling。该库包含一个预先训练的模型,可识别 600 多种图片。因此,该工具非常适合入门学习。

请注意,机器学习套件还允许您使用相同的 API 使用自定义模型。因此,准备就绪后,您可以打破“使用入门”的指示,开始构建个性化的图片标注应用,该应用会利用根据您的场景训练的模型。

在此场景中,您将构建一个花卉识别器。当您创建首个应用并向其显示一张花卉图片时,它会将其识别为鲜花。(稍后,当您构建自己的花卉检测器模型时,凭借机器学习套件,您只需稍作更改即可将其放入您的应用中,并让新模型告诉您它属于哪种花卉,例如郁金香或玫瑰)。

  1. 在 Android Studio 中,使用项目资源管理器,确保在顶部选择了 Android
  2. 打开 Gradle Scripts 文件夹,并为应用选择 build.gradle 文件。可能会存在 2 个或更多个,因此请确保您使用的是如下所示的 app 级别 1:

93c2e157136671aa.png

  1. 在文件底部,您会看到一个名为 dependencies 的部分,用于存储 implementationtestImplementationandroidImplementation 设置的列表。使用以下代码,在此文件中添加一个新设置:
implementation 'com.google.mlkit:image-labeling:17.0.3'

(请确保此设置位于依赖项 { } 中)

  1. 您会在窗口顶部看到指示 build.gradle 已更改的栏。您需要重新同步。执行上述操作。如果您没有看到该指示栏,请在右上角的工具栏中找到小象图标,然后点击该图标。

5ef40c7a719077a0.png

您现在已导入机器学习套件,可以开始给图片加标签了。

接下来,您将创建一个简单的界面来渲染图片,并提供一个按钮,当用户按下它时,机器学习套件会调用图片标签器模型来解析图片的内容。

4.构建界面

在 Android Studio 中,您可以使用基于 xml 的布局文件为每个屏幕(或 Activity)修改界面。您创建的基本应用只有一个 activity(其代码位于 MainActivity 中,您很快就会看到),界面声明位于 activity_main.xml 中。

您可以在 Android 项目资源管理器 res > layout 文件夹中找到此目录,如下所示:

3ed772e9563061e9.png

这将打开一个完整的编辑器,可让您设计您的 Activity 界面。其中的内容非常多,本 Codelab 的意图并不是为了让您了解如何使用它。如需详细了解布局编辑器,请访问:https://developer.android.com/studio/write/layout-editor

在此 Codelab 中,请选择编辑器右上角的代码工具。

1f7dbdef48d9ade6.png

现在,您在窗口的主要部分只会看到 XML 代码。将代码更改为以下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/imageToLabel"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <Button
            android:id="@+id/btnTest"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Label Image"
            android:layout_gravity="center"/>
        <TextView
            android:id="@+id/txtOutput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:gravity="start|top" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

这将为您提供一个非常简单的布局,其中包含 ImageView(用于渲染图片)、Button(供用户按下)以及 TextView(用于显示标签)。

现在,您已经定义了界面。在开始编码之前,请添加一些图片作为资源,应用将对这些图片进行推断。

5. 将图片与应用捆绑在一起

将额外的文件与 Android 应用捆绑在一起的一种方法是,将其添加为要编译到应用中的资源。为使该应用简单易用,我们需要添加一些花卉图片。稍后,您可以扩展此应用,以使用 CameraX 或其他库拍照并使用该照片。但为简单起见,我们暂时只将图片捆绑在一起。

  1. 在项目资源管理器顶部的 app 中,右键点击并选择“New Directory”。
  2. 在随即显示的对话框中,列出了不同的目录,选择 src/main/assets

c93650ea68bb60e9.png

完成此操作后,您会在项目资源管理器中看到一个新的 assets 文件夹:

444b4afab73433b8.png

  1. 右键点击此文件夹,您将看到一个带有选项列表的弹出式窗口。其中一项会在文件系统中打开文件夹。找到适用于您的操作系统的选项,然后选择它。(在 Mac 上,此项操作为 Reveal in Finder,在 Windows 上为 Open in Explorer,而在 Ubuntu 上为 Show in Files。)

95e0eca881d35f6b.png

  1. 将一个文件复制到其中。您可以从 Pixabay 等网站下载图片。建议将图片重命名为简单的名称。在此示例中,图片已重命名为 flower1.jpg

完成上述操作后,请返回 Android Studio,您应该会在 assets 文件夹中看到相应文件。

cfa53c9c75a033d8.png

您现在可以为此图片加标签了!

6.编写分类代码以给图片加标签

(现在到了我们一直期待的部分 — 在 Android 上实现计算机视觉技术!)

  1. 您将在 MainActivity 文件中编写代码,因此请在 com.google.devrel.imageclassifierstep1(或您选择的其他命名空间)下的项目文件夹中找到该文件。请注意,Android Studio 项目中通常会设置 3 个命名空间文件夹,一个用于应用,一个用于 Android Test,一个用于测试。您可在后面没有用括号备注说明的文件夹中找到 MainActivity

b5aef8dd5e26b6c2.png

如果您选择使用 Kotlin,您可能想知道为什么父文件夹名为 Java。这是一个 Android Studio 只支持 Java 时就开始有的历史工件。将来的版本可能会解决这个问题,但如果您想要使用 Kotlin,也不必担心。这只是源代码的文件夹名称。

  1. 打开 MainActivity 文件,您将在代码编辑器中看到一个名为 MainActivity 的类文件。它应如下所示:
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

在右大括号下方,您可以添加不属于该类但可由此类使用的扩展程序代码。您需要使用扩展程序来从 assets 中读取位图文件。这会用来加载您之前复制到 assets 文件夹中的图片。

  1. 添加以下代码:
// extension function to get bitmap from assets
fun Context.assetsToBitmap(fileName: String): Bitmap?{
    return try {
        with(assets.open(fileName)){
            BitmapFactory.decodeStream(this)
        }
    } catch (e: IOException) { null }
}

此时 Android Studio 可能会发出警告,并用红色突出显示部分代码,例如 ContextBitmapIOException

d2bde17e3c04aeed.png

不用担心!这是因为您尚未导入包含它们的库。Android Studio 提供了一个便捷的快捷键。

  1. 将光标悬停在该字词上,然后按 Alt + Enter(在 Mac 上,按 Option + Enter)后,系统就会为您生成导入作业。
  2. 接下来,您可以从 assets 加载位图,并将其放入 ImageView。返回 MainActivity 的 onCreateFunction,在 setContentView 行的正下方添加以下代码:
val img: ImageView = findViewById(R.id.imageToLabel)
// assets folder image file name with extension
val fileName = "flower1.jpg"
// get bitmap from assets folder
val bitmap: Bitmap? = assetsToBitmap(fileName)
bitmap?.apply {
    img.setImageBitmap(this)
}
  1. 与之前一样,一些代码会以红色突出显示。将光标置于该行上,然后使用 Alt + Enter / Option + Enter 自动添加导入操作。
  2. 在您之前创建的 layout.xml 文件中,您为 ImageView 指定了 imageToLabel 名称,因此第一行会使用该布局信息创建一个名为 Image 的 ImageView 对象实例。它使用内置的 Android 函数 findViewById 查找详细信息。然后,它会使用文件名 flower1.jpg,通过您在上一步中创建的 assetsToBitmap 函数从 assets 文件夹中加载图片。最后,它使用位图抽象类将位图加载到 img 中。
  3. 布局文件具有一个 TextView,用于渲染为图片推断的标签。接下来,获取代码对象。在上一个代码下方,添加以下代码:
val txtOutput : TextView = findViewById(R.id.txtOutput)

和之前一样,此操作会使用文本视图的名称(检查其名为 txtOutput 的 XML)查找布局文件信息,并用它实例化名为 txtOutput 的 TextView 对象。

同样,您将创建一个按钮对象来表示该按钮,并使用布局文件内容实例化该对象。

在布局文件中,我们将按钮命名为 btnTest,因此可以按如下方式实例化:

val btn: Button = findViewById(R.id.btnTest)

现在,您已初始化所有代码和控件,下一步(也是最终步骤)将是使用它们对图片进行推断。

在继续操作之前,请确保 onCreate 代码如下所示:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val img: ImageView = findViewById(R.id.imageToLabel)
    // assets folder image file name with extension
    val fileName = "flower1.jpg"
    // get bitmap from assets folder
    val bitmap: Bitmap? = assetsToBitmap(fileName)
    bitmap?.apply {
        img.setImageBitmap(this)
    }
    val txtOutput : TextView = findViewById(R.id.txtOutput)
    val btn: Button = findViewById(R.id.btnTest)
}

所有关键字都不应显示为红色,表示这些关键字尚未导入。如果是,请返回并按 ALT + Enter 键生成导入作业。

使用机器学习套件的图片标签器时,第一步是创建一个 Options 对象以自定义该行为。将图片转换为机器学习套件可以识别的 InputImage 格式。然后,创建一个 Labeler 对象以执行推理。它将为您提供一个返回结果的异步回调,然后您可以进行解析。

在您刚刚创建的按钮上,在其 onClickListener 事件中完成上述所有操作。完整代码如下:

btn.setOnClickListener {
  val labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS)
  val image = InputImage.fromBitmap(bitmap!!, 0)
  var outputText = ""
  labeler.process(image)
    .addOnSuccessListener { labels ->
      // Task completed successfully
      for (label in labels) {
        val text = label.text
        val confidence = label.confidence
        outputText += "$text : $confidence\n"
      }
      txtOutput.text = outputText
  }
    .addOnFailureListener { e ->
      // Task failed with an exception
  }
}
  • 用户首次点击该按钮时,代码会使用 ImageLabeling.getClient 实例化标签器,并向其传递 ImageLabelerOptions。它附带 DEFAULT_OPTIONS 属性,以便我们快速启动并运行。
  • 接下来,系统将使用位图的 fromBitmap 方法根据位图创建 InputImage。InputImage 是机器学习套件处理图片所需的格式。
  • 最后,标签器将处理图片,并在成功或失败时提供异步回调。如果推断成功,回调将包含一个标签列表。然后,您可以解析此标签列表,以读取标签的文本和置信度值。如果推断失败,它将发回异常,您可以使用该异常来向用户报告该问题。

就这么简单!现在,您可以在 Android 设备上或模拟器中运行该应用。如果您之前从未使用过此工具,可点击此处:https://developer.android.com/studio/run/emulator

以下是在模拟器中运行的应用。最初,您会看到图片和按钮,标签将为空。

c07f5f307f070dc7.png

按下该按钮,您将获得一组图片标签。

550ccaa783363551.png

在这里,您可以看到标签器判断这张图片包含花瓣、花卉、植物和天空的概率很高。这些都正确,都表明模型正在解析图片。

但是目前还无法确定这是一张雏菊图片。为此,您需要一个针对特定花卉的自定义模型,您将在下一个 Codelab 中了解如何执行此操作。

在以下步骤中,您将探索如何构建同一应用的 iOS 版。

7. 创建 iOS 版图像分类器 - 操作入门

您可以使用 Xcode 创建类似的 iOS 版应用。

  1. 启动 Xcode,然后从文件菜单中选择 New Project。您会看到以下对话框:

8fb0e6a9d6ac275e.png

  1. 如上所示选择 APP,然后点击 Next。系统会要求您为您的项目选择选项。为其输入名称和组织标识符,如下所示。请确保界面类型为 Storyboard,且语言为 Swift

76c6bdb5aee7659c.png

  1. 如果您想部署到手机并设置开发者资料,可以设置您的团队资料。如果不是,请保留 None,您可以使用 iOS 模拟器运行您的应用。
  2. 点击 Next,选择一个文件夹来存储您的项目及其文件。记住此项目的位置,您需要在下一步中使用它。
  3. 暂时关闭 Xcode,因为您在下一步后需要使用不同的工作区文件重新打开。

8. 使用 CocoaPods 集成机器学习套件

由于机器学习套件也适用于 iOS,因此您可以非常类似的方式使用它构建图像分类器。如需进行集成,请使用 CocoaPods。如果您尚未安装此应用,可以按照 https://cocoapods.org/ 上的说明进行安装。

  1. 打开您创建项目的目录。其中应包含 .xcodeproj 文件。

在这里,您可以看到指示位于正确位置的 .xcodeproj 文件。

e2966a47e84eb398.png

  1. 在此文件夹中创建一个名为 Podfile 的新文件。没有扩展名,只是 Podfile。在其中添加以下内容:
platform :ios, '10.0'

target 'ImageClassifierStep1' do
        pod 'GoogleMLKit/ImageLabeling'
end
  1. 保存,然后返回终端。在同一目录中,输入 pod install。CocoaPods 会下载相应的库和依赖项,并创建一个将您的项目与其外部依赖项组合的新工作区。

3b4c628b0cbface8.png

请注意,最后它会要求您关闭 Xcode 会话,并从现在起使用工作区文件。打开此文件后,Xcode 将使用您的原始项目以及外部依赖项启动。

32090e0024b6b5ef.png

现在,您可以继续执行下一步,创建界面了。

9. 使用 Storyboard 创建 iOS 界面

  1. 打开 Main.storyboard 文件,您会看到带手机设计界面的界面布局。
  2. 您可以使用屏幕右上角的 + 按钮来添加控件。点击该图标以获取 Controls palette。

e63bc3bafa54cc21.png

  1. ImageViewButtonLabel 拖放到设计界面上。请按从上到下的顺序排列这些内容,如下所示:

f9dfc55616b25f11.png

  1. 双击该按钮以将文本从 Button 修改为 Classify
  2. 拖动标签周围的控制手柄即可放大标签。(假设宽度与 UIImageView 大致相同,高度是其两倍)。
  3. 在标签仍处于选中状态的情况下,点击右上角的 selectors 按钮以显示 Inspectors Palette。
  4. 完成后,找到 Lines 设置,并确保将其设置为 0。这样,标签就可以呈现动态的行数。

a39708b320b56b30.png

现在,您可以执行下一步 - 使用 Outlet 和 Action 将界面连接到代码。

10. 创建 Action 和 Outlet

使用 Storyboard 进行 iOS 开发时,您需要通过 Outlet 引用控件的布局信息,并定义当用户使用 Action 对控件执行操作时要运行的代码。

在下一步中,您需要为 ImageView 和标签创建 Outlet。在代码中引用 ImageView 是为了将图片加载到其中。代码将引用标签,以便根据机器学习套件返回的推断设置标签文本。

  1. 要关闭 Inspectors Palette,请点击屏幕右上角的控件,然后点击紧邻其下方的 Add Editor on Right 按钮。

77255f7d6284750.png

  1. 您会有一个混乱的屏幕布局,其中主 Storyboard 会打开两次。在左侧的项目导航器中,选择 ViewController.swift,以便打开视图控制器代码。您的设计界面看起来好像从左侧的 Storyboard 编辑器中消失了,但不用担心,它仍然存在!
  2. 如需返回,请在视图控制器场景中点击 View Controller。请尝试使界面如下所示,其中左侧的 Storyboard 显示了您的设计,而 ViewController.swift 的代码则在右侧。

7eb21c7f9d43c9bc.png

  1. 从左侧的设计界面中选择 UIImageView,在按住 CONTROL 键的同时,将其拖动到右侧的代码中,将其放在 class 关键字(在上述屏幕截图的第 11 行)的正下方。

拖动时,您会看到一个箭头。当您拖动时,您会看到一个如下所示的弹出式窗口:

37477f0611948318.png

  1. Name 字段中填写“imageView”,然后点击 Connect
  2. 对标签重复此过程,并将其命名为“lblOutput”。
  3. 重要提示:对于该按钮,您需执行相同的操作,但要确保将连接类型设为 Action(而非 Outlet)!

7281b6eea9fb6c23.png

  1. 将该文件命名为“doClassification”,然后点击 Connect

完成后,代码应如下所示(请注意,标签和图片视图声明为 IBOutlet (Interface Builder Outlet),按钮声明为 IBAction (Interface Builder Action)。)

import UIKit

class ViewController: UIViewController {

    @IBAction func doClassification(_ sender: Any) {
    }
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var lblOutput: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

}
  1. 最后,将图片与应用捆绑在一起,以方便我们进行分类。要执行此操作,请将文件从文件资源管理器拖动到 Xcode 左侧的资源管理器中。放下它后,您会看到如下弹出式窗口:

889ff33eaec785ec.png

  1. 确保选中了 Add to Tagets 部分中的复选框,如下所示,然后点击 Finish

该文件将与您的应用捆绑在一起,您现在可以轻松地对其进行分类。您现在可以通过编码界面对图像进行分类!

11. 编写图像分类代码

现在,一切都已设置完毕,所以编写用于执行图像分类的代码非常简单。

  1. 首先,点击设计界面左上角的 X,关闭 Storyboard 设计器。这样,您就可以专注于代码。在本 Codelab 的剩余部分中,您将修改 ViewController.swift。
  2. 导入以下代码,将其导入 UIKit 的正下方,导入 MLKitVision 和 MLKit ImageLabeling 库:
import MLKitVision
import MLKitImageLabeling
  1. 然后,在您的 viewDidLoad 函数中,使用我们在应用中捆绑的文件来初始化 ImageView:
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    imageView.image = UIImage(named:"flower1.jpg")
}
  1. 创建一个辅助函数,用于获取图片的标签(位于 viewDidLoad() 的正下方):
func getLabels(with image: UIImage){
  1. 基于图片创建 VisionImage。机器学习套件在执行图像分类时会使用此类型。因此,请在 getLabels 功能函数中添加以下代码:
let visionImage = VisionImage(image: image)
visionImage.orientation = image.imageOrientation
  1. 接下来,为图片标签器创建选项。将使用这些选项进行初始化。在本例中,您只需设置 confidenceThreshold 的基本选项。也就是说,您只会要求标签器返回置信度值不低于 0.4 的标签。例如,在我们的花卉中,“植物”或“花瓣”等类别的置信度较高,但“篮球”或“汽车”等类别的置信度较低。
let options = ImageLabelerOptions()
options.confidenceThreshold = 0.4
  1. 现在,使用以下选项创建标签器:
let labeler = ImageLabeler.imageLabeler(options: options)
  1. 获得标签器后,您就可以对其进行处理。它将为您提供带有标签的异步回调(如果成功)和错误提示(如果失败),您稍后可以在另一个函数中进行处理(稍后我们将创建函数)。
labeler.process(visionImage) { labels, error in
    self.processResult(from: labels, error: error)
  }

如果 Xcode 提示没有 processResult 成员,请不要担心。因为您尚未实现此功能,接下来就会实现。

为方便起见,以下是完整的 getLabels 功能:

// This is called when the user presses the button
func getLabels(with image: UIImage){
    // Get the image from the UI Image element and set its orientation
    let visionImage = VisionImage(image: image)
    visionImage.orientation = image.imageOrientation

    // Create Image Labeler options, and set the threshold to 0.4
    // so we will ignore all classes with a probability of 0.4 or less
    let options = ImageLabelerOptions()
    options.confidenceThreshold = 0.4

    // Initialize the labeler with these options
    let labeler = ImageLabeler.imageLabeler(options: options)

    // And then process the image, with the callback going to self.processresult
    labeler.process(visionImage) { labels, error in
        self.processResult(from: labels, error: error)
 }
}

现在,您需要实现 processResult 函数。现在,这非常简单,只要有标签以及向我们返回的错误对象即可。标签应从机器学习套件转换为 ImageLabel 类型。

完成后,您只需遍历标签集,提取说明和置信度值,然后将其添加到名为 labeltextsvar 中。在遍历所有内容后,只需将 lblOutput.text 设置为该值即可。

以下是完整的函数:

// This gets called by the labeler's callback
func processResult(from labels: [ImageLabel]?, error: Error?){
    // String to hold the labels
    var labeltexts = ""
    // Check that we have valid labels first
    guard let labels = labels else{
        return
    }
  // ...and if we do we can iterate through the set to get the description and confidence
    for label in labels{
        let labelText = label.text + " : " + label.confidence.description + "\n"
        labeltexts += labelText
    }
    // And when we're done we can update the UI with the list of labels
    lblOutput.text = labeltexts
}

最后要做的就是在用户按下该按钮时调用 getLabels

当您创建 Action 时,所有内容都已连接到系统,因此您只需更新您之前创建的名为 doClassificaitonIBAction 以调用 getLabels

以下是只使用 imageView 内容来调用的代码:

@IBAction func doClassification(_ sender: Any) {
    getLabels(with: imageView.image!)
}

现在,运行应用并试一试。您可在下图中查看实际运行情况:

eb8e6c1b2e2c65e0.png

请注意,布局可能会因设备而异。

此 Codelab 不会探索每台设备的不同布局类型,这本身是一个相当复杂的概念。如果您无法正常显示界面,请返回 Storyboard 编辑器,并在底部看到一个 View as: 部分,您可以在其中选择特定设备。选择一个来匹配要测试的图片或设备,也可以修改界面,使之与您要测试的图片或设备相匹配。

随着您深入学习 iOS 开发,您将学习如何使用约束条件来确保界面在各个手机上保持一致,但这超出了本 Codelab 的讨论范围。

12. 恭喜!

现在,您已经实现了 Android 和 iOS 版应用,您可以通过通用模型实现基本的计算机视觉功能。您已经完成了大部分繁重工作。

在下一个 Codelab 中,您将构建一个自定义模型,以识别不同类型的花卉。只需几行代码,即可在应用中实现自定义模型以使其更加有用!