Android के लिए, हाथ के लैंडमार्क का पता लगाने वाली गाइड

MediaPipe के 'हाथ के लैंडमार्क' टास्क की मदद से, किसी इमेज में हाथ के लैंडमार्क का पता लगाया जा सकता है. इन निर्देशों में, Android ऐप्लिकेशन के साथ हैंड लैंडमार्कर का इस्तेमाल करने का तरीका बताया गया है. इन निर्देशों में बताया गया कोड सैंपल, GitHub पर उपलब्ध है.

इस टास्क की सुविधाओं, मॉडल, और कॉन्फ़िगरेशन के विकल्पों के बारे में ज़्यादा जानने के लिए, खास जानकारी देखें.

कोड का उदाहरण

MediaPipe Tasks के उदाहरण के तौर पर दिया गया कोड, Android के लिए हाथ के हिस्सों की पहचान करने वाले ऐप्लिकेशन को आसानी से लागू करने का तरीका है. इस उदाहरण में, हाथ के लैंडमार्क का लगातार पता लगाने के लिए, किसी Android डिवाइस के कैमरे का इस्तेमाल किया गया है. साथ ही, हाथ के लैंडमार्क का स्टैटिक तरीके से पता लगाने के लिए, डिवाइस की गैलरी में मौजूद इमेज और वीडियो का भी इस्तेमाल किया जा सकता है.

इस ऐप्लिकेशन का इस्तेमाल, अपने Android ऐप्लिकेशन के लिए शुरुआती बिंदु के तौर पर किया जा सकता है. इसके अलावा, किसी मौजूदा ऐप्लिकेशन में बदलाव करते समय, इसका इस्तेमाल किया जा सकता है. हाथ के लैंडमार्क का उदाहरण कोड, GitHub पर होस्ट किया गया है.

कोड डाउनलोड करना

यहां दिए गए निर्देशों में, git कमांड-लाइन टूल का इस्तेमाल करके, उदाहरण के कोड की लोकल कॉपी बनाने का तरीका बताया गया है.

उदाहरण के तौर पर दिया गया कोड डाउनलोड करने के लिए:

  1. यहां दिए गए कमांड का इस्तेमाल करके, Git डेटा स्टोर करने की जगह को क्लोन करें:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. इसके अलावा, अपने git इंस्टेंस को स्पैर्स चेकआउट का इस्तेमाल करने के लिए कॉन्फ़िगर करें, ताकि आपके पास सिर्फ़ Hand Landmarker के उदाहरण वाले ऐप्लिकेशन की फ़ाइलें हों:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/hand_landmarker/android
    

उदाहरण के तौर पर दिए गए कोड का लोकल वर्शन बनाने के बाद, प्रोजेक्ट को Android Studio में इंपोर्ट करके ऐप्लिकेशन चलाया जा सकता है. निर्देशों के लिए, Android के लिए सेटअप गाइड देखें.

मुख्य कॉम्पोनेंट

इन फ़ाइलों में, हाथ के लैंडमार्क का पता लगाने वाले इस उदाहरण वाले ऐप्लिकेशन के लिए ज़रूरी कोड शामिल है:

  • HandLandmarkerHelper.kt - हाथ के लैंडमार्क का पता लगाने वाले डिटेक्टर को शुरू करता है. साथ ही, मॉडल और प्रतिनिधि चुनने की प्रोसेस को मैनेज करता है.
  • MainActivity.kt - HandLandmarkerHelper को कॉल करने के साथ-साथ, ऐप्लिकेशन को लागू करता है.

सेटअप

इस सेक्शन में, खास तौर पर हाथ के लैंडमार्क का इस्तेमाल करने के लिए, डेवलपमेंट एनवायरमेंट और कोड प्रोजेक्ट सेट अप करने के मुख्य चरणों के बारे में बताया गया है. MediaPipe Tasks का इस्तेमाल करने के लिए, डेवलपमेंट एनवायरमेंट सेट अप करने के बारे में सामान्य जानकारी पाने के लिए, Android के लिए सेटअप गाइड देखें. इसमें प्लैटफ़ॉर्म के वर्शन से जुड़ी ज़रूरी शर्तें भी शामिल हैं.

डिपेंडेंसी

हाथ के लैंडमार्क का पता लगाने वाला टास्क, com.google.mediapipe:tasks-vision लाइब्रेरी का इस्तेमाल करता है. अपने Android ऐप्लिकेशन की build.gradle फ़ाइल में यह डिपेंडेंसी जोड़ें:

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

मॉडल

