Table of Contents

Crear una extension de entrada de imagen

Antes de empezar

Crear la clase de fuente de datos de fotogramas externa

Herede de ExternalImageStreamFrameSource para crear una extension de entrada de imagen. Es una subclase de MonoBehaviour, y el nombre del archivo debe ser igual al nombre de la clase.

Por ejemplo:

public class MyFrameSource : ExternalImageStreamFrameSource
{
}

El ejemplo Workflow_FrameSource_ExternalImageStream es una implementacion de extension de entrada de imagen basada en un video grabado con ARCore en un telefono como entrada. El video se capturo con ARCore en un Pixel2 mediante callback de camara (no es una grabacion de pantalla).

Definicion del dispositivo

Sobrescriba IsCameraUnderControl y devuelva true.

Sobrescriba IsHMD para definir si el dispositivo es un headset.

Por ejemplo, establezcalo en false al usar un video como entrada.

protected override bool IsHMD => false;

Sobrescriba Display para definir la pantalla del dispositivo.

Por ejemplo, si solo se ejecuta en un telefono, puede usar Display.DefaultSystemDisplay; su valor de rotacion cambia automaticamente segun el estado de pantalla actual del sistema operativo.

protected override IDisplay Display => easyar.Display.DefaultSystemDisplay;

Disponibilidad

Sobrescriba IsAvailable para definir si el dispositivo esta disponible.

Por ejemplo, al usar un video como entrada, siempre esta disponible:

protected override Optional<bool> IsAvailable => true;

Si IsAvailable no se puede determinar durante el ensamblaje de la session, puede sobrescribir la corrutina CheckAvailability() para bloquear el proceso de ensamblaje hasta que se confirme si esta disponible.

Camara virtual

Sobrescriba Camera para proporcionar la camara virtual.

Por ejemplo, a veces puede usar Camera.main como camara virtual de la session:

protected override Camera Camera => Camera.main;

Camara fisica

Sobrescriba DeviceCameras con el tipo FrameSourceCamera para proporcionar informacion de la camara fisica del dispositivo. Estos datos se usaran al introducir datos de fotogramas de camara. La creacion debe estar completa cuando CameraFrameStarted sea true.

Por ejemplo, al usar el video empleado en el ejemplo Workflow_FrameSource_ExternalImageStream:

private FrameSourceCamera deviceCamera;
protected override List<FrameSourceCamera> DeviceCameras => new List<FrameSourceCamera> { deviceCamera };

{
    var size = new Vector2Int(640, 360);
    var cameraType = CameraDeviceType.Back;
    var cameraOrientation = 90;
    deviceCamera = new FrameSourceCamera(cameraType, cameraOrientation, size, new Vector2(30, 30));
    started = true;
}
Precaución

Estos parametros de entrada deben configurarse segun el video real que se use. Los parametros del codigo anterior solo son aplicables al video del ejemplo.

Sobrescriba CameraFrameStarted para proporcionar la marca que indica que ha empezado la entrada de fotogramas de camara.

Por ejemplo:

protected override bool CameraFrameStarted => started;

Inicio y detencion de la session

Sobrescriba OnSessionStart(ARSession) y realice la inicializacion especifica de AR. Asegurese de llamar primero a base.OnSessionStart.

Por ejemplo:

protected override void OnSessionStart(ARSession session)
{
    base.OnSessionStart(session);
    ...
}

Este es un lugar adecuado para abrir la camara del dispositivo, especialmente si esas camaras no estan disenadas para permanecer siempre abiertas. Tambien es un lugar adecuado para obtener datos de calibracion que no cambiaran durante todo el ciclo de vida. A veces puede ser necesario esperar a que el dispositivo este listo o a que se actualicen los datos antes de poder obtenerlos.

Tambien es un lugar adecuado para iniciar el bucle de entrada de datos. Tambien puede escribir este bucle en Update() u otros metodos, especialmente cuando los datos deben obtenerse en un punto concreto del orden de ejecucion de Unity. No introduzca datos antes de que la session este lista (ready).

Si es necesario, tambien puede omitir el proceso de inicio y comprobar los datos en cada actualizacion; esto depende por completo de los requisitos concretos.

Por ejemplo, al usar un video como entrada, puede iniciar aqui la reproduccion del video y el bucle de entrada de datos:

protected override void OnSessionStart(ARSession session)
{
    base.OnSessionStart(session);
    ...
    player.Play();
    StartCoroutine(VideoDataToInputFrames());
}

Sobrescriba OnSessionStop() y libere los recursos. Asegurese de llamar a base.OnSessionStop.

Por ejemplo, al usar un video como entrada, puede detener aqui la reproduccion del video y liberar los recursos relacionados:

protected override void OnSessionStop()
{
    base.OnSessionStop();

    StopAllCoroutines();
    player.Stop();
    if (renderTexture) { Destroy(renderTexture); }
    cameraParameters?.Dispose();
    cameraParameters = null;
    frameIndex = -1;
    started = false;
    deviceCamera?.Dispose();
    deviceCamera = null;
}

Obtener datos de fotogramas de camara desde un dispositivo o archivo

Puede obtener imagenes desde cualquier origen, como la camara del sistema, una camara USB, un archivo de video o la red. Basta con poder convertir los datos al formato requerido por Image. La forma de obtener datos desde estos dispositivos o archivos varia, por lo que debe consultar las instrucciones del dispositivo o archivo correspondiente.

