1. מבוא
מהו MediaPipe?
MediaPipe Solutions מאפשר לכם להחיל פתרונות של למידת מכונה (ML) על האפליקציות שלכם. הוא מספק מסגרת להגדרת צינורות עיבוד מוכנים מראש שמספקים למשתמשים פלט מיידי, מעניין ומועיל. אפשר גם להתאים אישית את הפתרונות האלה באמצעות MediaPipe Model Maker כדי לעדכן את מודלי ברירת המחדל.
סיווג תמונות הוא אחת מכמה משימות ראייה מבוססת-למידה שאפשר לבצע באמצעות פתרונות MediaPipe. MediaPipe Tasks זמין ל-Android, ל-iOS, ל-Python (כולל Raspberry Pi!) ולאינטרנט.
ב-Codelab הזה נתחיל מאפליקציית Android שמאפשרת לצייר ספרות על המסך, ולאחר מכן נוסיף פונקציונליות שסווגת את הספרות המצוירות כערך יחיד מ-0 עד 9.
מה תלמדו
- איך לשלב משימה של סיווג תמונות באפליקציה ל-Android באמצעות MediaPipe Tasks.
מה נדרש
- גרסת Android Studio מותקנת (הקודלאב הזה נכתב ונבדק באמצעות Android Studio Giraffe).
- מכשיר Android או אמולטור להרצת האפליקציה.
- ידע בסיסי בפיתוח ל-Android (זה לא 'Hello World', אבל זה לא רחוק מזה!).
2. הוספת משימות של MediaPipe לאפליקציה ל-Android
הורדת אפליקציית ההתחלה ל-Android
בקודלאב הזה נתחיל מדוגמה מוכנה מראש שמאפשרת לצייר במסך. האפליקציה הזו מופיעה במאגר הרשמי של דוגמאות ל-MediaPipe כאן. כדי לשכפל את המאגר או להוריד את קובץ ה-zip, לוחצים על Code (קוד) > Download ZIP (הורדת קובץ ZIP).
ייבוא האפליקציה ל-Android Studio
- פותחים את Android Studio.
- במסך Welcome to Android Studio, בוחרים באפשרות Open (פתיחה) בפינה השמאלית העליונה.
- עוברים למקום שבו קלונתם או הורדת את המאגר ופותחים את הספרייה codelabs/digitclassifier/android/start.
- כדי לוודא שהכל נפתח כמו שצריך, לוחצים על החץ הירוק run ( ) בפינה השמאלית העליונה של Android Studio.
- האפליקציה אמורה להיפתח עם מסך שחור שאפשר לצייר עליו, וגם עם לחצן ניקוי כדי לאפס את המסך. אפשר לצייר במסך הזה, אבל הוא לא עושה הרבה דברים אחרים, אז נתחיל לתקן את זה עכשיו.
דגם
בפעם הראשונה שתפעילו את האפליקציה, יכול להיות שתבחינו שקובץ בשם mnist.tflite מוריד ונשמר בספרייה assets של האפליקציה. כדי לפשט את התהליך, כבר השתמשנו במודל ידוע, MNIST, שמסווג ספרות, והוספנו אותו לאפליקציה באמצעות הסקריפט download_models.gradle בפרויקט. אם תחליטו לאמן מודל מותאם אישית משלכם, למשל מודל לזיהוי אותיות בכתב יד, תצטרכו להסיר את הקובץ download_models.gradle, למחוק את ההפניה אליו בקובץ build.gradle ברמת האפליקציה ולשנות את שם המודל בהמשך הקוד (במיוחד בקובץ DigitClassifierHelper.kt).
עדכון build.gradle
לפני שתוכלו להתחיל להשתמש ב-MediaPipe Tasks, תצטרכו לייבא את הספרייה.
- פותחים את הקובץ build.gradle שנמצא במודול app, גוללים למטה אל הבלוק dependencies.
- בתחתית הבלוק הזה אמורה להופיע התגובה // STEP 1 Dependency Import.
- מחליפים את השורה הזו בהטמעה הבאה
implementation("com.google.mediapipe:tasks-vision:latest.release")
- כדי להוריד את התלות הזו, לוחצים על הלחצן סנכרון עכשיו שמופיע בבאנר בחלק העליון של Android Studio.
3. יצירת עוזר לסיווג ספרות ב-MediaPipe Tasks
בשלב הבא, עליכם למלא את הפרטים של הכיתה שתבצע את העבודה הקשה של סיווג למידת המכונה. פותחים את הקובץ DigitClassifierHelper.kt ומתחילים.
- מחפשים את התגובה בחלק העליון של הכיתה עם הכיתוב // שלב 2: יצירת מאזין.
- מחליפים את השורה הזו בקוד הבא. הפעולה הזו תיצור מאזין שישמש להעברת תוצאות מהקלאס DigitClassifierHelper חזרה למקום שבו מתבצע האזנה לתוצאות האלה (במקרה הזה, זה יהיה הכיתה DigitCanvasFragment, אבל נגיע לשם בקרוב).
// STEP 2 Create listener
interface DigitClassifierListener {
fun onError(error: String)
fun onResults(
results: ImageClassifierResult,
inferenceTime: Long
)
}
- בנוסף, תצטרכו לקבל DigitClassifierListener כפרמטר אופציונלי למחלקה:
class DigitClassifierHelper(
val context: Context,
val digitClassifierListener: DigitClassifierListener?
) {
- יורדים לשורה // STEP 3 define classifier ומוסיפים את השורה הבאה כדי ליצור placeholder ל-ImageClassifier שישמש את האפליקציה הזו:
// שלב 3: הגדרת הסיווג
private var digitClassifier: ImageClassifier? = null
- מוסיפים את הפונקציה הבאה למקום שבו מופיע התגובה // STEP 4 set up classifier:
// STEP 4 set up classifier
private fun setupDigitClassifier() {
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath("mnist.tflite")
// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
.setRunningMode(RunningMode.IMAGE)
.setBaseOptions(baseOptionsBuilder.build())
try {
digitClassifier =
ImageClassifier.createFromOptions(
context,
optionsBuilder.build()
)
} catch (e: IllegalStateException) {
digitClassifierListener?.onError(
"Image classifier failed to initialize. See error logs for " +
"details"
)
Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
}
}
יש כמה דברים שמתרחשים בקטע שלמעלה, לכן נבחן חלקים קטנים יותר כדי להבין באמת מה קורה.
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath("mnist.tflite")
// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
.setRunningMode(RunningMode.IMAGE)
.setBaseOptions(baseOptionsBuilder.build())
בבלוק הזה יתבצעו הגדרות הפרמטרים שבהם משתמש ImageClassifier. האפשרויות האלה כוללות את המודל שנשמר באפליקציה (mnist.tflite) בקטע BaseOptions ואת RunningMode בקטע ImageClassifierOptions. במקרה הזה, האפשרות היא IMAGE, אבל אפשרויות נוספות זמינות הן VIDEO ו-LIVE_STREAM. פרמטרים זמינים אחרים הם MaxResults, שמגביל את המודל להחזרת מספר מקסימלי של תוצאות, ו-ScoreThreshold, שמגדיר את רמת האמון המינימלית שהמודל צריך לקבל בתוצאה לפני שהוא מחזיר אותה.
try {
digitClassifier =
ImageClassifier.createFromOptions(
context,
optionsBuilder.build()
)
} catch (e: IllegalStateException) {
digitClassifierListener?.onError(
"Image classifier failed to initialize. See error logs for " +
"details"
)
Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
}
אחרי שיוצרים את אפשרויות ההגדרה, אפשר ליצור את ImageClassifier החדש על ידי העברת הקשר והאפשרויות. אם משהו משתבש בתהליך האיפוס, תוחזר שגיאה דרך DigitClassifierListener.
- אנחנו רוצים לאתחל את ImageClassifier לפני שמשתמשים בו, לכן אפשר להוסיף בלוק init כדי לקרוא ל-setupDigitClassifier().
init {
setupDigitClassifier()
}
- לסיום, גוללים למטה לתגובה // STEP 5 create classify function ומוסיפים את הקוד הבא. הפונקציה הזו תקבל Bitmap, שהוא במקרה הזה הספרה שצוירה, תמיר אותו לאובייקט תמונה של MediaPipe (MPImage), ולאחר מכן תסווג את התמונה הזו באמצעות ImageClassifier, ותתעד את משך הזמן של היסק התוצאות, לפני שהיא תחזיר את התוצאות האלה דרך DigitClassifierListener.
// STEP 5 create classify function
fun classify(image: Bitmap) {
if (digitClassifier == null) {
setupDigitClassifier()
}
// Convert the input Bitmap object to an MPImage object to run inference.
// Rotating shouldn't be necessary because the text is being extracted from
// a view that should always be correctly positioned.
val mpImage = BitmapImageBuilder(image).build()
// Inference time is the difference between the system time at the start and finish of the
// process
val startTime = SystemClock.uptimeMillis()
// Run image classification using MediaPipe Image Classifier API
digitClassifier?.classify(mpImage)?.also { classificationResults ->
val inferenceTimeMs = SystemClock.uptimeMillis() - startTime
digitClassifierListener?.onResults(classificationResults, inferenceTimeMs)
}
}
זהו, סיימתם עם קובץ העזרה. בקטע הבא עליכם למלא את השלבים האחרונים כדי להתחיל לסווג את המספרים שהוגרלו.
4. הפעלת הסקת מסקנות באמצעות MediaPipe Tasks
כדי להתחיל את הקטע הזה, פותחים את הכיתה DigitCanvasFragment ב-Android Studio, שם יתבצע כל העבודה.
- בתחתית הקובץ אמור להופיע התגובה // שלב 6: הגדרת מאזין. כאן מוסיפים את הפונקציות onResults() ו-onError() שמשויכות למאזין.
// STEP 6 Set up listener
override fun onError(error: String) {
activity?.runOnUiThread {
Toast.makeText(requireActivity(), error, Toast.LENGTH_SHORT).show()
fragmentDigitCanvasBinding.tvResults.text = ""
}
}
override fun onResults(
results: ImageClassifierResult,
inferenceTime: Long
) {
activity?.runOnUiThread {
fragmentDigitCanvasBinding.tvResults.text = results
.classificationResult()
.classifications().get(0)
.categories().get(0)
.categoryName()
fragmentDigitCanvasBinding.tvInferenceTime.text = requireActivity()
.getString(R.string.inference_time, inferenceTime.toString())
}
}
הפונקציה onResults() חשובה במיוחד כי היא תציג את התוצאות שהתקבלו מ-ImageClassifier. מאחר שהקריאה החוזרת הזו מופעלת משרשור רקע, תצטרכו להריץ את עדכוני ממשק המשתמש גם בשרשור של ממשק המשתמש של Android.
- כשמוסיפים פונקציות חדשות מממשק בשלב שלמעלה, צריך גם להוסיף את הצהרת ההטמעה בחלק העליון של הכיתה.
class DigitCanvasFragment : Fragment(), DigitClassifierHelper.DigitClassifierListener
- בחלק העליון של הכיתה אמורה להופיע התגובה // STEP 7a Initialize classifier. כאן תצטרכו להציב את ההצהרה על DigitClassifierHelper.
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
- בהמשך, בקטע // STEP 7b Initialize classifier, אפשר לאתחל את digitClassifierHelper בתוך הפונקציה onViewCreated().
// STEP 7b Initialize classifier
// Initialize the digit classifier helper, which does all of the
// ML work. This uses the default values for the classifier.
digitClassifierHelper = DigitClassifierHelper(
context = requireContext(), digitClassifierListener = this
)
- בשלבים האחרונים, מחפשים את התגובה // STEP 8a*: classify* ומוסיפים את הקוד הבא כדי לקרוא לפונקציה חדשה שתוסיפו תוך רגע. בלוק הקוד הזה יפעיל את הסיווג כשמרימים את האצבע מאזור הציור באפליקציה.
// STEP 8a: classify
classifyDrawing()
- לבסוף, מחפשים את התגובה // STEP 8b classify כדי להוסיף את הפונקציה החדשה classifyDrawing(). הפונקציה הזו תגרום לחילוץ של קובץ bitmap מהלוח, ולאחר מכן להעברתו אל DigitClassifierHelper כדי לבצע סיווג ולקבל את התוצאות בפונקציית הממשק onResults().
// STEP 8b classify
private fun classifyDrawing() {
val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()
digitClassifierHelper.classify(bitmap)
}
5. פריסה ובדיקה של האפליקציה
אחרי כל זה, אמורה להיות לכם אפליקציה שפועלת ויכולה לסווג ספרות שמצוירות במסך. עכשיו אפשר לפרוס את האפליקציה ב-Android Emulator או במכשיר Android פיזי כדי לבדוק אותה.
- לוחצים על סמל ההפעלה ( ) בסרגל הכלים של Android Studio כדי להפעיל את האפליקציה.
- מציירים מספר כלשהו בלוח הציור ובודקים אם האפליקציה מזהה אותו. צריך להציג את הספרה שלדעת המודל נמשכה, וגם את משך הזמן שנדרש כדי לחזות את הספרה הזו.
6. מעולה!
כל הכבוד! ב-codelab הזה למדתם איך להוסיף סיווג תמונות לאפליקציה ל-Android, ובמיוחד איך לסווג ספרות שמצוירות ביד באמצעות מודל MNIST.
השלבים הבאים
- עכשיו, אחרי שאפשר לסווג ספרות, כדאי לאמן מודל משלכם כדי לסווג אותיות שמצוירות, או כדי לסווג בעלי חיים או מספר אינסופי של פריטים אחרים. המסמכים בנושא אימון מודל חדש לסיווג תמונות באמצעות MediaPipe Model Maker זמינים בדף developers.google.com/mediapipe.
- מידע נוסף על משימות MediaPipe שזמינות ל-Android, כולל זיהוי נקודות ציון בפנים, זיהוי תנועות וסיווג אודיו.
אנחנו מחכים לראות את הדברים המגניבים שייצרת.