MediaPipe के हाथ के लैंडमार्क का पता लगाने वाले टास्क के लिए, ऐसा मॉडल बंडल ज़रूरी है जो इस टास्क के साथ काम करता हो. हाथ के लैंडमार्क के लिए, ट्रेन किए गए उपलब्ध मॉडल के बारे में ज़्यादा जानकारी के लिए, टास्क की खास जानकारी वाला मॉडल सेक्शन देखें.

मॉडल चुनें और डाउनलोड करें. इसके बाद, उसे अपनी प्रोजेक्ट डायरेक्ट्री में सेव करें:

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

ModelAssetPath पैरामीटर में मॉडल का पाथ बताएं. उदाहरण के कोड में, मॉडल को HandLandmarkerHelper.kt फ़ाइल में तय किया गया है:

baseOptionBuilder.setModelAssetPath(MP_HAND_LANDMARKER_TASK)

टास्क बनाना

MediaPipe Hand Landmarker टास्क, टास्क सेट अप करने के लिए createFromOptions() फ़ंक्शन का इस्तेमाल करता है. createFromOptions() फ़ंक्शन, कॉन्फ़िगरेशन के विकल्पों के लिए वैल्यू स्वीकार करता है. कॉन्फ़िगरेशन के विकल्पों के बारे में ज़्यादा जानने के लिए, कॉन्फ़िगरेशन के विकल्प लेख पढ़ें.

हाथ के लैंडमार्क की सुविधा, इनपुट डेटा के तीन टाइप के साथ काम करती है: स्टिल इमेज, वीडियो फ़ाइलें, और लाइव स्ट्रीम. टास्क बनाते समय, आपको अपने इनपुट डेटा टाइप के हिसाब से रनिंग मोड तय करना होगा. टास्क बनाने और अनुमान लगाने का तरीका जानने के लिए, अपने इनपुट डेटा टाइप से जुड़ा टैब चुनें.

इमेज

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)
    

हाथ के लैंडमार्क का उदाहरण देने वाले कोड को लागू करने से, उपयोगकर्ता को प्रोसेसिंग मोड के बीच स्विच करने की अनुमति मिलती है. इस तरीके से, टास्क बनाने का कोड ज़्यादा जटिल हो जाता है और हो सकता है कि यह आपके इस्तेमाल के उदाहरण के लिए सही न हो. इस कोड को HandLandmarkerHelper.kt फ़ाइल में, setupHandLandmarker() फ़ंक्शन में देखा जा सकता है.

कॉन्फ़िगरेशन विकल्प

इस टास्क में, Android ऐप्लिकेशन के लिए कॉन्फ़िगरेशन के ये विकल्प हैं:

विकल्प का नाम ब्यौरा वैल्यू की रेंज डिफ़ॉल्ट मान
runningMode टास्क के लिए रनिंग मोड सेट करता है. इसके तीन मोड हैं:

IMAGE: एक इमेज इनपुट के लिए मोड.

वीडियो: किसी वीडियो के डिकोड किए गए फ़्रेम के लिए मोड.

LIVE_STREAM: कैमरे से मिले इनपुट डेटा की लाइव स्ट्रीम के लिए मोड. इस मोड में, नतीजे असींक्रोनस तरीके से पाने के लिए, एक listener सेट अप करने के लिए, resultListener को कॉल करना होगा.
{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 जब हाथ का लैंडमार्कर लाइव स्ट्रीम मोड में हो, तो पहचान के नतीजे पाने के लिए रिज़ल्ट लिसनर को असिंक्रोनस तरीके से सेट करता है. यह सिर्फ़ तब लागू होता है, जब रनिंग मोड को LIVE_STREAM पर सेट किया गया हो लागू नहीं लागू नहीं
errorListener गड़बड़ी सुनने वाले को सेट करता है. हालांकि, ऐसा करना ज़रूरी नहीं है. लागू नहीं लागू नहीं

डेटा तैयार करना

हाथ के लैंडमार्क की सुविधा, इमेज, वीडियो फ़ाइल, और लाइव स्ट्रीम वीडियो के साथ काम करती है. यह टास्क, डेटा इनपुट को प्रोसेस करने से पहले की प्रोसेस को मैनेज करता है. इसमें, साइज़ बदलना, घुमाना, और वैल्यू को सामान्य करना शामिल है.

यहां दिए गए कोड में, डेटा को प्रोसेस करने के लिए उसे हैंड ऑफ करने का तरीका बताया गया है. इन सैंपल में, इमेज, वीडियो फ़ाइलों, और लाइव वीडियो स्ट्रीम के डेटा को मैनेज करने का तरीका बताया गया है.

इमेज

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()
    

हाथ के लैंडमार्कर के उदाहरण वाले कोड में, डेटा तैयार करने की प्रोसेस को 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)
    

