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

ARCore artık kamera önizlemesinin sorunsuz bir şekilde oluşturulmasına yardımcı olan elektronik görüntü sabitlemeyi (EIS) destekliyor. EIS, jiroskopu kullanarak telefon hareketini gözlemleyerek ve kamera dokusunun sınırları içinde küçük sarsıntılara karşı dengeleyici homography örgüsü uygulayarak sabitleme sağlar. EIS yalnızca cihazın dikey yönünde desteklenir. ARCore'un 1.39.0 sürümünde tüm yönler desteklenir.

EIS desteği için sorgu gönderme ve EIS'i etkinleştirme

EIS'i etkinleştirmek için oturumunuzu ImageStabilizationMode.EIS kullanacak şekilde yapılandırın. Cihaz EIS özelliğini desteklemiyorsa ARCore'dan bir istisna atılır.

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çıkken oluşturma aracının, kamera arka planını oluştururken EIS telafisini içeren değiştirilmiş cihaz koordinatlarını ve eşleşen doku koordinatlarını kullanması gerekir. EIS telafi edilmiş koordinatlarını almak için Frame.transformCoordinates3d() kullanın. Giriş olarak OPENGL_NORMALIZED_DEVICE_COORDINATES, 3D cihaz koordinatlarını almak için EIS_NORMALIZED_DEVICE_COORDINATES ve 3D doku koordinatlarını almak için EIS_TEXTURE_NORMALIZED çıkışını kullanın. Şu anda Frame.transformCoordinates3d() için desteklenen tek giriş koordinatı türü OPENGL_NORMALIZED_DEVICE_COORDINATES'tür.

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ıyken çıkıştaki 3D koordinatlar, 2D karşılıklarıyla eşdeğerdir ve z değerleri hiçbir değişiklik oluşturacak şekilde ayarlanır.

Gölgelendiricileri değiştirme

Hesaplanan 3D koordinatlar arka plan oluşturma gölgelendiricilerine iletilmelidir. Köşe arabellekleri artık EIS ile 3D'dir:

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

Ayrıca, 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 bilgi için hello_eis_kotlin örnek uygulamasına bakın.