Creare un'estensione di input per immagini
Prima di iniziare
- Comprendere i concetti di base di fotocamera, frame di input, ecc.
- Leggere Origine dati frame esterna per informazioni dettagliate sulle interfacce necessarie per creare un'origine dati frame esterna.
- Leggere Dati frame di input esterni per comprendere i dati frame della fotocamera e i dati frame di rendering.
Creare una classe sorgente dati frame esterna
Eredita da ExternalImageStreamFrameSource per creare un'estensione di input immagine. È una sottoclasse di MonoBehaviour e il nome del file deve corrispondere al nome della classe.
Ad esempio:
public class MyFrameSource : ExternalImageStreamFrameSource
{
}
L'esempio Workflow_FrameSource_ExternalImageStream è un'implementazione di un'estensione di input immagine che utilizza come input un video preregistrato con ARCore su un telefono. Il video è stato acquisito su un Pixel2 con ARCore tramite callback della fotocamera (non una registrazione dello schermo).
Definizione del dispositivo
Sovrascrivi IsCameraUnderControl e restituisci true.
Sovrascrivi IsHMD per definire se il dispositivo è un visore.
Ad esempio, impostalo su false quando si utilizza un video come input.
protected override bool IsHMD => false;
Sovrascrivi Display per definire il display del dispositivo.
Ad esempio, se eseguito solo su telefono, puoi usare Display.DefaultSystemDisplay, il cui valore di rotazione cambia automaticamente in base allo stato corrente del display del sistema operativo.
protected override IDisplay Display => easyar.Display.DefaultSystemDisplay;
Disponibilità
Sovrascrivi IsAvailable per definire se il dispositivo è disponibile.
Ad esempio, quando si utilizza un video come input, è sempre disponibile:
protected override Optional<bool> IsAvailable => true;
Se IsAvailable non può essere determinato durante l'assemblaggio della sessione, puoi sovrascrivere la coroutine CheckAvailability() per bloccare il processo di assemblaggio finché non si determina la disponibilità.
Fotocamera virtuale
Sostituisci Camera per fornire una fotocamera virtuale.
Ad esempio, a volte puoi utilizzare Camera.main come fotocamera virtuale per la sessione:
protected override Camera Camera => Camera.main;
Fotocamera fisica
Utilizza FrameSourceCamera per sovrascrivere DeviceCameras per fornire informazioni sulle fotocamere fisiche del dispositivo. Questi dati vengono utilizzati durante l'input dei frame della fotocamera. Deve essere completato quando CameraFrameStarted è true.
Ad esempio, utilizzando il video dell'esempio 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;
}
Attenzione
Diversi parametri di input qui devono essere impostati in base al video effettivamente utilizzato. I parametri nel codice sopra sono validi solo per il video di esempio.
Sovrascrivi CameraFrameStarted per fornire l'indicatore di inizio input del frame della fotocamera.
Ad esempio:
protected override bool CameraFrameStarted => started;
Session avvio e arresto
Sovrascrivi OnSessionStart(ARSession) per eseguire inizializzazioni specifiche per AR. Assicurati di chiamare prima base.OnSessionStart.
Esempio:
protected override void OnSessionStart(ARSession session)
{
base.OnSessionStart(session);
...
}
Questo è il punto appropriato per attivare dispositivi come fotocamere, specialmente se non sono progettati per rimanere sempre attivi. È anche il momento per acquisire dati di calibrazione che non cambiano durante il ciclo di vita. A volte potrebbe essere necessario attendere che il dispositivo sia pronto o che i dati vengano aggiornati prima che siano disponibili.
È anche un buon posto per avviare cicli di input dati. Puoi anche implementare questi cicli in Update() o altri metodi, specialmente se i dati devono essere acquisiti in un momento specifico dell'ordine di esecuzione di Unity. Non inserire dati finché la sessione non è pronta.
Se necessario, puoi ignorare l'avvio e controllare i dati ad ogni aggiornamento, a seconda delle tue esigenze.
Esempio, quando si utilizza un video come input, avvia la riproduzione e il ciclo di inserimento dati qui:
protected override void OnSessionStart(ARSession session)
{
base.OnSessionStart(session);
...
player.Play();
StartCoroutine(VideoDataToInputFrames());
}
Sovrascrivi OnSessionStop() per rilasciare le risorse, assicurandoti di chiamare base.OnSessionStop.
Esempio, quando si utilizza un video come input, ferma la riproduzione e rilascia le risorse qui:
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;
}
Ottenere dati del fotogramma della fotocamera da dispositivi o file
È possibile acquisire immagini da qualsiasi origine: fotocamera di sistema, fotocamera USB, file video, rete, ecc. Purché i dati possano essere convertiti nel formato richiesto da Image. I metodi per ottenere dati da questi dispositivi o file variano e richiedono il riferimento alle istruzioni d'uso pertinenti.
Ad esempio, quando si utilizza un video come input, è possibile usare Texture2D.ReadPixels(Rect, int, int, bool) per ottenere i dati del fotogramma della fotocamera dalla RenderTexture del lettore video, quindi copiare i dati di Texture2D.GetRawTextureData() in 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);
}
}
[!ATTENZIONE] Come nel codice sopra, i dati copiati dal puntatore di Texture2D richiedono un capovolgimento verticale affinché la disposizione in memoria rappresenti un'immagine corretta.
Durante l'acquisizione dell'immagine, è necessario ottenere anche i dati di calibrazione della fotocamera o di una equivalente e creare un'istanza di CameraParameters.
Se l'origine dati proviene dal callback della fotocamera di un telefono e i dati non sono stati ritagliati manualmente, è possibile utilizzare direttamente i dati di calibrazione della fotocamera del telefono. Quando si utilizzano interfacce come ARCore o ARKit per ottenere dati dal callback della fotocamera, è possibile fare riferimento alla documentazione pertinente per ottenere i parametri intrinseci della fotocamera. Se la funzionalità AR da utilizzare è il tracciamento di immagini o oggetti, in questo caso è anche possibile utilizzare CameraParameters.createWithDefaultIntrinsics(Vec2I, CameraDeviceType, int) per creare i parametri intrinseci della fotocamera. Ciò potrebbe influenzare leggermente le prestazioni dell'algoritmo, ma generalmente l'impatto è limitato.
Se i dati provengono da altre fonti come una fotocamera USB o file video non generati da callback di fotocamera, è necessario calibrare la fotocamera o il fotogramma video per ottenere i parametri intrinseci corretti.
[!ATTENZIONE] I dati del callback della fotocamera non possono essere ritagliati; dopo il ritaglio è necessario ricalcolare i parametri intrinseci. Se i dati provengono da immagini acquisite tramite registrazione dello schermo, generalmente non è possibile utilizzare i dati di calibrazione della fotocamera del telefono. In questo caso, è necessario calibrare la fotocamera o il fotogramma video per ottenere i parametri intrinseci corretti.
Parametri intrinseci errati impediscono il corretto funzionamento delle funzionalità AR, causando comuni problemi come disallineamento tra contenuti virtuali e oggetti reali, nonché difficoltà nel tracciamento AR o perdite frequenti.
Ad esempio, per il video utilizzato nell'esempio Workflow_FrameSource_ExternalImageStream, i corrispondenti parametri intrinseci della fotocamera e il processo di creazione di CameraParameters sono i seguenti:
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);
[!ATTENZIONE] I parametri nel codice sopra sono validi solo per il video nell'esempio, poiché i parametri intrinseci della fotocamera sono stati acquisiti contemporaneamente al video. Se si utilizzano dati da altri video o dispositivi, è essenziale ottenere i parametri intrinseci del dispositivo o eseguire una calibrazione manuale.
Input dati del frame della fotocamera
Dopo aver ottenuto l'aggiornamento dei dati del frame della fotocamera, chiamare HandleCameraFrameData(double, Image, CameraParameters) per inserire i dati del frame della fotocamera.
Ad esempio, quando si utilizza un video come input, l'implementazione è la seguente:
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);
}
}
}
Attenzione
Non dimenticare di eseguire Dispose() dopo l'uso o rilasciare Image, Buffer e altri dati correlati attraverso meccanismi come using. Altrimenti si verificheranno gravi perdite di memoria e potrebbe non essere possibile acquisire il buffer dal buffer pool.