适用于 iOS 的图片分类指南

借助图片分类器任务,您可以对图片进行分类。您可以使用此任务从训练时定义的一组类别中找出某张图片所代表的含义。以下说明介绍了如何在 iOS 应用中使用图片分类器。GitHub 上提供了这些说明中所述的代码示例。

您可以通过查看此网页演示了解此任务的实际运用。如需详细了解此任务的功能、模型和配置选项,请参阅概览

代码示例

MediaPipe Tasks 示例代码是 iOS 版图像分类器应用的基本实现。该示例使用 iOS 实体设备上的相机来持续对对象进行分类,还可以使用设备图库中的图片和视频对对象进行静态分类。

您可以将该应用作为基础来开发自己的 iOS 应用,也可以在修改现有应用时引用该应用。图像分类器示例代码托管在 GitHub 上。

下载代码

以下说明介绍了如何使用 git 命令行工具创建示例代码的本地副本。

如需下载示例代码,请执行以下操作:

  1. 使用以下命令克隆 git 代码库:

    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. (可选)将您的 Git 实例配置为使用稀疏检出,以便只获得图像分类器示例应用的文件:

    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_classification/ios/
    

创建示例代码的本地版本后,您可以安装 MediaPipe 任务库,使用 Xcode 打开项目并运行应用。如需了解相关说明,请参阅 iOS 设置指南

关键组件

以下文件包含图片分类器示例应用的关键代码:

设置

本部分介绍设置开发环境和代码项目以使用图像分类器的关键步骤。如需了解有关为使用 MediaPipe 任务设置开发环境的一般信息(包括平台版本要求),请参阅 iOS 设置指南

依赖项

Image Classifier 会使用 MediaPipeTasksVision 库,该库必须使用 CocoaPods 进行安装。该库与 Swift 和 Objective-C 应用兼容,并且不需要任何额外的语言特定设置。

如需了解如何在 macOS 上安装 CocoaPods,请参阅 CocoaPods 安装指南。 如需了解如何使用应用所需的 Pod 创建 Podfile,请参阅使用 CocoaPods

使用以下代码在 Podfile 中添加 MediaPipeTasksVision pod:

target 'MyImageClassifierApp' do
  use_frameworks!
  pod 'MediaPipeTasksVision'
end

如果您的应用包含单元测试目标,请参阅 iOS 设置指南,详细了解如何设置 Podfile

模型

MediaPipe 图像分类器任务需要一个与此任务兼容的经过训练的模型。如需详细了解图片分类器可用的经过训练的模型,请参阅任务概览“模型”部分

选择并下载模型,然后使用 Xcode 将其添加到您的项目目录。 如需了解如何向 Xcode 项目添加文件,请参阅管理 Xcode 项目中的文件和文件夹

使用 BaseOptions.modelAssetPath 属性指定 App Bundle 中模型的路径。如需查看代码示例,请参阅下一部分。

创建任务

您可以通过调用图片分类器的初始化程序之一来创建该任务。ImageClassifier(options:) 初始化程序用于设置运行模式、显示名称语言区域、结果数上限、置信度阈值、类别许可名单和拒绝名单等配置选项的值。

如果您不需要使用自定义配置选项初始化的图片分类器,则可以使用 ImageClassifier(modelPath:) 初始化程序创建使用默认选项的图片分类器。如需详细了解配置选项,请参阅配置概览

图像分类器任务支持 3 种输入数据类型:静态图片、视频文件和直播视频流。默认情况下,ImageClassifier(modelPath:) 会为静态图片初始化任务。如果您希望将任务初始化以处理视频文件或直播视频流,请使用 ImageClassifier(options:) 指定视频或直播的运行模式。实时流式传输模式还需要一个额外的 imageClassifierLiveStreamDelegate 配置选项,使图片分类器能够将图片分类结果异步提供给代理。

选择与运行模式对应的标签页,了解如何创建任务并运行推断。

Swift

映像

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageClassifierOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .image
options.maxResults = 5

let imageClassifier = try ImageClassifier(options: options)
    

视频

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageClassifierOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.maxResults = 5

let imageClassifier = try ImageClassifier(options: options)
    

直播

import MediaPipeTasksVision

// Class that conforms to the `ImageClassifierLiveStreamDelegate` protocol and
// implements the method that the image classifier calls once it
// finishes performing classification on each input frame.
class ImageClassifierResultProcessor: NSObject, ImageClassifierLiveStreamDelegate {

   func imageClassifier(
    _ imageClassifier: ImageClassifier,
    didFinishClassification result: ImageClassifierResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the image classifier result or errors here.

  }
}

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = ImageClassifierOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.maxResults = 5

// Assign an object of the class to the `imageClassifierLiveStreamDelegate`
// property.
let processor = ImageClassifierResultProcessor()
options.imageClassifierLiveStreamDelegate = processor

