feat: implement camera feature for plant disease detection
This commit is contained in:
parent
6d9aa910b6
commit
69b7cbac0f
|
|
@ -0,0 +1,13 @@
|
|||
package com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.camera.view.LifecycleCameraController
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
interface CameraRepository {
|
||||
fun takePicture(
|
||||
controller: LifecycleCameraController,
|
||||
executor: Executor,
|
||||
onPictureTaken: (Bitmap) -> Unit
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Matrix
|
||||
import android.util.Log
|
||||
import androidx.camera.core.ImageCapture.OnImageCapturedCallback
|
||||
import androidx.camera.core.ImageCaptureException
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.camera.view.LifecycleCameraController
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
class CameraRepositoryImpl : CameraRepository {
|
||||
|
||||
override fun takePicture(
|
||||
controller: LifecycleCameraController,
|
||||
executor: Executor,
|
||||
onPictureTaken: (Bitmap) -> Unit
|
||||
) {
|
||||
Log.d("CameraRepository", "Starting takePicture")
|
||||
|
||||
controller.takePicture(
|
||||
executor,
|
||||
object : OnImageCapturedCallback() {
|
||||
override fun onCaptureSuccess(image: ImageProxy) {
|
||||
Log.d("CameraRepository", "Capture success")
|
||||
|
||||
try {
|
||||
val rotationDegrees = image.imageInfo.rotationDegrees
|
||||
Log.d("CameraRepository", "Rotation: $rotationDegrees")
|
||||
|
||||
val matrix = Matrix().apply {
|
||||
postRotate(rotationDegrees.toFloat())
|
||||
}
|
||||
|
||||
val originalBitmap = image.toBitmap()
|
||||
Log.d("CameraRepository", "Original bitmap size: ${originalBitmap.width} x ${originalBitmap.height}")
|
||||
|
||||
val rotatedBitmap = Bitmap.createBitmap(
|
||||
originalBitmap,
|
||||
0, 0,
|
||||
image.width,
|
||||
image.height,
|
||||
matrix,
|
||||
true
|
||||
)
|
||||
|
||||
Log.d("CameraRepository", "Rotated bitmap size: ${rotatedBitmap.width} x ${rotatedBitmap.height}")
|
||||
|
||||
onPictureTaken(rotatedBitmap)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("CameraRepository", "Error while processing image: ${e.message}", e)
|
||||
} finally {
|
||||
image.close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
Log.e("CameraRepository", "Couldn't take photo: ${exception.message}", exception)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.syaroful.agrilinkvocpro.plant_disease_detection_feature.di
|
||||
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository.CameraRepository
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository.CameraRepositoryImpl
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera.CameraViewModel
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val cameraModule = module {
|
||||
single<CameraRepository> { CameraRepositoryImpl() }
|
||||
viewModel { CameraViewModel(get(), get())}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
package com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera
|
||||
|
||||
import androidx.camera.view.CameraController
|
||||
import androidx.camera.view.LifecycleCameraController
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.navigation.NavHostController
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.R
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera.component.CustomCameraShutter
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CameraScreen(
|
||||
navController: NavHostController,
|
||||
viewModel: CameraViewModel
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
// val bitmaps by viewModel.bitmaps.collectAsState()
|
||||
val bitmap by viewModel.bitmap.collectAsState()
|
||||
val scope = rememberCoroutineScope()
|
||||
// val scaffoldState = rememberBottomSheetScaffoldState()
|
||||
|
||||
val controller = remember {
|
||||
LifecycleCameraController(context).apply {
|
||||
setEnabledUseCases(
|
||||
CameraController.IMAGE_CAPTURE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
"Deteksi Penyakit Tanaman", style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { navController.popBackStack() }) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding)
|
||||
.padding(top = 80.dp)
|
||||
) {
|
||||
AndroidView(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopCenter)
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f),
|
||||
factory = {
|
||||
PreviewView(it).apply {
|
||||
this.controller = controller
|
||||
controller.bindToLifecycle(lifecycleOwner)
|
||||
this.scaleType = PreviewView.ScaleType.FILL_CENTER
|
||||
}
|
||||
}
|
||||
)
|
||||
CustomCameraShutter(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = 16.dp, start = 20.dp, end = 20.dp),
|
||||
onShutterClick = {
|
||||
if (!uiState.isLoading)
|
||||
viewModel.takePicture(
|
||||
controller,
|
||||
ContextCompat.getMainExecutor(context)
|
||||
) {
|
||||
navController.navigate("detail_screen")
|
||||
}
|
||||
|
||||
},
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(24.dp)
|
||||
.align(Alignment.TopCenter),
|
||||
painter = painterResource(id = R.drawable.scan_frame),
|
||||
contentDescription = "scan frame"
|
||||
)
|
||||
if (uiState.isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera
|
||||
|
||||
data class CameraUIState(
|
||||
val isLoading: Boolean = false
|
||||
)
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.util.Log
|
||||
import androidx.camera.view.LifecycleCameraController
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.core.AppConstant
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.core.extention.cropToSquare
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository.CameraRepository
|
||||
import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.detail.PlantDiagnosisViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
private const val TAG = "CameraViewModel"
|
||||
|
||||
class CameraViewModel(
|
||||
private val cameraRepository: CameraRepository,
|
||||
private val diagnosisViewModel: PlantDiagnosisViewModel
|
||||
) : ViewModel() {
|
||||
|
||||
|
||||
private val _uiState = MutableStateFlow(CameraUIState())
|
||||
val uiState: StateFlow<CameraUIState> = _uiState
|
||||
|
||||
private val _bitmap = MutableStateFlow<Bitmap?>(null)
|
||||
val bitmap: StateFlow<Bitmap?> = _bitmap
|
||||
|
||||
val prompt = AppConstant().prompt
|
||||
|
||||
fun takePicture(
|
||||
controller: LifecycleCameraController,
|
||||
executor: Executor,
|
||||
onFinish: () -> Unit
|
||||
) {
|
||||
Log.d(TAG, "takePicture() called")
|
||||
_uiState.value = _uiState.value.copy(isLoading = true)
|
||||
|
||||
cameraRepository.takePicture(controller, executor) { bitmap ->
|
||||
Log.d(TAG, "Picture taken, updating state")
|
||||
val croppedImage = cropToSquare(bitmap)
|
||||
_bitmap.value = croppedImage
|
||||
// croppedImage.let {
|
||||
// diagnosisViewModel.analyzePlant(bitmap = it, prompt = prompt)
|
||||
// Log.d(TAG, "analyzePlant() called")
|
||||
// }
|
||||
_uiState.value = _uiState.value.copy(isLoading = false)
|
||||
|
||||
onFinish()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user