val takePictureLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
val extras = result.data?.extras
}
private fun startCamera(takePictureLauncher: ManagedActivityResultLauncher<Intent, ActivityResult>) {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
takePictureLauncher.launch(intent)
}
안드로이드 내장 카메라로 촬영된 이미지를 전송을 하게 된다.
그러나 이미지를 고화질 이미지를 가져오지 못하는 문제가 발생하였다.
(참고 자료: https://stackoverflow.com/questions/8552514/is-there-any-limit-of-bundle-in-android)
Bundle에 담을 수 있는 데이터의 크기에는 제한이 있다. 이 제한은 약 1MB로 알려져 있다. 일반적으로 비트맵 이미지는 픽셀 당 24비트를 사용한다. 따라서 1920x1080 해상도의 이미지 크기를 계산하면 다음과 같다
따라서 전체 메가바이트 수는 약 5.93MB가 된다. 이러한 이유로, 이미지를 Bundle에 담아 전송할 때는 압축이 필요하며, 이로 인해 고해상도의 이미지를 전송하는 대신 저해상도의 이미지를 전달하게 된다.
(참고자료: https://developer.android.com/reference/androidx/core/content/FileProvider)
fun getImageUri(context: Context): Uri {
val directory = File(context.cacheDir, "images")
directory.mkdirs()
val file = File.createTempFile(
"selected_image_",
".jpg",
directory,
)
val authority = context.packageName + ".provider"
return getUriForFile(
context,
authority,
file,
)
이미지를 저장할 디렉토리를 생성한 후, File.createTempFile를 사용하여 임시 파일을 만든다. 그런 다음, FileProvider를 위한 authority를 생성하고, FileProvider.getUriForFile 메서드를 사용하여 해당 파일의 URI를 생성하고 반환한다.
이 과정을 통해 카메라 앱이 고해상도 이미지를 해당 파일에 저장할 수 있으며, 안전하게 저장된 파일에 접근할 수 있다.
내장 카메라를 사용할 때 커스터마이징이 어렵고, 물체를 인식하여 크기를 측정하는 데 한계가 있었다.
이 문제를 해결하기 위해 CameraX 라이브러리를 도입하였다. CameraX는 Android Jetpack 라이브러리의 일부로, 안드로이드에서 카메라 애플리케이션을 쉽게 개발할 수 있도록 도와준다. Camera2 API의 강력한 기능을 유지하면서도 사용하기 쉽게 설계되어, 다양한 카메라 기능을 간편하게 구현할 수 있다. 특히, CameraX는 ML Kit과의 통합이 용이하여, 이미지 분석 기능을 쉽게 추가할 수 있다.
핵심 구성 요소
cameraProviderFuture.addListener(Runnable {
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
}
Preview: 카메라 미리보기 기능 제공
imageCapture.takePicture(
outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
L
}
override fun onCaptureSuccess(image: ImageProxy) {
}
})
Image Capture: 사진 촬영 기능 제공
val imageAnalyzer = ImageAnalysis.Builder()
.setDefaultResolution(screenSize)
.build()
val imageAnalysis = ImageAnalysis(imageAnalyzer)
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(context)) { imageProxy ->
})
Image Analysis: 실시간 이미지 분석을 위해 각 프레임 제공
val camera = cameraProvider.bindToLifecycle(
this as LifecycleOwner, cameraSelector, preview, imageCapture)
Lifecycle Bind: CameraX 라이브러라를 사용하면 Lifecycle 문제를 해결하기 위해 사용
ML Kit은 구글이 제공하는 모바일용 머신러닝 SDK로, 머신러닝 기능을 손쉽게 통합할 수 있도록 도와준다. ML Kit은 Google의 머신러닝 모델을 활용하여 이미지 레이블링, 얼굴 인식, 텍스트 인식, 바코드 스캔 등의 기능을 제공한다. 물고기 사이즈를 측정하기 위해서 Object-Detection를 사용하였다.
val options = ObjectDetectorOptions.Builder()
.setDetectorMode(ObjectDetectorOptions.SINGLE_IMAGE_MODE)
.enableMultipleObjects()
.enableClassification()
.build()
objectDetector.process(image)
.addOnSuccessListener { detectedObjects ->
}
.addOnFailureListener { e ->
}
enableClassification 옵션을 설정했음에도 불구하고 물고기와 카드를 구분하는 데 한계가 있었다. 이 문제를 해결하기 위해, 인식된 이미지에서 위쪽은 물고기로, 아래쪽은 카드로 처리하는 방법을 선택했다. 이를 위해 y 좌표의 중심값을 기준으로 이미지를 구분하였고, 결과를 리스트 형태로 받아서 처리했다.