ARCore की रॉ डेप्थ

1. परिचय

ARCore, मोबाइल डिवाइसों पर ऑगमेंटेड रिएलिटी (एआर) ऐप्लिकेशन बनाने के लिए प्लैटफ़ॉर्म है. Google के ARCore depth API में, किसी ARCore सेशन में हर फ़्रेम के लिए डेप्थ इमेज का ऐक्सेस होता है. डेप्थ इमेज में मौजूद हर पिक्सल, कैमरे से आस-पास की जगह की दूरी मापता है.

रॉ डेप्थ एपीआई, डेप्थ की जानकारी देता है. यह ऐसी इमेज होती है जिन्हें स्क्रीन-स्पेस फ़िल्टर करने की कार्रवाइयों से पास नहीं किया जाता है. इन इमेज को नतीजों को बराबर और इंटरपोलेट करने के लिए डिज़ाइन किया गया है. ये वैल्यू, ज्यामितीय तौर पर ज़्यादा सटीक होती हैं. हालांकि, हो सकता है कि इनमें कुछ डेटा मौजूद न हो. साथ ही, ये वैल्यू कैमरे से जोड़ी गई इमेज के साथ ज़्यादा अलाइन हो सकती हैं.

यह कोडलैब आपको दिखाता है कि किसी सीन का 3D ज्यामिति विश्लेषण करने के लिए, रॉ डेप्थ एपीआई का इस्तेमाल कैसे किया जाए. आपको एआर (ऑगमेंटेड रिएलिटी) की सुविधा वाला एक आसान ऐप्लिकेशन बनाना होगा. यह ऐप्लिकेशन, दुनिया की ज्यामिति का पता लगाने और उसे विज़ुअलाइज़ करने के लिए, रॉ डेप्थ डेटा का इस्तेमाल करेगा.

डेप्थ और रॉ डेप्थ एपीआई सिर्फ़ ARCore की सुविधा वाले डिवाइसों के सबसेट पर काम करते हैं. depth API सिर्फ़ Android पर उपलब्ध है.

आपको क्या बनाना होगा

इस कोडलैब में, आपको एक ऐसा ऐप्लिकेशन बनाना है जो आपके आस-पास की जगहों का ज्यामितीय विश्लेषण करने के लिए, हर फ़्रेम की डेप्थ वाली इमेज का इस्तेमाल करे. यह ऐप्लिकेशन:

  1. देखें कि टारगेट किए गए डिवाइस में, 'डेप्थ' सुविधा काम करती है या नहीं.
  2. हर कैमरा फ़्रेम के लिए, रॉ डेप्थ इमेज वापस पाएं.
  3. रॉ डेप्थ इमेज को 3D पॉइंट में फिर से प्रोजेक्ट करें और उन पॉइंट को कॉन्फ़िडेंस और ज्यामिति के आधार पर फ़िल्टर करें.
  4. अपनी पसंद के 3D ऑब्जेक्ट को सेगमेंट में बांटने के लिए, रॉ डेप्थ पॉइंट क्लाउड का इस्तेमाल करें.

आपको किस तरह का बनाया जाएगा, इसकी झलक देखें.

ध्यान दें: अगर इस प्रोसेस के दौरान आपको कोई समस्या आती है, तो उसे हल करने के बारे में सलाह पाने के लिए आखिरी सेक्शन पर जाएं.

2. ज़रूरी शर्तें

इस कोडलैब को पूरा करने के लिए, आपको खास हार्डवेयर और सॉफ़्टवेयर की ज़रूरत होगी.

हार्डवेयर की आवश्यकताएं

  • ARCore के साथ काम करने वाला ऐसा डिवाइस जिसमें यूएसबी डीबग करने की सुविधा चालू हो और जिसे यूएसबी केबल के ज़रिए आपकी डेवलपमेंट मशीन से कनेक्ट किया गया हो. इस डिवाइस में गहराई का एपीआई भी काम करना चाहिए.

ज़रूरी सॉफ़्टवेयर

  • ARCore SDK 1.31.0 या इसके बाद का वर्शन.
  • ऐसी डेवलपमेंट मशीन जिसमें Android Studio (v4.0.1 या उसके बाद का वर्शन) इंस्टॉल किया गया हो.

3. सेट अप करें

डेवलपमेंट मशीन सेट अप करना

यूएसबी केबल के ज़रिए ARCore डिवाइस को अपने कंप्यूटर से कनेक्ट करें. पक्का करें कि आपका डिवाइस यूएसबी डीबग करने की अनुमति देता हो. टर्मिनल खोलें और adb devices चलाएं, जैसा कि नीचे दिखाया गया है:

adb devices

List of devices attached
<DEVICE_SERIAL_NUMBER>    device

<DEVICE_SERIAL_NUMBER> आपके डिवाइस के लिए एक यूनीक स्ट्रिंग होगी. जारी रखने से पहले पक्का करें कि आपको बिलकुल एक डिवाइस दिख रहा हो.

कोड को डाउनलोड और इंस्टॉल करना

आपके पास रिपॉज़िटरी का क्लोन बनाने का विकल्प भी होता है:

git clone https://github.com/googlecodelabs/arcore-rawdepthapi

या कोई ZIP फ़ाइल डाउनलोड करें और उसे एक्सट्रैक्ट करें:

कोड के साथ काम करना शुरू करने के लिए, यह तरीका अपनाएं.

  1. Android Studio लॉन्च करें और कोई मौजूदा Android Studio प्रोजेक्ट खोलें को चुनें.
  2. उस लोकल डायरेक्ट्री पर जाएं जहां आपने रॉ डेप्थ ZIP फ़ाइल सेव की है.
  3. arcore_rawdepthapi_codelab डायरेक्ट्री पर दो बार क्लिक करें.

