ARCore Ham Derinliği

1. Giriş

ARCore, mobil cihazlarda Artırılmış Gerçeklik (AR) uygulamaları oluşturmaya yönelik bir platformdur. Google'ın ARCore Depth API'si, ARCore oturumundaki her kare için bir derinlik görüntüsüne erişim sağlar. Derinlikli resimdeki her piksel, kamera ile çevre arasındaki mesafenin ölçülmesini sağlar.

Raw Depth API, sonuçları düzgünleştirmek ve interpolasyon sağlamak için tasarlanmış ekran alanı filtreleme işlemlerinden geçirilmeyen derinlik görüntüleri sağlar. Bu değerler geometrik açıdan daha doğrudur, ancak eksik veriler içerebilir ve ilişkili kamera resmiyle daha az hizalı olabilir.

Bu codelab'de, sahnenin 3D geometrik analizini gerçekleştirmek için Raw Depth API'nin nasıl kullanılacağı anlatılmaktadır. Dünyanın geometrisini tespit edip görselleştirmek için ham derinlik verileri kullanan, AR özellikli, basit bir uygulama geliştireceksiniz.

Derinlik ve Ham Derinlik API'leri yalnızca ARCore özellikli cihazların bir alt kümesinde desteklenir. Depth API yalnızca Android'de kullanılabilir.

Neler oluşturacaksınız?

Bu codelab'de, etrafınızdaki dünyanın geometrik analizini yapmak üzere her kare için ham derinlikli görüntüler kullanan bir uygulama geliştireceksiniz. Bu uygulama:

  1. Hedef cihazın, Derinliği destekleyip desteklemediğini kontrol edin.
  2. Her kamera çerçevesi için ham derinlikli resim alın.
  3. Ham derinlikli görüntüleri yeniden 3D noktalara dönüştürün ve bu noktaları güven ve geometriye göre filtreleyin.
  4. İlgilenilen 3D nesneleri segmentlere ayırmak için ham derinlik noktası bulutunu kullanın.

Geliştireceğiniz öğenin önceden önizlemesine göz atın.

Not: İşlem sırasında sorunlarla karşılaşırsanız sorun giderme ipuçları için son bölüme geçin.

2. Ön koşullar

Bu codelab'i tamamlamak için belirli donanım ve yazılımlara ihtiyacınız olacaktır.

Donanım gereksinimleri

  • USB üzerinden hata ayıklamanın etkin olduğu, USB kablosuyla geliştirme makinenize bağlanmış, ARCore destekli cihaz. Bu cihaz, Depth API'yi de desteklemelidir.

Yazılım gereksinimleri

3. Kur

Geliştirme makinesini kurma

ARCore cihazınızı USB kablosuyla bilgisayarınıza bağlayın. Cihazınızın USB üzerinden hata ayıklamaya izin verdiğinden emin olun. Bir terminal açın ve aşağıda gösterildiği gibi adb devices komutunu çalıştırın:

adb devices

List of devices attached
<DEVICE_SERIAL_NUMBER>    device

<DEVICE_SERIAL_NUMBER> cihazınıza özel bir dize olur. Devam etmeden önce tam olarak bir cihaz gördüğünüzden emin olun.

Kodu indirme ve yükleme

Depoyu klonlayabilirsiniz:

git clone https://github.com/googlecodelabs/arcore-rawdepthapi

Veya bir ZIP dosyası indirip dosyayı çıkarın:

Kodla çalışmaya başlamak için aşağıdaki adımları uygulayın.

  1. Android Studio'yu başlatın ve Mevcut bir Android Studio projesini aç'ı seçin.
  2. Ham Derinlik ZIP dosyasını depoladığınız yerel dizine gidin.
  3. arcore_rawdepthapi_codelab dizinini çift tıklayın.

arcore_rawdepthapi_codelab dizini, birden fazla modüle sahip tek bir Gradle projesidir. Android Studio'nun sol üst tarafındaki Proje bölmesi Proje bölmesinde görünmüyorsa açılır menüden Projeler'i tıklayın.

Sonuç şu şekilde görünmelidir:

Bu proje aşağıdaki modülleri içermektedir:

  • part0_work: Başlangıç uygulaması. Bu codelab'i yaparken bu modülde düzenlemeler yapmanız gerekir. Diğer tüm bölümler referans kodu içerir.
  • part1: 1. Bölüm'ü tamamladığınızda düzenlemelerinizin nasıl görünmesi gerektiğine dair referans kodu.
  • part2: 2. bölümü tamamladığınızda referans kodu alırsınız.
  • part3_completed: Codelab'in sonundaki 3. bölümü tamamladığınızda kullanılacak referans kodu.

