ML Kit מספק שתי ערכות SDK שעברו אופטימיזציה לזיהוי תנוחה.
שם ה-SDK | זיהוי תנוחת החזה | זיהוי מדויק של התנוחה |
---|---|---|
הטמעה | הקוד והנכסים מקושרים לאפליקציה באופן סטטי בזמן ה-build. | הקוד והנכסים מקושרים לאפליקציה באופן סטטי בזמן ה-build. |
ההשפעה של גודל האפליקציה (כולל קוד ונכסים) | כ-10.1MB | כ-13.3MB |
ביצועים | Pixel 3XL: כ-30FPS | Pixel 3XL: כ-23 FPS עם מעבד (CPU) ו-30 FPS עם GPU |
אני רוצה לנסות
- מומלץ להתנסות באפליקציה לדוגמה כדי לראות דוגמה לשימוש ב-API הזה.
לפני שמתחילים
- בקובץ
build.gradle
ברמת הפרויקט, חשוב לכלול את מאגר Maven של Google בקטעbuildscript
וגם בקטעallprojects
. מוסיפים את יחסי התלות של ספריות ML Kit Android לקובץ GRid ברמת האפליקציה של המודול, שהוא בדרך כלל
app/build.gradle
:dependencies { // If you want to use the base sdk implementation 'com.google.mlkit:pose-detection:18.0.0-beta4' // If you want to use the accurate sdk implementation 'com.google.mlkit:pose-detection-accurate:18.0.0-beta4' }
1. יצירת מכונה של PoseDetector
PoseDetector
אפשרויות
כדי לזהות תנוחה בתמונה, קודם יוצרים מופע של PoseDetector
, ואפשר גם לציין את הגדרות המזהה.
מצב זיהוי
המכשיר PoseDetector
פועל בשני מצבי זיהוי. הקפידו לבחור בתרחיש שתואם לתרחיש לדוגמה שלכם.
-
STREAM_MODE
(ברירת מחדל) - מזהה התנוחה יזהה קודם את האדם הבולט ביותר בתמונה, ואז יזהה את התנוחה. במסגרות הבאות, השלב של זיהוי האדם לא יתבצע, אלא אם האדם לא יסתיר אותו או יזוהה ברמת מהימנות גבוהה יותר. גלאי התנוחה ינסה לעקוב אחר האדם הבולט ביותר ולהחזיר את התנוחה שלו בכל מסקנות. כך אפשר לקצר את זמן האחזור ולהחלק את הזיהוי. השתמשו במצב הזה אם רוצים לזהות פוזה במהלך הווידאו בסטרימינג.
SINGLE_IMAGE_MODE
- גלאי התנוחה יזהה אדם כלשהו ולאחר מכן יריץ זיהוי תנוחה. השלב של זיהוי אדם יפעל בכל תמונה, כך שזמן האחזור יהיה ארוך יותר ואין מעקב אחרי אנשים. משתמשים במצב הזה כשמשתמשים בזיהוי תנוחה בתמונות סטטיות, או כשלא רוצים לעקוב אחריו.
הגדרת החומרה
ה-PoseDetector
תומך במספר הגדרות חומרה כדי לשפר את הביצועים:
CPU
: הפעלת הגלאי באמצעות מעבד (CPU) בלבדCPU_GPU
: הפעלת הגלאי באמצעות מעבד (CPU) ו-GPU
כשמפתחים את אפשרויות המזהה, אפשר להשתמש ב-API setPreferredHardwareConfigs
כדי לשלוט בבחירת החומרה. כברירת מחדל, כל הגדרות החומרה נקבעות כמועדפות.
ערכת ML Kit תביא בחשבון את הזמינות, היציבות, הדיוק וזמן האחזור של כל הגדרה, ותבחר את ההגדרה הטובה ביותר מבין התצורות המועדפות. אם אף אחת מההגדרות המועדפות לא רלוונטית, ההגדרה CPU
תשמש באופן אוטומטי כחלופה. ML Kit יבצע את הבדיקות האלה ואת ההכנות שקשורות אליו ללא חסימה לפני הפעלת האצה, ולכן סביר להניח שזו הפעם הראשונה שהמשתמש יפעיל את הגלאי, הוא ישתמש ב-CPU
. בסיום כל ההכנהים נשתמש בהגדרה הטובה ביותר בהפעלות הבאות.
שימושים לדוגמה ב-setPreferredHardwareConfigs
:
- כדי לאפשר ל-ML Kit לבחור את ההגדרה הטובה ביותר, אין לקרוא ל-API הזה.
- אם אינך רוצה להפעיל האצה כלשהי, עליך להעביר רק את
CPU
. - אם רוצים להשתמש ב-GPU כדי להפחית את העומס על המעבד (CPU), גם אם ה-GPU עשוי להיות איטי יותר, צריך להעביר רק ב-
CPU_GPU
.
הגדרת האפשרויות של מזהה התנוחה:
Kotlin
// Base pose detector with streaming frames, when depending on the pose-detection sdk val options = PoseDetectorOptions.Builder() .setDetectorMode(PoseDetectorOptions.STREAM_MODE) .build() // Accurate pose detector on static images, when depending on the pose-detection-accurate sdk val options = AccuratePoseDetectorOptions.Builder() .setDetectorMode(AccuratePoseDetectorOptions.SINGLE_IMAGE_MODE) .build()
Java
// Base pose detector with streaming frames, when depending on the pose-detection sdk PoseDetectorOptions options = new PoseDetectorOptions.Builder() .setDetectorMode(PoseDetectorOptions.STREAM_MODE) .build(); // Accurate pose detector on static images, when depending on the pose-detection-accurate sdk AccuratePoseDetectorOptions options = new AccuratePoseDetectorOptions.Builder() .setDetectorMode(AccuratePoseDetectorOptions.SINGLE_IMAGE_MODE) .build();
לבסוף, יוצרים מופע של PoseDetector
. מעבירים את האפשרויות שציינתם:
Kotlin
val poseDetector = PoseDetection.getClient(options)
Java
PoseDetector poseDetector = PoseDetection.getClient(options);
2. הכנת תמונת הקלט
כדי לזהות תנוחות בתמונה, צריך ליצור אובייקט InputImage
מ-Bitmap
, media.Image
, ByteBuffer
, ממערך בייטים או מקובץ במכשיר. לאחר מכן מעבירים את האובייקט InputImage
ל-PoseDetector
.
לזיהוי תנוחה, צריך להשתמש בתמונה במידות של 480x360 פיקסלים לפחות. אם אתם מזהים תנוחות בזמן אמת, צילום הפריימים ברזולוציה המינימלית הזו יכול לצמצם את זמן האחזור.
אפשר ליצור אובייקט InputImage
ממקורות שונים, כפי שמוסבר בהמשך.
באמצעות media.Image
כדי ליצור אובייקט InputImage
מאובייקט media.Image
, למשל כשמצלמים תמונה ממצלמה של מכשיר, מעבירים את האובייקט media.Image
ואת הסיבוב של התמונה אל InputImage.fromMediaImage()
.
אם משתמשים בספריית
CameraX, המחלקות OnImageCapturedListener
ו-ImageAnalysis.Analyzer
תחשבות את ערך הסיבוב
בשבילכם.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
אם לא משתמשים בספריית מצלמה שמאפשרת לקבוע את זווית הסיבוב של התמונה, אפשר לחשב אותה לפי זווית הסיבוב של המכשיר והכיוון של חיישן המצלמה:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
לאחר מכן, מעבירים את האובייקט media.Image
ואת ערך מידת הסיבוב ל-InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
שימוש ב-URI של קובץ
כדי ליצור אובייקט InputImage
מ-URI של קובץ, מעבירים את ההקשר של האפליקציה ואת ה-URI של הקובץ ל-InputImage.fromFilePath()
. האפשרות הזו שימושית כשמשתמשים
ב-Intent של ACTION_GET_CONTENT
כדי לבקש מהמשתמש לבחור
תמונה מאפליקציית הגלריה.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
שימוש ב-ByteBuffer
או ב-ByteArray
כדי ליצור אובייקט InputImage
מ-ByteBuffer
או מ-ByteArray
, קודם צריך לחשב את מידת הסיבוב של התמונה כפי שתואר קודם לכן לקלט media.Image
.
לאחר מכן, יוצרים את האובייקט InputImage
עם מאגר הנתונים הזמני או המערך, יחד עם הגובה, הרוחב, פורמט קידוד הצבע ורמת הסיבוב של התמונה:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
באמצעות Bitmap
כדי ליצור אובייקט InputImage
מאובייקט Bitmap
, צריך להשתמש בהצהרה הבאה:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
התמונה מיוצגת על ידי אובייקט Bitmap
ביחד עם מעלות סיבוב.
3. עיבוד התמונה
מעבירים את האובייקט InputImage
המוכן ל-method process
של PoseDetector
.
Kotlin
Task<Pose> result = poseDetector.process(image) .addOnSuccessListener { results -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
Task<Pose> result = poseDetector.process(image) .addOnSuccessListener( new OnSuccessListener<Pose>() { @Override public void onSuccess(Pose pose) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. קבלת מידע על התנוחה שזוהתה
אם מזוהה אדם בתמונה, ה-API לזיהוי התנוחה מחזיר אובייקט Pose
עם 33 נקודות PoseLandmark
.
אם האדם לא היה בתוך התמונה, המודל מקצה לציוני הדרך החסרים את הקואורדינטות החסרות מחוץ למסגרת, ונותן להם ערכים נמוכים של ודאות InFrame.
אם לא זוהה אדם במסגרת, האובייקט Pose
לא מכיל אובייקטים מסוג PoseLandmark
.
Kotlin
// Get all PoseLandmarks. If no person was detected, the list will be empty val allPoseLandmarks = pose.getAllPoseLandmarks() // Or get specific PoseLandmarks individually. These will all be null if no person // was detected val leftShoulder = pose.getPoseLandmark(PoseLandmark.LEFT_SHOULDER) val rightShoulder = pose.getPoseLandmark(PoseLandmark.RIGHT_SHOULDER) val leftElbow = pose.getPoseLandmark(PoseLandmark.LEFT_ELBOW) val rightElbow = pose.getPoseLandmark(PoseLandmark.RIGHT_ELBOW) val leftWrist = pose.getPoseLandmark(PoseLandmark.LEFT_WRIST) val rightWrist = pose.getPoseLandmark(PoseLandmark.RIGHT_WRIST) val leftHip = pose.getPoseLandmark(PoseLandmark.LEFT_HIP) val rightHip = pose.getPoseLandmark(PoseLandmark.RIGHT_HIP) val leftKnee = pose.getPoseLandmark(PoseLandmark.LEFT_KNEE) val rightKnee = pose.getPoseLandmark(PoseLandmark.RIGHT_KNEE) val leftAnkle = pose.getPoseLandmark(PoseLandmark.LEFT_ANKLE) val rightAnkle = pose.getPoseLandmark(PoseLandmark.RIGHT_ANKLE) val leftPinky = pose.getPoseLandmark(PoseLandmark.LEFT_PINKY) val rightPinky = pose.getPoseLandmark(PoseLandmark.RIGHT_PINKY) val leftIndex = pose.getPoseLandmark(PoseLandmark.LEFT_INDEX) val rightIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_INDEX) val leftThumb = pose.getPoseLandmark(PoseLandmark.LEFT_THUMB) val rightThumb = pose.getPoseLandmark(PoseLandmark.RIGHT_THUMB) val leftHeel = pose.getPoseLandmark(PoseLandmark.LEFT_HEEL) val rightHeel = pose.getPoseLandmark(PoseLandmark.RIGHT_HEEL) val leftFootIndex = pose.getPoseLandmark(PoseLandmark.LEFT_FOOT_INDEX) val rightFootIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_FOOT_INDEX) val nose = pose.getPoseLandmark(PoseLandmark.NOSE) val leftEyeInner = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_INNER) val leftEye = pose.getPoseLandmark(PoseLandmark.LEFT_EYE) val leftEyeOuter = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_OUTER) val rightEyeInner = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_INNER) val rightEye = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE) val rightEyeOuter = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_OUTER) val leftEar = pose.getPoseLandmark(PoseLandmark.LEFT_EAR) val rightEar = pose.getPoseLandmark(PoseLandmark.RIGHT_EAR) val leftMouth = pose.getPoseLandmark(PoseLandmark.LEFT_MOUTH) val rightMouth = pose.getPoseLandmark(PoseLandmark.RIGHT_MOUTH)
Java
// Get all PoseLandmarks. If no person was detected, the list will be empty List<PoseLandmark> allPoseLandmarks = pose.getAllPoseLandmarks(); // Or get specific PoseLandmarks individually. These will all be null if no person // was detected PoseLandmark leftShoulder = pose.getPoseLandmark(PoseLandmark.LEFT_SHOULDER); PoseLandmark rightShoulder = pose.getPoseLandmark(PoseLandmark.RIGHT_SHOULDER); PoseLandmark leftElbow = pose.getPoseLandmark(PoseLandmark.LEFT_ELBOW); PoseLandmark rightElbow = pose.getPoseLandmark(PoseLandmark.RIGHT_ELBOW); PoseLandmark leftWrist = pose.getPoseLandmark(PoseLandmark.LEFT_WRIST); PoseLandmark rightWrist = pose.getPoseLandmark(PoseLandmark.RIGHT_WRIST); PoseLandmark leftHip = pose.getPoseLandmark(PoseLandmark.LEFT_HIP); PoseLandmark rightHip = pose.getPoseLandmark(PoseLandmark.RIGHT_HIP); PoseLandmark leftKnee = pose.getPoseLandmark(PoseLandmark.LEFT_KNEE); PoseLandmark rightKnee = pose.getPoseLandmark(PoseLandmark.RIGHT_KNEE); PoseLandmark leftAnkle = pose.getPoseLandmark(PoseLandmark.LEFT_ANKLE); PoseLandmark rightAnkle = pose.getPoseLandmark(PoseLandmark.RIGHT_ANKLE); PoseLandmark leftPinky = pose.getPoseLandmark(PoseLandmark.LEFT_PINKY); PoseLandmark rightPinky = pose.getPoseLandmark(PoseLandmark.RIGHT_PINKY); PoseLandmark leftIndex = pose.getPoseLandmark(PoseLandmark.LEFT_INDEX); PoseLandmark rightIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_INDEX); PoseLandmark leftThumb = pose.getPoseLandmark(PoseLandmark.LEFT_THUMB); PoseLandmark rightThumb = pose.getPoseLandmark(PoseLandmark.RIGHT_THUMB); PoseLandmark leftHeel = pose.getPoseLandmark(PoseLandmark.LEFT_HEEL); PoseLandmark rightHeel = pose.getPoseLandmark(PoseLandmark.RIGHT_HEEL); PoseLandmark leftFootIndex = pose.getPoseLandmark(PoseLandmark.LEFT_FOOT_INDEX); PoseLandmark rightFootIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_FOOT_INDEX); PoseLandmark nose = pose.getPoseLandmark(PoseLandmark.NOSE); PoseLandmark leftEyeInner = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_INNER); PoseLandmark leftEye = pose.getPoseLandmark(PoseLandmark.LEFT_EYE); PoseLandmark leftEyeOuter = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_OUTER); PoseLandmark rightEyeInner = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_INNER); PoseLandmark rightEye = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE); PoseLandmark rightEyeOuter = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_OUTER); PoseLandmark leftEar = pose.getPoseLandmark(PoseLandmark.LEFT_EAR); PoseLandmark rightEar = pose.getPoseLandmark(PoseLandmark.RIGHT_EAR); PoseLandmark leftMouth = pose.getPoseLandmark(PoseLandmark.LEFT_MOUTH); PoseLandmark rightMouth = pose.getPoseLandmark(PoseLandmark.RIGHT_MOUTH);
טיפים לשיפור הביצועים
איכות התוצאות תלויה באיכות של תמונת הקלט:
- כדי ש-ML Kit יזהה במדויק את התנוחה, האדם בתמונה צריך להיות מיוצג על ידי מספיק נתוני פיקסלים. כדי להשיג את הביצועים הטובים ביותר, נושא התמונה צריך להיות לפחות 256x256 פיקסלים.
- אם אתם מזהים תנוחה באפליקציה בזמן אמת, כדאי גם לקחת בחשבון את המידות הכוללות של תמונות הקלט. אפשר לעבד תמונות קטנות יותר מהר יותר. לכן, כדי לצמצם את זמן האחזור, לצלם תמונות ברזולוציות נמוכות יותר, אבל חשוב לשים לב לדרישות הרזולוציה שצוינו למעלה ולוודא שהנושא תופס מקום רב ככל האפשר מהתמונה.
- גם מיקוד תמונה לא טוב יכול להשפיע על רמת הדיוק. אם לא מתקבלות תוצאות מקובלות, בקשו מהמשתמש לצלם מחדש את התמונה.
אם אתם רוצים להשתמש בזיהוי תנוחה באפליקציה בזמן אמת, צריך לפעול לפי ההנחיות הבאות כדי להשיג את קצב הפריימים הטוב ביותר:
- יש להשתמש ב-SDK לזיהוי תנוחת הבסיס וב-
STREAM_MODE
. - כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב לזכור גם את הדרישות בנוגע למידות תמונה ב-API הזה.
- אם משתמשים ב-API של
Camera
אוcamera2
, צריך לווסת את הקריאות למזהה. אם פריים חדש בווידאו הופך לזמין בזמן שהגלאי פועל, משחררים את הפריים. לדוגמה, תוכלו לעיין בשיעורVisionProcessorBase
באפליקציה לדוגמה של המדריך למתחילים. - אם משתמשים ב-API של
CameraX
, צריך לוודא שאסטרטגיית לחיצה לאחור מוגדרת לערך ברירת המחדל שלהImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. כך אפשר להבטיח שרק תמונה אחת תוצג לניתוח בכל פעם. אם יופקו עוד תמונות כשהכלי לניתוח נתונים עמוס, הן יוסרו באופן אוטומטי ולא ימתינו לתור למשלוח. לאחר סגירת התמונה שמנתחת על ידי קריאה ל-ImageProxy.close(), מתקבלת התמונה העדכנית ביותר. - אם משתמשים בפלט של הגלאי כדי ליצור שכבת-על של גרפיקה בתמונת הקלט, מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה ושכבת-העל בפעולה אחת. הרינדור של משטח המסך מתבצע פעם אחת בלבד לכל מסגרת קלט. לדוגמה, אפשר לעיין בכיתות
CameraSourcePreview
ו-GraphicOverlay
באפליקציה לדוגמה של המדריך למתחילים. - אם משתמשים ב- Camera2 API, צריך לצלם תמונות
בפורמט
ImageFormat.YUV_420_888
. אם משתמשים בגרסה הקודמת של Camera API, צריך לצלם תמונות בפורמטImageFormat.NV21
.
השלבים הבאים
- כדי ללמוד איך להשתמש בציוני דרך שמבוססים על ציוני דרך שמבוססים על תנוחות, כדאי לעיין בטיפים לסיווג פוזה.