คู่มือการตรวจหาจุดสังเกตของมือสำหรับ Android

งาน MediaPipe Hand Outdoorer จะช่วยให้คุณตรวจจับจุดสังเกตของมือในรูปภาพได้ คำแนะนำเหล่านี้จะแสดงวิธีใช้ Handจุดสังเกตกับแอป Android ตัวอย่างโค้ดที่อธิบายไว้ในวิธีการเหล่านี้จะอยู่ใน GitHub

ดูข้อมูลเพิ่มเติมเกี่ยวกับความสามารถ โมเดล และตัวเลือกการกำหนดค่าของงานนี้ได้ที่ภาพรวม

ตัวอย่างโค้ด

โค้ดตัวอย่างงาน MediaPipe Tasks เป็นการติดตั้งใช้งานแอป Handจุดสังเกตสำหรับ Android แบบง่ายๆ ตัวอย่างนี้ใช้กล้องบนอุปกรณ์ Android จริงเพื่อตรวจจับจุดสังเกตของมืออย่างต่อเนื่อง และยังใช้รูปภาพและวิดีโอจากแกลเลอรีอุปกรณ์เพื่อตรวจจับจุดสังเกตของมือแบบคงที่ได้ด้วย

คุณสามารถใช้แอปนี้เป็นจุดเริ่มต้นสำหรับแอป Android ของคุณเองหรือใช้อ้างอิงเมื่อแก้ไขแอปที่มีอยู่ โค้ดตัวอย่างของ Hand Marker นั้นโฮสต์อยู่ใน GitHub

ดาวน์โหลดโค้ด

วิธีการต่อไปนี้แสดงวิธีสร้างสำเนาโค้ดตัวอย่างในเครื่องโดยใช้เครื่องมือบรรทัดคำสั่ง git

วิธีดาวน์โหลดโค้ดตัวอย่าง

  1. โคลนที่เก็บ Git โดยใช้คำสั่งต่อไปนี้
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. (ไม่บังคับ) กำหนดค่าอินสแตนซ์ Git เพื่อใช้การชำระเงินแบบกะทัดรัด เพื่อให้คุณมีเฉพาะไฟล์สำหรับแอปตัวอย่าง Handจุดสังเกต ดังนี้
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/hand_landmarker/android
    

หลังจากสร้างโค้ดตัวอย่างเวอร์ชันในเครื่องแล้ว คุณจะนำเข้าโปรเจ็กต์ไปยัง Android Studio และเรียกใช้แอปได้ โปรดดูวิธีการที่หัวข้อคู่มือการตั้งค่าสำหรับ Android

องค์ประกอบหลัก

ไฟล์ต่อไปนี้มีโค้ดที่สำคัญสำหรับแอปพลิเคชันตัวอย่าง การตรวจหาจุดสังเกตนี้ด้วย

  • HandLandmarkerHelper.kt - เริ่มใช้ตัวตรวจจับจุดสังเกตของมือ และจัดการโมเดลและมอบสิทธิ์การเลือก
  • MainActivity.kt - ใช้งานแอปพลิเคชัน รวมถึงการเรียกใช้ HandLandmarkerHelper

ตั้งค่า

ส่วนนี้จะอธิบายขั้นตอนสำคัญในการตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์และโปรเจ็กต์โค้ดเพื่อใช้ Handจุดสังเกตโดยเฉพาะ ดูข้อมูลทั่วไปเกี่ยวกับการตั้งค่าสภาพแวดล้อมการพัฒนาเพื่อใช้งาน MediaPipe รวมถึงข้อกำหนดเวอร์ชันแพลตฟอร์มได้ที่คำแนะนำการตั้งค่าสำหรับ Android

การอ้างอิง

งาน Hand Marker จะใช้ไลบรารี com.google.mediapipe:tasks-vision เพิ่มทรัพยากร Dependency นี้ไปยังไฟล์ build.gradle ของแอป Android:

dependencies {
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
}

รุ่น

งาน MediaPipe Hand Marker ต้องมีแพ็กเกจโมเดลที่ผ่านการฝึกแล้ว ซึ่งเข้ากันได้กับงานนี้ ดูข้อมูลเพิ่มเติมเกี่ยวกับโมเดลที่ผ่านการฝึกแล้วสำหรับ Handจุดสังเกตได้ที่ภาพรวมงานส่วนโมเดล

