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

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

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

קוד לדוגמה

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

הגדרה

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

יחסי תלות

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

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

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

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

אם האפליקציה שלך כוללת יעדים לבדיקת יחידות, יש לעיין במדריך ההגדרה של iOS למידע נוסף על ההגדרה Podfile.

דגם

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

בוחרים מודל ומורידים אותו, ומוסיפים אותו לספריית הפרויקט באמצעות Xcode. לקבלת הוראות להוספת קבצים לפרויקט Xcode, קראו את המאמר ניהול קבצים ותיקיות ב-Xcode. project.

משתמשים במאפיין 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 מאפשר למסווג התמונות לקבל תוצאות סיווג באופן אסינכרוני במצב סטרימינג בשידור חי. המחלקה שהמופע שלה מוגדר לנכס הזה חייבת להטמיע את imageClassifier(_:didFinishClassification:timestampInMilliseconds:error:) . לא רלוונטי לא מוגדר

הכנת נתונים

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

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

CVPixelBuffer

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

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

  • תמונות: אפליקציות שיוצרות CVPixelBuffer תמונות לאחר עיבוד מסוים באמצעות ה-framework 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 זמין ב-CMSampleBuffer Apple מפתחי המשחק מסמכים.

הרצת המשימה

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

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

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