1. ভূমিকা
ARCore হল মোবাইল ডিভাইসে অগমেন্টেড রিয়েলিটি (AR) অ্যাপ তৈরি করার একটি প্ল্যাটফর্ম। Google-এর ARCore Depth API একটি ARCore সেশনে প্রতিটি ফ্রেমের জন্য একটি গভীরতার চিত্রে অ্যাক্সেস প্রদান করে। গভীরতার চিত্রের প্রতিটি পিক্সেল ক্যামেরা থেকে পরিবেশে একটি দূরত্ব পরিমাপ সরবরাহ করে।
Raw Depth API গভীরতার ছবি দেয় যা স্ক্রিন-স্পেস ফিল্টারিং অপারেশনের মাধ্যমে পাস করা হয় না যা ফলাফলগুলিকে মসৃণ এবং ইন্টারপোলেট করার জন্য ডিজাইন করা হয়েছে। এই মানগুলি আরও জ্যামিতিকভাবে সঠিক কিন্তু এতে অনুপস্থিত ডেটা থাকতে পারে এবং সংশ্লিষ্ট ক্যামেরা ছবির সাথে কম সারিবদ্ধ হতে পারে।
এই কোডল্যাবটি দৃশ্যের 3D জ্যামিতি বিশ্লেষণ করতে কীভাবে Raw Depth API ব্যবহার করতে হয় তা প্রদর্শন করে। আপনি একটি সাধারণ AR-সক্ষম অ্যাপ তৈরি করবেন যা বিশ্বের জ্যামিতি সনাক্ত এবং কল্পনা করতে কাঁচা গভীরতার ডেটা ব্যবহার করে।
Depth এবং Raw Depth APIs শুধুমাত্র ARCore সক্ষম ডিভাইসের একটি উপসেটে সমর্থিত। Depth API শুধুমাত্র Android এ উপলব্ধ।
আপনি কি নির্মাণ করবেন
এই কোডল্যাবে, আপনি একটি অ্যাপ তৈরি করবেন যা আপনার চারপাশের বিশ্বের জ্যামিতিক বিশ্লেষণ করতে প্রতিটি ফ্রেমের জন্য কাঁচা গভীরতার চিত্র ব্যবহার করে। এই অ্যাপটি হবে:
- লক্ষ্য ডিভাইস গভীরতা সমর্থন করে কিনা তা পরীক্ষা করুন।
- প্রতিটি ক্যামেরা ফ্রেমের জন্য কাঁচা গভীরতার চিত্র পুনরুদ্ধার করুন।
- কাঁচা গভীরতার ছবিগুলিকে 3D পয়েন্টে পুনঃপ্রজেক্ট করুন এবং সেই বিন্দুগুলিকে আস্থা ও জ্যামিতির উপর ভিত্তি করে ফিল্টার করুন।
- আগ্রহের 3D বস্তুকে ভাগ করতে কাঁচা গভীরতা পয়েন্ট ক্লাউড ব্যবহার করুন।
আপনি কি তৈরি করবেন তার পূর্বরূপ দেখুন। |
দ্রষ্টব্য: আপনি যদি পথে সমস্যায় পড়েন তবে কিছু সমস্যা সমাধানের টিপসের জন্য শেষ বিভাগে যান।
2. পূর্বশর্ত
এই কোডল্যাবটি সম্পূর্ণ করতে আপনার নির্দিষ্ট হার্ডওয়্যার এবং সফ্টওয়্যার প্রয়োজন হবে।
হার্ডওয়্যার প্রয়োজনীয়তা
- USB ডিবাগিং সক্ষম সহ একটি ARCore সমর্থিত ডিভাইস , একটি USB কেবলের মাধ্যমে আপনার ডেভেলপমেন্ট মেশিনে সংযুক্ত। এই ডিভাইসটি অবশ্যই Depth API সমর্থন করবে৷
সফ্টওয়্যার প্রয়োজনীয়তা
- ARCore SDK 1.31.0 বা তার পরে।
- অ্যান্ড্রয়েড স্টুডিও (v4.0.1 বা পরবর্তী) সহ একটি ডেভেলপমেন্ট মেশিন ইনস্টল করা হয়েছে।
3. সেট আপ করুন
ডেভেলপমেন্ট মেশিন সেট আপ করুন
USB তারের মাধ্যমে আপনার ARCore ডিভাইসটিকে আপনার কম্পিউটারের সাথে সংযুক্ত করুন। নিশ্চিত করুন যে আপনার ডিভাইস USB ডিবাগ করার অনুমতি দেয় ৷ একটি টার্মিনাল খুলুন এবং adb devices
চালান, যেমনটি নীচে দেখানো হয়েছে:
adb devices List of devices attached <DEVICE_SERIAL_NUMBER> device
<DEVICE_SERIAL_NUMBER> আপনার ডিভাইসের জন্য অনন্য একটি স্ট্রিং হবে৷ চালিয়ে যাওয়ার আগে নিশ্চিত করুন যে আপনি ঠিক একটি ডিভাইস দেখতে পাচ্ছেন।
কোডটি ডাউনলোড করে ইন্সটল করুন
আপনি হয় সংগ্রহস্থল ক্লোন করতে পারেন:
git clone https://github.com/googlecodelabs/arcore-rawdepthapi
অথবা একটি জিপ ফাইল ডাউনলোড করুন এবং এটি বের করুন:
কোড দিয়ে কাজ শুরু করতে এই ধাপগুলি অনুসরণ করুন।
- অ্যান্ড্রয়েড স্টুডিও চালু করুন এবং একটি বিদ্যমান অ্যান্ড্রয়েড স্টুডিও প্রকল্প খুলুন নির্বাচন করুন।
- স্থানীয় ডিরেক্টরিতে নেভিগেট করুন যেখানে আপনি Raw Depth ZIP ফাইলটি সংরক্ষণ করেছেন।
-
arcore_rawdepthapi_codelab
ডিরেক্টরিতে ডাবল ক্লিক করুন।
arcore_rawdepthapi_codelab
ডিরেক্টরিটি একাধিক মডিউল সহ একটি একক Gradle প্রকল্প। অ্যান্ড্রয়েড স্টুডিওর উপরের বাম দিকের প্রজেক্ট ফলকটি ইতিমধ্যেই প্রজেক্ট প্যানে প্রদর্শিত না হলে, ড্রপ-ডাউন মেনু থেকে প্রজেক্টে ক্লিক করুন।
ফলাফল এই মত হওয়া উচিত:
এই প্রকল্পে নিম্নলিখিত মডিউল রয়েছে:
|
আপনি part0_work
মডিউলে কাজ করবেন। কোডল্যাবের প্রতিটি অংশের জন্য সম্পূর্ণ সমাধানও রয়েছে। প্রতিটি মডিউল একটি নির্মাণযোগ্য অ্যাপ।
4. স্টার্টার অ্যাপ চালান
Raw Depth Starter অ্যাপটি চালানোর জন্য এই ধাপগুলি অনুসরণ করুন।
- Run > Run... > 'part0_work'- এ নেভিগেট করুন।
- ডিপ্লয়মেন্ট টার্গেট নির্বাচন করুন ডায়ালগে, সংযুক্ত ডিভাইস তালিকা থেকে আপনার ডিভাইসটি নির্বাচন করুন এবং ঠিক আছে ক্লিক করুন।
অ্যান্ড্রয়েড স্টুডিও প্রাথমিক অ্যাপ তৈরি করবে এবং এটি আপনার ডিভাইসে চালাবে।
আপনি যখন প্রথমবার অ্যাপটি চালাবেন, তখন এটি CAMERA অনুমতির জন্য অনুরোধ করবে। চালিয়ে যেতে অনুমতিতে ট্যাপ করুন। |
বর্তমানে, অ্যাপটি কিছু করে না । এটি হল সবচেয়ে মৌলিক AR অ্যাপ্লিকেশন, আপনার দৃশ্যের ক্যামেরা ভিউ দেখায়, কিন্তু অন্য কিছু করছে না। বিদ্যমান কোডটি ARCore SDK-এর সাথে প্রকাশিত Hello AR নমুনার মতো। |
এরপরে, আপনি আপনার চারপাশের দৃশ্যের জ্যামিতি পুনরুদ্ধার করতে Raw Depth API ব্যবহার করবেন।
5. Raw Depth API সেট আপ করুন (পার্ট 1)
লক্ষ্য ডিভাইস গভীরতা সমর্থন করে তা নিশ্চিত করুন
সমস্ত ARCore সমর্থিত ডিভাইস Depth API চালাতে পারে না। RawDepthCodelabActivity.java
এর onResume()
ফাংশনের ভিতরে আপনার অ্যাপে কার্যকারিতা যোগ করার আগে লক্ষ্য ডিভাইসটি গভীরতা সমর্থন করে তা নিশ্চিত করুন, যেখানে একটি নতুন সেশন তৈরি করা হয়েছে।
বিদ্যমান কোড খুঁজুন:
// Create the ARCore session.
session = new Session(/* context= */ this);
এটিকে আপডেট করুন যে অ্যাপ্লিকেশনটি শুধুমাত্র সেই ডিভাইসগুলিতে চলে যা ডেপথ 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 {
// ************ 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 কল করুন। একটি নতুন ফাইল তৈরি করে একটি নতুন ক্লাসে গভীরতার ডেটা এনক্যাপসুলেট করুন। 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()
মেথড যোগ করে 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. গভীরতার ডেটা রেন্ডার করুন (পার্ট 2)
এখন যেহেতু আপনার সাথে খেলার জন্য একটি গভীরতা পয়েন্টক্লাউড আছে, এটি পর্দায় রেন্ডার করা ডেটা কেমন দেখাচ্ছে তা দেখার সময়।
গভীরতা পয়েন্ট কল্পনা করতে একটি রেন্ডারার যোগ করুন
গভীরতা পয়েন্ট কল্পনা করতে একটি রেন্ডারার যোগ করুন।
প্রথমে, রেন্ডারিং লজিক ধারণ করতে একটি নতুন ক্লাস যোগ করুন। গভীরতা পয়েন্টক্লাউড কল্পনা করার জন্য শেডারগুলিকে আরম্ভ করার জন্য এই ক্লাসটি 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 শেডার যোগ করা হচ্ছেঅ্যান্ড্রয়েড স্টুডিওতে:
|
নতুন .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;
}
এই শেডারটি উন্নত ভিজ্যুয়ালাইজেশনের জন্য টার্বো কালারম্যাপ ব্যবহার করে। এটি নিম্নলিখিত পদক্ষেপগুলি সম্পাদন করে:
- প্রতিটি বিন্দুর উচ্চতা পুনরুদ্ধার করে (বিশ্ব স্থানাঙ্কে 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
ভিতরে ট্রাই-ক্যাচ ব্লকের শেষে নিম্নলিখিত কোডটি যোগ করুন।
// Visualize depth points.
depthRenderer.update(points);
depthRenderer.draw(camera);
এই পরিবর্তনগুলির সাথে, অ্যাপটি এখন সফলভাবে তৈরি করা উচিত এবং গভীরতা পয়েন্টক্লাউড দেখাতে হবে।
কাঁচা গভীরতার পয়েন্টক্লাউড ভিজ্যুয়ালাইজেশনের উদাহরণ
|
7. 3D পয়েন্ট ক্লাউড বিশ্লেষণ করুন (পর্ব 3)
একবার আপনি একটি AR সেশনে উপস্থিত রয়েছে কিনা তা যাচাই করার পরে আপনি গভীরতার ডেটা বিশ্লেষণ করতে পারেন। গভীরতা বিশ্লেষণের জন্য একটি গুরুত্বপূর্ণ টুল হল প্রতিটি পিক্সেলের জন্য আত্মবিশ্বাসের মান। 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 | আত্মবিশ্বাস >= ০.৭ | আত্মবিশ্বাস >= ০.৯ |
দূরত্ব অনুযায়ী পিক্সেল ফিল্টার করুন
আপনি দূরত্ব অনুযায়ী গভীরতার পিক্সেল ফিল্টার করতে পারেন। এই পরবর্তী পদক্ষেপগুলি ক্যামেরার কাছাকাছি জ্যামিতি নিয়ে কাজ করে৷ পারফরম্যান্স অপ্টিমাইজেশানের জন্য, আপনি অনেক দূরে থাকা পয়েন্টগুলিকে উপেক্ষা করতে পারেন৷
আপনি এইমাত্র নিম্নলিখিতগুলির সাথে যোগ করেছেন এমন কনফিডেন্স-চেকিং কোড আপডেট করুন:
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 পয়েন্ট এবং প্লেনগুলির তুলনা করতে পারেন এবং একে অপরকে ফিল্টার করতে ব্যবহার করতে পারেন, যেমন পর্যবেক্ষিত AR প্লেনের কাছাকাছি পয়েন্টগুলি সরানো।
এই পদক্ষেপটি শুধুমাত্র "নন-প্ল্যানার" পয়েন্টগুলি ছেড়ে দেবে যা পরিবেশের বস্তুর উপরিভাগের প্রতিনিধিত্ব করে। DepthData
ক্লাসের নীচে filterUsingPlanes()
পদ্ধতি যোগ করুন। এই পদ্ধতিটি বিদ্যমান পয়েন্টগুলির মাধ্যমে পুনরাবৃত্তি করে, প্রতিটি সমতলের বিপরীতে প্রতিটি বিন্দুকে পরীক্ষা করে এবং একটি AR সমতলের খুব কাছাকাছি যে কোনও বিন্দুকে বাতিল করে, নন-প্লানার এলাকাগুলিকে রেখে যা দৃশ্যের বস্তুগুলিকে হাইলাইট করে।
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);
পরিশেষে, Retrieved পয়েন্টক্লাউডগুলিকে ক্লাস্টারে গোষ্ঠীবদ্ধ করতে এবং ফলাফলগুলিকে অক্ষ-সারিবদ্ধ বাউন্ডিং বাক্স হিসাবে রেন্ডার করতে 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. বিল্ড-রান-টেস্ট
আপনার অ্যাপ তৈরি করুন, চালান এবং পরীক্ষা করুন।
আপনার অ্যাপ তৈরি করুন এবং চালান
আপনার অ্যাপ তৈরি এবং চালানোর জন্য এই পদক্ষেপগুলি অনুসরণ করুন:
- USB এর মাধ্যমে ARCore সমর্থিত ডিভাইসে প্লাগ ইন করুন।
- মেনু বারে ► বোতাম দিয়ে আপনার প্রকল্প চালান।
- অ্যাপটি তৈরি এবং আপনার ডিভাইসে স্থাপন করার জন্য অপেক্ষা করুন।
প্রথমবার যখন আপনি আপনার ডিভাইসে অ্যাপটি স্থাপন করার চেষ্টা করবেন তখন আপনাকে এটি করতে হবে
USB ডিবাগ করার অনুমতি দিন
ডিভাইসে চালিয়ে যেতে ঠিক আছে নির্বাচন করুন।
প্রথমবার যখন আপনি ডিভাইসে আপনার অ্যাপ চালাবেন, আপনাকে জিজ্ঞাসা করা হবে যে অ্যাপটির কাছে আপনার ডিভাইসের ক্যামেরা ব্যবহার করার অনুমতি আছে কিনা। AR কার্যকারিতা ব্যবহার চালিয়ে যেতে আপনাকে অবশ্যই অ্যাক্সেসের অনুমতি দিতে হবে।
আপনার অ্যাপ পরীক্ষা করা হচ্ছে
আপনি যখন আপনার অ্যাপটি চালান, তখন আপনি আপনার ডিভাইসটি ধরে রেখে, আপনার স্থানের চারপাশে ঘুরতে এবং ধীরে ধীরে একটি এলাকা স্ক্যান করে এর মৌলিক আচরণ পরীক্ষা করতে পারেন। কমপক্ষে 10 সেকেন্ডের ডেটা সংগ্রহ করার চেষ্টা করুন এবং পরবর্তী ধাপে যাওয়ার আগে বিভিন্ন দিক থেকে এলাকাটি স্ক্যান করুন।
9. অভিনন্দন
অভিনন্দন, আপনি Google-এর ARCore Raw Depth API ব্যবহার করে আপনার প্রথম গভীরতা-ভিত্তিক অগমেন্টেড রিয়েলিটি অ্যাপটি সফলভাবে তৈরি এবং চালাতে পেরেছেন। আপনি কি নির্মাণ করবেন তা দেখতে আমরা উত্তেজিত!
10. সমস্যা সমাধান
বিকাশের জন্য আপনার অ্যান্ড্রয়েড ডিভাইস সেট আপ করা হচ্ছে৷
- একটি USB কেবল দিয়ে আপনার ডিভাইসটিকে আপনার ডেভেলপমেন্ট মেশিনের সাথে সংযুক্ত করুন। আপনি যদি উইন্ডোজ ব্যবহার করে বিকাশ করেন তবে আপনাকে আপনার ডিভাইসের জন্য উপযুক্ত USB ড্রাইভার ইনস্টল করতে হতে পারে।
- বিকাশকারী বিকল্প উইন্ডোতে USB ডিবাগিং সক্ষম করতে নিম্নলিখিত পদক্ষেপগুলি সম্পাদন করুন:
- সেটিংস অ্যাপ খুলুন।
- যদি আপনার ডিভাইস Android v8.0 বা উচ্চতর ব্যবহার করে, তাহলে সিস্টেম নির্বাচন করুন।
- নীচে স্ক্রোল করুন এবং ফোন সম্পর্কে নির্বাচন করুন।
- নীচে স্ক্রোল করুন এবং বিল্ড নম্বরটি সাতবার আলতো চাপুন৷
- পূর্ববর্তী স্ক্রিনে ফিরে যান, নীচে স্ক্রোল করুন এবং বিকাশকারী বিকল্পগুলি আলতো চাপুন৷
- বিকাশকারী বিকল্প উইন্ডোতে, USB ডিবাগিং খুঁজে পেতে এবং সক্ষম করতে নীচে স্ক্রোল করুন৷
আপনি Google এর অ্যান্ড্রয়েড বিকাশকারী ওয়েবসাইটে এই প্রক্রিয়া সম্পর্কে আরও বিস্তারিত তথ্য পেতে পারেন।
লাইসেন্স সম্পর্কিত ব্যর্থতা তৈরি করুন
আপনি যদি লাইসেন্সগুলির সাথে সম্পর্কিত একটি বিল্ড ব্যর্থতার সম্মুখীন হন ( Failed to install the following Android SDK packages as some licences have not been accepted
), আপনি এই লাইসেন্সগুলি পর্যালোচনা এবং গ্রহণ করতে নিম্নলিখিত কমান্ডগুলি ব্যবহার করতে পারেন:
cd
<path to Android SDK>
tools/bin/sdkmanager --licenses