เลือกและดาวน์โหลดโมเดล แล้วจัดเก็บไว้ในไดเรกทอรีโปรเจ็กต์ของคุณ ดังนี้

<dev-project-root>/src/main/assets

ระบุเส้นทางของโมเดลภายในพารามิเตอร์ ModelAssetPath ในโค้ดตัวอย่าง โมเดลจะได้รับการกำหนดในไฟล์ HandLandmarkerHelper.kt ดังนี้

baseOptionBuilder.setModelAssetPath(MP_HAND_LANDMARKER_TASK)

สร้างงาน

งาน MediaPipe Hand Marker ใช้ฟังก์ชัน createFromOptions() เพื่อตั้งค่างาน ฟังก์ชัน createFromOptions() จะยอมรับค่าสำหรับตัวเลือกการกำหนดค่า ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกการกำหนดค่าได้ที่ตัวเลือกการกำหนดค่า

Hand Lander รองรับข้อมูลอินพุต 3 ประเภท ได้แก่ ภาพนิ่ง ไฟล์วิดีโอ และสตรีมแบบสด คุณต้องระบุโหมดที่ทำงานตามประเภทข้อมูลอินพุตเมื่อสร้างงาน เลือกแท็บที่ตรงกับประเภทข้อมูลอินพุตของคุณเพื่อดูวิธีสร้างงานและใช้การอนุมาน

รูปภาพ

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

วิดีโอ

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

สตรีมแบบสด

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

การใช้โค้ดตัวอย่าง Hand Marker ช่วยให้ผู้ใช้เปลี่ยนโหมดการประมวลผลได้ วิธีนี้ทำให้โค้ดการสร้างงานซับซ้อนขึ้นและอาจไม่เหมาะกับกรณีการใช้งานของคุณ คุณดูโค้ดนี้ได้ในฟังก์ชัน setupHandLandmarker() ในไฟล์ HandLandmarkerHelper.kt

ตัวเลือกการกำหนดค่า

งานมีตัวเลือกการกำหนดค่าต่อไปนี้สำหรับแอป Android

ชื่อตัวเลือก คำอธิบาย ช่วงของค่า ค่าเริ่มต้น
runningMode ตั้งค่าโหมดการทำงาน มี 3 โหมดดังนี้

IMAGE: โหมดสำหรับการป้อนข้อมูลรูปภาพเดียว

วิดีโอ: โหมดสำหรับเฟรมที่ถอดรหัสของวิดีโอ

LIVE_Stream: โหมดสำหรับสตรีมแบบสดของข้อมูลอินพุต เช่น จากกล้อง ในโหมดนี้ ต้องมีการเรียกใช้ resultsListener เพื่อตั้งค่า Listener เพื่อรับผลลัพธ์แบบไม่พร้อมกัน
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numHands จำนวนมือสูงสุดที่เครื่องมือตรวจจับจุดสังเกตของมือตรวจพบ Any integer > 0 1
minHandDetectionConfidence คะแนนความเชื่อมั่นขั้นต่ำสำหรับการตรวจจับมือที่จะถือว่าประสบความสำเร็จในโมเดลการตรวจจับฝ่ามือ 0.0 - 1.0 0.5
minHandPresenceConfidence คะแนนความเชื่อมั่นขั้นต่ำสำหรับคะแนนการแสดงตัวของมือในโมเดลการตรวจจับจุดสังเกตของมือ ในโหมดวิดีโอและโหมดสตรีมแบบสด หากคะแนนความเชื่อมั่นของมือจากโมเดลจุดสังเกตของมืออยู่ต่ำกว่าเกณฑ์นี้ จุดสังเกตของมือจะทริกเกอร์โมเดลการตรวจจับฝ่ามือ หากไม่เป็นเช่นนั้น อัลกอริทึมการติดตามมือน้ำหนักเบาจะเป็นตัวกำหนดตำแหน่งของมือสำหรับการตรวจจับจุดสังเกตที่ตามมา 0.0 - 1.0 0.5
minTrackingConfidence คะแนนความเชื่อมั่นขั้นต่ำสำหรับการติดตามมือจะถือว่าประสบความสำเร็จ นี่คือเกณฑ์ IoU ของกรอบล้อมรอบระหว่างมือในเฟรมปัจจุบันและเฟรมสุดท้าย ในโหมดวิดีโอและโหมดสตรีมของมือจุดสังเกต หากติดตามไม่สำเร็จ จุดสังเกตของมือจะทริกเกอร์การตรวจจับมือ มิฉะนั้นระบบจะข้ามการตรวจจับมือ 0.0 - 1.0 0.5
resultListener ตั้งค่า Listener ผลลัพธ์ให้รับผลการตรวจจับแบบอะซิงโครนัสเมื่อตัวทำเครื่องหมายสำหรับมืออยู่ในโหมดสตรีมแบบสด ใช้ได้เมื่อตั้งค่าโหมดทำงานเป็น LIVE_STREAM เท่านั้น ไม่มีข้อมูล ไม่มีข้อมูล
errorListener ตั้งค่า Listener ข้อผิดพลาดที่ไม่บังคับ ไม่มีข้อมูล ไม่มีข้อมูล

