מדריך לסיווג תמונות ב-iOS

המשימה 'מסווג תמונות' מאפשרת לכם לסווג תמונות. אפשר להשתמש במשימה הזו כדי לזהות מה תמונה מייצגת מתוך קבוצת קטגוריות שהוגדרה בזמן האימון. ההוראות הבאות מראות איך להשתמש במסווג התמונות באפליקציות ל-iOS. דוגמת הקוד שמתוארת בהוראות האלו זמינה ב-GitHub.

אפשר לראות את המשימה הזו בהדגמה של האינטרנט. בסקירה הכללית תוכלו לקרוא עוד על היכולות, המודלים ואפשרויות ההגדרה של המשימה הזו.

קוד לדוגמה

הקוד לדוגמה של Tasks ב-MediaPipe הוא הטמעה בסיסית של אפליקציית סיווג התמונות ל-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.

רכיבים מרכזיים

הקבצים הבאים מכילים את הקוד החיוני לאפליקציה לדוגמה של מסווג התמונות:

  • ImageClassifierService.swift: הפעלת מסווג התמונות, טיפול בבחירת המודל והפעלת ההסקה על נתוני הקלט.
  • CameraViewController.swift: הטמעה של ממשק המשתמש עבור מצב הקלט של פיד המצלמה בשידור חי והצגת התוצאות באופן חזותי.
  • MediaLibraryViewController.swift הטמעה של ממשק המשתמש עבור מצב קלט תמונה וסרטון וידאו והצגת התוצאות באופן חזותי.

הגדרה

בקטע הזה מתוארים השלבים העיקריים להגדרת סביבת הפיתוח ופרויקטים בקוד לשימוש במסווג התמונות. במדריך ההגדרה ל-iOS תוכלו לקרוא מידע כללי על הגדרת סביבת הפיתוח לשימוש במשימות MediaPipe, כולל הדרישות לגרסת הפלטפורמה.

יחסי תלות

מסווג התמונות משתמש בספרייה MediaPipeTasksVision, שאותה צריך להתקין באמצעות CocoaPods. הספרייה תואמת גם לאפליקציות Swift ו-Objective-C ואין צורך בהגדרות נוספות ספציפיות לשפה.

הוראות להתקנת CocoaPods ב-macOS מפורטות במדריך ההתקנה של CocoaPods. במאמר שימוש ב-CocoaPods מוסבר איך ליצור Podfile עם ה-Pods שדרושים לאפליקציה.

מוסיפים את הרצף של MediaPipeTasksVision ב-Podfile באמצעות הקוד הבא:

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: המצב שמאפשר קלט של תמונה יחידה.

סרטון: המצב של פריימים מפוענחים בסרטון.

LIVE_STREAM: המצב לשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, יש להפעיל את resultListener כדי להגדיר האזנה לקבלת תוצאות באופן אסינכרוני.
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
displayNamesLocale ההגדרה הזו מגדירה את השפה של התוויות שישמשו בשמות המוצגים שבמטא-נתונים של מודל המשימה, אם זמין. ברירת המחדל היא en לאנגלית. אפשר להוסיף תוויות שמותאמות לשוק המקומי למטא-נתונים של מודל מותאם אישית באמצעות TensorFlow Lite Metadata Writer API קוד שפה en
maxResults מגדיר את המספר המקסימלי האופציונלי של תוצאות סיווג עם התוצאות המובילות. אם הערך הוא פחות מ-0, יוחזרו כל התוצאות הזמינות. מספרים חיוביים כלשהם -1
scoreThreshold מגדירה את סף הציון של החיזוי (אם יש כזה) שמבטל את הסף שצוין במטא-נתונים של המודל. תוצאות מתחת לערך הזה נדחות. כל מספר ממשי (float) לא הוגדר
categoryAllowlist מגדיר את הרשימה האופציונלית של שמות קטגוריות מותרות. אם השדה לא ריק, תוצאות הסיווג שבהן שם הקטגוריה לא נמצא בקבוצה הזו יסוננו. המערכת מתעלמת משמות קטגוריות כפולים או לא ידועים. האפשרות הזו קיימת רק ב-categoryDenylist ומשתמשת בשתיהן כדי ליצור שגיאה. כל מחרוזת לא הוגדר
categoryDenylist מגדיר את הרשימה האופציונלית של שמות קטגוריות שאינם מותרים. אם השדה לא ריק, תוצאות הסיווג שבהן שם הקטגוריה נמצא בקבוצה הזו יסוננו. המערכת מתעלמת משמות קטגוריות כפולים או לא ידועים. האפשרות הזו בלעדית באופן הדדי ל-categoryAllowlist, והשימוש בשתי האפשרויות יוביל לשגיאה. כל מחרוזת לא הוגדר
resultListener מגדיר את אוזן התוצאות לקבל את תוצאות הסיווג באופן אסינכרוני כאשר מסווג התמונות נמצא במצב של שידור חי. אפשר להשתמש באפשרות הזו רק כשמצב הריצה מוגדר ל-LIVE_STREAM לא רלוונטי לא הוגדר