arcore_rawdepthapi_codelab डायरेक्ट्री, ऐसा सिंगल Gradle प्रोजेक्ट है जिसमें कई मॉड्यूल होते हैं. अगर Android Studio के सबसे ऊपर बाईं ओर मौजूद प्रोजेक्ट पैनल, प्रोजेक्ट पैनल में पहले से नहीं दिख रहा है, तो ड्रॉप-डाउन मेन्यू में प्रोजेक्ट पर क्लिक करें.

नतीजा कुछ ऐसा दिखना चाहिए:

इस प्रोजेक्ट में ये मॉड्यूल शामिल हैं:

  • part0_work: स्टार्टर ऐप्लिकेशन. कोडलैब का यह मॉड्यूल करते समय आपको इस मॉड्यूल में बदलाव करने चाहिए. बाकी सभी हिस्सों में रेफ़रंस कोड होता है.
  • part1: इस बारे में रेफ़रंस कोड कि पहला हिस्सा पूरा करने के बाद, आपने जो बदलाव किए हैं वे कैसे दिखने चाहिए.
  • part2: दूसरा हिस्सा पूरा करने पर, रेफ़रंस कोड
  • part3_completed: कोडलैब का तीसरा पार्ट पूरा करने पर रेफ़रंस कोड.

आपको part0_work मॉड्यूल में काम करना होगा. कोडलैब के हर हिस्से के लिए पूरा समाधान भी मौजूद है. हर मॉड्यूल एक बिल्डेबल ऐप्लिकेशन होता है.

4. स्टार्टर ऐप्लिकेशन चलाएं

रॉ डेप्थ स्टार्टर ऐप्लिकेशन को चलाने के लिए, यह तरीका अपनाएं.

  1. Run > चलाएं... &gt; ‘पार्ट0_work' का इस्तेमाल करें.
  2. डिप्लॉयमेंट टारगेट चुनें डायलॉग में, कनेक्ट किए गए डिवाइस की सूची से अपना डिवाइस चुनें और ठीक है पर क्लिक करें.

Android Studio, शुरुआती ऐप्लिकेशन बनाएगा और उसे आपके डिवाइस पर चलाएगा.

ऐप्लिकेशन को पहली बार इस्तेमाल करने पर, कैमरे के लिए अनुमति का अनुरोध किया जाएगा. जारी रखने के लिए, अनुमति दें पर टैप करें.

फ़िलहाल, ऐप्लिकेशन कुछ नहीं करता.यह सबसे सामान्य एआर ऐप्लिकेशन है, जिसमें आपके सीन का कैमरा व्यू दिखाया जाता है, लेकिन कुछ और नहीं किया जा रहा है.मौजूदा कोड, ARCore SDK टूल से पब्लिश किए गए हैलो एआर सैंपल की तरह ही है.

इसके बाद, अपने आस-पास के सीन की ज्यामिति वापस पाने के लिए, Start depth API का इस्तेमाल किया जाएगा.

5. रॉ डेप्थ एपीआई सेट अप करना (पार्ट 1)

पक्का करें कि टारगेट किए गए डिवाइस में, 'डेप्थ' सुविधा काम करती हो

यह ज़रूरी नहीं है कि ARCore काम करने वाले सभी डिवाइस इस एपीआई पर काम करते हों. RawDepthCodelabActivity.java के onResume() फ़ंक्शन में आपके ऐप्लिकेशन में कोई फ़ंक्शन जोड़ने से पहले, पक्का करें कि टारगेट डिवाइस में डेप्थ की सुविधा काम करती हो. इससे एक नया सेशन बनाया जाएगा.

मौजूदा कोड ढूंढें:

// Create the ARCore session.
session = new Session(/* context= */ this);

इसे अपडेट करें, ताकि यह पक्का किया जा सके कि ऐप्लिकेशन सिर्फ़ उन डिवाइसों पर ही चलाया जाए जो डेप्थ एपीआई के साथ काम करते हैं.

// Create the ARCore session.
session = new Session(/* context= */ this);
if (!session.isDepthModeSupported(Config.DepthMode.RAW_DEPTH_ONLY)) {
  message =
     "This device does not support the ARCore Raw Depth API. See" +
     "https://developers.google.com/ar/devices for 
     a list of devices that do.";
}

रॉ डेप्थ की सुविधा चालू करें

रॉ डेप्थ एपीआई की मदद से, आपको बिना किसी रुकावट के गहराई वाली इमेज मिलती है. साथ ही, आपको रॉ डेप्थ इमेज में हर पिक्सल के लिए डेप्थ कॉन्फ़िडेंस की मदद से मिलती-जुलती कॉन्फ़िडेंस इमेज मिलती है. रॉ डेप्थ चालू करने के लिए, हाल ही में 'ट्राई कैच करें' स्टेटमेंट में दिए गए कोड को अपडेट करें.

try {
  // ************ New code to add ***************
  // Enable raw depth estimation and auto focus mode while ARCore is running.
  Config config = session.getConfig();
  config.setDepthMode(Config.DepthMode.RAW_DEPTH_ONLY);
  config.setFocusMode(Config.FocusMode.AUTO);
  session.configure(config);
  // ************ End new code to add ***************
  session.resume();
} catch (CameraNotAvailableException e) {
  messageSnackbarHelper.showError(this, "Camera not available. Try restarting the app.");
  session = null;
  return;
}

अब एआर सेशन को सही तरीके से कॉन्फ़िगर किया गया है और ऐप्लिकेशन में, डिवाइस के हर पहलू को ध्यान में रखकर बनाई गई सुविधाएं इस्तेमाल की जा सकती हैं.

डेप्थ एपीआई को कॉल करें

इसके बाद, हर फ़्रेम के लिए डेप्थ इमेज वापस पाने के लिए, डेप्थ एपीआई को कॉल करें. नई फ़ाइल बनाकर, डेप्थ डेटा को नई क्लास में एन्क्रिप्ट करें. rawdepth फ़ोल्डर पर राइट क्लिक करें और New > Java Class को चुनें. इससे एक खाली फ़ाइल बन जाएगी. इस क्लास में इन्हें जोड़ें:

