using UnityEngine; using Unity.Barracuda; using UnityEngine.UI; using TMPro; public class ModelInference : MonoBehaviour { // Referensi ke model ONNX (drag & drop file ONNX di Inspector) public NNModel onnxModel; private Model runtimeModel; private IWorker worker; // Ukuran input model (sesuai dengan training, misalnya 224x224) public int inputWidth = 224; public int inputHeight = 224; // Referensi UI untuk menampilkan input dan hasil public RawImage inputDisplay; public TextMeshProUGUI resultText; void Start() { // Muat model dari asset runtimeModel = ModelLoader.Load(onnxModel); // Buat worker untuk melakukan inferensi (auto memilih antara CPU/GPU) worker = WorkerFactory.CreateWorker(WorkerFactory.Type.Auto, runtimeModel); } // Fungsi untuk melakukan inferensi pada Texture2D public void RunModel(Texture2D inputTexture) { // Tampilkan input (opsional) if (inputDisplay != null) { inputDisplay.texture = inputTexture; } // Resize texture ke ukuran input model Texture2D resized = ResizeTexture(inputTexture, inputWidth, inputHeight); // Konversi texture ke tensor dengan normalisasi piksel ke rentang [-1, 1] Color32[] pixels = resized.GetPixels32(); float[] floatValues = new float[inputWidth * inputHeight * 3]; for (int i = 0; i < pixels.Length; i++) { // Normalisasi: (value / 127.5f) - 1.0f floatValues[i * 3 + 0] = (pixels[i].r / 127.5f) - 1.0f; floatValues[i * 3 + 1] = (pixels[i].g / 127.5f) - 1.0f; floatValues[i * 3 + 2] = (pixels[i].b / 127.5f) - 1.0f; } // Buat tensor input dengan dimensi [batch, height, width, channels] Tensor inputTensor = new Tensor(1, inputHeight, inputWidth, 3, floatValues); // Jalankan inferensi worker.Execute(inputTensor); Tensor outputTensor = worker.PeekOutput(); // Misalnya, output memiliki 2 nilai (anorganik dan organik) int predictedIndex = ArgMax(outputTensor.ToReadOnlyArray()); string predictedLabel = predictedIndex == 0 ? "anorganik" : "organik"; // Tampilkan hasil prediksi pada UI resultText.text = "Predicted: " + predictedLabel; // Dispose tensor untuk menghindari memory leak inputTensor.Dispose(); outputTensor.Dispose(); } // Fungsi untuk mendapatkan indeks dengan nilai maksimum private int ArgMax(float[] array) { int index = 0; float max = array[0]; for (int i = 1; i < array.Length; i++) { if (array[i] > max) { max = array[i]; index = i; } } return index; } // Fungsi untuk meresize Texture2D private Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight) { RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight); RenderTexture.active = rt; Graphics.Blit(source, rt); Texture2D newTexture = new Texture2D(newWidth, newHeight); newTexture.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0); newTexture.Apply(); RenderTexture.active = null; RenderTexture.ReleaseTemporary(rt); return newTexture; } // Fungsi untuk mengambil gambar menggunakan NativeCamera dan mengoreksi orientasinya public void TakePicture() { // Periksa izin kamera terlebih dahulu NativeCamera.Permission permission = NativeCamera.CheckPermission(true); if (permission == NativeCamera.Permission.Denied || permission == NativeCamera.Permission.ShouldAsk) { NativeCamera.RequestPermission(true); } // Panggil kamera untuk mengambil gambar dengan ukuran maksimal inputWidth NativeCamera.TakePicture((string path) => { if (!string.IsNullOrEmpty(path)) { // Muat gambar dari path yang diambil Texture2D capturedTexture = NativeCamera.LoadImageAtPath(path, inputWidth); if (capturedTexture != null) { // Pastikan texture dari kamera bisa diakses (readable) Texture2D readableTexture = MakeTextureReadable(capturedTexture); // Koreksi orientasi gambar yang diambil dari kamera Texture2D correctedTexture = CorrectCameraImage(readableTexture); // Lakukan inferensi pada gambar yang telah dikoreksi RunModel(correctedTexture); } else { Debug.LogError("Gagal memuat gambar dari kamera"); } } }, inputWidth); // inputWidth sebagai ukuran maksimum gambar } // Fungsi untuk melakukan inferensi pada sample texture dari Resources public void TestInference() { Texture2D sampleTexture = Resources.Load("n3"); RunModel(sampleTexture); } // Fungsi untuk mengoreksi orientasi gambar yang diambil dari NativeCamera // Contoh ini memutar gambar 90° searah jarum jam private Texture2D CorrectCameraImage(Texture2D original) { int width = original.width; int height = original.height; Texture2D rotated = new Texture2D(height, width, original.format, false); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { rotated.SetPixel(j, width - i - 1, original.GetPixel(i, j)); } } rotated.Apply(); return rotated; } // Fungsi untuk membuat texture menjadi readable private Texture2D MakeTextureReadable(Texture2D tex) { RenderTexture tmp = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear); Graphics.Blit(tex, tmp); RenderTexture previous = RenderTexture.active; RenderTexture.active = tmp; Texture2D readableTex = new Texture2D(tex.width, tex.height); readableTex.ReadPixels(new Rect(0, 0, tmp.width, tmp.height), 0, 0); readableTex.Apply(); RenderTexture.active = previous; RenderTexture.ReleaseTemporary(tmp); return readableTex; } void OnDestroy() { // Pastikan untuk dispose worker saat objek dihancurkan worker.Dispose(); } }