EcoQuest/Assets/Script/ModelInference.cs

183 lines
6.4 KiB
C#

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<Texture2D>("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();
}
}