part0_work modülünde çalışacaksınız. Codelab'in her bölümü için eksiksiz çözümler de mevcuttur. Her modül, derlenebilir bir uygulamadır.

4. Başlangıç uygulamasını çalıştırma

Ham Derinlik başlangıç uygulamasını çalıştırmak için aşağıdaki adımları uygulayın.

  1. Çalıştır > Çalıştır... &gt; "part0_work".
  2. Select Deployment Target (Dağıtım Hedefi Seçin) iletişim kutusunda, Bağlı Cihazlar listesinden cihazınızı seçin ve Tamam'ı tıklayın.

Android Studio ilk uygulamayı oluşturur ve cihazınızda çalıştırır.

Uygulamayı ilk kez çalıştırdığınızda CAMERA izni istenir. Devam etmek için İzin ver'e dokunun.

Şu anda uygulama herhangi bir işlem yapmıyor.Bu, sahnenizin kamera görünümünü gösteren ancak başka bir işlem yapmayan en temel AR uygulamasıdır.Mevcut kod, ARCore SDK'sı ile yayınlanan Hello AR örneğine benzer.

Ardından, çevrenizdeki sahnenin geometrisini almak için Raw Depth API'yi kullanırsınız.

5. Raw Depth API'yi ayarlama (1. Bölüm)

Hedef cihazın, Derinliği desteklediğinden emin olun

ARCore'u destekleyen cihazların tümü Depth API'yi çalıştıramaz. Yeni bir Oturumun oluşturulduğu RawDepthCodelabActivity.java ürününün onResume() işlevi içinde uygulamanıza işlev eklemeden önce hedef cihazın Derinliği desteklediğinden emin olun.

Mevcut kodu bulun:

// Create the ARCore session.
session = new Session(/* context= */ this);

Uygulamanın yalnızca Depth API'yi destekleyebilen cihazlarda çalıştığından emin olmak için uygulamayı güncelleyin.

// 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.";
}

Ham Derinliği etkinleştir

Ham Depth API, ham derinlik görüntüsündeki her piksel için derinlik güvenirliğini içeren, pürüzsüz bir derinlik görüntüsü ve buna karşılık gelen bir güven görüntüsü sağlar. Az önce değiştirdiğiniz deneme yakalama deyiminin altında aşağıdaki kodu güncelleyerek Ham Derinlik'i etkinleştirin.

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;
}

AR Oturumu artık uygun şekilde yapılandırıldı ve uygulama, derinliğe dayalı özellikleri kullanabilir.

Depth API'yi çağırma

Ardından, her kare için derinlik görüntüleri almak üzere Depth API'yi çağırın. Yeni bir dosya oluşturarak derinlik verilerini yeni bir sınıfa alın. rawdepth klasörünü sağ tıklayın ve New > Java Class seçeneğini belirleyin. Bu işlem boş bir dosya oluşturur. Bu sınıfa şunları ekleyin:

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.

}

Bu sınıf, derinlikli görüntüleri Pointcloud'lara dönüştürmek için kullanılır. Nokta bulutları, her birinin 3D koordinatına (x, y, z) ve 0 ile 1 aralığında güven değerine sahip olan noktaların listesiyle birlikte sahne geometrisini temsil eder.

Sınıfın altına bir create() yöntemi ekleyerek Ham Depth API'yi kullanarak bu değerleri dolduracak çağrılar ekleyin. Bu yöntem, en son derinlik ve güven görüntülerini sorgulayarak elde edilen Pointcloud'u depolar. Derinlik ve güven görüntülerinde eşleşen veriler yer alır.

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()

Kod, şu anda kamera çapasını da depolar. Böylece derinlik bilgisi, yardımcı bir yöntem olan convertRawDepthImagesTo3dPointBuffer() kullanılarak dünya koordinatlarına dönüştürülebilir. Bu yardımcı yöntem, derinlik resmindeki her bir pikseli alır ve derinliği kameraya göre bir 3D noktaya çıkarmak için kamera içsel özelliklerini kullanır. Daha sonra, kamera çapası, noktanın konumunu dünya koordinatlarına dönüştürmek için kullanılır. Mevcut her piksel bir 3D noktaya (metre cinsinden) dönüştürülür ve güveniyle birlikte depolanır.