let imageClassifier = try ImageClassifier(options: options)
    

Objective-C

映像

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageClassifierOptions *options = [[MPPImageClassifierOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.maxResults = 5;

MPPImageClassifier *imageClassifier =
      [[MPPImageClassifier alloc] initWithOptions:options error:nil];
    

视频

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageClassifierOptions *options = [[MPPImageClassifierOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.maxResults = 5;

MPPImageClassifier *imageClassifier =
      [[MPPImageClassifier alloc] initWithOptions:options error:nil];
    

直播

@import MediaPipeTasksVision;

// Class that conforms to the `MPPImageClassifierLiveStreamDelegate` protocol
// and implements the method that the image classifier calls once it finishes
// performing classification on each input frame.

@interface APPImageClassifierResultProcessor : NSObject 

@end

@implementation APPImageClassifierResultProcessor

-   (void)imageClassifier:(MPPImageClassifier *)imageClassifier
    didFinishClassificationWithResult:(MPPImageClassifierResult *)imageClassifierResult
              timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                                error:(NSError *)error {

    // Process the image classifier result or errors here.

}

@end

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageClassifierOptions *options = [[MPPImageClassifierOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.maxResults = 5;

// Assign an object of the class to the `imageClassifierLiveStreamDelegate`
// property.
APPImageClassifierResultProcessor *processor = [APPImageClassifierResultProcessor new];
options.imageClassifierLiveStreamDelegate = processor;

MPPImageClassifier *imageClassifier =
      [[MPPImageClassifier alloc] initWithOptions:options error:nil];
    

配置选项

此任务具有以下适用于 iOS 应用的配置选项:

选项名称 说明 值范围 默认值
runningMode 设置任务的运行模式。有三种模式:

IMAGE:单图输入的模式。

VIDEO:视频已解码帧的模式。

LIVE_STREAM:输入数据实时流式传输的模式,例如来自摄像头的直播模式。在此模式下,必须调用 resultListener 才能设置监听器以异步接收结果。
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
displayNamesLocale 设置任务模型元数据(如果有)中提供的显示名称要使用的标签语言。默认值为 en(英语)。您可以使用 TensorFlow Lite Metadata Writer API 将本地化标签添加到自定义模型的元数据中 语言区域代码 en
maxResults 设置要返回的评分最高的分类结果的可选数量上限。如果 < 0,则返回所有可用的结果。 任何正数 -1
scoreThreshold 设置预测分数阈值,以替换模型元数据中提供的阈值(如果有)。低于此值的结果将被拒绝。 任意浮点数 未设置
categoryAllowlist 设置允许的类别名称的可选列表。如果为非空值,则会过滤掉类别名称不在此集中的分类结果。重复或未知的类别名称会被忽略。 此选项与 categoryDenylist 互斥,两者都会导致错误。 任何字符串 未设置
categoryDenylist 设置不允许使用的类别名称的可选列表。如果为非空值,则类别名称在此集中的分类结果将被滤除。重复或未知的类别名称会被忽略。此选项与 categoryAllowlist 互斥,同时使用这两个选项会导致错误。 任何字符串 未设置
resultListener 将结果监听器设置为在图片分类器处于直播模式时异步接收分类结果。仅在跑步模式设为“LIVE_STREAM”时才能使用 N/A 未设置

直播配置

当运行模式设置为直播时,图片分类器需要额外的 imageClassifierLiveStreamDelegate 配置选项,以便分类器异步传送分类结果。该委托会实现 imageClassifier(_:didFinishClassification:timestampInMilliseconds:error:) 方法,图片分类器会在处理每一帧的分类结果后调用该方法。

选项名称 说明 值范围 默认值
imageClassifierLiveStreamDelegate 启用图片分类器,以在直播模式下异步接收分类结果。实例被设置为此属性的类必须实现 imageClassifier(_:didFinishClassification:timestampInMilliseconds:error:) 方法。 不适用 未设置

准备数据

您需要先将输入图片或帧转换为 MPImage 对象,然后才能将其传递给图片分类器。MPImage 支持不同类型的 iOS 图片格式,并且可以在任何运行模式下使用它们进行推理。如需详细了解 MPImage,请参阅 MPImage API

根据您的用例和应用所需的运行模式选择 iOS 图片格式。MPImage 接受 UIImageCVPixelBufferCMSampleBuffer iOS 图片格式。

UIImage

UIImage 格式非常适合以下运行模式:

  • 图片:可以将来自 app bundle、用户图库或文件系统中的图片转换为 UIImage 图片格式。MPImage 对象

  • 视频:使用 AVAssetImageGenerator 将视频帧提取为 CGImage 格式,然后将其转换为 UIImage 图片。

Swift

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(uiImage: image)
    

Objective-C

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

此示例使用默认的 UIImage.Orientation.Up 方向初始化 MPImage。您可以使用任何受支持的 UIImage.Orientation 值初始化 MPImage。图片分类器不支持 .upMirrored.downMirrored.leftMirrored.rightMirrored 等镜像方向。

如需详细了解 UIImage,请参阅 UIImage Apple 开发者文档

CVPixelBuffer

CVPixelBuffer 格式非常适合用于生成帧并使用 iOS CoreImage 框架进行处理的应用。

CVPixelBuffer 格式非常适合以下运行模式:

  • 图片:在使用 iOS 的 CoreImage 框架进行一些处理后生成 CVPixelBuffer 图片的应用可以在图片运行模式下发送到图片分类器。

  • 视频:可以将视频帧转换为 CVPixelBuffer 格式进行处理,然后在视频模式下将其发送到图片分类器。

  • 直播:使用 iOS 相机生成帧的应用可以先转换为 CVPixelBuffer 格式进行处理,然后再发送到直播模式下的图片分类器。

Swift

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(pixelBuffer: pixelBuffer)
    

Objective-C

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

如需详细了解 CVPixelBuffer,请参阅 CVPixelBuffer Apple 开发者文档

CMSampleBuffer

CMSampleBuffer 格式会存储统一媒体类型的媒体样本,非常适合直播运行模式。来自 iOS 相机的实时帧由 iOS AVCaptureVideoDataOutputCMSampleBuffer 格式异步传送。

Swift

// Obtain a CMSampleBuffer.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(sampleBuffer: sampleBuffer)
    

Objective-C

// Obtain a `CMSampleBuffer`.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithSampleBuffer:sampleBuffer error:nil];
    

如需详细了解 CMSampleBuffer,请参阅 CMSampleBuffer Apple 开发者文档

运行任务

如需运行图片分类器,请使用专用于指定运行模式的 classify() 方法:

  • 静态图片:classify(image:)
  • 视频:classify(videoFrame:timestampInMilliseconds:)
  • 直播:classifyAsync(image:timestampInMilliseconds:)

图片分类器会返回输入图片或帧中对象的可能类别。

以下代码示例显示了如何在这些不同的运行模式下运行图像分类器的基本示例:

Swift

映像

let result = try imageClassifier.classify(image: image)
    

视频

let result = try imageClassifier.classify(
  videoFrame: image,
  timestampInMilliseconds: timestamp)
    

直播

try imageClassifier.classifyAsync(
  image: image,
  timestampInMilliseconds: timestamp)
    

Objective-C

映像

MPPImageClassifierResult *result = [imageClassifier classifyImage:image
                                                            error:nil];
    

视频

MPPImageClassifierResult *result = [imageClassifier classifyVideoFrame:image
                                               timestampInMilliseconds:timestamp
                                                                 error:nil];
    

直播

BOOL success = [imageClassifier classifyAsyncImage:image
                          timestampInMilliseconds:timestamp
                                            error:nil];
    

图片分类器代码示例更详细地展示了每种模式的实现:classify(image:)classify(videoFrame:timestampInMilliseconds:)classifyAsync(image:timestampInMilliseconds:)。该示例代码允许用户在多种处理模式之间切换,您的用例可能并不需要这些模式。

请注意以下几点:

  • 在视频模式或直播模式下运行时,您还必须向图像分类器任务提供输入帧的时间戳。

  • 在图片或视频模式下运行时,图片分类器任务会阻塞当前线程,直到它处理完输入图片或帧为止。为避免阻塞当前线程,请使用 iOS DispatchNSOperation 框架在后台线程中执行处理。

  • 在实时模式下运行时,图像分类器任务会立即返回,并且不会阻塞当前线程。它会在处理每个输入帧后调用带有分类结果的 imageClassifier(_:didFinishClassification:timestampInMilliseconds:error:) 方法。图片分类器会在专用串行调度队列上异步调用此方法。如需在界面上显示结果,请在处理结果后将结果分派到主队列。如果在图像分类器任务正忙于处理另一个帧时调用 classifyAsync 函数,则图像分类器会忽略新的输入帧。

处理和显示结果

运行推理时,图片分类器任务会返回一个 ImageClassifierResult 对象,其中包含输入图片或帧中对象的可能类别列表。

以下示例展示了此任务的输出数据:

ImageClassifierResult:
 Classifications #0 (single classification head):
  head index: 0
  category #0:
   category name: "/m/01bwb9"
   display name: "Passer domesticus"
   score: 0.91406
   index: 671
  category #1:
   category name: "/m/01bwbt"
   display name: "Passer montanus"
   score: 0.00391
   index: 670

对以下项运行 Bird Classifier 可获得此结果:

图像分类器示例代码演示了如何显示从任务返回的分类结果。如需了解详情,请参阅代码示例