เตรียมข้อมูล

Hand Lander ใช้งานได้กับรูปภาพ ไฟล์วิดีโอ และวิดีโอสตรีมแบบสด งานนี้จะจัดการกับการประมวลผลอินพุตข้อมูลล่วงหน้า ซึ่งรวมถึงการปรับขนาด การหมุน และการปรับค่าให้เป็นมาตรฐาน

โค้ดต่อไปนี้แสดงวิธีส่งต่อข้อมูลเพื่อการประมวลผล ตัวอย่างดังกล่าวจะมีรายละเอียดเกี่ยวกับวิธีจัดการข้อมูลจากรูปภาพ ไฟล์วิดีโอ และสตรีมวิดีโอสด

รูปภาพ

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(image).build()
    

วิดีโอ

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

val argb8888Frame =
    if (frame.config == Bitmap.Config.ARGB_8888) frame
    else frame.copy(Bitmap.Config.ARGB_8888, false)

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(argb8888Frame).build()
    

สตรีมแบบสด

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(rotatedBitmap).build()
    

ในโค้ดตัวอย่าง Handจุดสังเกต ระบบจะจัดการการเตรียมข้อมูลในไฟล์ HandLandmarkerHelper.kt

เรียกใช้งาน

ใช้เมธอด HandLandmarker.detect...() ที่เจาะจงสำหรับประเภทข้อมูลนั้น ทั้งนี้ขึ้นอยู่กับประเภทข้อมูลที่ใช้งานอยู่ ใช้ detect() สำหรับรูปภาพแต่ละรูป detectForVideo() สำหรับเฟรมในไฟล์วิดีโอ และ detectAsync() สำหรับสตรีมวิดีโอ เมื่อตรวจหาสตรีมวิดีโอ ให้เรียกใช้การตรวจจับในชุดข้อความแยกต่างหากเพื่อหลีกเลี่ยงการบล็อกชุดข้อความของอินเทอร์เฟซผู้ใช้

ตัวอย่างโค้ดต่อไปนี้แสดงตัวอย่างง่ายๆ ของวิธีเรียกใช้จุดสังเกตด้วยมือในโหมดข้อมูลที่แตกต่างกัน

รูปภาพ

val result = handLandmarker?.detect(mpImage)
    

วิดีโอ

val timestampMs = i * inferenceIntervalMs

handLandmarker?.detectForVideo(mpImage, timestampMs)
    ?.let { detectionResult ->
        resultList.add(detectionResult)
    }
    

สตรีมแบบสด

val mpImage = BitmapImageBuilder(rotatedBitmap).build()
val frameTime = SystemClock.uptimeMillis()

handLandmarker?.detectAsync(mpImage, frameTime)
    

