Membuat ekstensi input gambar
Sebelum memulai
- Pahami konsep dasar seperti kamera, frame input.
- Baca sumber data frame eksternal untuk memahami penjelasan antarmuka terperinci yang diperlukan saat membuat sumber data frame eksternal.
- Baca data frame input eksternal untuk memahami data frame kamera dan data frame render.
Membuat kelas sumber data frame eksternal
Turunkan dari ExternalImageStreamFrameSource untuk membuat ekstensi input gambar. Ini adalah subclass dari MonoBehaviour, dan nama file harus sama dengan nama kelas.
Misalnya:
public class MyFrameSource : ExternalImageStreamFrameSource
{
}
Contoh Workflow_FrameSource_ExternalImageStream adalah implementasi ekstensi input gambar yang menggunakan video yang direkam dengan ARCore di ponsel sebagai input. Video tersebut dikumpulkan menggunakan ARCore pada Pixel2 melalui callback kamera, bukan rekaman layar.
Definisi perangkat
Override IsCameraUnderControl dan kembalikan true.
Override IsHMD untuk mendefinisikan apakah perangkat adalah headset.
Misalnya, saat menggunakan video sebagai input, atur ke false.
protected override bool IsHMD => false;
Override Display untuk mendefinisikan tampilan perangkat.
Misalnya, jika hanya berjalan di ponsel, dapat menggunakan Display.DefaultSystemDisplay; nilai rotasinya berubah otomatis sesuai status tampilan sistem operasi saat ini.
protected override IDisplay Display => easyar.Display.DefaultSystemDisplay;
Ketersediaan
Override IsAvailable untuk mendefinisikan apakah perangkat tersedia.
Misalnya, saat menggunakan video sebagai input, selalu tersedia:
protected override Optional<bool> IsAvailable => true;
Jika IsAvailable tidak dapat ditentukan saat perakitan session, Anda dapat meng-override coroutine CheckAvailability() untuk memblokir proses perakitan hingga ketersediaannya dipastikan.
Kamera virtual
Override Camera untuk menyediakan kamera virtual.
Misalnya, terkadang Camera.main dapat digunakan sebagai kamera virtual session:
protected override Camera Camera => Camera.main;
Kamera fisik
Gunakan tipe FrameSourceCamera untuk meng-override DeviceCameras guna menyediakan informasi kamera fisik perangkat. Data ini akan digunakan saat memasukkan data frame kamera. Pembuatan harus sudah selesai saat CameraFrameStarted bernilai true.
Misalnya, menggunakan video yang dipakai dalam contoh 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;
}
Hati-Hati
Beberapa parameter input di sini perlu diatur sesuai video yang benar-benar digunakan. Parameter pada kode di atas hanya berlaku untuk video dalam contoh.
Override CameraFrameStarted untuk menyediakan penanda bahwa input frame kamera telah dimulai.
Misalnya:
protected override bool CameraFrameStarted => started;
Memulai dan menghentikan session
Override OnSessionStart(ARSession) lalu lakukan inisialisasi khusus AR. Pastikan untuk memanggil base.OnSessionStart terlebih dahulu.
Misalnya:
protected override void OnSessionStart(ARSession session)
{
base.OnSessionStart(session);
...
}
Di sinilah tempat yang cocok untuk membuka kamera perangkat, terutama jika kamera tersebut tidak dirancang untuk selalu aktif. Ini juga tempat yang cocok untuk memperoleh data kalibrasi yang tidak berubah sepanjang siklus hidup. Terkadang, sebelum data ini dapat diperoleh, perlu menunggu perangkat siap atau menunggu pembaruan data.
Pada saat yang sama, ini juga tempat yang cocok untuk memulai loop input data. Loop ini juga dapat ditulis di Update() atau metode lain, terutama saat data perlu diperoleh pada titik waktu tertentu dalam urutan eksekusi Unity. Jangan memasukkan data sebelum session siap (ready).
Jika diperlukan, proses start juga dapat diabaikan dan pemeriksaan data dilakukan pada setiap update; ini sepenuhnya bergantung pada kebutuhan spesifik.
Misalnya, saat menggunakan video sebagai input, Anda dapat mulai memutar video dan memulai loop input data di sini:
protected override void OnSessionStart(ARSession session)
{
base.OnSessionStart(session);
...
player.Play();
StartCoroutine(VideoDataToInputFrames());
}
Override OnSessionStop() dan lepaskan resource. Pastikan untuk memanggil base.OnSessionStop.
Misalnya, saat menggunakan video sebagai input, Anda dapat menghentikan pemutaran video dan melepaskan resource terkait di sini:
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;
}
Mengambil data frame kamera dari perangkat atau file
Anda dapat mengambil gambar dari sumber apa pun, seperti kamera sistem, kamera USB, file video, atau jaringan, selama data dapat dikonversi ke format yang diperlukan oleh Image. Cara mengambil data dari perangkat atau file ini berbeda-beda, sehingga perlu merujuk pada petunjuk penggunaan perangkat atau file terkait.
Misalnya, saat menggunakan video sebagai input, Anda dapat menggunakan Texture2D.ReadPixels(Rect, int, int, bool) untuk memperoleh data frame kamera dari RenderTexture pemutar video, lalu menyalin data Texture2D.GetRawTextureData() ke 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);
}
}
Hati-Hati
Seperti pada kode di atas, data yang disalin dari pointer Texture2D harus dibalik secara vertikal terlebih dahulu agar susunan memori datanya menjadi gambar yang normal.
Saat mengambil gambar, Anda juga perlu memperoleh data kalibrasi kamera atau kamera ekuivalen dan membuat instance CameraParameters.
Jika sumber asli data berasal dari callback kamera ponsel dan data tidak dipotong secara manual, data kalibrasi kamera ponsel dapat langsung digunakan. Saat menggunakan antarmuka seperti ARCore atau ARKit untuk memperoleh data callback kamera, Anda dapat merujuk dokumentasi terkait untuk memperoleh intrinsik kamera. Jika fitur AR yang perlu digunakan adalah pelacakan gambar atau pelacakan objek, pada kondisi ini Anda juga dapat menggunakan CameraParameters.createWithDefaultIntrinsics(Vec2I, CameraDeviceType, int) untuk membuat intrinsik kamera. Efek algoritma akan sedikit terpengaruh, tetapi biasanya tidak besar.
Jika data berasal dari kamera USB, file video yang tidak dibuat dari callback kamera, atau sumber lain, kamera atau frame video perlu dikalibrasi untuk memperoleh intrinsik yang benar.
Hati-Hati
Data callback kamera tidak boleh dipotong; setelah dipotong, intrinsik harus dihitung ulang. Jika data berasal dari gambar yang diperoleh melalui rekaman layar dan cara serupa, biasanya data kalibrasi kamera ponsel tidak dapat digunakan, sehingga kamera atau frame video juga perlu dikalibrasi untuk memperoleh intrinsik yang benar.
Intrinsik yang tidak benar akan menyebabkan fitur AR tidak dapat digunakan secara normal. Gejala yang umum adalah konten virtual tidak dapat sejajar dengan objek nyata, dan pelacakan AR sulit berhasil atau mudah hilang.
Misalnya, untuk video yang digunakan dalam contoh Workflow_FrameSource_ExternalImageStream, intrinsik kamera yang sesuai dan proses pembuatan CameraParameters adalah sebagai berikut:
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);
Hati-Hati
Parameter pada kode di atas hanya berlaku untuk video dalam contoh. Intrinsik kamera tersebut dan video dikumpulkan pada waktu yang sama. Jika perlu menggunakan data dari video atau perangkat lain, pastikan untuk memperoleh intrinsik perangkat secara bersamaan atau melakukan kalibrasi manual.
Memasukkan data frame kamera
Setelah memperoleh pembaruan data frame kamera, panggil HandleCameraFrameData(double, Image, CameraParameters) untuk memasukkan data frame kamera.
Misalnya, saat menggunakan video sebagai input, implementasinya adalah sebagai berikut:
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);
}
}
}
Hati-Hati
Jangan lupa menjalankan Dispose() setelah digunakan, atau melepaskan Image, Buffer, dan data terkait lain melalui mekanisme seperti using. Jika tidak, kebocoran memori serius akan terjadi, dan buffer pool juga mungkin gagal memperoleh buffer.