Kedalaman Mentah ARCore

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 bingkai dalam sesi ARCore. Setiap piksel dalam gambar kedalaman menyediakan pengukuran jarak dari kamera ke lingkungan.

Raw Depth API memberikan gambar kedalaman yang tidak diteruskan melalui operasi pemfilteran ruang layar yang dirancang untuk menghaluskan dan menginterpolasi hasilnya. Nilai ini lebih akurat secara geometris, tetapi mungkin berisi data yang tidak ada dan kurang sejajar dengan gambar kamera terkait.

Codelab ini menampilkan cara menggunakan Raw Depth API untuk melakukan analisis geometri 3D pada adegan. Anda akan membangun aplikasi sederhana berkemampuan AR yang menggunakan data kedalaman mentah untuk mendeteksi dan memvisualisasikan geometri dunia.

Depth API dan Raw Depth API hanya didukung pada subset perangkat yang diaktifkan ARCore. Depth API hanya tersedia di Android.

Yang akan Anda build

Dalam codelab ini, Anda akan membangun aplikasi yang menggunakan gambar kedalaman mentah untuk setiap bingkai guna melakukan analisis geometris dunia di sekitar Anda. Aplikasi ini akan:

  1. Periksa apakah perangkat target mendukung Depth.
  2. Ambil gambar kedalaman mentah untuk setiap bingkai kamera.
  3. Proyek ulang gambar kedalaman mentah ke titik 3D dan filter titik tersebut berdasarkan kepercayaan diri dan geometri.
  4. Gunakan cloud titik kedalaman mentah untuk menyegmentasi objek 3D yang menarik.

Pratinjau singkat tentang apa yang akan Anda buat.

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 mendukung ARCore dengan proses debug USB aktif, yang terhubung melalui kabel USB ke mesin pengembangan. Perangkat ini juga harus mendukung Depth API.

Persyaratan software

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.

Mendownload dan menginstal 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.

  1. Luncurkan Android Studio dan pilih Open an existing Android Studio project.
  2. Buka direktori lokal tempat Anda menyimpan file ZIP Raw Depth.
  3. Klik dua kali pada 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:

  • part0_work: Aplikasi awal. Anda harus mengedit modul ini saat menjalani codelab ini. Semua bagian lainnya berisi kode referensi.
  • part1: Kode referensi mengenai bagaimana seharusnya hasil editan Anda saat menyelesaikan Bagian 1.
  • part2: Kode referensi saat Anda menyelesaikan Bagian 2.
  • part3_completed: Kode referensi saat Anda menyelesaikan Bagian 3, yang merupakan akhir codelab.

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.

  1. Buka Run > Jalankan... > ‘part0_work'.
  2. Dalam dialog Select Deployment Target, pilih perangkat dari daftar Terhubung Devices dan 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 menunjukkan tampilan kamera adegan Anda, tetapi tidak melakukan hal lain.Kode yang ada mirip dengan contoh Hello AR yang dipublikasikan dengan ARCore SDK.

Selanjutnya, Anda akan menggunakan Raw Depth API untuk mengambil geometri adegan di sekitar.

5. Menyiapkan Raw Depth API (Bagian 1)

Pastikan perangkat target mendukung Depth

Tidak semua perangkat yang didukung ARCore dapat menjalankan Depth API. Pastikan perangkat target mendukung Depth 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);

Update untuk memastikan aplikasi hanya berjalan pada 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.";
}

Aktifkan Kedalaman Mentah

Raw Depth API memberikan gambar kedalaman yang tidak dihaluskan dan gambar keyakinan terkait 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;
}

Sekarang Sesi AR telah dikonfigurasi dengan benar, dan aplikasi dapat menggunakan fitur berbasis kedalaman.

Memanggil Depth API

Selanjutnya, panggil Depth API untuk mengambil gambar kedalaman untuk setiap bingkai. 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 pointcloud. Pointclouds mewakili geometri adegan dengan daftar titik yang masing-masing memiliki koordinat 3D (x, y, z) dan nilai kepercayaan 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 membuat kueri gambar kedalaman dan tingkat keyakinan terbaru, yang menyimpan pointcloud yang dihasilkan. Gambar kedalaman dan tingkat 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;
}

acquireCameraImage()

acquireDepthImage16Bits()

acquireRawDepthImage16Bits()

acquireRawDepthConfidenceImage()

Kode ini juga menyimpan anchor kamera pada saat ini, sehingga informasi kedalaman dapat diubah menjadi koordinat dunia dengan memanggil metode helper convertRawDepthImagesTo3dPointBuffer(). Metode bantuan ini mengambil setiap piksel dalam gambar kedalaman dan menggunakan intrinsik kamera untuk membatalkan proyeksi kedalaman ke titik 3D yang relatif terhadap kamera. Selanjutnya, anchor kamera digunakan untuk mengonversi posisi titik menjadi koordinat dunia. Setiap piksel yang ada dikonversi ke 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 Raw Depth terbaru untuk setiap frame

Memodifikasi aplikasi untuk mengambil informasi kedalaman dan menyelaraskannya dengan koordinat dunia untuk setiap pose.

Di RawDepthCodelabActivity.java, dalam metode onDrawFrame(), temukan baris yang 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)

Sekarang setelah Anda memiliki titik kedalaman yang dapat digunakan, saatnya untuk 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 pointcloud kedalaman.

Menambahkan class DepthRenderer

  1. Klik kanan direktori sumber rendering
  2. Pilih New > Java Class.
  3. Beri nama 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, sediakan sumber untuk shader rendering. Tambahkan metode update() berikut di bagian bawah class DepthRenderer. Metode ini mengambil informasi kedalaman terbaru sebagai input dan menyalin data pointcloud 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");
    }