โปรดทราบดังต่อไปนี้

  • เมื่อทำงานในโหมดวิดีโอหรือโหมดสตรีมแบบสด คุณต้องระบุการประทับเวลาของเฟรมอินพุตในงานมือจุดสังเกตด้วย
  • เมื่อเรียกใช้ในรูปภาพหรือวิดีโอ งาน "มือจุดสังเกต" จะบล็อกชุดข้อความปัจจุบันจนกว่าจะประมวลผลภาพหรือเฟรมอินพุตเสร็จ เพื่อหลีกเลี่ยงการบล็อกอินเทอร์เฟซผู้ใช้ ให้เรียกใช้การประมวลผลในชุดข้อความในเบื้องหลัง
  • เมื่อทำงานในโหมดสตรีมแบบสด งาน Handจุดสังเกตจะไม่บล็อกเทรดปัจจุบัน แต่จะแสดงผลทันที โดยจะเรียกใช้ Listener ผลลัพธ์พร้อมกับผลการตรวจจับทุกครั้งที่ประมวลผลเฟรมอินพุตเสร็จ หากมีการเรียกฟังก์ชันการตรวจจับเมื่องานมือจุดสังเกตกำลังยุ่งอยู่กับการประมวลผลเฟรมอื่น งานนั้นจะไม่สนใจเฟรมอินพุตใหม่

ในโค้ดตัวอย่าง Handจุดสังเกต จะมีการกำหนดฟังก์ชัน detect, detectForVideo และ detectAsync ไว้ในไฟล์ HandLandmarkerHelper.kt

แฮนเดิลและแสดงผลลัพธ์

เครื่องมือจุดสังเกตด้วยมือจะสร้างวัตถุผลลัพธ์ที่เป็นจุดสังเกตด้วยมือสำหรับการตรวจจับแต่ละครั้ง วัตถุผลลัพธ์มีจุดสังเกตของมือในพิกัดรูปภาพ จุดสังเกตของมือในพิกัดโลก และความถนัดของมือ(ซ้าย/ขวา) ของมือที่ตรวจพบ

ตัวอย่างต่อไปนี้แสดงตัวอย่างข้อมูลเอาต์พุตจากงานนี้

เอาต์พุต HandLandmarkerResult มีคอมโพเนนต์ 3 คอมโพเนนต์ แต่ละคอมโพเนนต์คืออาร์เรย์ โดยแต่ละองค์ประกอบจะมีผลดังต่อไปนี้สำหรับมือที่ตรวจพบเพียงข้างเดียว

  • ความถนัดของมือ

    ความถนัดของมือแสดงให้เห็นว่ามือที่ตรวจพบเป็นมือซ้ายหรือมือขวา

  • จุดสังเกต

    มีจุดสังเกต 21 จุด แต่ละจุดประกอบด้วยพิกัด x, y และ z ระบบจะปรับพิกัด x และ y ให้เป็นมาตรฐานเป็น [0.0, 1.0] ตามความกว้างและความสูงของรูปภาพตามลำดับ พิกัด z แสดงถึงความลึกของจุดสังเกต โดยความลึกที่ข้อมือเป็นต้นทาง ค่ายิ่งน้อยเท่าไหร่ จุดสังเกตก็จะใกล้เคียงกับกล้องมากขึ้น ขนาดของ z ใช้สเกลใกล้เคียงกับ x

  • สถานที่สำคัญของโลก

    นอกจากนี้ยังมีจุดสังเกต 21 จุดในพิกัดของโลกด้วย จุดสังเกตแต่ละรายการประกอบด้วย x, y และ z ซึ่งแสดงพิกัด 3 มิติในชีวิตจริงในหน่วยเมตรที่มีจุดเริ่มต้นอยู่ที่จุดศูนย์กลางเรขาคณิตของมือ

HandLandmarkerResult:
  Handedness:
    Categories #0:
      index        : 0
      score        : 0.98396
      categoryName : Left
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : -3.41E-7
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
    ... (21 landmarks for a hand)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
    ... (21 world landmarks for a hand)

รูปภาพต่อไปนี้แสดงภาพเอาต์พุตของงาน

โค้ดตัวอย่าง Hand Marker จะแสดงวิธีแสดงผลลัพธ์ ที่ได้จากงาน ดูรายละเอียดเพิ่มเติมได้ที่ชั้นเรียน OverlayView