১. ভূমিকা
ARCore হলো মোবাইল ডিভাইসে অগমেন্টেড রিয়েলিটি (AR) অ্যাপ তৈরির একটি প্ল্যাটফর্ম। গুগলের ARCore ডেপথ এপিআই একটি ARCore সেশনের প্রতিটি ফ্রেমের জন্য ডেপথ ইমেজ অ্যাক্সেস করার সুযোগ দেয়। ডেপথ ইমেজের প্রতিটি পিক্সেল ক্যামেরা থেকে পারিপার্শ্বিক পরিবেশের দূরত্বের পরিমাপ সরবরাহ করে।
র ডেপথ এপিআই এমন ডেপথ ইমেজ প্রদান করে যা ফলাফলকে মসৃণ ও ইন্টারপোলেট করার জন্য ডিজাইন করা স্ক্রিন-স্পেস ফিল্টারিং অপারেশনের মধ্য দিয়ে যায় না। এই মানগুলি জ্যামিতিকভাবে অধিক নির্ভুল, কিন্তু এতে অনুপস্থিত ডেটা থাকতে পারে এবং সংশ্লিষ্ট ক্যামেরা ইমেজের সাথে কম সামঞ্জস্যপূর্ণ হতে পারে।
এই কোডল্যাবটিতে দেখানো হয়েছে কীভাবে র ডেপথ এপিআই (Raw Depth API) ব্যবহার করে কোনো দৃশ্যের ত্রিমাত্রিক জ্যামিতিক বিশ্লেষণ করা যায়। আপনি একটি সাধারণ এআর-সক্ষম অ্যাপ তৈরি করবেন যা র ডেপথ ডেটা ব্যবহার করে পারিপার্শ্বিক জগতের জ্যামিতি শনাক্ত ও দৃশ্যমান করে।
Depth এবং Raw Depth API-গুলো শুধুমাত্র ARCore-সক্ষম ডিভাইসগুলোর একটি ক্ষুদ্র অংশে সমর্থিত। Depth API-টি কেবল Android-এ উপলব্ধ।
আপনি যা তৈরি করবেন
এই কোডল্যাবে, আপনি এমন একটি অ্যাপ তৈরি করবেন যা প্রতিটি ফ্রেমের র ডেপথ ইমেজ ব্যবহার করে আপনার চারপাশের জগতের জ্যামিতিক বিশ্লেষণ করবে। এই অ্যাপটি যা করবে:
- লক্ষ্য ডিভাইসটি ডেপথ (Depth) সমর্থন করে কিনা তা যাচাই করুন।
- প্রতিটি ক্যামেরা ফ্রেমের জন্য র ডেপথ ইমেজটি পুনরুদ্ধার করুন।
- কাঁচা ডেপথ ইমেজগুলোকে 3D পয়েন্টে পুনঃপ্রক্ষেপণ করুন এবং কনফিডেন্স ও জ্যামিতির উপর ভিত্তি করে সেই পয়েন্টগুলোকে ফিল্টার করুন।
- আগ্রহের ত্রিমাত্রিক বস্তুগুলোকে বিভক্ত করতে কাঁচা ডেপথ পয়েন্ট ক্লাউড ব্যবহার করুন।
|
আপনি যা তৈরি করবেন তার এক ঝলক। |
দ্রষ্টব্য: কাজ করতে গিয়ে কোনো সমস্যার সম্মুখীন হলে, সমাধানমূলক কিছু পরামর্শের জন্য একেবারে শেষ অংশে চলে যান।
২. পূর্বশর্তসমূহ
এই কোডল্যাবটি সম্পন্ন করতে আপনার নির্দিষ্ট হার্ডওয়্যার ও সফটওয়্যার প্রয়োজন হবে।
হার্ডওয়্যারের প্রয়োজনীয়তা
- আপনার ডেভেলপমেন্ট মেশিনের সাথে ইউএসবি ক্যাবলের মাধ্যমে সংযুক্ত, ইউএসবি ডিবাগিং সক্ষম একটি ARCore সমর্থিত ডিভাইস । এই ডিভাইসটিকে অবশ্যই ডেপথ এপিআই (Depth API) সমর্থন করতে হবে।
সফটওয়্যার প্রয়োজনীয়তা
- ARCore SDK 1.31.0 বা তার পরবর্তী সংস্করণ।
- অ্যান্ড্রয়েড স্টুডিও (সংস্করণ ৪.০.১ বা তার পরবর্তী সংস্করণ) ইনস্টল করা একটি ডেভেলপমেন্ট মেশিন।
৩. স্থাপন করুন
ডেভেলপমেন্ট মেশিন সেট আপ করুন
আপনার ARCore ডিভাইসটিকে USB কেবলের মাধ্যমে কম্পিউটারের সাথে সংযুক্ত করুন। নিশ্চিত করুন যে আপনার ডিভাইসটিতে USB ডিবাগিংয়ের সুবিধা রয়েছে । একটি টার্মিনাল খুলুন এবং নিচে দেখানো অনুযায়ী adb devices চালান:
adb devices List of devices attached <DEVICE_SERIAL_NUMBER> device
<ডিভাইস_সিরিয়াল_নম্বর> আপনার ডিভাইসের জন্য একটি অনন্য স্ট্রিং হবে। চালিয়ে যাওয়ার আগে নিশ্চিত করুন যে আপনি ঠিক একটি ডিভাইস দেখতে পাচ্ছেন।
কোডটি ডাউনলোড এবং ইনস্টল করুন
আপনি রিপোজিটরিটি ক্লোন করতে পারেন:
git clone https://github.com/googlecodelabs/arcore-rawdepthapi
অথবা একটি ZIP ফাইল ডাউনলোড করে তা এক্সট্র্যাক্ট করুন:
কোড নিয়ে কাজ শুরু করতে এই ধাপগুলো অনুসরণ করুন।
- অ্যান্ড্রয়েড স্টুডিও চালু করুন এবং 'একটি বিদ্যমান অ্যান্ড্রয়েড স্টুডিও প্রজেক্ট খুলুন' বিকল্পটি বেছে নিন।
- সেই স্থানীয় ডিরেক্টরিতে যান যেখানে আপনি Raw Depth ZIP ফাইলটি সংরক্ষণ করেছেন।
-
arcore_rawdepthapi_codelabডিরেক্টরিতে ডাবল-ক্লিক করুন।
` arcore_rawdepthapi_codelab ডিরেক্টরিটি একাধিক মডিউল সহ একটি একক গ্রেডল প্রজেক্ট। যদি অ্যান্ড্রয়েড স্টুডিওর উপরের বাম দিকের প্রজেক্ট প্যানেটি আগে থেকেই প্রদর্শিত না থাকে, তাহলে ড্রপ-ডাউন মেনু থেকে `Projects`- এ ক্লিক করুন।
ফলাফলটি দেখতে এইরকম হবে:
| এই প্রকল্পে নিম্নলিখিত মডিউলগুলো রয়েছে:
|
আপনি part0_work মডিউলে কাজ করবেন। কোডল্যাবের প্রতিটি অংশের সম্পূর্ণ সমাধানও রয়েছে। প্রতিটি মডিউল একটি বিল্ডযোগ্য অ্যাপ।
৪. স্টার্টার অ্যাপটি চালান।
Raw Depth স্টার্টার অ্যাপটি চালানোর জন্য এই ধাপগুলো অনুসরণ করুন।
- রান > রান... > 'part0_work'- এ যান।
- 'Select Deployment Target' ডায়ালগ বক্সে, 'Connected Devices' তালিকা থেকে আপনার ডিভাইসটি নির্বাচন করুন এবং 'OK' ক্লিক করুন।
অ্যান্ড্রয়েড স্টুডিও প্রাথমিক অ্যাপটি তৈরি করবে এবং আপনার ডিভাইসে তা চালাবে।
| আপনি যখন প্রথমবার অ্যাপটি চালাবেন, তখন এটি ক্যামেরার অনুমতি চাইবে। চালিয়ে যেতে Allow-এ ট্যাপ করুন। |
| বর্তমানে, অ্যাপটি কিছুই করে না । এটি সবচেয়ে সাধারণ একটি এআর অ্যাপ্লিকেশন, যা আপনার দৃশ্যের একটি ক্যামেরা ভিউ দেখায়, কিন্তু অন্য কিছু করে না। বিদ্যমান কোডটি এআরকোর এসডিকে-এর সাথে প্রকাশিত হ্যালো এআর স্যাম্পলের অনুরূপ। |
এরপরে, আপনি আপনার চারপাশের দৃশ্যের জ্যামিতি পুনরুদ্ধার করতে Raw Depth API ব্যবহার করবেন।
৫. র ডেপথ এপিআই (Raw Depth API) সেট আপ করুন (পর্ব ১)
নিশ্চিত করুন যে লক্ষ্য ডিভাইসটি গভীরতা সমর্থন করে।
ARCore সমর্থিত সব ডিভাইসে Depth API চালানো যায় না। RawDepthCodelabActivity.java এর onResume() ফাংশনের ভিতরে, যেখানে একটি নতুন সেশন তৈরি করা হয়, আপনার অ্যাপে কার্যকারিতা যোগ করার আগে নিশ্চিত করুন যে টার্গেট ডিভাইসটি Depth সমর্থন করে।
বিদ্যমান কোডটি খুঁজুন:
// 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.";
}
কাঁচা গভীরতা সক্ষম করুন
র ডেপথ এপিআই একটি আনস্মুথড ডেপথ ইমেজ এবং একটি সংশ্লিষ্ট কনফিডেন্স ইমেজ প্রদান করে, যেখানে র ডেপথ ইমেজের প্রতিটি পিক্সেলের জন্য ডেপথ কনফিডেন্স থাকে। আপনি এইমাত্র পরিবর্তন করা 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) কল করুন। একটি নতুন ফাইল তৈরি করে ডেপথ ডেটা একটি নতুন ক্লাসের মধ্যে আবদ্ধ করুন। 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.
}
এই ক্লাসটি ডেপথ ইমেজকে পয়েন্টক্লাউডে রূপান্তর করতে ব্যবহৃত হয়। পয়েন্টক্লাউড হলো দৃশ্যের জ্যামিতিকে এমন কিছু বিন্দুর তালিকার মাধ্যমে উপস্থাপন করা, যার প্রতিটির একটি ত্রিমাত্রিক স্থানাঙ্ক (x, y, z) এবং ০ থেকে ১ পরিসরের মধ্যে একটি কনফিডেন্স ভ্যালু থাকে।
ক্লাসের শেষে একটি 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;
}
|
|
|
|
| | | |
The code also stores the camera anchor at this time, so that the depth information can be transformed into world coordinates by calling a helper method convertRawDepthImagesTo3dPointBuffer() . This helper method takes each pixel in the depth image and uses the camera intrinsics to unproject the depth into a 3D point relative to the camera. Then the camera anchor is used to convert the point's position into world coordinates. Each pixel that exists is converted to a 3D point (in units of meters) and stored alongside its confidence.
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);
}
৬. গভীরতার ডেটা রেন্ডার করুন (পর্ব ২)
এখন যেহেতু আপনার কাছে কাজ করার জন্য একটি ডেপথ পয়েন্টক্লাউড আছে, তাই স্ক্রিনে রেন্ডার করা ডেটাগুলো কেমন দেখায় তা দেখার সময় এসেছে।
গভীরতার বিন্দুগুলো দৃশ্যমান করতে একটি রেন্ডারার যোগ করুন
গভীরতার বিন্দুগুলো দৃশ্যমান করার জন্য একটি রেন্ডারার যোগ করুন।
প্রথমে, রেন্ডারিং লজিক ধারণ করার জন্য একটি নতুন ক্লাস যোগ করুন। এই ক্লাসটি ডেপথ পয়েন্টক্লাউডকে ভিজ্যুয়ালাইজ করার জন্য শেডার ইনিশিয়ালাইজ করতে OpenGL অপারেশনগুলো সম্পাদন করে।
| ডেপথ রেন্ডারার ক্লাস যোগ করুন
|
নিম্নলিখিত কোড দিয়ে এই ক্লাসটি পূরণ করুন:
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 মান ৫ পিক্সেল সেট করা আছে।
নতুন শেডার যোগ করুন
আপনার অ্যাপে ডেপথ দেখার এবং ডেপথ ডেটা প্রদর্শন করার অনেক উপায় আছে। এখানে, আপনি কয়েকটি শেডার যোগ করে একটি সাধারণ কালার ম্যাপিং ভিজ্যুয়ালাইজেশন তৈরি করবেন।
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();
বিদ্যমান backgroundRenderer মতোই, depthRenderer RawDepthCodelabActivity.onSurfaceCreated() ভিতরে ইনিশিয়ালাইজ করতে হবে।
depthRenderer.createOnGlThread(/*context=*/ this);
বর্তমান ফ্রেমের সর্বশেষ ডেপথ দেখানোর জন্য onDrawFrame ভেতরের try-catch ব্লকের শেষে নিম্নলিখিত কোডটি যোগ করুন।
// Visualize depth points.
depthRenderer.update(points);
depthRenderer.draw(camera);
এই পরিবর্তনগুলোর ফলে, অ্যাপটি এখন সফলভাবে বিল্ড হবে এবং ডেপথ পয়েন্টক্লাউড প্রদর্শন করবে।
| কাঁচা গভীরতা পয়েন্টক্লাউড ভিজ্যুয়ালাইজেশনের উদাহরণ
|
৭. ত্রিমাত্রিক পয়েন্ট ক্লাউড বিশ্লেষণ (পর্ব ৩)
একটি AR সেশনে ডেপথ ডেটার অস্তিত্ব যাচাই করার পর আপনি তা বিশ্লেষণ করতে পারেন। ডেপথ বিশ্লেষণের জন্য একটি গুরুত্বপূর্ণ টুল হলো প্রতিটি পিক্সেলের কনফিডেন্স ভ্যালু। 3D পয়েন্ট ক্লাউড বিশ্লেষণ করতে কনফিডেন্স ভ্যালু ব্যবহার করুন।
কম আত্মবিশ্বাসের পিক্সেলগুলি বাতিল করুন
আপনি প্রতিটি ডেপথ পিক্সেলের কনফিডেন্স ভ্যালু সংগ্রহ করে DepthData ভেতরে প্রতিটি পয়েন্টের পাশে সংরক্ষণ করেছেন, কিন্তু এখনও সেটি ব্যবহার করেননি।
confidenceNormalized এর মান ০ থেকে ১ পর্যন্ত হয়ে থাকে, যেখানে ০ কম আত্মবিশ্বাস এবং ১ পূর্ণ আত্মবিশ্বাস নির্দেশ করে। 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 *********
কনফিডেন্স লেভেলের জন্য বিভিন্ন থ্রেশহোল্ড পরীক্ষা করে দেখুন, প্রতিটি লেভেলে কতগুলো ডেপথ পয়েন্ট রাখা হয়।
|
|
|
|
|
আত্মবিশ্বাস >= ০.১ | আত্মবিশ্বাস >= ০.৩ | আত্মবিশ্বাস >= ০.৫ | আত্মবিশ্বাস >= ০.৭ | আত্মবিশ্বাস >= ০.৯ |
দূরত্ব অনুসারে পিক্সেল ফিল্টার করুন
আপনি দূরত্ব অনুসারেও ডেপথ পিক্সেল ফিল্টার করতে পারেন। পরবর্তী ধাপগুলো ক্যামেরার কাছাকাছি জ্যামিতি নিয়ে কাজ করে। পারফরম্যান্স অপ্টিমাইজেশনের জন্য, আপনি খুব বেশি দূরে থাকা পয়েন্টগুলোকে উপেক্ষা করতে পারেন।
আপনি এইমাত্র যে কনফিডেন্স-চেকিং কোডটি যোগ করেছেন, সেটি নিম্নলিখিত কোড দিয়ে আপডেট করুন:
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;
}
এখন আপনি শুধু উচ্চ-আত্মবিশ্বাস এবং নিকটবর্তী পয়েন্টগুলো দেখতে পাবেন।
| দূরত্ব ফিল্টারিংপয়েন্টক্লাউডটিকে ক্যামেরা থেকে ১.৫ মিটারের মধ্যে সীমাবদ্ধ রাখে। |
ত্রিমাত্রিক বিন্দু এবং সমতলের তুলনা করুন
আপনি জ্যামিতিক 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);
}
}
}
আপনি এই পদ্ধতিটি 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 সেশনের মাধ্যমে Raw Depth পুনরুদ্ধার করতে, সেই ডেপথ তথ্যকে 3D পয়েন্টক্লাউডে রূপান্তর করতে এবং সেই পয়েন্টগুলোর উপর মৌলিক ফিল্টারিং ও রেন্ডারিং অপারেশন সম্পাদন করতে পারবেন।
৮. নির্মাণ করুন-চালান করুন-পরীক্ষা করুন
আপনার অ্যাপটি তৈরি করুন, চালান এবং পরীক্ষা করুন।
আপনার অ্যাপ তৈরি করুন এবং চালান
আপনার অ্যাপটি তৈরি ও চালানোর জন্য এই ধাপগুলো অনুসরণ করুন:
- USB-এর মাধ্যমে একটি ARCore সমর্থিত ডিভাইস সংযুক্ত করুন।
- মেনু বারের ► বোতামটি দিয়ে আপনার প্রজেক্টটি চালান।
- অ্যাপটি বিল্ড হয়ে আপনার ডিভাইসে ডেপ্লয় হওয়া পর্যন্ত অপেক্ষা করুন।
প্রথমবার আপনার ডিভাইসে অ্যাপটি স্থাপন করার চেষ্টা করার সময় আপনাকে যা করতে হবে
ইউএসবি ডিবাগিংয়ের অনুমতি দিন
on the device. Select OK to continue.
ডিভাইসে প্রথমবার আপনার অ্যাপটি চালানোর সময়, অ্যাপটির ডিভাইসের ক্যামেরা ব্যবহার করার অনুমতি আছে কিনা তা জানতে চাওয়া হবে। এআর (AR) কার্যকারিতা ব্যবহার চালিয়ে যাওয়ার জন্য আপনাকে অবশ্যই অনুমতি দিতে হবে।
আপনার অ্যাপ পরীক্ষা করা হচ্ছে
যখন আপনি আপনার অ্যাপটি চালাবেন, তখন আপনার ডিভাইসটি হাতে ধরে, চারপাশের জায়গায় ঘুরে এবং ধীরে ধীরে একটি এলাকা স্ক্যান করে এর প্রাথমিক আচরণ পরীক্ষা করতে পারেন। পরবর্তী ধাপে যাওয়ার আগে অন্তত ১০ সেকেন্ডের ডেটা সংগ্রহ করার এবং বিভিন্ন দিক থেকে এলাকাটি স্ক্যান করার চেষ্টা করুন।
৯. অভিনন্দন
অভিনন্দন, আপনি গুগলের ARCore Raw Depth API ব্যবহার করে আপনার প্রথম ডেপথ-ভিত্তিক অগমেন্টেড রিয়েলিটি অ্যাপটি সফলভাবে তৈরি ও চালু করেছেন। আপনি ভবিষ্যতে কী তৈরি করবেন তা দেখার জন্য আমরা অধীর আগ্রহে অপেক্ষা করছি!
১০. সমস্যা সমাধান
ডেভেলপমেন্টের জন্য আপনার অ্যান্ড্রয়েড ডিভাইস সেট আপ করা
- একটি ইউএসবি ক্যাবলের মাধ্যমে আপনার ডিভাইসটিকে ডেভেলপমেন্ট মেশিনের সাথে সংযুক্ত করুন। আপনি যদি উইন্ডোজ ব্যবহার করে ডেভেলপ করেন, তাহলে আপনার ডিভাইসের জন্য উপযুক্ত ইউএসবি ড্রাইভার ইনস্টল করার প্রয়োজন হতে পারে।
- ডেভেলপার অপশন উইন্ডোতে ইউএসবি ডিবাগিং চালু করতে নিম্নলিখিত ধাপগুলো অনুসরণ করুন:
- সেটিংস অ্যাপটি খুলুন।
- আপনার ডিভাইসটি অ্যান্ড্রয়েড ৮.০ বা তার উচ্চতর সংস্করণ ব্যবহার করলে, সিস্টেম নির্বাচন করুন।
- একদম নিচে স্ক্রোল করুন এবং 'About phone' নির্বাচন করুন।
- একদম নিচে স্ক্রোল করুন এবং বিল্ড নম্বরে সাতবার ট্যাপ করুন।
- আগের স্ক্রিনে ফিরে যান, একদম নিচে স্ক্রোল করুন এবং 'ডেভেলপার অপশনস'-এ ট্যাপ করুন।
- ডেভেলপার অপশন উইন্ডোতে, নিচে স্ক্রল করে ইউএসবি ডিবাগিং (USB debugging) খুঁজুন এবং এটি চালু করুন।
এই প্রক্রিয়াটি সম্পর্কে আরও বিস্তারিত তথ্য আপনি গুগলের অ্যান্ড্রয়েড ডেভেলপার ওয়েবসাইটে পেতে পারেন।
লাইসেন্স সম্পর্কিত বিল্ড ব্যর্থতা
লাইসেন্স-সম্পর্কিত কোনো বিল্ড ব্যর্থতার সম্মুখীন হলে ( Failed to install the following Android SDK packages as some licences have not been accepted ), আপনি এই লাইসেন্সগুলি পর্যালোচনা ও গ্রহণ করার জন্য নিম্নলিখিত কমান্ডগুলি ব্যবহার করতে পারেন:
cd <path to Android SDK>
tools/bin/sdkmanager --licenses

