src/main/java/com/google/ar/core/codelab/rawdepth/DepthData.java

package com.google.ar.core.codelab.rawdepth;

import android.media.Image;
import android.opengl.Matrix;

import com.google.ar.core.Anchor;
import com.google.ar.core.CameraIntrinsics;
import com.google.ar.core.Frame;
import com.google.ar.core.exceptions.NotYetAvailableException;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

/**
 * Convert depth data from ARCore depth images to 3D pointclouds. Points are added by calling the
 * Raw Depth API, and reprojected into 3D space.
 */
public class DepthData {
    public static final int FLOATS_PER_POINT = 4; // X,Y,Z,confidence.

}

इस क्लास का इस्तेमाल, गहराई वाली इमेज को पॉइंटक्लाउड में बदलने के लिए किया जाता है. पॉइंट क्लाउड, सीन की ज्यामिति को पॉइंट की सूची के साथ दिखाते हैं. इसमें हर पॉइंट की सूची 3D कोऑर्डिनेट (x, y, z) होती है और कॉन्फ़िडेंस वैल्यू होती है. यह वैल्यू 0 से 1 की रेंज में होती है.

क्लास में सबसे नीचे एक create()तरीका जोड़कर, कॉल की जानकारी अपने-आप भरने के लिए, रॉ डेप्थ एपीआई का इस्तेमाल करें. यह तरीका, नई डेप्थ और कॉन्फ़िडेंस इमेज के बारे में क्वेरी करता है, ताकि उससे बनने वाले Pointcloud को स्टोर किया जा सके. डेप्थ और कॉन्फ़िडेंस इमेज के लिए, मिलता-जुलता डेटा दिखेगा.

public static FloatBuffer create(Frame frame, Anchor cameraPoseAnchor) {
    try {
        Image depthImage = frame.acquireRawDepthImage16Bits();
        Image confidenceImage = frame.acquireRawDepthConfidenceImage();

        // Retrieve the intrinsic camera parameters corresponding to the depth image to
        // transform 2D depth pixels into 3D points. See more information about the depth values
        // at
        // https://developers.google.com/ar/develop/java/depth/overview#understand-depth-values.

        final CameraIntrinsics intrinsics = frame.getCamera().getTextureIntrinsics();
        float[] modelMatrix = new float[16];
        cameraPoseAnchor.getPose().toMatrix(modelMatrix, 0);
        final FloatBuffer points = convertRawDepthImagesTo3dPointBuffer(
                depthImage, confidenceImage, intrinsics, modelMatrix);

        depthImage.close();
        confidenceImage.close();

        return points;
    } catch (NotYetAvailableException e) {
        // This normally means that depth data is not available yet.
        // This is normal, so you don't have to spam the logcat with this.
    }
    return null;
}

acquireCameraImage()

acquireDepthImage16Bits()

acquireRawDepthImage16Bits()

acquireRawDepthConfidenceImage()

इस समय कोड में कैमरा ऐंकर भी सेव होता है, ताकि हेल्पर तरीके convertRawDepthImagesTo3dPointBuffer() का इस्तेमाल करके, गहराई की जानकारी को वर्ल्ड निर्देशांक में बदला जा सके. यह हेल्पर तरीका, हर पिक्सल को डेप्थ इमेज के तौर पर लेता है और कैमरे के मुकाबले 3D पॉइंट में डेप्थ को अनप्रोजेक्ट करने के लिए, कैमरे के इंट्रीनिक्स का इस्तेमाल करता है. इसके बाद, पॉइंट की पोज़िशन को वर्ल्ड निर्देशांक में बदलने के लिए कैमरा ऐंकर का इस्तेमाल किया जाता है. मौजूद हर पिक्सल को 3D पॉइंट (मीटर की इकाई में) में बदला जाता है और उसके कॉन्फ़िडेंस के साथ स्टोर किया जाता है.

depthData.java में नीचे दिए गए हेल्पर तरीके को जोड़ें:

