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

1. 准备工作

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

前提条件

此 Codelab 是图片分类入门在线课程的一部分。这篇文章面向经验丰富的机器学习开发者。

构建内容

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

所需物品

2. 开始

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

例如,在计算机视觉领域,除了构成猫的像素之外,系统还可以将其标记为包含猫。计算机视觉技术还有一些其他的领域会比这更具体,例如对象检测功能,在这种技术中,计算机可以在图片中找到多个项并推导边界框。

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

为尽可能简化应用,它会使用与它捆绑在一起的图片作为资源,并向您显示这些图片的分类。日后还可以使用扩展功能,例如使用图片选择器或直接从相机中提取图片。

首先,您将使用 Android Studio 完成在 Android 上构建应用的流程。(请跳至第 7 步,在 iOS 设备上执行相同的操作。)

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

859b1875e37c321a.png

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

ee3b6a81bad87b3.png

  1. 选择您的首选语言:Kotlin 或 Java。本实验使用的是 Kotlin,因此,如果您希望完全按照 Kotlin 语言编写代码,您可能需要选择 Kotlin。
  2. 准备就绪后,点击“完成”。Android Studio 将为您创建应用。完成设置可能需要一些时间。

3.导入机器学习套件的图片标签库

机器学习套件 (https://developers.google.com/ml-kit) 为开发者提供了许多解决方案,满足机器学习领域的常见场景,并使他们能够在跨平台中轻松实现和工作。机器学习套件提供了一个名为“图片标签”的万能库。此库包含一个预训练模型,可以识别超过 600 类图片。因此,最好立即开始使用。

请注意,借助机器学习套件,您也可以使用相同的 API 来使用自定义模型,因此,准备就绪后,您便可以不局限于“开始使用”,并着手构建使用针对您的场景训练的模型的个性化图片标记应用。

在此场景中,您将构建一个花卉识别器。当您创建首个应用并显示鲜花图片时,系统会将其识别为鲜花。(稍后,在构建自己的花卉检测器模型时,借助机器学习套件,您只需做出极少更改即可将其拖放到应用中,并让新模型告诉您其类型是郁金香还是“玫瑰”。)

  1. 在 Android Studio 中,使用项目浏览器,确保在顶部选择了 Android
  2. 打开 Gradle Scripts 文件夹,并为应用选择 build.gradle 文件。应用可能会有 2 个或更多,因此请务必使用应用 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 界面。其中提供了许多功能,而本实验并非要教您如何使用它。如需详细了解布局编辑器,请访问:https://developer.android.com/studio/write/layout-editor

在本实验中,请选择编辑器右上角的代码工具。

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. 在 Project Explorer 中,右键点击顶部的 app,然后选择“New Directory”。
  2. 在随即显示的对话框(列出了不同的目录)中,选择 src/main/assets

c93650ea68bb60e9.png

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

444b4afab73433b8.png

  1. 右键点击此文件夹,您会看到一个弹出式窗口,其中包含选项列表。其中一种方法是在文件系统中打开该文件夹。 找到适合您的操作系统的操作系统,然后选择该操作系统。(在 Mac 上,这将是在查找器中显示,在 Windows 中则为在资源管理器中打开,在 Ubuntu 上则是在“文件”中显示。)

95e0eca881d35f6b.png

  1. 将文件复制到该文件中。您可以从 Pixabay 等网站下载图片。建议将映像重命名为简单名称。在这种情况下,映像已重命名为 flower1.jpg

完成此操作后,请返回到 Android Studio,您应该会在资源文件夹中看到您的文件。

cfa53c9c75a033d8.png

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

6.编写分类代码来标记图片

(现在,大家都期待了在 Android 上实现计算机视觉!)

  1. 您要在 MainActivity 文件中编写代码,因此请在项目文件夹中的 com.google.devrel.imageclassifierstep1 下查找(如果选择了不同的命名空间,则等同于您的任何等效命名空间)。请注意,Android Studio 项目中通常设置有 3 个命名空间文件夹,一个用于应用,一个用于 Android 测试,一个用于测试。您会看到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)
    }
}

在右花括号下方,您可以添加不属于该类但可供相应类使用的扩展代码。您需要添加扩展程序才能以位图形式从素材资源中读取文件。这会用于加载您之前复制到资源文件夹中的图片。

  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. 接下来,您可以从素材资源中加载位图,并将其放入 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,因此第一行将使用该布局信息创建名为 ImageView 的对象实例。它使用内置 Android 函数 findViewById 查找详细信息。然后,它会使用文件名 flower1.jpg,通过您在上一步中创建的 assetsToBitmap 函数,从素材资源文件夹加载图片。最后,它使用位图抽象类将位图加载到 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 是机器学习套件处理图片所需的格式。
  • 最后,标签添加者会处理图片,并在成功或失败时提供异步回调。如果推断成功,回调将包含一个标签列表。然后,您可以解析此标签列表,读取标签的文本和置信度值。如果执行失败,系统会返回异常,您可以用该异常来向用户报告。

