1. מבוא
מה זה MediaPipe?
פתרונות MediaPipe מאפשרים לכם להחיל פתרונות של למידת מכונה (ML) על האפליקציות שלכם. היא מספקת מסגרת להגדרת צינורות עיבוד מוכנים מראש שמספקים למשתמשים פלט מיידי, מעניין ושימושי. אפשר אפילו להתאים אישית את הפתרונות האלה באמצעות MediaPipe Model Maker כדי לעדכן את מודלי ברירת המחדל.
סיווג תמונות הוא אחת מכמה משימות ראייה ממוחשבת (CV) של ML שפתרונות MediaPipe מציעים. MediaPipe Tasks זמין ל-Android, ל-iOS, ל-Python (כולל Raspberry Pi!) ולאינטרנט.
ב-Codelab הזה תתחילו עם אפליקציית Android שמאפשרת לכם לצייר ספרות מספריות על המסך, ואז תוסיפו פונקציונליות שמסווגת את הספרות המצוירות כערך יחיד מ-0 עד 9.
מה תלמדו
- איך משלבים משימת סיווג תמונות באפליקציית Android באמצעות MediaPipe Tasks.
הדרישות
- גרסה מותקנת של Android Studio (ה-Codelab הזה נכתב ונבדק באמצעות Android Studio Giraffe).
- מכשיר Android או אמולטור להרצת האפליקציה.
- ידע בסיסי בפיתוח ל-Android (זה לא "Hello World", אבל זה לא רחוק מזה!).
2. הוספת MediaPipe Tasks לאפליקציית Android
הורדת אפליקציה לתחילת הדרך ל-Android
ב-codelab הזה נתחיל עם דוגמה מוכנה מראש שמאפשרת לכם לצייר על המסך. אפשר למצוא את אפליקציית ההתחלה במאגר הדוגמאות הרשמי של MediaPipe כאן. משכפלים את המאגר או מורידים את קובץ ה-ZIP על ידי לחיצה על Code (קוד) > Download ZIP (הורדת קובץ ZIP).
ייבוא האפליקציה ל-Android Studio
- פותחים את Android Studio.
- במסך Welcome to Android Studio (ברוכים הבאים ל-Android Studio), לוחצים על Open (פתיחה) בפינה השמאלית העליונה.

- עוברים למיקום שבו שיבטתם או הורדתם את המאגר ופותחים את codelabs/digitclassifier/android/start directory.
- כדי לוודא שהכול נפתח כמו שצריך, לוחצים על החץ הירוק הפעלה (
) בפינה השמאלית העליונה של 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")
- לוחצים על הלחצן Sync Now (סנכרון עכשיו) שמופיע בבאנר בחלק העליון של Android Studio כדי להוריד את התלות הזו.
3. יצירת כלי עזר לסיווג ספרות באמצעות MediaPipe Tasks
בשלב הבא, ממלאים כיתה שתבצע את העבודה הכבדה של סיווג הלמידה החישובית. פותחים את DigitClassifierHelper.kt ומתחילים!
- מוצאים את התגובה בחלק העליון של הכיתה עם הכיתוב // STEP 2 Create listener
- מחליפים את השורה הזו בקוד הבא. הפעולה הזו תיצור מאזין שישמש להעברת תוצאות מהמחלקה 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 שישמש את האפליקציה הזו:
// STEP 3 define classifier
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 Image (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, שבה יתבצעו כל הפעולות.
- בתחתית הקובץ אמורה להופיע התגובה // STEP 6 Set up listener. כאן מוסיפים את הפונקציות onResults() ו-onError() שמשויכות ל-listener.
// 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. מכיוון שהקריאה החוזרת הזו מופעלת משרשור ברקע, תצטרכו גם להפעיל את עדכוני ממשק המשתמש בשרשור UI של 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(). הפעולה הזו תחלץ מפת סיביות מבד הציור, ואז תעביר אותה אל DigitClassifierHelper כדי לבצע סיווג ולקבל את התוצאות בפונקציית הממשק onResults().
// STEP 8b classify
private fun classifyDrawing() {
val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()
digitClassifierHelper.classify(bitmap)
}
5. פריסה ובדיקה של האפליקציה
אחרי כל הפעולות האלה, אמורה להיות לכם אפליקציה עובדת שיכולה לסווג ספרות שמציירים על המסך. כדי לבדוק את האפליקציה, אפשר לפרוס אותה באמולטור Android או במכשיר Android פיזי.
- לוחצים על Run (הפעלה) (
) בסרגל הכלים של Android Studio כדי להפעיל את האפליקציה. - מציירים ספרה בלוח הציור ובודקים אם האפליקציה מזהה אותה. הוא צריך להציג את הספרה שהמודל חושב שצוירה, וגם את הזמן שלקח לחזות את הספרה הזו.

6. מעולה!
כל הכבוד! ב-Codelab הזה למדתם איך להוסיף סיווג תמונות לאפליקציית Android, ובמיוחד איך לסווג ספרות מצוירות ביד באמצעות מודל MNIST.
השלבים הבאים
- אחרי שלמדתם איך לסווג ספרות, יכול להיות שתרצו לאמן מודל משלכם לסיווג של אותיות מצוירות, או לסיווג של בעלי חיים, או לסיווג של מספר אינסופי של פריטים אחרים. בכתובת developers.google.com/mediapipe אפשר למצוא את המסמכים להדרכת מודל חדש לסיווג תמונות באמצעות MediaPipe Model Maker.
- מידע נוסף על MediaPipe Tasks אחרים שזמינים ל-Android, כולל זיהוי נקודות ציון בפנים, זיהוי תנועות וסיווג אודיו.
אנחנו כבר סקרנים לראות את כל הדברים המגניבים שתצרו!