/** Apply camera intrinsics to convert depth image into a 3D pointcloud. */
    private static FloatBuffer convertRawDepthImagesTo3dPointBuffer(
            Image depth, Image confidence, CameraIntrinsics cameraTextureIntrinsics, float[] modelMatrix) {
        // Java uses big endian so change the endianness to ensure
        // that the depth data is in the correct byte order.
        final Image.Plane depthImagePlane = depth.getPlanes()[0];
        ByteBuffer depthByteBufferOriginal = depthImagePlane.getBuffer();
        ByteBuffer depthByteBuffer = ByteBuffer.allocate(depthByteBufferOriginal.capacity());
        depthByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
        while (depthByteBufferOriginal.hasRemaining()) {
            depthByteBuffer.put(depthByteBufferOriginal.get());
        }
        depthByteBuffer.rewind();
        ShortBuffer depthBuffer = depthByteBuffer.asShortBuffer();

        final Image.Plane confidenceImagePlane = confidence.getPlanes()[0];
        ByteBuffer confidenceBufferOriginal = confidenceImagePlane.getBuffer();
        ByteBuffer confidenceBuffer = ByteBuffer.allocate(confidenceBufferOriginal.capacity());
        confidenceBuffer.order(ByteOrder.LITTLE_ENDIAN);
        while (confidenceBufferOriginal.hasRemaining()) {
            confidenceBuffer.put(confidenceBufferOriginal.get());
        }
        confidenceBuffer.rewind();

        // To transform 2D depth pixels into 3D points, retrieve the intrinsic camera parameters
        // corresponding to the depth image. See more information about the depth values at
        // https://developers.google.com/ar/develop/java/depth/overview#understand-depth-values.
        final int[] intrinsicsDimensions = cameraTextureIntrinsics.getImageDimensions();
        final int depthWidth = depth.getWidth();
        final int depthHeight = depth.getHeight();
        final float fx =
                cameraTextureIntrinsics.getFocalLength()[0] * depthWidth / intrinsicsDimensions[0];
        final float fy =
                cameraTextureIntrinsics.getFocalLength()[1] * depthHeight / intrinsicsDimensions[1];
        final float cx =
                cameraTextureIntrinsics.getPrincipalPoint()[0] * depthWidth / intrinsicsDimensions[0];
        final float cy =
                cameraTextureIntrinsics.getPrincipalPoint()[1] * depthHeight / intrinsicsDimensions[1];

        // Allocate the destination point buffer. If the number of depth pixels is larger than
        // `maxNumberOfPointsToRender` we uniformly subsample. The raw depth image may have
        // different resolutions on different devices.
        final float maxNumberOfPointsToRender = 20000;
        int step = (int) Math.ceil(Math.sqrt(depthWidth * depthHeight / maxNumberOfPointsToRender));

        FloatBuffer points = FloatBuffer.allocate(depthWidth / step * depthHeight / step * FLOATS_PER_POINT);
        float[] pointCamera = new float[4];
        float[] pointWorld = new float[4];

        for (int y = 0; y < depthHeight; y += step) {
            for (int x = 0; x < depthWidth; x += step) {
                // Depth images are tightly packed, so it's OK to not use row and pixel strides.
                int depthMillimeters = depthBuffer.get(y * depthWidth + x); // Depth image pixels are in mm.
                if (depthMillimeters == 0) {
                    // Pixels with value zero are invalid, meaning depth estimates are missing from
                    // this location.
                    continue;
                }
                final float depthMeters = depthMillimeters / 1000.0f; // Depth image pixels are in mm.

                // Retrieve the confidence value for this pixel.
                final byte confidencePixelValue =
                        confidenceBuffer.get(
                                y * confidenceImagePlane.getRowStride()
                                        + x * confidenceImagePlane.getPixelStride());
                final float confidenceNormalized = ((float) (confidencePixelValue & 0xff)) / 255.0f;

                // Unproject the depth into a 3D point in camera coordinates.
                pointCamera[0] = depthMeters * (x - cx) / fx;
                pointCamera[1] = depthMeters * (cy - y) / fy;
                pointCamera[2] = -depthMeters;
                pointCamera[3] = 1;

                // Apply model matrix to transform point into world coordinates.
                Matrix.multiplyMV(pointWorld, 0, modelMatrix, 0, pointCamera, 0);
                points.put(pointWorld[0]); // X.
                points.put(pointWorld[1]); // Y.
                points.put(pointWorld[2]); // Z.
                points.put(confidenceNormalized);
            }
        }

        points.rewind();
        return points;
    }

हर फ़्रेम के लिए, रॉ डेप्थ का नया डेटा पाएं

ऐप में बदलाव करें और गहराई की जानकारी हासिल करें. साथ ही, इसे हर पोज़ के लिए दुनिया के निर्देशांकों के हिसाब से अलाइन करें.

RawDepthCodelabActivity.java में, onDrawFrame() विधि में, मौजूदा लाइनें खोजें:

Frame frame = session.update();
Camera camera = frame.getCamera();

// If the frame is ready, render the camera preview image to the GL surface.
backgroundRenderer.draw(frame);

इसके ठीक नीचे ये लाइनें जोड़ें:

// Retrieve the depth data for this frame.
FloatBuffer points = DepthData.create(frame, session.createAnchor(camera.getPose()));
if (points == null) {
  return;
}

if (messageSnackbarHelper.isShowing() && points != null) {
  messageSnackbarHelper.hide(this);
}

6. डेप्थ की जानकारी देने वाला डेटा रेंडर करना (पार्ट 2)

अब आपके पास एक डेप्थ Pointcloud है. अब यह देखने का समय है कि डेटा, स्क्रीन पर रेंडर किया गया कैसा दिखता है.

डेप्थ पॉइंट देखने के लिए रेंडरर जोड़ें

डेप्थ पॉइंट को विज़ुअलाइज़ करने के लिए रेंडरर जोड़ें.

सबसे पहले, रेंडरिंग लॉजिक शामिल करने के लिए एक नई क्लास जोड़ें. यह क्लास डेप्थ पॉइंटक्लाउड को विज़ुअलाइज़ करने के लिए, शेडर शुरू करने के लिए OpenGL कार्रवाइयां करती है.

depthRenderer क्लास जोड़ें

  1. rendering सोर्स डायरेक्ट्री पर राइट क्लिक करें
  2. New > Java Class चुनें.
  3. क्लास DepthRenderer को नाम दें.

इस क्लास को नीचे दिए गए कोड से भरें:

src/main/java/com/google/ar/core/codelab/common/rendering/DepthRenderer.java

package com.google.ar.core.codelab.common.rendering;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;

import com.google.ar.core.Camera;
import com.google.ar.core.codelab.rawdepth.DepthData;

import java.io.IOException;
import java.nio.FloatBuffer;

public class DepthRenderer {
    private static final String TAG = DepthRenderer.class.getSimpleName();

    // Shader names.
    private static final String VERTEX_SHADER_NAME = "shaders/depth_point_cloud.vert";
    private static final String FRAGMENT_SHADER_NAME = "shaders/depth_point_cloud.frag";

    public static final int BYTES_PER_FLOAT = Float.SIZE / 8;
    private static final int BYTES_PER_POINT = BYTES_PER_FLOAT * DepthData.FLOATS_PER_POINT;
    private static final int INITIAL_BUFFER_POINTS = 1000;

    private int arrayBuffer;
    private int arrayBufferSize;

    private int programName;
    private int positionAttribute;
    private int modelViewProjectionUniform;
    private int pointSizeUniform;

    private int numPoints = 0;

    public DepthRenderer() {}

