1. Pengantar
ARCore adalah platform untuk mem-build aplikasi Augmented Reality (AR) di perangkat seluler. ARCore Depth API Google memberikan akses ke gambar kedalaman untuk setiap frame dalam sesi ARCore. Setiap piksel dalam gambar kedalaman memberikan pengukuran jarak dari kamera ke lingkungan.
Raw Depth API memberikan gambar kedalaman yang tidak diteruskan melalui operasi pemfilteran ruang layar yang dirancang untuk memperlancar dan menginterpolasi hasilnya. Nilai ini lebih akurat secara geometris, tetapi mungkin berisi data yang hilang dan kurang selaras dengan gambar kamera terkait.
Codelab ini menunjukkan cara menggunakan Raw Depth API untuk melakukan analisis geometri 3D pada adegan. Anda akan mem-build aplikasi sederhana yang berkemampuan AR dan menggunakan data kedalaman mentah untuk mendeteksi dan memvisualisasikan geometri dunia.
Depth API dan Raw Depth API hanya didukung di subkumpulan perangkat yang kompatibel dengan ARCore. Depth API hanya tersedia di Android.
Yang akan Anda build
Dalam codelab ini, Anda akan mem-build aplikasi yang menggunakan gambar kedalaman mentah untuk setiap frame guna melakukan analisis geometris dunia di sekitar Anda. Aplikasi ini akan:
- Periksa apakah perangkat target mendukung Depth.
- Ambil gambar kedalaman mentah untuk setiap frame kamera.
- Memproyeksikan ulang gambar kedalaman mentah ke dalam titik 3D dan memfilter titik tersebut berdasarkan keyakinan dan geometri.
- Gunakan cloud titik kedalaman mentah untuk menyegmentasikan objek 3D yang diminati.
|
Pratinjau singkat tentang apa yang akan Anda bangun. |
Catatan: Jika ada masalah selama menjalani proses, langsung lihat bagian terakhir untuk mengetahui tips pemecahan masalah.
2. Prasyarat
Anda membutuhkan hardware dan software tertentu untuk menyelesaikan codelab ini.
Persyaratan hardware
- Perangkat yang didukung ARCore dengan proses debug USB yang diaktifkan, terhubung melalui kabel USB ke mesin pengembangan Anda. Perangkat ini juga harus mendukung Depth API.
Persyaratan software
- ARCore SDK 1.31.0 atau yang lebih baru.
- Mesin pengembangan dengan Android Studio (v4.0.1 atau yang lebih baru) terinstal.
3. Siapkan
Menyiapkan mesin pengembangan
Hubungkan perangkat ARCore ke komputer melalui kabel USB. Pastikan perangkat memungkinkan proses debug USB. Buka terminal dan jalankan adb devices, seperti yang ditampilkan di bawah:
adb devices List of devices attached <DEVICE_SERIAL_NUMBER> device
<DEVICE_SERIAL_NUMBER> akan berupa string yang unik untuk perangkat. Pastikan bahwa Anda hanya melihat satu perangkat sebelum melanjutkan.
Download dan instal kode
Anda dapat meng-clone repositori:
git clone https://github.com/googlecodelabs/arcore-rawdepthapi
Atau mendownload file ZIP lalu mengekstraknya:
Ikuti langkah-langkah berikut untuk mulai menggunakan kode.
- Luncurkan Android Studio dan pilih Open an existing Android Studio project.
- Buka direktori lokal tempat Anda menyimpan file ZIP Raw Depth.
- Klik dua kali direktori
arcore_rawdepthapi_codelab.
Direktori arcore_rawdepthapi_codelab adalah satu project Gradle dengan beberapa modul. Jika panel Project di kiri atas Android Studio belum ditampilkan di panel Project, klik Projects dari menu drop-down.
Hasilnya akan terlihat seperti ini:
| Project ini berisi modul berikut:
|
Anda akan mengerjakan modul part0_work. Selain itu, ada solusi lengkap untuk setiap bagian codelab. Setiap modul adalah aplikasi yang dapat di-build.
4. Menjalankan aplikasi awal
Ikuti langkah-langkah berikut untuk menjalankan aplikasi awal Raw Depth.
- Buka Run > Run... > ‘part0_work'.
- Dalam dialog Select Deployment Target, pilih perangkat Anda dari daftar Connected Devices, lalu klik OK.
Android Studio akan mem-build aplikasi awal dan menjalankannya di perangkat Anda.
| Saat Anda menjalankan aplikasi untuk pertama kalinya, aplikasi akan meminta izin KAMERA. Ketuk Izinkan untuk melanjutkan. |
| Saat ini, aplikasi tidak melakukan apa pun.Ini adalah aplikasi AR paling dasar, yang menampilkan tampilan kamera dari adegan Anda, tetapi tidak melakukan apa pun.Kode yang ada mirip dengan sampel Hello AR yang dipublikasikan dengan ARCore SDK. |
Selanjutnya, Anda akan menggunakan Raw Depth API untuk mengambil geometri adegan di sekitar Anda.
5. Menyiapkan Raw Depth API (Bagian 1)
Pastikan perangkat target mendukung Kedalaman
Tidak semua perangkat yang didukung ARCore dapat menjalankan Depth API. Pastikan perangkat target mendukung Kedalaman sebelum menambahkan fungsi ke aplikasi Anda di dalam fungsi onResume() dari RawDepthCodelabActivity.java, tempat Sesi baru dibuat.
Temukan kode yang sudah ada:
// Create the ARCore session.
session = new Session(/* context= */ this);
Perbarui untuk memastikan bahwa aplikasi hanya berjalan di perangkat yang dapat mendukung 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.";
}
Mengaktifkan Kedalaman Mentah
Raw Depth API menyediakan gambar kedalaman yang tidak dihaluskan dan gambar keyakinan yang sesuai yang berisi keyakinan kedalaman untuk setiap piksel dalam gambar kedalaman mentah. Aktifkan Raw Depth dengan memperbarui kode berikut di bawah pernyataan try-catch yang baru saja Anda ubah.
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;
}
Sesi AR sudah dikonfigurasi dengan benar. Kini, aplikasi dapat menggunakan fitur berbasis kedalaman.
Memanggil Depth API
Selanjutnya, panggil Depth API untuk mengambil gambar kedalaman untuk setiap frame. Enkapsulasi data kedalaman ke dalam class baru dengan membuat file baru. Klik kanan folder rawdepth, lalu pilih New > Java Class. Tindakan ini akan membuat file kosong. Tambahkan kode berikut ke class ini:
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.
}
Class ini digunakan untuk mengonversi gambar kedalaman menjadi point cloud. Point cloud merepresentasikan geometri adegan dengan daftar titik yang masing-masing memiliki koordinat 3D (x, y, z) dan nilai keyakinan dalam rentang 0 hingga 1.
Tambahkan panggilan untuk mengisi nilai ini menggunakan Raw Depth API dengan menambahkan metode create() di bagian bawah class. Metode ini mengkueri gambar kedalaman dan keyakinan terbaru, lalu menyimpan point cloud yang dihasilkan. Gambar depth dan keyakinan akan memiliki data yang cocok.
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;
}
|
|
|
|
|
|
|
|
Kode juga menyimpan anchor kamera pada saat ini, sehingga informasi kedalaman dapat diubah menjadi koordinat dunia dengan memanggil metode helper convertRawDepthImagesTo3dPointBuffer(). Metode helper ini mengambil setiap piksel dalam gambar kedalaman dan menggunakan intrinsik kamera untuk membatalkan proyeksi kedalaman menjadi titik 3D relatif terhadap kamera. Kemudian, anchor kamera digunakan untuk mengonversi posisi titik menjadi koordinat dunia. Setiap piksel yang ada dikonversi menjadi titik 3D (dalam satuan meter) dan disimpan bersama keyakinannya.
Tambahkan metode helper berikut ke 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;
}
Mendapatkan data Kedalaman Mentah terbaru untuk setiap frame
Ubah aplikasi untuk mengambil informasi kedalaman dan menyelaraskannya dengan koordinat dunia untuk setiap pose.
Di RawDepthCodelabActivity.java, dalam metode onDrawFrame(), temukan baris yang sudah ada:
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);
Tambahkan baris berikut tepat di bawahnya:
// 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. Merender data kedalaman (Bagian 2)
Setelah Anda memiliki point cloud kedalaman yang dapat dijadikan eksperimen, sekarang saatnya melihat tampilan data yang dirender di layar.
Menambahkan perender untuk memvisualisasikan titik kedalaman
Tambahkan perender untuk memvisualisasikan titik kedalaman.
Pertama, tambahkan class baru untuk memuat logika rendering. Class ini melakukan operasi OpenGL untuk menginisialisasi shader guna memvisualisasikan point cloud kedalaman.
| Menambahkan class DepthRenderer
|
Isi class ini dengan kode berikut:
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");
}
}
Merender data kedalaman
Selanjutnya, berikan sumber untuk shader rendering. Tambahkan metode update() berikut di bagian bawah class DepthRenderer. Metode ini mengambil informasi kedalaman terbaru sebagai input dan menyalin data point cloud ke 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");
}
Gambarkan data terbaru ke layar dengan menambahkan metode draw() di bagian bawah class DepthRenderer. Metode ini mengambil informasi point cloud 3D dan memproyeksikannya kembali ke tampilan kamera sehingga dapat dirender di layar.
/** 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");
}
Anda dapat menetapkan ukuran huruf ke ukuran yang berbeda,dalam piksel, menggunakan variabel pointSizeUniform. pointSizeUniform ditetapkan ke 5 piksel di aplikasi contoh.
Menambahkan shader baru
Ada banyak cara untuk melihat kedalaman dan menampilkan data kedalaman di aplikasi Anda. Di sini, Anda akan menambahkan beberapa shader dan membuat visualisasi pemetaan warna sederhana.
Tambahkan shader .vert dan .frag baru ke direktori src/main/assets/shaders/.
| Menambahkan shader .vert yang baruDi Android Studio:
|
Di file .vert baru, tambahkan kode berikut:
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;
}
Shader ini menggunakan peta warna Turbo untuk visualisasi yang ditingkatkan. Skrip ini melakukan langkah-langkah berikut:
- Mengambil elevasi setiap titik (sumbu y dalam koordinat dunia).
- Menghitung warna yang terkait dengan elevasi tersebut (merah=rendah, biru=tinggi).
- Menghitung posisi layar setiap titik.
- Menetapkan ukuran (dalam piksel) untuk setiap titik, seperti yang ditentukan dalam metode
DepthRenderer.update().
Buat shader fragmen di direktori yang sama dan beri nama depth_point_cloud.frag, dengan mengulangi langkah-langkah yang sama di bagian ini.
Kemudian, tambahkan kode berikut ke file baru ini untuk merender setiap titik sebagai satu verteks dengan warna seragam, seperti yang ditentukan dalam shader verteks.
src/main/assets/shaders/depth_point_cloud.frag
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
Untuk menerapkan rendering ini, tambahkan panggilan ke class DepthRenderer di dalam RawDepthCodelabActivity Anda.
src/main/java/com/google/ar/core/codelab/common/rendering/RawDepthCodelabActivity.java
import com.google.ar.core.codelab.common.rendering.DepthRenderer;
Di bagian atas class, tambahkan anggota pribadi di samping backgroundRenderer.
private final DepthRenderer depthRenderer = new DepthRenderer();
depthRenderer perlu diinisialisasi di dalam RawDepthCodelabActivity.onSurfaceCreated(), seperti backgroundRenderer yang ada.
depthRenderer.createOnGlThread(/*context=*/ this);
Tambahkan kode berikut di akhir blok try-catch di dalam onDrawFrame untuk menampilkan kedalaman terbaru untuk frame saat ini.
// Visualize depth points.
depthRenderer.update(points);
depthRenderer.draw(camera);
Dengan perubahan ini, aplikasi kini akan berhasil dibuat dan menampilkan point cloud kedalaman.
| Contoh visualisasi point cloud kedalaman mentah
|
7. Menganalisis point cloud 3D (Bagian 3)
Anda dapat menganalisis data kedalaman setelah memverifikasi bahwa data tersebut ada dalam sesi AR. Alat penting untuk menganalisis kedalaman adalah nilai keyakinan untuk setiap piksel. Gunakan nilai keyakinan untuk menganalisis awan titik 3D.
Membatalkan validasi piksel dengan keyakinan rendah
Anda telah mengambil nilai keyakinan untuk setiap piksel kedalaman dan menyimpannya bersama setiap titik di dalam DepthData, tetapi Anda belum menggunakannya.
Nilai confidenceNormalized berkisar dari 0 hingga 1, dengan 0 menunjukkan keyakinan rendah, dan 1 menunjukkan keyakinan penuh. Ubah metode convertRawDepthImagesTo3dPointBuffer() di class DepthData untuk menghindari penyimpanan piksel yang tingkat keyakinannya terlalu rendah sehingga tidak berguna.
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 *********
Coba berbagai nilai minimum untuk tingkat keyakinan guna melihat jumlah titik kedalaman yang dipertahankan di setiap tingkat.
|
|
|
|
|
Keyakinan >= 0,1 | Keyakinan >= 0,3 | Keyakinan >= 0,5 | Keyakinan >= 0,7 | Keyakinan >= 0,9 |
Memfilter piksel menurut jarak
Anda juga dapat memfilter piksel kedalaman menurut jarak. Langkah berikutnya ini berkaitan dengan geometri yang dekat dengan kamera. Untuk pengoptimalan performa, Anda dapat mengabaikan titik yang terlalu jauh.
Perbarui kode pemeriksaan keyakinan yang baru saja Anda tambahkan dengan kode berikut:
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;
}
Sekarang Anda hanya akan melihat titik yang memiliki keyakinan tinggi dan berdekatan.
| Pemfilteran jarakMembatasi point cloud agar berada dalam jarak 1,5 meter dari kamera. |
Membandingkan titik dan bidang 3D
Anda dapat membandingkan titik dan bidang 3D geometri serta menggunakannya untuk memfilter satu sama lain, seperti menghapus titik yang dekat dengan bidang AR yang diamati.
Langkah ini hanya akan menyisakan titik "non-planar" yang cenderung merepresentasikan permukaan pada objek di lingkungan. Tambahkan metode filterUsingPlanes() ke bagian bawah class DepthData. Metode ini melakukan iterasi melalui titik yang ada, memeriksa setiap titik terhadap setiap bidang, dan membatalkan validasi titik yang terlalu dekat dengan bidang AR, sehingga menyisakan area non-planar yang menyoroti objek dalam adegan.
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);
}
}
}
Anda dapat menambahkan metode ini ke RawDepthCodelabActivity dalam metode 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);
Menjalankan codelab sekarang akan menghasilkan subset titik yang dirender. Titik-titik ini merepresentasikan objek dalam adegan, sekaligus mengabaikan permukaan datar tempat objek berada. Anda dapat menggunakan data ini untuk memperkirakan ukuran dan posisi objek dengan mengelompokkan titik-titik.
|
|
|
|
Secangkir Teh | Mikrofon | Headphone | Bantal |
Titik cluster
Codelab ini berisi algoritma pengelompokan point cloud yang sangat sederhana. Perbarui codelab untuk mengelompokkan point cloud yang diambil ke dalam cluster yang ditentukan oleh kotak pembatas yang sejajar dengan sumbu.
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;
Tambahkan BoxRenderer ke class ini di bagian atas file, dengan perender lainnya.
private final BoxRenderer boxRenderer = new BoxRenderer();
Di dalam metode onSurfaceCreated(), tambahkan yang berikut bersama perender lainnya:
boxRenderer.createOnGlThread(/*context=*/this);
Terakhir, tambahkan baris berikut ke onDrawFrame() di dalam RawDepthCodelabActivity untuk mengelompokkan point cloud yang diambil ke dalam cluster dan merender hasilnya sebagai kotak pembatas yang sejajar dengan sumbu.
// 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 ***************
|
|
|
|
Secangkir Teh | Mikrofon | Headphone | Bantal |
Anda kini dapat mengambil Kedalaman Mentah melalui sesi ARCore, mengonversi informasi kedalaman menjadi point cloud 3D, dan melakukan operasi pemfilteran dan rendering dasar pada titik tersebut.
8. Build-Jalankan-Uji
Bangun, jalankan, dan uji aplikasi Anda.
Membuat dan menjalankan aplikasi
Ikuti langkah-langkah berikut untuk mem-build dan menjalankan aplikasi:
- Colokkan perangkat yang didukung ARCore melalui USB.
- Jalankan project Anda dengan tombol ► di panel menu.
- Tunggu aplikasi mem-build dan men-deploy ke perangkat Anda.
Saat pertama kali Anda mencoba men-deploy aplikasi ke perangkat, Anda harus
Izinkan proses debug USB
di perangkat. Pilih Oke untuk melanjutkan.
Saat pertama kali menjalankan aplikasi di perangkat, Anda akan ditanya apakah aplikasi memiliki izin untuk menggunakan kamera perangkat. Anda harus mengizinkan akses untuk melanjutkan penggunaan fungsi AR.
Menguji Aplikasi
Saat menjalankan aplikasi, Anda dapat menguji perilaku dasarnya dengan memegang perangkat, menggerakkan perangkat, dan memindai area secara perlahan. Cobalah mengumpulkan data setidaknya selama 10 detik dan pindai area dari beberapa arah sebelum melanjutkan ke langkah berikutnya.
9. Selamat
Selamat, Anda berhasil mem-build dan menjalankan aplikasi Augmented Reality berbasis kedalaman pertama Anda menggunakan ARCore Raw Depth API Google. Kami tidak sabar ingin melihat hasil karya Anda.
10. Pemecahan masalah
Menyiapkan perangkat Android untuk pengembangan
- Hubungkan perangkat Anda ke mesin pengembangan dengan kabel USB. Jika mengembangkan menggunakan Windows, Anda mungkin harus menginstal driver USB yang sesuai dengan perangkat.
- Jalankan langkah-langkah berikut untuk mengaktifkan Proses debug USB di jendela Opsi developer:
- Buka aplikasi Setelan.
- Jika perangkat Anda menggunakan Android v8.0 atau lebih tinggi, pilih Sistem.
- Scroll ke bagian bawah, lalu pilih Tentang ponsel.
- Scroll ke bagian bawah, lalu ketuk Nomor build tujuh kali.
- Kembali ke layar sebelumnya, scroll ke bagian bawah, lalu ketuk Opsi developer.
- Di jendela Opsi developer, scroll ke bawah untuk menemukan dan mengaktifkan Proses debug USB.
Anda dapat menemukan informasi yang lebih mendetail tentang proses ini di situs developer Android Google.
Kegagalan build terkait dengan lisensi
Jika mengalami kegagalan build terkait dengan lisensi (Failed to install the following Android SDK packages as some licences have not been accepted), Anda dapat menggunakan perintah berikut untuk meninjau dan menyetujui lisensi berikut:
cd <path to Android SDK>
tools/bin/sdkmanager --licenses

























