Android SDK'sında (Kotlin/Java) kamera görüntülerini sabitleme

ARCore artık sorunsuz bir kamera önizlemesi üretilmesini sağlayan Elektronik Görüntü Sabitleme'yi (EIS) destekliyor. EIS, jiroskop kullanarak telefonun hareketini gözlemleyerek ve küçük sarsıntılara karşı koyan kamera dokusu sınırları içinde dengeleme homografi ağı uygulayarak sabitleme sağlar. EIS yalnızca cihazın dikey yönünde desteklenir. Tüm yönler, ARCore'un 1.39.0 sürümünde desteklenir.

EIS desteği için sorgu oluşturma ve EIS'yi etkinleştirme

EIS'yi etkinleştirmek için oturumunuzu ImageStabilizationMode.EIS kullanacak şekilde yapılandırın. Cihaz, EIS özelliğini desteklemiyorsa ARCore'dan istisna gönderilmesine neden olur.

Java

if (!session.isImageStabilizationModeSupported(Config.ImageStabilizationMode.EIS)) {
  return;
}
Config config = session.getConfig();
config.setImageStabilizationMode(Config.ImageStabilizationMode.EIS);
session.configure(config);

Kotlin

if (!session.isImageStabilizationModeSupported(Config.ImageStabilizationMode.EIS)) return
session.configure(
  session.config.apply { imageStabilizationMode = Config.ImageStabilizationMode.EIS }
)

Koordinatları dönüştürme

EIS açık olduğunda oluşturucunun, değiştirilmiş cihaz koordinatlarını ve kamera arka planını oluştururken EIS telafisini içeren eşleşen doku koordinatlarını kullanması gerekir. EIS destekli koordinatları almak için Frame.transformCoordinates3d() kullanarak 3D cihaz koordinatlarını almak için OPENGL_NORMALIZED_DEVICE_COORDINATES, çıktı olarak EIS_NORMALIZED_DEVICE_COORDINATES, 3D doku koordinatlarını almak için ise çıktı olarak EIS_TEXTURE_NORMALIZED kullanın. Şu anda Frame.transformCoordinates3d() için desteklenen tek giriş koordinatı türü OPENGL_NORMALIZED_DEVICE_COORDINATES'dir.

Java

final FloatBuffer cameraTexCoords =
    ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer();

final FloatBuffer screenCoords =
    ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer();

final FloatBuffer NDC_QUAD_COORDS_BUFFER =
    ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_2D)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer()
        .put(
            new float[] {
              /*0:*/ -1f, -1f, /*1:*/ +1f, -1f, /*2:*/ -1f, +1f, /*3:*/ +1f, +1f,
            });

final VertexBuffer screenCoordsVertexBuffer =
    new VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null);
final VertexBuffer cameraTexCoordsVertexBuffer =
    new VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null);

NDC_QUAD_COORDS_BUFFER.rewind();
frame.transformCoordinates3d(
    Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
    NDC_QUAD_COORDS_BUFFER,
    Coordinates3d.EIS_NORMALIZED_DEVICE_COORDINATES,
    screenCoords);
screenCoordsVertexBuffer.set(screenCoords);

NDC_QUAD_COORDS_BUFFER.rewind();
frame.transformCoordinates3d(
    Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
    NDC_QUAD_COORDS_BUFFER,
    Coordinates3d.EIS_TEXTURE_NORMALIZED,
    cameraTexCoords);
cameraTexCoordsVertexBuffer.set(cameraTexCoords);

Kotlin

val COORDS_BUFFER_SIZE_2D = 2 * 4 * Float.SIZE_BYTES
val COORDS_BUFFER_SIZE_3D = 3 * 4 * Float.SIZE_BYTES
val cameraTexCoords =
  ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
val screenCoords =
  ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
val cameraTexCoordsVertexBuffer = VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null)
val screenCoordsVertexBuffer = VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null)
val NDC_QUAD_COORDS_BUFFER =
  ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_2D)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
    .apply {
      put(
        floatArrayOf(
          /* 0: */
          -1f,
          -1f,
          /* 1: */
          +1f,
          -1f,
          /* 2: */
          -1f,
          +1f,
          /* 3: */
          +1f,
          +1f
        )
      )
    }
NDC_QUAD_COORDS_BUFFER.rewind()
frame.transformCoordinates3d(
  Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
  NDC_QUAD_COORDS_BUFFER,
  Coordinates3d.EIS_NORMALIZED_DEVICE_COORDINATES,
  screenCoords
)
screenCoordsVertexBuffer.set(screenCoords)

NDC_QUAD_COORDS_BUFFER.rewind()
frame.transformCoordinates3d(
  Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
  NDC_QUAD_COORDS_BUFFER,
  Coordinates3d.EIS_TEXTURE_NORMALIZED,
  cameraTexCoords
)
cameraTexCoordsVertexBuffer.set(cameraTexCoords)

EIS kapalı olduğunda, çıktı 3D koordinatları 2D eşdeğerlerine eşdeğerdir ve z değerleri değişiklik üretmeyecek şekilde ayarlanır.

Gölgelendiricileri değiştirme

Hakkında hesaplanan 3D koordinatlar, arka plan oluşturma gölgelendiricilere iletilmelidir. Köşe arabellekleri artık EIS ile 3D hale getirildi:

layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec3 a_CameraTexCoord;
out vec3 v_CameraTexCoord;
void main() {
  gl_Position = a_Position;
  v_CameraTexCoord = a_CameraTexCoord;
}

Buna ek olarak, parça gölgelendiricinin perspektif düzeltmesi uygulaması gerekir:

precision mediump float;
uniform samplerExternalOES u_CameraColorTexture;
in vec3 v_CameraTexCoord;
layout(location = 0) out vec4 o_FragColor;
void main() {
  vec3 tc = (v_CameraTexCoord / v_CameraTexCoord.z);
  o_FragColor = texture(u_CameraColorTexture, tc.xy);
}

Daha fazla ayrıntı için hello_eis_kotlin örnek uygulamasına bakın.