    public void createOnGlThread(Context context) throws IOException {
        ShaderUtil.checkGLError(TAG, "Bind");

        int[] buffers = new int[1];
        GLES20.glGenBuffers(1, buffers, 0);
        arrayBuffer = buffers[0];
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, arrayBuffer);

        arrayBufferSize = INITIAL_BUFFER_POINTS * BYTES_PER_POINT;
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, arrayBufferSize, null, GLES20.GL_DYNAMIC_DRAW);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        ShaderUtil.checkGLError(TAG, "Create");

        int vertexShader =
                ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
        int fragmentShader =
                ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);

        programName = GLES20.glCreateProgram();
        GLES20.glAttachShader(programName, vertexShader);
        GLES20.glAttachShader(programName, fragmentShader);
        GLES20.glLinkProgram(programName);
        GLES20.glUseProgram(programName);

        ShaderUtil.checkGLError(TAG, "Program");

        positionAttribute = GLES20.glGetAttribLocation(programName, "a_Position");
        modelViewProjectionUniform = GLES20.glGetUniformLocation(programName, "u_ModelViewProjection");
        // Sets the point size, in pixels.
        pointSizeUniform = GLES20.glGetUniformLocation(programName, "u_PointSize");

        ShaderUtil.checkGLError(TAG, "Init complete");
    }
}

डेप्थ डेटा रेंडर करें

इसके बाद, रेंडरिंग शेडर का सोर्स बताएं. DepthRenderer क्लास के सबसे नीचे, यह update()तरीका जोड़ें. यह तरीका, इनपुट के तौर पर गहराई से अपडेट की गई नई जानकारी को लेता है और Pointcloud डेटा को जीपीयू पर कॉपी करता है.

    /**
     * Update the OpenGL buffer contents to the provided point. Repeated calls with the same point
     * cloud will be ignored.
     */
    public void update(FloatBuffer points) {
        ShaderUtil.checkGLError(TAG, "Update");
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, arrayBuffer);

        // If the array buffer is not large enough to fit the new point cloud, resize it.
        points.rewind();
        numPoints = points.remaining() / DepthData.FLOATS_PER_POINT;
        if (numPoints * BYTES_PER_POINT > arrayBufferSize) {
            while (numPoints * BYTES_PER_POINT > arrayBufferSize) {
                arrayBufferSize *= 2;
            }
            GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, arrayBufferSize, null, GLES20.GL_DYNAMIC_DRAW);
        }

        GLES20.glBufferSubData(
                GLES20.GL_ARRAY_BUFFER, 0, numPoints * BYTES_PER_POINT, points);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        ShaderUtil.checkGLError(TAG, "Update complete");
    }

DepthRenderer क्लास के सबसे नीचे draw() तरीका जोड़कर, स्क्रीन पर सबसे नया डेटा ड्रॉ करें. यह तरीका 3D Pointcloud जानकारी लेकर उसे वापस कैमरा व्यू पर प्रोजेक्ट करता है, ताकि इसे स्क्रीन पर रेंडर किया जा सके.

    /** Render the point cloud. The ARCore point cloud is given in world space. */
    public void draw(Camera camera) {
        float[] projectionMatrix = new float[16];
        camera.getProjectionMatrix(projectionMatrix, 0, 0.1f, 100.0f);
        float[] viewMatrix = new float[16];
        camera.getViewMatrix(viewMatrix, 0);
        float[] viewProjection = new float[16];
        Matrix.multiplyMM(viewProjection, 0, projectionMatrix, 0, viewMatrix, 0);

        ShaderUtil.checkGLError(TAG, "Draw");

        GLES20.glUseProgram(programName);
        GLES20.glEnableVertexAttribArray(positionAttribute);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, arrayBuffer);
        GLES20.glVertexAttribPointer(positionAttribute, 4, GLES20.GL_FLOAT, false, BYTES_PER_POINT, 0);
        GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, viewProjection, 0);
        // Set point size to 5 pixels.
        GLES20.glUniform1f(pointSizeUniform, 5.0f);

        GLES20.glDrawArrays(GLES20.GL_POINTS, 0, numPoints);
        GLES20.glDisableVertexAttribArray(positionAttribute);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        ShaderUtil.checkGLError(TAG, "Draw complete");
    }

pointSizeUniform वैरिएबल का इस्तेमाल करके, पॉइंट के साइज़ को पिक्सल में अलग-अलग साइज़ में सेट किया जा सकता है. pointSizeUniform को सैंपल ऐप्लिकेशन में 5 पिक्सल पर सेट किया गया है.

नए शेडर जोड़ें

अपने ऐप्लिकेशन में डेप्थ की जानकारी देखने और उसे दिखाने के कई तरीके हैं. यहां, आपको कुछ शेडर जोड़ने होंगे और एक सामान्य कलर मैपिंग विज़ुअलाइज़ेशन बनाना होगा.

src/main/assets/shaders/ डायरेक्ट्री में नए .vert और .frag शेडर जोड़ें.

नया .बिंदु शेडर जोड़ा जा रहा है

Android Studio में:

  1. शेडर डायरेक्ट्री पर राइट क्लिक करें
  2. नया चुनें -> फ़ाइल
  3. इसे depth_point_cloud.vert नाम दें
  4. इसे टेक्स्ट फ़ाइल के तौर पर सेट करें.

नई .VER फ़ाइल में, यह कोड जोड़ें:

src/main/assets/shaders/depth_point_cloud.vert

uniform mat4 u_ModelViewProjection;
uniform float u_PointSize;

attribute vec4 a_Position;

varying vec4 v_Color;

