Estabilizar imagens da câmera no Android NDK (C)

O ARCore agora oferece suporte à estabilização eletrônica de imagem (EIS, na sigla em inglês), que ajuda a produzir uma visualização suave da câmera. A EIS consegue a estabilização observando o movimento do smartphone usando o giroscópio e aplicando a malha de homografia de compensação dentro dos limites da textura da câmera que contrapõe os tremores menores. O EIS só é compatível com a orientação retrato do dispositivo. Todas as orientações terão suporte na versão 1.39.0 do ARCore.

Consultar e ativar o suporte ao EIS

Para ativar a EIS, configure a sessão para usar AR_IMAGE_STABILIZATION_MODE_EIS. Se o dispositivo não oferecer suporte ao recurso EIS, isso vai causar uma exceção do 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);

Coordenadas de transformação

Quando a EIS está ativada, o renderizador precisa usar as coordenadas do dispositivo modificadas e as coordenadas de textura correspondentes que incorporam a compensação da EIS ao renderizar o plano de fundo da câmera. Para receber as coordenadas compensadas do EIS, use ArFrame_transformCoordinates3d, com AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES como entrada e AR_COORDINATES_3D_EIS_NORMALIZED_DEVICE_COORDINATES como saída para receber as coordenadas do dispositivo em 3D e AR_COORDINATES_3D_EIS_TEXTURE_NORMALIZED como saída para receber as coordenadas da textura em 3D. Por enquanto, o único tipo de coordenadas de entrada compatível com ArFrame_transformCoordinates3d é 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_);

Quando a EIS está desativada, as coordenadas 3D de saída são equivalentes às 2D, com valores z definidos para não produzir mudanças.

Modificar sombreadores

As coordenadas 3D calculadas precisam ser transmitidas para os shaders de renderização em segundo plano. Os buffers de vértice agora são 3D com 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;
}

Além disso, o sombreador de fragmentos precisa aplicar a correção 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);
}

Consulte o app de exemplo hello_eis_kotlin para mais detalhes.