using System.Collections; using UnityEngine; using Unity.Barracuda; using TMPro; using UnityEngine.SceneManagement; using UnityEngine.UI; // Untuk komponen Image public class AcakClassifier : MonoBehaviour { [Header("Model Barracuda")] public NNModel onnxModel; private Model runtimeModel; private IWorker worker; [Header("UI Text")] public TextMeshProUGUI outputText; public TextMeshProUGUI scoreText; public TextMeshProUGUI titleText; public TextMeshProUGUI descriptionText; [Header("UI Image")] public Image displayImage; // Menampilkan foto hasil kamera [Header("Initial Display Sprites")] public Sprite organikSprite; public Sprite anorganikSprite; public Sprite bukansampahSprite; [Header("Feedback Images")] public Image feedbackCorrectImage; public Image feedbackWrongImage; public float feedbackDelay = 0.5f; [Header("Canvas")] public GameObject canvasBerhasil; public GameObject canvasGagal; [Header("Audio")] [SerializeField] private AudioSource audioSource; [SerializeField] private AudioClip correctClip; [SerializeField] private AudioClip wrongClip; [SerializeField] private AudioClip awardClip; private string[] classes = { "anorganik", "bukansampah", "organik" }; private string[] scenarioSequence; private int currentIndex = 0; private int inputWidth = 224; private int inputHeight = 224; private int currentScore = 0; private int maxScore = 0; void Start() { runtimeModel = ModelLoader.Load(onnxModel); worker = WorkerFactory.CreateWorker(WorkerFactory.Type.Auto, runtimeModel); int selectedLevel = PlayerPrefs.GetInt("selectedLevel", 1); Debug.Log("Selected Level: " + selectedLevel); // Set target quest sesuai level yang dipilih switch (selectedLevel) { case 1: scenarioSequence = new string[] { "organik" }; break; case 2: scenarioSequence = new string[] { "bukansampah" }; break; case 3: scenarioSequence = new string[] { "anorganik" }; break; case 4: scenarioSequence = new string[] { "bukansampah" }; break; case 5: scenarioSequence = new string[] { "anorganik" }; break; case 6: scenarioSequence = new string[] { "organik" }; break; default: scenarioSequence = new string[] { "organik" }; break; } maxScore = scenarioSequence.Length; if (feedbackCorrectImage != null) feedbackCorrectImage.gameObject.SetActive(false); if (feedbackWrongImage != null) feedbackWrongImage.gameObject.SetActive(false); UpdateUI(); UpdateScoreText(); } private void UpdateUI() { if (currentIndex >= scenarioSequence.Length) { titleText.text = "SELESAI!"; descriptionText.text = "Kamu sudah menemukan target."; return; } string target = scenarioSequence[currentIndex]; switch (target) { case "organik": titleText.text = "JELAJAHI SAMPAH ORGANIK"; descriptionText.text = "Cari sampah organik, misalnya kulit buah."; if (displayImage != null && organikSprite != null) displayImage.sprite = organikSprite; break; case "anorganik": titleText.text = "JELAJAHI SAMPAH ANORGANIK"; descriptionText.text = "Cari sampah anorganik, misalnya botol plastik."; if (displayImage != null && anorganikSprite != null) displayImage.sprite = anorganikSprite; break; case "bukansampah": titleText.text = "JELAJAHI OBJEK BUKAN SAMPAH"; descriptionText.text = "Cari objek non-sampah, misalnya buah apel atau jeruk."; if (displayImage != null && bukansampahSprite != null) displayImage.sprite = bukansampahSprite; break; } } public void TakePicture() { NativeCamera.Permission permission = NativeCamera.CheckPermission(true); if (permission == NativeCamera.Permission.Denied || permission == NativeCamera.Permission.ShouldAsk) { NativeCamera.RequestPermission(true); } NativeCamera.TakePicture((path) => { if (!string.IsNullOrEmpty(path)) { Texture2D texture = NativeCamera.LoadImageAtPath(path, inputWidth); if (texture == null) { Debug.LogError("Gagal memuat gambar dari path: " + path); return; } Texture2D readableTexture = MakeTextureReadable(texture); Texture2D correctedTexture = CorrectCameraImage(readableTexture); if (displayImage != null) { Sprite newSprite = Sprite.Create(correctedTexture, new Rect(0, 0, correctedTexture.width, correctedTexture.height), new Vector2(0.5f, 0.5f)); displayImage.sprite = newSprite; } RunInference(correctedTexture); } }, maxSize: inputWidth); } public void RunInference(Texture2D inputTexture) { if (inputTexture == null) { Debug.LogError("Gambar tidak tersedia untuk inferensi!"); return; } Texture2D resized = ResizeTexture(inputTexture, inputWidth, inputHeight); Color32[] pixels = resized.GetPixels32(); float[] floatValues = new float[inputWidth * inputHeight * 3]; for (int i = 0; i < pixels.Length; i++) { 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; } Tensor inputTensor = new Tensor(1, inputHeight, inputWidth, 3, floatValues); worker.Execute(inputTensor); Tensor outputTensor = worker.PeekOutput(); float[] scores = outputTensor.ToReadOnlyArray(); int predictedIndex = ArgMax(scores); float confidence = scores[predictedIndex]; string predictedClass = classes[predictedIndex]; Debug.Log($"Predicted Class: {predictedClass}\nConfidence: {confidence:F2}"); if (outputText != null) { outputText.text = $"Predicted Class: {predictedClass}\nConfidence: {confidence:F2}"; } // Cek apakah prediksi sesuai dengan target if (currentIndex < scenarioSequence.Length) { string currentTarget = scenarioSequence[currentIndex]; if (predictedClass == currentTarget) { currentScore++; if (audioSource != null && correctClip != null) { audioSource.PlayOneShot(correctClip); } currentIndex++; if (currentIndex >= scenarioSequence.Length) { StartCoroutine(ShowFeedback(true, true)); } else { StartCoroutine(ShowFeedback(true, false)); } } else { if (audioSource != null && wrongClip != null) { audioSource.PlayOneShot(wrongClip); } // Jawaban salah, tampilkan feedback lalu canvas gagal StartCoroutine(ShowFeedback(false, true)); } } inputTensor.Dispose(); outputTensor.Dispose(); } private IEnumerator ShowFeedback(bool isCorrect, bool isFinal) { if (isCorrect) { if (feedbackCorrectImage != null) feedbackCorrectImage.gameObject.SetActive(true); } else { if (feedbackWrongImage != null) feedbackWrongImage.gameObject.SetActive(true); } yield return new WaitForSeconds(feedbackDelay); if (isCorrect) { if (feedbackCorrectImage != null) feedbackCorrectImage.gameObject.SetActive(false); } else { if (feedbackWrongImage != null) feedbackWrongImage.gameObject.SetActive(false); } // Jika isFinal true, tampilkan canvas hasil dan (jika benar) unlock level berikutnya if (isFinal) { if (isCorrect) { int selectedLevel = PlayerPrefs.GetInt("selectedLevel", 1); int maxLevelUnlocked = PlayerPrefs.GetInt("maxLevelUnlocked", 1); if (selectedLevel >= maxLevelUnlocked) { PlayerPrefs.SetInt("maxLevelUnlocked", selectedLevel + 1); } if (audioSource != null && awardClip != null) { audioSource.PlayOneShot(awardClip); } if (canvasBerhasil != null) canvasBerhasil.SetActive(true); } else { if (canvasGagal != null) canvasGagal.SetActive(true); } } else { UpdateUI(); } } 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; } 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; } 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; } 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; } private void UpdateScoreText() { int selectedLevel = PlayerPrefs.GetInt("selectedLevel", 1); if (scoreText != null) { scoreText.text = "Level " + selectedLevel; } } private void OnDestroy() { worker?.Dispose(); } }