// Return an interpolated color in a 6 degree polynomial interpolation.
vec3 GetPolynomialColor(in float x,
  in vec4 kRedVec4, in vec4 kGreenVec4, in vec4 kBlueVec4,
  in vec2 kRedVec2, in vec2 kGreenVec2, in vec2 kBlueVec2) {
  // Moves the color space a little bit to avoid pure red.
  // Removes this line for more contrast.
  x = clamp(x * 0.9 + 0.03, 0.0, 1.0);
  vec4 v4 = vec4(1.0, x, x * x, x * x * x);
  vec2 v2 = v4.zw * v4.z;
  return vec3(
    dot(v4, kRedVec4) + dot(v2, kRedVec2),
    dot(v4, kGreenVec4) + dot(v2, kGreenVec2),
    dot(v4, kBlueVec4) + dot(v2, kBlueVec2)
  );
}

// Return a smooth Percept colormap based upon the Turbo colormap.
vec3 PerceptColormap(in float x) {
  const vec4 kRedVec4 = vec4(0.55305649, 3.00913185, -5.46192616, -11.11819092);
  const vec4 kGreenVec4 = vec4(0.16207513, 0.17712472, 15.24091500, -36.50657960);
  const vec4 kBlueVec4 = vec4(-0.05195877, 5.18000081, -30.94853351, 81.96403246);
  const vec2 kRedVec2 = vec2(27.81927491, -14.87899417);
  const vec2 kGreenVec2 = vec2(25.95549545, -5.02738237);
  const vec2 kBlueVec2 = vec2(-86.53476570, 30.23299484);
  const float kInvalidDepthThreshold = 0.01;
  return step(kInvalidDepthThreshold, x) *
         GetPolynomialColor(x, kRedVec4, kGreenVec4, kBlueVec4,
                            kRedVec2, kGreenVec2, kBlueVec2);
}

void main() {
   // Color the pointcloud by height.
   float kMinHeightMeters = -2.0f;
   float kMaxHeightMeters = 2.0f;
   float normalizedHeight = clamp((a_Position.y - kMinHeightMeters) / (kMaxHeightMeters - kMinHeightMeters), 0.0, 1.0);
   v_Color = vec4(PerceptColormap(normalizedHeight), 1.0);
   gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
   gl_PointSize = u_PointSize;
}

यह शेडर, बेहतर विज़ुअलाइज़ेशन के लिए टर्बो कलरमैप का इस्तेमाल करता है. यह इन चरणों को पूरा करता है:

  1. हर पॉइंट की ऊंचाई का पता लगाता है (दुनिया के निर्देशांकों में y-ऐक्सिस).
  2. उस ऊंचाई से जुड़े रंग का हिसाब लगाता है (लाल=कम, नीला=ज़्यादा).
  3. हर पॉइंट की स्क्रीन की पोज़िशन का हिसाब लगाता है.
  4. हर पॉइंट का साइज़ (पिक्सल में) सेट करता है, जैसा कि DepthRenderer.update() तरीके में बताया गया है.

उसी डायरेक्ट्री में फ़्रैगमेंट शेडर बनाएं और इसे depth_point_cloud.frag नाम दें. इस सेक्शन में यही तरीका दोहराएं.

इसके बाद, वर्टेक्स शेडर में बताए गए तरीके से, हर पॉइंट को एक जैसे वर्टेक्स के तौर पर रेंडर करने के लिए, इस नई फ़ाइल में नीचे दिया गया कोड जोड़ें.

src/main/assets/shaders/depth_point_cloud.frag

precision mediump float;
varying vec4 v_Color;

void main() {
    gl_FragColor = v_Color;
}

इस रेंडरिंग को लागू करने के लिए, अपने RawDepthCodelabActivity में DepthRenderer क्लास में कॉल जोड़ें.

src/main/java/com/google/ar/core/codelab/common/rendering/RawDepthCodelabActivity.java

import com.google.ar.core.codelab.common.rendering.DepthRenderer;

क्लास में सबसे ऊपर, backgroundRenderer के आगे किसी निजी सदस्य को जोड़ें.

private final DepthRenderer depthRenderer = new DepthRenderer();

depthRenderer को RawDepthCodelabActivity.onSurfaceCreated() में शुरू किया जाना चाहिए, बिलकुल मौजूदा backgroundRenderer की तरह.

depthRenderer.createOnGlThread(/*context=*/ this);

मौजूदा फ़्रेम की सबसे नई डेप्थ दिखाने के लिए, onDrawFrame में 'ट्राई-कैच करें' ब्लॉक के आखिर में नीचे दिया गया कोड जोड़ें.

// Visualize depth points.
depthRenderer.update(points);
depthRenderer.draw(camera);

इन बदलावों के साथ, ऐप्लिकेशन अब सही से बन जाएगा और डेप्थ Pointcloud दिखेगा.

रॉ डेप्थ पॉइंट क्लाउड विज़ुअलाइज़ेशन का उदाहरण

  • हर पॉइंट सैंपल को उसकी डेप्थ के हिसाब से रंगा जाता है.
  • लाल पॉइंट पास में हैं और हरे/नीले पॉइंट दूर हैं
  • कुछ अनुपलब्ध डेटा या "होल" उन क्षेत्रों में देखा जा सकता है जहां इमेज की ज़रूरतें पूरी नहीं होती हैं, जैसे खाली सफ़ेद दीवारें या छत.
  • DepthRenderer.draw() में लाइन GLES20.glUniform1f(pointSizeUniform, 5.0f); को अडजस्ट करके, रेंडर किए गए पॉइंट के साइज़ के साथ गेम खेला जा सकता है. बाईं ओर पॉइंट साइज़ 5 और 10 दिखाए गए हैं.

7. 3D पॉइंट क्लाउड का विश्लेषण करना (पार्ट 3)

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

लो-कॉन्फ़िडेंस पिक्सल अमान्य करें

आपने हर डेप्थ पिक्सल के लिए कॉन्फ़िडेंस वैल्यू हासिल कर ली है और उसे DepthData के अंदर हर पॉइंट के साथ सेव कर लिया है, लेकिन आपने अभी तक उसका इस्तेमाल नहीं किया है.