Por ejemplo, al usar un video como entrada, puede usar Texture2D.ReadPixels(Rect, int, int, bool) para obtener datos de fotogramas de camara desde el RenderTexture del reproductor de video, y luego copiar los datos de Texture2D.GetRawTextureData() a Buffer:

void VideoDataToInputFrames()
{
    ...
    RenderTexture.active = renderTexture;
    var pixelSize = new Vector2Int((int)player.width, (int)player.height);
    var texture = new Texture2D(pixelSize.x, pixelSize.y, TextureFormat.RGB24, false);
    texture.ReadPixels(new Rect(0, 0, pixelSize.x, pixelSize.y), 0, 0);
    texture.Apply();
    RenderTexture.active = null;
    ...
    CopyRawTextureData(buffer, texture.GetRawTextureData<byte>(), pixelSize);
} 

static unsafe void CopyRawTextureData(Buffer buffer, Unity.Collections.NativeArray<byte> data, Vector2Int size)
{
    int oneLineLength = size.x * 3;
    int totalLength = oneLineLength * size.y;
    var ptr = new IntPtr(data.GetUnsafeReadOnlyPtr());
    for (int i = 0; i < size.y; i++)
    {
        buffer.tryCopyFrom(ptr, oneLineLength * i, totalLength - oneLineLength * (i + 1), oneLineLength);
    }
}
Precaución

Igual que en el codigo anterior, los datos copiados desde el puntero de Texture2D deben invertirse verticalmente para que la disposicion en memoria sea la de una imagen normal.

Mientras obtiene la imagen, tambien debe obtener los datos de calibracion de la camara o de una camara equivalente y crear una instancia de CameraParameters.

Si el origen original de los datos proviene del callback de la camara de un telefono y los datos no se han recortado manualmente, puede usar directamente los datos de calibracion de la camara del telefono. Al obtener datos de callback de camara mediante interfaces como ARCore o ARKit, puede consultar la documentacion correspondiente para obtener los parametros intrinsecos de la camara. Si la funcion AR que necesita usar es seguimiento de imagen o seguimiento de objetos, en este caso tambien puede usar CameraParameters.createWithDefaultIntrinsics(Vec2I, CameraDeviceType, int) para crear los parametros intrinsecos de la camara. El efecto del algoritmo se vera ligeramente afectado, pero por lo general el impacto no es grande.

Si los datos provienen de una camara USB, de un archivo de video no generado por callback de camara u otros origenes, debe calibrar la camara o los fotogramas de video para obtener los parametros intrinsecos correctos.

Precaución

Los datos de callback de camara no se pueden recortar; despues de recortarlos, debe recalcular los parametros intrinsecos. Si los datos provienen de una grabacion de pantalla u otros metodos similares, normalmente no se pueden usar los datos de calibracion de la camara del telefono; en ese caso tambien debe calibrar la camara o los fotogramas de video para obtener los parametros intrinsecos correctos.

Unos parametros intrinsecos incorrectos haran que las funciones AR no funcionen correctamente. Los problemas habituales incluyen que el contenido virtual no se alinee con los objetos reales y que el seguimiento AR no se consiga facilmente o se pierda con facilidad.

Por ejemplo, para el video utilizado en el ejemplo Workflow_FrameSource_ExternalImageStream, sus parametros intrinsecos de camara correspondientes y el proceso de creacion de CameraParameters son los siguientes:

var size = new Vector2Int(640, 360);
var cameraType = CameraDeviceType.Back;
var cameraOrientation = 90;
cameraParameters = new CameraParameters(size.ToEasyARVector(), new Vec2F(506.085f, 505.3105f), new Vec2F(318.1032f, 177.6514f), cameraType, cameraOrientation);
Precaución

Los parametros del codigo anterior solo son aplicables al video del ejemplo. Esos parametros intrinsecos de camara y el video se capturaron al mismo tiempo. Si necesita usar otro video o datos de otro dispositivo, asegurese de obtener al mismo tiempo los parametros intrinsecos del dispositivo o de realizar una calibracion manual.

Introducir datos de fotogramas de camara

Despues de obtener una actualizacion de datos de fotogramas de camara, llame a HandleCameraFrameData(double, Image, CameraParameters) para introducir los datos de fotogramas de camara.

Por ejemplo, al usar un video como entrada, la implementacion es la siguiente:

IEnumerator VideoDataToInputFrames()
{
    yield return new WaitUntil(() => player.isPrepared);
    var pixelSize = new Vector2Int((int)player.width, (int)player.height);
    ...
    yield return new WaitUntil(() => player.isPlaying && player.frame >= 0);
    while (true)
    {
        yield return null;
        if (frameIndex == player.frame) { continue; }
        frameIndex = player.frame;
        ...
        var pixelFormat = PixelFormat.RGB888;
        var bufferO = TryAcquireBuffer(pixelSize.x * pixelSize.y * 3);
        if (bufferO.OnNone) { continue; }

        var buffer = bufferO.Value;
        CopyRawTextureData(buffer, texture.GetRawTextureData<byte>(), pixelSize);

        using (buffer)
        using (var image = Image.create(buffer, pixelFormat, pixelSize.x, pixelSize.y, pixelSize.x, pixelSize.y))
        {
            HandleCameraFrameData(player.time, image, cameraParameters);
        }
    }
}
Precaución

No olvide ejecutar Dispose() despues del uso, o liberar Image, Buffer y otros datos relacionados mediante mecanismos como using. De lo contrario, se produciran fugas de memoria graves, y la obtencion de buffers desde el buffer pool tambien puede fallar.

Temas relacionados