הגדרות השידור החי

כשמצב ההרצה מוגדר כשידור חי, למסווג התמונות נדרשת אפשרות הגדרה נוספת imageClassifierLiveStreamDelegate, שמאפשרת למסווג לספק תוצאות סיווג באופן אסינכרוני. מקבל הגישה מיישם את השיטה imageClassifier(_:didFinishClassification:timestampInMilliseconds:error:), שאליה מסווג התמונות, אחרי עיבוד תוצאות הסיווג של כל פריים.

שם האפשרות תיאור טווח ערכים ערך ברירת מחדל
imageClassifierLiveStreamDelegate מאפשר למסווג התמונות לקבל תוצאות סיווג באופן אסינכרוני במצב חי. המחלקה שהמכונה שלה מוגדרת למאפיין הזה צריכה להטמיע את ה-method imageClassifier(_:didFinishClassification:timestampInMilliseconds:error:). לא רלוונטי לא הוגדר

הכנת נתונים

צריך להמיר את תמונת הקלט או את המסגרת לאובייקט MPImage לפני ההעברה למסווג התמונות. ב-MPImage יש תמיכה בסוגים שונים של פורמטים של תמונות ב-iOS, ותוכלו להשתמש בהם בכל מצב פעיל כדי להסיק את הנתונים. מידע נוסף על MPImage זמין ב-MPImage API

בוחרים פורמט תמונה ל-iOS בהתאם לתרחיש לדוגמה ולמצב ההרצה של האפליקציה.MPImage מקבל את הפורמטים UIImage של התמונות ל-iOS, CVPixelBuffer ו-CMSampleBuffer ל-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];
    

הדוגמה מפעילה MPImage עם כיוון ברירת המחדל UIImage.Orientation.Up. אפשר לאתחל MPImage עם כל אחד מהערכים הנתמכים של UIImage.Orientation. במסווג התמונות אין תמיכה בכיוונים משוכפלים כמו .upMirrored, .downMirrored, .leftMirrored, .rightMirrored.

מידע נוסף על UIImage זמין במסמכי התיעוד למפתחים של UIImage של Apple.

CVPixelBuffer

הפורמט CVPixelBuffer מתאים מאוד לאפליקציות שיוצרות פריימים ומשתמשות במסגרת CoreImage של iOS לצורך עיבוד.

הפורמט CVPixelBuffer מתאים במיוחד למצבי הריצה הבאים:

  • תמונות: אפשר לשלוח אפליקציות שיוצרות תמונות CVPixelBuffer אחרי עיבוד באמצעות מסגרת CoreImage של iOS למסווג התמונות במצב הרצה של תמונות.

  • סרטונים: אפשר להמיר פריימים של וידאו לפורמט 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 נשלחים באופן אסינכרוני בפורמט CMSampleBuffer על ידי iOS AVCaptureVideoDataOutput.

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 זמין במסמכי התיעוד למפתחים של Apple CMSampleBuffer.

הרצת המשימה

כדי להפעיל את מסווג התמונות, צריך להשתמש ב-method 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:). הקוד לדוגמה מאפשר למשתמשים לעבור בין מצבי עיבוד שלא נדרשים בתרחיש לדוגמה שלכם.

שימו לב לנקודות הבאות:

  • כשמריצים במצב וידאו או במצב שידור חי, צריך גם לציין את חותמת הזמן של מסגרת הקלט למשימה של מסווג התמונות.

  • כשעובדים במצב תמונה או וידאו, המשימה 'מסווג תמונות' חוסמת את השרשור הנוכחי עד לסיום העיבוד של התמונה או המסגרת. כדי למנוע את חסימת ה-thread הנוכחי, צריך לבצע את העיבוד ב-thread הנוכחי ברקע באמצעות frameworks של iOS Dispatch או של NSOperation.

  • במצב שידור חי, המשימה של מסווג התמונות חוזרת מיד ולא חוסמת את השרשור הנוכחי. הוא מפעיל את ה-method 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 על:

הקוד לדוגמה של מסווג התמונות שמדגים איך להציג את תוצאות הסיווג שהוחזרו מהמשימה מופיע בדוגמה לקוד.