这样就大功告成了!现在,您可以在 Android 设备或模拟器中运行该应用。如果您之前从未尝试过,可以参考此处的说明:https://developer.android.com/studio/run/emulator

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

c07f5f307f070dc7.png

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

550ccaa783363551.png

在这里,您可以看到标签添加者确定图片包含花瓣、花朵、植物和天空的概率较高。这些都正确,它们都展示模型正在解析图像。

但目前还不能确定这是一张菊花的图片。为此,您需要一个针对特定花卉进行训练的自定义模型,您将在下一个实验中看到具体操作方式。

在以下步骤中,您将了解如何在 iOS 上构建同一应用。

7. 在 iOS 上创建图像分类器 - 开始使用

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

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

8fb0e6a9d6ac275e.png

  1. 如下所示,选择 App(应用),然后点击 Next(下一步)。系统会要求您选择项目选项。输入名称和组织标识符,如下所示。确保接口类型为故事板,语言为 Swift(如下所示)。

76c6bdb5aee7659c.png

  1. 如果您要部署到手机上,并设置了开发者资料,则可以设置您的团队设置。否则,请将其保持为 None,您可以使用 iOS 模拟器运行您的应用。
  2. 点击 Next,然后选择用于存储项目及其文件的文件夹。记住此项目的位置,您需要在下一步中使用该项目。
  3. 请暂时关闭 Xcode,因为在下一步中您将使用其他工作区文件重新打开 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. 屏幕右上角会显示一个 + 按钮,供您添加控件。点击即可获取控件面板。

e63bc3bafa54cc21.png

  1. ImageViewButtonLabel 拖放到设计图面上。将图片上下排列,如下所示:

f9dfc55616b25f11.png

  1. 双击此按钮可将其文本从按钮修改为分类
  2. 拖动标签周围的控件手柄可放大标签。(假设宽度与 UIImageView 大致相同,高度为两倍)。
  3. 仍选择标签,请点击右上角的选择器按钮,以显示检查器调色板。
  4. 完成此操作后,找到线条设置,并确保将其设置为 0。这允许标签呈现动态数量的行。

a39708b320b56b30.png

现在,您可以执行下一步了 - 使用插座和操作将界面连接到代码中。

10. 创建操作和输出口

使用 Storyboard 进行 iOS 开发时,您可以使用输出口引用控件的布局信息,并定义当用户使用操作针对控件执行操作时要运行的代码。

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

  1. 点击屏幕右上方的控件,然后点击紧邻其下方的在右侧添加编辑器按钮,以关闭检查器调色板。

77255f7d6284750.png

  1. 屏幕布局会令人困惑,其中 main.storyboard 会打开两次。在左侧的项目导航器中,选择 ViewController.swift,以便打开视图控制器代码。左侧设计图板看起来似乎消失了,但别担心,它仍在原处!
  2. 如需重新显示,请点击 View Controller 场景中的 View Controller。尽量使您的界面如下所示:左侧的故事板显示设计,右侧是 ViewController.swift 的代码。

7eb21c7f9d43c9bc.png

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

拖动时,您将看到一个箭头,当您放下时,会看到如下弹出式窗口:

37477f0611948318.png

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

7281b6eea9fb6c23.png

  1. 将其命名为“doClassification”,然后点击连接

完成后,您的代码应如下所示:(请注意,标签和图片视图声明为 IBOutlet(接口构建器输出)和按钮,即 IBAction(接口构建器操作)。)

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. 确保选中添加到目标部分中的复选框(如图所示),然后点击完成

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

11. 编写图片分类代码

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

  1. 首先关闭设计板设计器,方法是点击设计图面左上角的 X。这样您就可以专注于代码。在本实验的其余部分,您将修改 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

创建操作时,已为您连接所有服务,因此您只需更新之前创建的名为 doClassificaitonIBAction 即可调用 getLabels

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

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

现在,您可以运行应用,然后试试看。您可以在这里查看实际应用:

eb8e6c1b2e2c65e0.png

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

此 Codelab 不会探讨每种设备的不同布局类型,这本身是一个相当复杂的概念。如果您没有看到界面,请返回故事板编辑器,您会在底部看到 View as: 部分,可以从中选择特定设备。请根据您要测试的图片或设备选择一项,并修改界面以使其适应测试。

随着深入探讨 iOS 开发,您将了解如何使用约束来确保界面在各手机之间保持一致,但这超出了本次实验的范围。

12. 恭喜!

现在,您已经在 Android 和 iOS 上实现了应用,它可利用通用模型为您提供基本计算机视觉。你已经完成了这项繁重工作。

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