निम्न पर ध्यान दें:

  • वीडियो मोड या लाइव स्ट्रीम मोड में चलाने के दौरान, आपको हाथ के लैंडमार्क करने वाले टास्क के लिए, इनपुट फ़्रेम का टाइमस्टैंप भी देना होगा.
  • इमेज या वीडियो मोड में चलने पर, हाथ के लैंडमार्क का पता लगाने वाला टास्क, मौजूदा थ्रेड को तब तक ब्लॉक कर देगा, जब तक वह इनपुट इमेज या फ़्रेम को प्रोसेस नहीं कर लेता. यूज़र इंटरफ़ेस को ब्लॉक होने से बचाने के लिए, प्रोसेसिंग को बैकग्राउंड थ्रेड में चलाएं.
  • लाइव स्ट्रीम मोड में चलने पर, हाथ के लैंडमार्क का टास्क मौजूदा थ्रेड को ब्लॉक नहीं करता, बल्कि तुरंत वापस आ जाता है. यह हर बार इनपुट फ़्रेम को प्रोसेस करने के बाद, अपने नतीजे के लिसनर को पहचान के नतीजे के साथ कॉल करेगा. अगर हाथ के लैंडमार्क का पता लगाने वाले टास्क के किसी फ़्रेम को प्रोसेस करने के दौरान, पहचान करने वाले फ़ंक्शन को कॉल किया जाता है, तो टास्क नए इनपुट फ़्रेम को अनदेखा कर देगा.

हाथ के लैंडमार्कर के उदाहरण वाले कोड में, detect, detectForVideo, और detectAsync फ़ंक्शन को HandLandmarkerHelper.kt फ़ाइल में तय किया गया है.

नतीजों को मैनेज और दिखाना

हाथ की गतिविधि का पता लगाने वाली सुविधा, हर बार पहचान करने के लिए, हाथ की गतिविधि का पता लगाने वाला नतीजा ऑब्जेक्ट जनरेट करती है. नतीजे के ऑब्जेक्ट में, इमेज के कोऑर्डिनेट में हाथ के लैंडमार्क, दुनिया के कोऑर्डिनेट में हाथ के लैंडमार्क, और पहचाने गए हाथों के लिए, बायां/दायां हाथ की जानकारी होती है.

यहां इस टास्क के आउटपुट डेटा का उदाहरण दिया गया है:

HandLandmarkerResult आउटपुट में तीन कॉम्पोनेंट होते हैं. हर कॉम्पोनेंट एक कलेक्शन होता है. इसमें हर एलिमेंट में, पहचाने गए एक हाथ के लिए ये नतीजे होते हैं:

  • किसी खास हाथ का इस्तेमाल

    इस एट्रिब्यूट से पता चलता है कि पहचाने गए हाथ बाएं हैं या दाएं.

  • लैंडमार्क

    हाथ के 21 लैंडमार्क हैं. हर लैंडमार्क में x, y, और z कोऑर्डिनेट होते हैं. x और y निर्देशांक को इमेज की चौड़ाई और ऊंचाई के हिसाब से, [0.0, 1.0] पर नॉर्मलाइज़ किया जाता है. z निर्देशांक, लैंडमार्क की गहराई दिखाता है. इसमें कलाई की गहराई को ऑरिजिन माना जाता है. वैल्यू जितनी कम होगी, लैंडमार्क कैमरे के उतना ही करीब होगा. z के मैग्नीट्यूड के लिए, x के स्केल का इस्तेमाल किया जाता है.

  • विश्व भू-स्थल

    हाथ के 21 लैंडमार्क, वर्ल्ड कोऑर्डिनेट में भी दिखाए जाते हैं. हर लैंडमार्क, x, y, और z से बना होता है. यह मीटर में, असल दुनिया के 3D कोऑर्डिनेट दिखाता है. इसमें हाथ के ज्यामितीय केंद्र को ऑरिजिन माना जाता है.

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)

नीचे दी गई इमेज में, टास्क के आउटपुट को विज़ुअलाइज़ किया गया है:

थंब्स अप का इशारा करते हुए हाथ की इमेज, जिसमें हाथ की हड्डियों की संरचना को मैप किया गया है

हाथ के लैंडमार्क का उदाहरण देने वाले कोड में, टास्क से मिले नतीजों को दिखाने का तरीका बताया गया है. ज़्यादा जानकारी के लिए, OverlayView क्लास देखें.