Gambar data terbaru ke layar dengan menambahkan metode draw() ke bagian bawah class DepthRenderer. Metode ini mengambil informasi pointcloud 3D dan memproyeksikan 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 titik ke ukuran yang berbeda,dalam piksel, menggunakan variabel pointSizeUniform. pointSizeUniform disetel 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 baru

Di Android Studio:

  1. Klik kanan pada direktori shader
  2. Pilih New -> File
  3. Beri nama shader dengan depth_point_cloud.vert
  4. Tetapkan shader sebagai file teks.

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. Ia melakukan langkah-langkah berikut:

  1. Mengambil ketinggian setiap titik (sumbu y dalam koordinat dunia).
  2. Menghitung warna yang terkait dengan elevasi tersebut (merah=rendah, biru=tinggi).
  3. Menghitung posisi layar setiap titik.
  4. 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 kelas, tambahkan anggota pribadi di samping backgroundRenderer.

private final DepthRenderer depthRenderer = new DepthRenderer();

depthRenderer harus diinisialisasi di dalam RawDepthCodelabActivity.onSurfaceCreated(), seperti backgroundRenderer yang ada.

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

Tambahkan kode berikut di akhir blok try-catch di dalam onDrawFrame guna menampilkan kedalaman terbaru untuk frame saat ini.

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

Dengan perubahan ini, aplikasi kini seharusnya berhasil dibangun dan menampilkan pointcloud kedalaman.

Contoh visualisasi pointcloud kedalaman mentah

  • Setiap sampel titik diberi warna berdasarkan kedalamannya.
  • Titik merah dekat, titik hijau/biru lebih jauh
  • Beberapa data atau "lubang" yang hilang dapat dilihat di area dengan fitur gambar yang tidak memadai, seperti dinding atau langit-langit putih yang kosong.
  • Anda dapat mencoba ukuran titik yang dirender dengan menyesuaikan baris GLES20.glUniform1f(pointSizeUniform, 5.0f); di dalam DepthRenderer.draw(). Ukuran titik 5 dan 10 yang ditampilkan di sebelah kiri.

7. Menganalisis awan titik 3D (Bagian 3)

Anda dapat menganalisis data kedalaman setelah memverifikasi bahwa data tersebut ada dalam sesi AR. Alat penting untuk menganalisis kedalaman adalah nilai confidence untuk setiap piksel. Gunakan nilai keyakinan untuk menganalisis cloud titik 3D.

Membatalkan piksel keyakinan rendah

Anda telah mengambil nilai keyakinan untuk setiap piksel kedalaman dan menyimpannya di samping 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 dapat digunakan.

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 ambang batas untuk tingkat kepercayaan untuk melihat berapa banyak titik kedalaman yang disimpan di setiap tingkat.

Keyakinan >= 0,1

Keyakinan >= 0,3

Keyakinan >= 0,5

Keyakinan >= 0,7

Keyakinan >= 0,9

Filter piksel menurut jarak

Anda juga dapat memfilter piksel kedalaman menurut jarak. Langkah-langkah berikutnya ini menangani 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 poin dengan keyakinan tinggi dan poin penutupan.

Pemfilteran jarak

Membatasi pointcloud berada dalam jarak 1,5 meter dari kamera.

Bandingkan titik 3D dan bidang

Anda dapat membandingkan titik geometri dan bidang 3D serta menggunakannya untuk memfilter satu sama lain, seperti menghapus titik yang dekat dengan bidang AR yang diamati.

Langkah ini hanya akan menyisakan "non-planar" titik yang cenderung mewakili 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 titik apa pun yang terlalu dekat dengan bidang AR, meninggalkan 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 menghasilkan subset titik yang dirender. Titik-titik ini mewakili objek dalam adegan, sambil mengabaikan permukaan datar tempat objek diletakkan. Anda dapat menggunakan data ini untuk memperkirakan ukuran dan posisi objek dengan mengelompokkan titik-titik pengelompokan.

Secangkir Teh

Mikrofon

Headphone

Bantal

Titik-titik klaster

Codelab ini berisi algoritma pengelompokan pointcloud yang sangat sederhana. Update codelab untuk mengelompokkan pointcloud yang diambil ke dalam cluster yang ditentukan oleh kotak pembatas yang disejajarkan 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();

Dan di dalam metode onSurfaceCreated(), tambahkan hal berikut bersama perender lainnya:

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

Terakhir, tambahkan baris berikut ke onDrawFrame() di dalam RawDepthCodelabActivity untuk mengelompokkan pointcloud yang diambil ke dalam cluster dan merender hasilnya sebagai kotak pembatas yang sejajar 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 sekarang dapat mengambil Raw Depth melalui sesi ARCore, mengonversi informasi kedalaman menjadi pointcloud 3D, serta 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:

  1. Colokkan perangkat yang didukung ARCore melalui USB.
  2. Jalankan proyek Anda dengan tombol ► di bilah menu.
  3. Tunggu aplikasi mem-build dan men-deploy ke perangkat Anda.

Saat pertama kali 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 telah berhasil membangun dan menjalankan aplikasi Augmented Reality berbasis kedalaman pertama Anda menggunakan ARCore Raw Depth API Google. Kami tidak sabar untuk melihat apa yang akan Anda bangun!

10. Pemecahan masalah

Menyiapkan perangkat Android untuk pengembangan

  1. Hubungkan perangkat Anda ke mesin pengembangan dengan kabel USB. Jika mengembangkan menggunakan Windows, Anda mungkin harus menginstal driver USB yang sesuai dengan perangkat.
  2. 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.

Jika mengalami kegagalan build terkait 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 ini:

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

Pertanyaan umum (FAQ)