confidenceNormalized की वैल्यू 0 से 1 तक है. इसमें 0 का मतलब कम कॉन्फ़िडेंस लेवल है और 1 का मतलब है पूरे भरोसे के साथ. DepthData क्लास में convertRawDepthImagesTo3dPointBuffer() तरीके में बदलाव करें, ताकि उन पिक्सल को सेव न किया जा सके जिनका कॉन्फ़िडेंस इतना कम है कि वे काम के न हों.

final float confidenceNormalized = ((float) (confidencePixelValue & 0xff)) / 255.0f;

// ******** New code to add ************
if (confidenceNormalized < 0.3) {
   // Ignores "low-confidence" pixels.
   continue;
}
// ******** End of new code to add *********

कॉन्फ़िडेंस लेवल के लिए अलग-अलग थ्रेशोल्ड आज़माएं और देखें कि हर लेवल पर कितने डेप्थ पॉइंट रखे जाते हैं.

कॉन्फ़िडेंस >= 0.1

कॉन्फ़िडेंस >= 0.3

कॉन्फ़िडेंस >= 0.5

कॉन्फ़िडेंस >= 0.7

कॉन्फ़िडेंस >= 0.9

दूरी के हिसाब से पिक्सल फ़िल्टर करें

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

आपने अभी-अभी इनकी मदद से, कॉन्फ़िडेंस जांच करने वाले जिस कोड को जोड़ा है उसे अपडेट करें:

src/main/java/com/google/ar/core/codelab/rawdepth/DepthData.java

if (confidenceNormalized < 0.3 || depthMeters > 1.5) {
    // Ignore "low-confidence" pixels or depth that is too far away.
   continue;
 }

अब आपको सिर्फ़ हाई कॉन्फ़िडेंस और क्लोज़ पॉइंट मिलेंगे.

दूरी फ़िल्टर करना

इससे Pointcloud, कैमरे के 1.5 मीटर के दायरे में नहीं रहता.

3D बिंदुओं और समतल की तुलना करें

आप ज्यामिति के 3D बिंदुओं और सतहों की तुलना कर सकते हैं और एक-दूसरे को फ़िल्टर करने के लिए उनका इस्तेमाल कर सकते हैं, जैसे कि ऐसे बिंदुओं को हटाना जो निगरानी में रखे गए एआर (ऑगमेंटेड रिएलिटी) हवाई जहाज़ों के करीब हैं.

इस चरण में सिर्फ़ "गैर-प्लेनर" दिखेगा ऐसे पॉइंट जो आस-पास मौजूद चीज़ों की सतह को दिखाते हैं. DepthData क्लास के सबसे नीचे filterUsingPlanes() तरीका जोड़ें. यह तरीका मौजूदा पॉइंट के ज़रिए फिर से चालू होता है, हर प्लेन के साथ हर पॉइंट की जांच करता है, और किसी भी पॉइंट को अमान्य कर देता है जो कि एआर प्लेन के बहुत पास है. इससे गैर-प्लेनर एरिया ऐसे हो जाते हैं जो सीन में ऑब्जेक्ट को हाइलाइट करते हैं.

src/main/java/com/google/ar/core/codelab/rawdepth/DepthData.java

    public static void filterUsingPlanes(FloatBuffer points, Collection<Plane> allPlanes) {
        float[] planeNormal = new float[3];

        // Allocate the output buffer.
        int numPoints = points.remaining() / DepthData.FLOATS_PER_POINT;

        // Check each plane against each point.
        for (Plane plane : allPlanes) {
            if (plane.getTrackingState() != TrackingState.TRACKING || plane.getSubsumedBy() != null) {
                continue;
            }

            // Compute the normal vector of the plane.
            Pose planePose = plane.getCenterPose();
            planePose.getTransformedAxis(1, 1.0f, planeNormal, 0);

            // Filter points that are too close to the plane.
            for (int index = 0; index < numPoints; ++index) {
                // Retrieves the next point.
                final float x = points.get(FLOATS_PER_POINT * index);
                final float y = points.get(FLOATS_PER_POINT * index + 1);
                final float z = points.get(FLOATS_PER_POINT * index + 2);

                // Transform point to be in world coordinates, to match plane info.
                float distance = (x - planePose.tx()) * planeNormal[0]
                        + (y - planePose.ty()) * planeNormal[1]
                        + (z - planePose.tz()) * planeNormal[2];
                // Controls the size of objects detected.
                // Smaller values mean smaller objects will be kept.
                // Larger values will only allow detection of larger objects, but also helps reduce noise.
                if (Math.abs(distance) > 0.03) {
                    continue;  // Keep this point, since it's far enough away from the plane.
                }

                // Invalidate points that are too close to planar surfaces.
                points.put(FLOATS_PER_POINT * index, 0);
                points.put(FLOATS_PER_POINT * index + 1, 0);
                points.put(FLOATS_PER_POINT * index + 2, 0);
                points.put(FLOATS_PER_POINT * index + 3, 0);
            }
        }
    }

आपके पास इस तरीके को RawDepthCodelabActivity में जोड़ने के लिए, onDrawFrame तरीके का इस्तेमाल करने का विकल्प है:

//  ********** New code to add ************
  // Filter the depth data.
  DepthData.filterUsingPlanes(points, session.getAllTrackables(Plane.class));
//  ********** End new code to add *******

  // Visualize depth points.
  depthRenderer.update(points);
  depthRenderer.draw(camera);

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

चाय का कप

माइक्रोफ़ोन

हेडफ़ोन

तकिया

क्लस्टर पॉइंट

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

src/main/java/com/google/ar/core/codelab/rawdepth/RawDepthCodelabActivity.java

import com.google.ar.core.codelab.common.helpers.AABB;
import com.google.ar.core.codelab.common.helpers.PointClusteringHelper;
import com.google.ar.core.codelab.common.rendering.BoxRenderer;
import java.util.List;