Aşağıdaki yardımcı yöntemi DepthData.java'ya ekleyin:

/** 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;
    }

Her kare için en son Ham Derinlik verilerini alın

Uygulamayı derinlik bilgilerini alacak şekilde değiştirin ve her poz için dünya koordinatlarıyla uyumlu hale getirin.

RawDepthCodelabActivity.java hücresinde, onDrawFrame() yönteminde mevcut satırları bulun:

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);

Hemen altına aşağıdaki satırları ekleyin:

// 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. Derinlik verilerini oluşturma (2. Bölüm)

Derinlikli Pointcloud'u kullanabileceğinize göre şimdi verilerin ekranda nasıl oluşturulduğunu görebilirsiniz.

Derinlik noktalarını görselleştirmek için oluşturucu ekleme

Derinlik noktalarını görselleştirmek için oluşturucu ekleyin.

Öncelikle oluşturma mantığını içerecek yeni bir sınıf ekleyin. Bu sınıf, derinlikli Pointcloud'u görselleştirmek üzere gölgelendiricileri başlatmak için OpenGL işlemlerini gerçekleştirir.

DepthRenderer sınıfı ekle

  1. rendering kaynak dizinini sağ tıklayın
  2. New > Java Class seçeneğini belirleyin.
  3. Sınıfı DepthRenderer olarak adlandırın.

Bu sınıfı aşağıdaki kodla doldurun:

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");
    }
}

Derinlik verilerini oluşturma

Ardından, oluşturma gölgelendiricilerinin kaynağını belirtin. DepthRenderer sınıfının sonuna aşağıdaki update() yöntemini ekleyin. Bu yöntem en yeni derinlik bilgilerini giriş olarak alır ve Pointcloud verilerini GPU'ya kopyalar.

    /**
     * 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 sınıfının altına bir draw() yöntemi ekleyerek en güncel verileri ekrana çizin. Bu yöntem, 3D Pointcloud bilgilerini alır ve ekranda oluşturulabilmesi için bu bilgileri tekrar kamera görünümüne yansıtır.

    /** 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 değişkenini kullanarak nokta boyutunu piksel cinsinden farklı boyutlara ayarlayabilirsiniz. pointSizeUniform, örnek uygulamada 5 piksele ayarlandı.

Yeni gölgelendirici ekle

Uygulamanızda derinlik ve derinlik verilerini görüntülemenin birçok yolu vardır. Burada birkaç gölgelendirici ekleyecek ve basit bir renk eşleme görselleştirmesi oluşturacaksınız.

src/main/assets/shaders/ dizinine yeni .vert ve .frag gölgelendiricileri ekleyin.

Yeni .vert gölgelendirici ekleniyor

Android Studio'da:

  1. Gölgelendirici dizinini sağ tıklayın
  2. Yeni -> seçeneğini belirleyin Dosya
  3. depth_point_cloud.vert olarak adlandır
  4. Dosyayı metin dosyası olarak ayarlayın.

Yeni .vert dosyasına aşağıdaki kodu ekleyin:

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;
}

Bu gölgelendirici, daha iyi görselleştirme için Turbo renk haritasını kullanır. Aşağıdaki adımları gerçekleştirir:

  1. Her bir noktanın yüksekliğini (dünya koordinatlarındaki y ekseni) alır.
  2. Bu yükseklikle ilişkili bir renk hesaplar (kırmızı=düşük, mavi=yüksek).
  3. Her bir noktanın ekran konumunu hesaplar.
  4. DepthRenderer.update() yönteminde tanımlandığı gibi, her noktanın boyutunu (piksel cinsinden) ayarlar.

Aynı dizinde bir parça gölgelendirici oluşturun ve bu bölümde aynı adımları tekrarlayarak depth_point_cloud.frag olarak adlandırın.

Ardından, köşe gölgelendiricisinde tanımlandığı gibi, her bir noktayı tek bir renk köşe noktası olarak oluşturmak için aşağıdaki kodu bu yeni dosyaya ekleyin.

src/main/assets/shaders/depth_point_cloud.frag

precision mediump float;
varying vec4 v_Color;

void main() {
    gl_FragColor = v_Color;
}

Bu oluşturmayı uygulamak için RawDepthCodelabActivity içindeki DepthRenderer sınıfına çağrı ekleyin.

src/main/java/com/google/ar/core/codelab/common/rendering/RawDepthCodelabActivity.java

import com.google.ar.core.codelab.common.rendering.DepthRenderer;

Sınıfın en üst tarafında, backgroundRenderer simgesinin yanına gizli üye ekleyin.

private final DepthRenderer depthRenderer = new DepthRenderer();

depthRenderer ürününün, mevcut backgroundRenderer gibi RawDepthCodelabActivity.onSurfaceCreated() içinde başlatılması gerekir.

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

Geçerli karenin en son derinliğini göstermek için onDrawFrame içindeki deneme yakalama bloğunun sonuna aşağıdaki kodu ekleyin.

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

Bu değişikliklerle birlikte uygulama artık başarılı bir şekilde derlemeli ve Pointcloud derinliğini göstermelidir.

Örnek ham derinlik Pointcloud görselleştirmesi

  • Her nokta örneği derinliğine göre renklendirilir.
  • Kırmızı noktalar yakın, yeşil/mavi noktalar daha uzakta
  • Bazı eksik veriler veya "delikler" boş beyaz duvarlar veya tavan gibi yetersiz görüntü özelliklerine sahip alanlarda görülebilir.
  • DepthRenderer.draw() içindeki GLES20.glUniform1f(pointSizeUniform, 5.0f); çizgisini ayarlayarak oluşturulan nokta boyutuyla oynayabilirsiniz. Solda, 5 ve 10 nokta boyutları gösterilmektedir.

7. 3D nokta bulutlarını analiz etme (3. Bölüm)

Derinlik verilerini, AR oturumunda mevcut olduğunu doğruladıktan sonra analiz edebilirsiniz. Derinliği analiz etmek için önemli araçlardan biri, her bir pikselin güven değeridir. 3D nokta bulutlarını analiz etmek için güven değerlerini kullanın.

Düşük güven düzeyine sahip pikselleri geçersiz kıl

Her derinlik pikseli için güven değerini aldınız ve DepthData içindeki her noktanın yanında kaydettiniz ancak bu değeri henüz kullanmadınız.

confidenceNormalized değerleri 0 ile 1 arasında değişir. 0 değeri düşük güveni, 1 ise tam güveni belirtir. Güveni, kullanışlı olamayacak kadar düşük olan piksellerin kaydedilmesini önlemek için DepthData sınıfındaki convertRawDepthImagesTo3dPointBuffer() yöntemini değiştirin.

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 *********

Her seviyede kaç derinlik noktasının tutulduğunu görmek amacıyla güven seviyesi için farklı eşikleri deneyin.

Güven >= 0,1

Güven >= 0,3

Güven >= 0,5

Güven >= 0,7

Güven >= 0,9

Pikselleri mesafeye göre filtrele

Derinlik piksellerini mesafeye göre de filtreleyebilirsiniz. Bu sonraki adımlar kameraya yakın geometriyle ilgilidir. Performans optimizasyonu için çok uzaktaki noktaları yoksayabilirsiniz.

Az önce eklediğiniz güven kontrolü kodunu aşağıdaki şekilde güncelleyin:

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;
 }

Artık yalnızca yüksek güven düzeyine sahip yerleri ve yakın noktaları görebilirsiniz.

Mesafe filtreleme

Pointcloud'u kameradan en fazla 1,5 metre uzakta olacak şekilde sınırlandırır.

3D noktaları ve düzlemleri karşılaştırma

Geometrideki 3D noktaları ve düzlemleri karşılaştırabilir ve bunları, gözlemlenen AR düzlemlerine yakın noktaları kaldırmak gibi birbirlerini filtrelemek için kullanabilirsiniz.

Bu adımda yalnızca "düz olmayan" öğe bırakılacaktır çevredeki nesnelerin yüzeylerini temsil etme eğiliminde olan noktalar. filterUsingPlanes() yöntemini DepthData sınıfının en altına ekleyin. Bu yöntem mevcut noktaları tekrarlar, her bir noktayı her düzleme karşı kontrol eder ve bir AR düzlemine çok yakın olan herhangi bir noktayı geçersiz kılar, sahnedeki nesneleri vurgulayan düzlem dışı alanlar bırakır.

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);
            }
        }
    }

Bu yöntemi onDrawFrame yöntemindeki RawDepthCodelabActivity öğesine ekleyebilirsiniz:

//  ********** 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);

Codelab'in çalıştırılması artık noktaların bir alt kümesiyle sonuçlanıyor. Bu noktalar, nesnelerin üzerinde durduğu düz yüzeyler yok sayılarak sahnedeki nesneleri temsil eder. Bu verileri, noktaları birleştirerek nesnelerin boyutunu ve konumunu tahmin etmek için kullanabilirsiniz.

bir fincan çay

Mikrofon

Kulaklık

Yastık

Küme noktaları

Bu codelab'de son derece basit bir pointcloud kümeleme algoritması yer almaktadır. Alınan nokta bulutlarını eksene hizalı sınırlayıcı kutularla tanımlanan kümeler halinde gruplandırmak için codelab'i güncelleyin.

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;

Dosyanın üst kısmına, diğer oluşturucularla birlikte bu sınıfa bir BoxRenderer ekleyin.

private final BoxRenderer boxRenderer = new BoxRenderer();

onSurfaceCreated() yönteminde, diğer oluşturucularla birlikte aşağıdaki kodu da ekleyin:

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

Son olarak, alınan Pointcloud'ları kümeler halinde gruplandırmak ve sonuçları eksene ayarlı sınırlayıcı kutular olarak oluşturmak için RawDepthCodelabActivity içindeki onDrawFrame() bölümüne aşağıdaki satırları ekleyin.

      // 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 ***************

bir fincan çay

Mikrofon

Kulaklık

Yastık

Artık ARCore oturumuyla ham derinlik alabilir, derinlik bilgilerini 3D nokta bulutlarına dönüştürebilir, bu noktalarda temel filtreleme ve oluşturma işlemleri gerçekleştirebilirsiniz.

8. Build-Run-Test

Uygulamanızı derleyin, çalıştırın ve test edin.

Uygulamanızı derleyip çalıştırma

Uygulamanızı geliştirmek ve çalıştırmak için aşağıdaki adımları uygulayın:

  1. USB üzerinden ARCore destekli bir cihaz takın.
  2. Menü çubuğundaki ► düğmesiyle projenizi çalıştırın.
  3. Uygulamanın derlenip cihazınıza dağıtılmasını bekleyin.

Uygulamayı cihazınıza ilk kez dağıtmayı denediğinizde şunları yapmanız gerekir:

USB üzerinden hata ayıklamaya izin ver

cihazdaki uygulamaları tarar. Devam etmek için Tamam'ı seçin.

Uygulamanızı cihazda ilk kez çalıştırdığınızda, uygulamanın cihazınızın kamerasını kullanma izni olup olmadığı sorulur. AR işlevini kullanmaya devam etmek için erişime izin vermeniz gerekir.

Uygulamanızı test etme

Uygulamanızı çalıştırdığınızda cihazınızı basılı tutarak, alanınızda gezinerek ve bir alanı yavaşça tarayarak uygulamanın temel davranışını test edebilirsiniz. Sonraki adıma geçmeden önce en az 10 saniyelik veri toplamaya çalışın ve alanı çeşitli yönlerden tarayın.

9. Tebrikler

Tebrikler, Google'ın ARCore Raw Depth API'sini kullanarak ilk derinlik tabanlı Artırılmış Gerçeklik uygulamanızı başarıyla derleyip çalıştırdınız. Neler geliştireceğinizi görmek için sabırsızlanıyoruz!

10. Sorun giderme

Android cihazınızı geliştirme için ayarlama

  1. Bir USB kablosuyla cihazınızı geliştirme makinenize bağlayın. Windows kullanarak geliştiriyorsanız cihazınıza uygun USB sürücüsünü yüklemeniz gerekebilir.
  2. Geliştirici seçenekleri penceresinde USB üzerinden hata ayıklama'yı etkinleştirmek için aşağıdaki adımları uygulayın:
  • Ayarlar uygulamasını açın.
  • Cihazınızda Android 8.0 veya sonraki bir sürüm kullanılıyorsa Sistem'i seçin.
  • Alta doğru ilerleyin ve Telefon hakkında'yı seçin.
  • En alta ilerleyin ve Derleme numarası'na yedi kez dokunun.
  • Önceki ekrana dönün, en alta gidin ve Geliştirici seçenekleri'ne dokunun.
  • Geliştirici seçenekleri penceresini aşağı kaydırarak USB üzerinden hata ayıklama'yı bulup etkinleştirin.

Bu işlemle ilgili daha ayrıntılı bilgileri Google'ın Android geliştirici web sitesinde bulabilirsiniz.

Lisanslarla (Failed to install the following Android SDK packages as some licences have not been accepted) ilgili bir derleme hatasıyla karşılaşırsanız bu lisansları incelemek ve kabul etmek için aşağıdaki komutları kullanabilirsiniz:

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

Sık sorulan sorular