Estabilización de imágenes de la cámara en el NDK de Android (C)

ARCore ahora es compatible con la estabilización electrónica de imágenes (EIS), que ayuda a producir una vista previa fluida de la cámara. EIS logra la estabilización observando el movimiento del teléfono con giroscopio y aplicando una malla de homografía de compensación dentro de los límites de la textura de la cámara que contrarresta las sacudidas menores. EIS solo es compatible con la orientación vertical del dispositivo. Todas las orientaciones serán compatibles con la versión 1.39.0 de ARCore.

Consulta la compatibilidad con EIS y habilítala

Si quieres habilitar EIS, configura tu sesión para que use AR_IMAGE_STABILIZATION_MODE_EIS. Si el dispositivo no admite la función EIS, se generará una excepción de ARCore.

int enableEis = 0;
ArSession_isImageStabilizationModeSupported(
    ar_session, AR_IMAGE_STABILIZATION_MODE_EIS, &enableEis);
if (!enableEis) {
  return;
}
// Create a session config.
ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);

// Enable Electronic Image Stabilization.
ArConfig_setImageStabilizationMode(ar_session, ar_config, AR_IMAGE_STABILIZATION_MODE_EIS);
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);

// Release config resources.
ArConfig_destroy(ar_config);

Cómo transformar coordenadas

Cuando EIS está activado, el renderizador debe usar las coordenadas modificadas del dispositivo y las coordenadas de texturas coincidentes que incorporan la compensación de EIS cuando renderiza el fondo de la cámara. Para obtener las coordenadas EIS con compensación, usa ArFrame_transformCoordinates3d con AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES como entrada, AR_COORDINATES_3D_EIS_NORMALIZED_DEVICE_COORDINATES como salida para obtener coordenadas 3D del dispositivo y AR_COORDINATES_3D_EIS_TEXTURE_NORMALIZED como salida para obtener coordenadas de textura 3D. Por ahora, el único tipo de coordenada de entrada admitido para ArFrame_transformCoordinates3d es AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES.

int kNumVertices = 4;
// Positions of the quad vertices in clip space (X, Y).
const GLfloat kVertices[] = {
    -1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f,
};
float transformed_vertices_[4 * 3];
float transformed_uvs_[4 * 3];

ArFrame_transformCoordinates3d(
    session, frame, AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES,
    kNumVertices, kVertices,
    AR_COORDINATES_3D_EIS_NORMALIZED_DEVICE_COORDINATES,
    transformed_vertices_);
ArFrame_transformCoordinates3d(
    session, frame, AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES,
    kNumVertices, kVertices, AR_COORDINATES_3D_EIS_TEXTURE_NORMALIZED,
    transformed_uvs_);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, camera_texture_id_);
glUseProgram(camera_program_);
glUniform1i(camera_texture_uniform_, 0);

// Set the vertex positions and texture coordinates.
glVertexAttribPointer(camera_position_attrib_, 3, GL_FLOAT, false, 0,
                      transformed_vertices_);
glVertexAttribPointer(camera_tex_coord_attrib_, 3, GL_FLOAT, false, 0,
                      transformed_uvs_);
glEnableVertexAttribArray(camera_position_attrib_);
glEnableVertexAttribArray(camera_tex_coord_attrib_);

Cuando EIS está desactivado, las coordenadas 3D de salida son equivalentes a sus equivalentes 2D, con valores z establecidos para no producir cambios.

Modifica los sombreadores

Las coordenadas 3D calculadas deben pasarse a sombreadores de renderización en segundo plano. Los búferes de vértices ahora son 3D con EIS:

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

Además, el sombreador de fragmentos debe aplicar la corrección de perspectiva:

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

Consulta la app de ejemplo hello_eis_kotlin para obtener más detalles.