mirror of
https://github.com/pendragonnn/PalmGuard-App-Thesis.git
synced 2025-08-10 09:22:22 +00:00
feat: [detection] update ui & change newest model
This commit is contained in:
@ -4,45 +4,6 @@ import com.example.palmguardapp.data.local.entity.DiseaseDiagnose
|
||||
import com.example.palmguardapp.data.local.room.DiseaseDiagnoseDao
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
//class DiseaseRepository private constructor(
|
||||
// private val apiService: ApiService
|
||||
//) {
|
||||
//
|
||||
// suspend fun getDiseaseById(id: String): Flow<Result<DiseaseByIdResponse>> = flow {
|
||||
// emit(Result.Loading)
|
||||
// val response = apiService.getDiseaseById(id)
|
||||
// emit(Result.Success(response))
|
||||
// }.catch { e ->
|
||||
// emit(Result.Error(e.message.toString()))
|
||||
// }
|
||||
//
|
||||
// suspend fun getAllDiseaseDetail(): Flow<Result<DiseaseDetailResponse>> = flow {
|
||||
// emit(Result.Loading)
|
||||
// val response = apiService.getAllDiseaseDetail()
|
||||
// emit(Result.Success(response))
|
||||
// }.catch { e ->
|
||||
// emit(Result.Error(e.message.toString()))
|
||||
// }
|
||||
//
|
||||
// suspend fun getDiseaseDetailById(id: String): Flow<Result<DiseaseDetailByIdResponse>> = flow {
|
||||
// emit(Result.Loading)
|
||||
// val response = apiService.getDiseaseDetailById(id)
|
||||
// emit(Result.Success(response))
|
||||
// }.catch { e ->
|
||||
// emit(Result.Error(e.message.toString()))
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
// @Volatile
|
||||
// private var instance: DiseaseRepository? = null
|
||||
//
|
||||
// fun getInstance(apiService: ApiService): DiseaseRepository =
|
||||
// instance ?: synchronized(this) {
|
||||
// instance ?: DiseaseRepository(apiService).also { instance = it }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
class DiseaseRepository private constructor(
|
||||
private val diseaseDiagnoseDao: DiseaseDiagnoseDao
|
||||
) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// Import library yang dibutuhkan untuk context, bitmap, logging, dan TensorFlow Lite
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.util.Log
|
||||
@ -9,51 +10,69 @@ import org.tensorflow.lite.support.image.TensorImage
|
||||
import org.tensorflow.lite.support.image.ops.ResizeOp
|
||||
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer
|
||||
|
||||
|
||||
// Kelas utama untuk klasifikasi gambar menggunakan model ML TFLite
|
||||
class ImageClassifier(private val context: Context) {
|
||||
|
||||
// Ukuran input gambar yang dibutuhkan model
|
||||
private val imageSize = 224
|
||||
|
||||
// Daftar label kelas dari model
|
||||
private val classes = arrayOf("Brown Spots", "Healthy", "Unknown")
|
||||
|
||||
// ImageProcessor untuk mengubah ukuran dan menormalisasi gambar agar sesuai dengan input model
|
||||
private val imageProcessor = ImageProcessor.Builder()
|
||||
.add(ResizeOp(imageSize, imageSize, ResizeOp.ResizeMethod.BILINEAR))
|
||||
.add(NormalizeOp(0f, 255f))
|
||||
.add(ResizeOp(imageSize, imageSize, ResizeOp.ResizeMethod.BILINEAR)) // Resize gambar ke 224x224
|
||||
.add(NormalizeOp(0f, 255f)) // Normalisasi piksel gambar ke range 0–1
|
||||
.build()
|
||||
|
||||
// Fungsi utama untuk mengklasifikasikan gambar
|
||||
fun classifyImage(image: Bitmap): Pair<String, Float>? {
|
||||
// Membuat instance model
|
||||
val model = Model.newInstance(context)
|
||||
|
||||
// Mengonversi gambar bitmap ke TensorImage dengan tipe data FLOAT32
|
||||
val tensorImage = TensorImage(DataType.FLOAT32)
|
||||
val convertedBitmap = image.copy(Bitmap.Config.ARGB_8888, true)
|
||||
val convertedBitmap = image.copy(Bitmap.Config.ARGB_8888, true) // Salin bitmap agar bisa diolah
|
||||
tensorImage.load(convertedBitmap)
|
||||
|
||||
// Proses preprocessing gambar: resize + normalisasi
|
||||
val processedImage = imageProcessor.process(tensorImage)
|
||||
|
||||
// Menyiapkan buffer input model dengan shape dan tipe data yang sesuai
|
||||
val inputFeature0 = TensorBuffer.createFixedSize(intArrayOf(1, imageSize, imageSize, 3), DataType.FLOAT32)
|
||||
inputFeature0.loadBuffer(processedImage.buffer)
|
||||
|
||||
// Memproses input melalui model dan mendapatkan output
|
||||
val outputs = model.process(inputFeature0)
|
||||
val outputFeature0 = outputs.outputFeature0AsTensorBuffer
|
||||
|
||||
// Mengambil array probabilitas prediksi dari hasil output
|
||||
val confidences = outputFeature0.floatArray
|
||||
Log.d("ImageClassifier", "Confidence: ${confidences.size}")
|
||||
|
||||
// Logging setiap confidence untuk tiap kelas
|
||||
for (i in confidences.indices) {
|
||||
Log.d("ImageClassifier", "Class $i (${classes[i]}): ${confidences[i]}")
|
||||
}
|
||||
|
||||
// Mencari indeks prediksi dengan confidence tertinggi
|
||||
val maxPos = confidences.indices.maxByOrNull { confidences[it] } ?: -1
|
||||
val maxConfidence = (maxOf(confidences[maxPos]) * 100).toFloat()
|
||||
val maxConfidence = (maxOf(confidences[maxPos]) * 100).toFloat() // Konversi ke persen
|
||||
Log.d("ImageClassifier", "Max Position: $maxPos, Max Confidence: $maxConfidence")
|
||||
|
||||
// Menutup instance model untuk membebaskan resource
|
||||
model.close()
|
||||
|
||||
// Mengembalikan hasil klasifikasi jika confidence mencukupi dan bukan kelas Unknown
|
||||
return if (maxPos >= 0 && maxConfidence > THRESHOLD_CONFIDENCE && classes[maxPos] != "Unknown") {
|
||||
Pair(classes[maxPos], maxConfidence)
|
||||
} else {
|
||||
null
|
||||
null // Tidak ada hasil yang cukup meyakinkan
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Batas minimum confidence yang dianggap valid (dalam persen)
|
||||
private const val THRESHOLD_CONFIDENCE = 0.5f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ class DetectionViewModel(private val historyDiagnoseRepository: HistoryDiagnoseR
|
||||
fun deleteHistory(historyDiagnose: HistoryDiagnose) {
|
||||
viewModelScope.launch {
|
||||
historyDiagnoseRepository.delete(historyDiagnose)
|
||||
// Refresh the list after deletion
|
||||
getDetectionList()
|
||||
}
|
||||
}
|
||||
|
@ -49,12 +49,6 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
|
||||
private val diseaseToId = mapOf(
|
||||
"Brown Spots" to "D-001",
|
||||
"Healthy" to "D-002",
|
||||
// "Bird Eye Spot" to "D-003",
|
||||
// "Brown Blight" to "D-004",
|
||||
// "Gray Light" to "D-005",
|
||||
// "Red Leaf Spot" to "D-006",
|
||||
// "White Spot" to "D-007",
|
||||
// "Healthy" to "D-008"
|
||||
)
|
||||
|
||||
private lateinit var cameraLauncher: ActivityResultLauncher<Intent>
|
||||
@ -219,38 +213,6 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
|
||||
Log.d("saveImageAndFetchData", "Date: $date")
|
||||
val dateNow = formatter.format(date)
|
||||
val diseaseId = diseaseToId[diagnosis]
|
||||
// diseaseId?.let { id ->
|
||||
// viewModel.getDiseaseById(id)
|
||||
// viewModel.dataDisease.collect { result ->
|
||||
// when (result) {
|
||||
// is Result.Success -> {
|
||||
// val diseaseData = result.data
|
||||
// Log.d("HomeFragment", "Disease Data: $diseaseData")
|
||||
// val historyDiagnose = HistoryDiagnose(
|
||||
// name = diagnosis,
|
||||
// imageUri = imageUri.toString(),
|
||||
// diagnosis = diseaseData.data?.diseaseExplanation ?: "",
|
||||
// recommendation = diseaseData.data?.diseaseRecommendation ?: "",
|
||||
// confidence = confidence.toString(),
|
||||
// date = dateNow
|
||||
// )
|
||||
//
|
||||
// viewModel.saveDiagnose(historyDiagnose)
|
||||
// lastDiagnosis = historyDiagnose
|
||||
// Log.d("HomeFragment", "History Diagnose: $historyDiagnose")
|
||||
// binding.progressResult.visibility = View.GONE
|
||||
// restartFragment()
|
||||
// navigateToDiagnoseDetail(historyDiagnose)
|
||||
// }
|
||||
// is Result.Error -> {
|
||||
// // Handle error state
|
||||
// }
|
||||
// Result.Loading -> {
|
||||
// binding.progressResult.visibility = View.VISIBLE
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
diseaseId?.let { id ->
|
||||
viewModel.getDiseaseById(id)
|
||||
|
||||
@ -317,7 +279,6 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
|
||||
binding.tvHsHistoryEmpty.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
|
@ -18,15 +18,6 @@ class DiseaseViewModel(private val diseaseRepository: DiseaseRepository) : ViewM
|
||||
private val _diseaseById = MutableSharedFlow<Result<DiseaseDiagnose?>>()
|
||||
val diseaseById: Flow<Result<DiseaseDiagnose?>> = _diseaseById.asSharedFlow()
|
||||
|
||||
// fun getAllDisease() {
|
||||
// viewModelScope.launch {
|
||||
// _listDisease.emit(Result.Loading)
|
||||
// diseaseRepository.getAllDisease().collect { diseases ->
|
||||
// _listDisease.emit(Result.Success(diseases))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fun getDiseaseById(id: String) {
|
||||
viewModelScope.launch {
|
||||
_diseaseById.emit(Result.Loading)
|
||||
|
Binary file not shown.
@ -124,7 +124,7 @@
|
||||
android:id="@+id/tv_hs_phone_analyze"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ambil Gambar"
|
||||
android:text="Analisis Hasil"
|
||||
android:textColor="@color/greenGeneral"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
|
@ -7,5 +7,5 @@
|
||||
<item
|
||||
android:id="@+id/detection"
|
||||
android:icon="@drawable/hs_trace_black_img"
|
||||
android:title="Deteksi" />
|
||||
android:title="Riwayat" />
|
||||
</menu>
|
Reference in New Issue
Block a user