फ़ाइल में सबसे ऊपर, दूसरे रेंडरर के साथ-साथ इस क्लास में BoxRenderer जोड़ें.

private final BoxRenderer boxRenderer = new BoxRenderer();

onSurfaceCreated() तरीके में, दूसरे रेंडरर के साथ-साथ यह भी जोड़ें:

boxRenderer.createOnGlThread(/*context=*/this);

आखिर में, वापस लाए गए पॉइंटक्लाउड को क्लस्टर में ग्रुप करने और नतीजों को ऐक्सिस की मदद से अलाइन किए गए बाउंडिंग बॉक्स के तौर पर रेंडर करने के लिए, RawDepthCodelabActivity के अंदर onDrawFrame() में नीचे दी गई लाइनें जोड़ें.

      // Visualize depth points.
      depthRenderer.update(points);
      depthRenderer.draw(camera);

// ************ New code to add ***************

      // Draw boxes around clusters of points.
      PointClusteringHelper clusteringHelper = new PointClusteringHelper(points);
      List<AABB> clusters = clusteringHelper.findClusters();
      for (AABB aabb : clusters) {
        boxRenderer.draw(aabb, camera);
      }

// ************ End new code to add ***************

चाय का कप

माइक्रोफ़ोन

हेडफ़ोन

तकिया

अब ARCore सेशन के ज़रिए, रॉ डेप्थ की जानकारी पाई जा सकती है और डेप्थ की जानकारी को 3D पॉइंटक्लाउड में बदला जा सकता है. इसके अलावा, इन पॉइंट को बुनियादी तरीके से फ़िल्टर और रेंडरिंग भी किया जा सकता है.

8. Build-Run-Test

अपना ऐप्लिकेशन बनाएं, चलाएं, और उसकी जांच करें.

अपना ऐप्लिकेशन बनाएं और चलाएं

अपना ऐप्लिकेशन बनाने और उसे चलाने के लिए यह तरीका अपनाएं:

  1. यूएसबी का इस्तेमाल करके, ARCore की सुविधा वाले डिवाइस को प्लग इन करें.
  2. मेन्यू बार में ► बटन इस्तेमाल करके प्रोजेक्ट चलाएं.
  3. ऐप्लिकेशन के बनने और उसे आपके डिवाइस पर डिप्लॉय होने का इंतज़ार करें.

पहली बार अपने डिवाइस पर इस ऐप्लिकेशन को डिप्लॉय करने के लिए, आपको ये काम करने होंगे

यूएसबी डीबग करने की अनुमति देना

को मापा जा सकता है. जारी रखने के लिए, 'ठीक है' को चुनें.

डिवाइस पर पहली बार अपना ऐप्लिकेशन इस्तेमाल करने पर, आपसे यह पूछा जाएगा कि ऐप्लिकेशन को आपके डिवाइस का कैमरा इस्तेमाल करने की अनुमति है या नहीं. आपको एआर की सुविधा का इस्तेमाल जारी रखने के लिए, ऐक्सेस देना होगा.

ऐप्लिकेशन को टेस्ट करना

ऐप्लिकेशन चलाते समय, डिवाइस को पकड़कर, उसके आस-पास की जगह पर जाकर, और धीरे-धीरे किसी जगह को स्कैन करके ऐप्लिकेशन की सामान्य सेटिंग की जांच की जा सकती है. अगले चरण पर जाने से पहले, कम से कम 10 सेकंड का डेटा इकट्ठा करें और जगह को कई दिशाओं से स्कैन करें.

9. बधाई हो

बधाई हो, आपने Google के ARCore Rain depth API का इस्तेमाल करके, सबसे पहले गहराई पर आधारित ऑगमेंटेड रिएलिटी (एआर) ऐप्लिकेशन बना लिया है और उसे चला लिया है. हम यह देखने के लिए उत्साहित हैं कि आप क्या बनाएंगे!

10. समस्या का हल

अपने Android डिवाइस को डेवलपमेंट के लिए सेट करना

  1. अपने डिवाइस को यूएसबी केबल के ज़रिए अपनी डेवलपमेंट मशीन से कनेक्ट करें. अगर Windows का इस्तेमाल करके डेवलप किया जाता है, तो आपको अपने डिवाइस के लिए सही यूएसबी ड्राइवर इंस्टॉल करना पड़ सकता है.
  2. डेवलपर के लिए सेटिंग और टूल की विंडो में, यूएसबी डीबग करना चालू करने के लिए यह तरीका अपनाएं:
  • Settings ऐप्लिकेशन खोलें.
  • अगर आपके डिवाइस में Android 8.0 या इसके बाद वाले वर्शन का इस्तेमाल किया जा रहा है, तो सिस्टम चुनें.
  • स्क्रोल करके सबसे नीचे जाएं और फ़ोन के बारे में जानकारी को चुनें.
  • स्क्रोल करके नीचे जाएं और बिल्ड नंबर पर सात बार टैप करें.
  • पिछली स्क्रीन पर वापस जाएं, स्क्रोल करके सबसे नीचे जाएं, और डेवलपर के लिए सेटिंग और टूल पर टैप करें.
  • डेवलपर के लिए सेटिंग और टूल की विंडो में, यूएसबी डीबग करना को ढूंढने और उसे चालू करने के लिए नीचे स्क्रोल करें.

आपको Google की Android डेवलपर वेबसाइट पर इस प्रोसेस के बारे में ज़्यादा जानकारी मिल सकती है.

अगर आपको लाइसेंस (Failed to install the following Android SDK packages as some licences have not been accepted) से जुड़ी किसी बिल्ड में गड़बड़ी होती है, तो इन लाइसेंस की समीक्षा करने और उन्हें स्वीकार करने के लिए, इन निर्देशों का इस्तेमाल करें:

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

अक्सर पूछे जाने वाले सवाल