1. परिचय
ARCore, मोबाइल डिवाइसों पर ऑगमेंटेड रिएलिटी (एआर) ऐप्लिकेशन बनाने का एक प्लैटफ़ॉर्म है. Google का ARCore Depth API, ARCore सेशन के हर फ़्रेम के लिए डेप्थ इमेज का ऐक्सेस देता है. डेप्थ इमेज में मौजूद हर पिक्सल, कैमरे से लेकर आस-पास की जगह तक की दूरी को मापता है.
रॉ डेप्थ एपीआई, डेप्थ इमेज देता है. इन इमेज को स्क्रीन-स्पेस फ़िल्टरिंग ऑपरेशंस से नहीं गुज़ारा जाता है. इन ऑपरेशंस को, नतीजों को बेहतर बनाने और इंटरपोलेट करने के लिए डिज़ाइन किया गया है. ये वैल्यू, ज्यामितीय रूप से ज़्यादा सटीक होती हैं. हालांकि, इनमें डेटा मौजूद नहीं हो सकता है. साथ ही, ये वैल्यू, कैमरे से ली गई इमेज से कम अलाइन हो सकती हैं.
इस कोडलैब में बताया गया है कि सीन का 3D विश्लेषण करने के लिए, Raw Depth API का इस्तेमाल कैसे किया जाता है. आपको एआर की सुविधा वाला एक आसान ऐप्लिकेशन बनाना होगा. यह ऐप्लिकेशन, डेप्थ के रॉ डेटा का इस्तेमाल करके दुनिया की ज्यामिति का पता लगाएगा और उसे विज़ुअलाइज़ करेगा.
डेप्थ और रॉ डेप्थ एपीआई, सिर्फ़ ARCore की सुविधा वाले कुछ डिवाइसों पर काम करते हैं. Depth API सिर्फ़ Android पर उपलब्ध है.
आपको क्या बनाना है
इस कोडलैब में, आपको एक ऐसा ऐप्लिकेशन बनाना है जो हर फ़्रेम के लिए रॉ डेप्थ इमेज का इस्तेमाल करता है. इससे आपके आस-पास की दुनिया का ज्योमेट्रिक विश्लेषण किया जा सकेगा. यह ऐप्लिकेशन:
- देखें कि टारगेट डिवाइस पर डेप्थ की सुविधा काम करती है या नहीं.
- हर कैमरा फ़्रेम के लिए, डेप्थ इमेज को फिर से पाएं.
- डेप्थ इमेज को 3D पॉइंट में फिर से प्रोजेक्ट करता है. साथ ही, कॉन्फ़िडेंस और ज्यामिति के आधार पर उन पॉइंट को फ़िल्टर करता है.
- दिलचस्पी दिखाने वाले 3D ऑब्जेक्ट को सेगमेंट करने के लिए, रॉ डेप्थ पॉइंट क्लाउड का इस्तेमाल करें.
|
आपको क्या बनाना है, इसकी झलक. |
ध्यान दें: अगर आपको कोई समस्या आती है, तो समस्या हल करने के लिए, आखिरी सेक्शन पर जाएं.
2. ज़रूरी शर्तें
इस कोडलैब को पूरा करने के लिए, आपको कुछ खास हार्डवेयर और सॉफ़्टवेयर की ज़रूरत होगी.
हार्डवेयर की ज़रूरी शर्तें
- ARCore के साथ काम करने वाला डिवाइस, जिसमें यूएसबी डीबगिंग की सुविधा चालू हो. साथ ही, उसे यूएसबी केबल के ज़रिए डेवलपमेंट मशीन से कनेक्ट किया गया हो. साथ ही, इस डिवाइस पर Depth API काम करना चाहिए.
ज़रूरी सॉफ़्टवेयर
- 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 फ़ाइल डाउनलोड करके उसे एक्सट्रैक्ट करें:
कोड का इस्तेमाल शुरू करने के लिए, यह तरीका अपनाएं.
- Android Studio लॉन्च करें और Open an existing Android Studio project चुनें.
- उस लोकल डायरेक्ट्री पर जाएं जहां आपने Raw Depth ZIP फ़ाइल सेव की है.
arcore_rawdepthapi_codelabडायरेक्ट्री पर दो बार क्लिक करें.
arcore_rawdepthapi_codelab डायरेक्ट्री, कई मॉड्यूल वाला एक Gradle प्रोजेक्ट है. अगर Android Studio में सबसे ऊपर बाईं ओर मौजूद प्रोजेक्ट पैन पहले से ही प्रोजेक्ट पैन में नहीं दिख रहा है, तो ड्रॉप-डाउन मेन्यू में जाकर प्रोजेक्ट पर क्लिक करें.
नतीजा कुछ ऐसा दिखना चाहिए:
| इस प्रोजेक्ट में ये मॉड्यूल शामिल हैं:
|
आपको part0_work मॉड्यूल में काम करना होगा. कोडलैब के हर हिस्से के लिए, पूरे समाधान भी दिए गए हैं. हर मॉड्यूल, एक ऐसा ऐप्लिकेशन होता है जिसे बनाया जा सकता है.
4. स्टार्टर ऐप्लिकेशन चलाना
रॉ डेप्थ स्टार्टर ऐप्लिकेशन चलाने के लिए, यह तरीका अपनाएं.
- Run > Run... > ‘part0_work'.
- डिप्लॉयमेंट का टारगेट चुनें डायलॉग में, कनेक्ट किए गए डिवाइस की सूची से अपना डिवाइस चुनें. इसके बाद, ठीक है पर क्लिक करें.
Android Studio, शुरुआती ऐप्लिकेशन बनाएगा और उसे आपके डिवाइस पर चलाएगा.
| पहली बार ऐप्लिकेशन चलाने पर, आपसे कैमरे का ऐक्सेस मांगा जाएगा. जारी रखने के लिए, अनुमति दें पर टैप करें. |
| फ़िलहाल, यह ऐप्लिकेशन कुछ नहीं करता. यह सबसे बुनियादी एआर ऐप्लिकेशन है. इसमें आपके सीन का कैमरा व्यू दिखता है, लेकिन यह और कुछ नहीं करता. मौजूदा कोड, ARCore SDK के साथ पब्लिश किए गए Hello AR सैंपल के जैसा है. |
इसके बाद, Raw Depth API का इस्तेमाल करके, अपने आस-पास के सीन की ज्यामिति को वापस पाया जाएगा.
5. Raw Depth API सेट अप करना (पहला हिस्सा)
पक्का करें कि टारगेट किए गए डिवाइस पर डेप्थ की सुविधा काम करती हो
सभी ARCore की सुविधा वाले डिवाइसों पर, Depth API काम नहीं करता. पक्का करें कि टारगेट डिवाइस पर डेप्थ की सुविधा काम करती हो. इसके बाद ही, अपने ऐप्लिकेशन में यह सुविधा जोड़ें. इसके लिए, RawDepthCodelabActivity.java के onResume() फ़ंक्शन का इस्तेमाल करें. इससे एक नया सेशन बनाया जाता है.
मौजूदा कोड ढूंढें:
// Create the ARCore session.
session = new Session(/* context= */ this);
इसे अपडेट करें, ताकि ऐप्लिकेशन सिर्फ़ उन डिवाइसों पर काम करे जिन पर Depth API काम करता है.
// 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.";
}
रॉ डेप्थ की सुविधा चालू करना
Raw Depth API, डेप्थ इमेज को स्मूद नहीं करता है. साथ ही, यह एक कॉन्फ़िडेंस इमेज भी उपलब्ध कराता है. इसमें रॉ डेप्थ इमेज के हर पिक्सल के लिए डेप्थ कॉन्फ़िडेंस होता है. आपने अभी-अभी जिस try-catch स्टेटमेंट में बदलाव किया है उसके नीचे दिए गए कोड को अपडेट करके, रॉ डेप्थ की सुविधा चालू करें.
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;
}
अब एआर सेशन को सही तरीके से कॉन्फ़िगर कर दिया गया है. साथ ही, ऐप्लिकेशन डेप्थ पर आधारित सुविधाओं का इस्तेमाल कर सकता है.
Depth API को कॉल करना
इसके बाद, हर फ़्रेम के लिए डेप्थ इमेज पाने के लिए, Depth API को कॉल करें. नई फ़ाइल बनाकर, डेप्थ डेटा को नई क्लास में शामिल करें. 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()method जोड़कर, Raw Depth API का इस्तेमाल करके इन वैल्यू को भरने के लिए कॉल जोड़ें. यह तरीका, डेप्थ और कॉन्फ़िडेंस की नई इमेज के बारे में क्वेरी करता है. साथ ही, इससे मिले पॉइंटक्लाउड को सेव करता है. डेप्थ और कॉन्फ़िडेंस इमेज में एक जैसा डेटा होगा.
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;
}
|
|
|
|
|
|
|
|
इस समय कोड, कैमरे के ऐंकर को भी सेव करता है, ताकि डेप्थ की जानकारी को वर्ल्ड कोऑर्डिनेट में बदला जा सके. इसके लिए, हेल्पर तरीके 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. डेप्थ डेटा रेंडर करना (दूसरा हिस्सा)
अब आपके पास डेप्थ पॉइंटक्लाउड है. इसलिए, अब यह देखने का समय है कि स्क्रीन पर रेंडर किया गया डेटा कैसा दिखता है.
डेप्थ पॉइंट को विज़ुअलाइज़ करने के लिए, रेंडरर जोड़ना
डेप्थ पॉइंट को विज़ुअलाइज़ करने के लिए, रेंडरर जोड़ें.
सबसे पहले, रेंडरिंग लॉजिक को शामिल करने के लिए एक नई क्लास जोड़ें. यह क्लास, OpenGL कार्रवाइयां करती है. इससे डेप्थ पॉइंटक्लाउड को विज़ुअलाइज़ करने के लिए, शेडर को शुरू किया जा सकता है.
| 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()तरीका जोड़ें. इस तरीके में, डेप्थ की नई जानकारी को इनपुट के तौर पर लिया जाता है. साथ ही, पॉइंटक्लाउड डेटा को GPU पर कॉपी किया जाता है.
/**
* 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 पॉइंटक्लाउड की जानकारी ली जाती है और उसे कैमरे के व्यू पर वापस प्रोजेक्ट किया जाता है, ताकि उसे स्क्रीन पर रेंडर किया जा सके.
/** 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 शेडर जोड़ें.
| नया .vert शेडर जोड़ा जा रहा हैAndroid Studio में:
|
नई .vert फ़ाइल में, यह कोड जोड़ें:
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;
}
इस शेडर में, बेहतर विज़ुअलाइज़ेशन के लिए Turbo colormap का इस्तेमाल किया गया है. यह प्रोसेस, ये चरण पूरे करती है:
- यह हर पॉइंट की ऊंचाई (दुनिया के कोऑर्डिनेट में y-ऐक्सिस) को वापस लाता है.
- यह ऊंचाई के हिसाब से रंग तय करता है (लाल=कम, नीला=ज़्यादा).
- यह हर पॉइंट की स्क्रीन पोज़िशन का हिसाब लगाता है.
- यह
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 में मौजूद try-catch ब्लॉक के आखिर में यह कोड जोड़ें, ताकि मौजूदा फ़्रेम के लिए डेप्थ की नई वैल्यू दिखे.
// Visualize depth points.
depthRenderer.update(points);
depthRenderer.draw(camera);
इन बदलावों के बाद, ऐप्लिकेशन अब सही तरीके से काम करेगा और डेप्थ पॉइंटक्लाउड दिखाएगा.
| डेप्थ पॉइंटक्लाउड विज़ुअलाइज़ेशन का उदाहरण
|
7. 3D पॉइंट क्लाउड का विश्लेषण करना (तीसरा भाग)
एआर सेशन में डेप्थ डेटा मौजूद होने की पुष्टि करने के बाद, उसका विश्लेषण किया जा सकता है. डेप्थ का विश्लेषण करने के लिए, हर पिक्सल के लिए कॉन्फ़िडेंस वैल्यू एक अहम टूल है. 3D पॉइंट क्लाउड का विश्लेषण करने के लिए, कॉन्फ़िडेंस वैल्यू का इस्तेमाल करें.
कम कॉन्फ़िडेंस वाले पिक्सल को अमान्य करना
आपने हर डेप्थ पिक्सल के लिए कॉन्फ़िडेंस वैल्यू को फिर से पाया है और उसे DepthData में मौजूद हर पॉइंट के साथ सेव किया है. हालांकि, आपने अब तक इसका इस्तेमाल नहीं किया है.
confidenceNormalized की वैल्यू 0 से 1 के बीच होती है. 0 का मतलब है कि मॉडल को कम भरोसा है और 1 का मतलब है कि मॉडल को पूरा भरोसा है. convertRawDepthImagesTo3dPointBuffer() क्लास में convertRawDepthImagesTo3dPointBuffer() तरीके में बदलाव करें, ताकि उन पिक्सल को सेव न किया जा सके जिनकी कॉन्फ़िडेंस वैल्यू बहुत कम है.DepthData
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;
}
अब आपको सिर्फ़ भरोसेमंद और मिलते-जुलते पॉइंट दिखेंगे.
| दूरी के हिसाब से फ़िल्टर करनाइससे पॉइंटक्लाउड को कैमरे से 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);
}
}
}
इस तरीके को onDrawFrame में RawDepthCodelabActivity तरीके से जोड़ा जा सकता है:
// ********** 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. बनाएं-चलाएं-जांच करें
ऐप्लिकेशन बनाएं, उसे चलाएं, और उसकी जांच करें.
अपना ऐप्लिकेशन बनाना और उसे चलाना
ऐप्लिकेशन बनाने और उसे चलाने के लिए, यह तरीका अपनाएं:
- यूएसबी के ज़रिए, ARCore की सुविधा वाले डिवाइस को प्लग इन करें.
- मेन्यू बार में मौजूद ► बटन की मदद से, प्रोजेक्ट चलाएं.
- ऐप्लिकेशन के बनने और आपके डिवाइस पर डिप्लॉय होने तक इंतज़ार करें.
अपने डिवाइस पर ऐप्लिकेशन को पहली बार डिप्लॉय करने के लिए, आपको यह करना होगा
यूएसबी डीबग करने की अनुमति दें
पर मौजूद हो. जारी रखने के लिए, ठीक है चुनें.
डिवाइस पर पहली बार ऐप्लिकेशन चलाने पर, आपसे पूछा जाएगा कि क्या ऐप्लिकेशन को आपके डिवाइस के कैमरे का इस्तेमाल करने की अनुमति है. एआर की सुविधा का इस्तेमाल जारी रखने के लिए, आपको ऐक्सेस करने की अनुमति देनी होगी.
ऐप्लिकेशन की टेस्टिंग
ऐप्लिकेशन को चलाने के दौरान, उसके बुनियादी व्यवहार की जांच की जा सकती है. इसके लिए, अपने डिवाइस को पकड़ें, अपने आस-पास घूमें, और किसी जगह को धीरे-धीरे स्कैन करें. कम से कम 10 सेकंड का डेटा इकट्ठा करें. साथ ही, अगले चरण पर जाने से पहले, आस-पास की जगह को कई दिशाओं से स्कैन करें.
9. बधाई हो
बधाई हो, आपने Google के ARCore Raw Depth API का इस्तेमाल करके, डेप्थ पर आधारित अपना पहला ऑगमेंटेड रिएलिटी ऐप्लिकेशन बना लिया है और उसे चला लिया है. हमें यह देखने में खुशी होगी कि आपने क्या बनाया है!
10. समस्या का हल
डेवलपमेंट के लिए, Android डिवाइस सेट अप करना
- यूएसबी केबल की मदद से, अपने डिवाइस को डेवलपमेंट मशीन से कनेक्ट करें. अगर Windows का इस्तेमाल करके डेवलपमेंट किया जा रहा है, तो आपको अपने डिवाइस के लिए सही यूएसबी ड्राइवर इंस्टॉल करना पड़ सकता है.
- डेवलपर के लिए सेटिंग और टूल विंडो में यूएसबी डीबग करना चालू करने के लिए, यह तरीका अपनाएं:
- Settings ऐप्लिकेशन खोलें.
- अगर आपके डिवाइस में Android v8.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

























