I am trying to implement an app to take pictures, I don't want the full pic, I need to crop three pictures of the main pic (represented with rectangles), and save them for later processing. I am using an A4 paper aspect ratio (1:1.414)
@Composable
private fun CameraScreenContent(
viewModel: ScanLabelViewModel,
navController: NavController,
){
Box(
contentAlignment = Alignment.BottomCenter,
modifier = Modifier
.fillMaxSize()
.background(Black)
) {
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
val cameraController: LifecycleCameraController = remember { LifecycleCameraController(context) }
val previewView = remember { PreviewView(context) }
val capturedImage = viewModel.capturedImage.collectAsState()
AndroidView(
modifier = Modifier
.align(Alignment.TopCenter)
.aspectRatio(1 / 1.414f)
.fillMaxSize()
.border(8.dp, Color(0x4A000000))
,
factory = {
previewView.apply {
setupCamera(
cameraController = cameraController,
lifecycleOwner = lifecycleOwner,
previewView = previewView,
)
}
}
)
Canvas(
modifier = Modifier
.align(Alignment.TopCenter)
.aspectRatio(1 / 1.414f)
.fillMaxSize()
) {
viewModel.setCropsMesurements(size)
//expediteur box
drawRect(
color = White,
topLeft = Offset(size.width*6/24, size.height*3/29),
size = Size((size.width/24)*11,(size.height/29)*4),
style = Stroke(4f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f,10f),0f))
)
//destinataire box
drawRect(
color = White,
topLeft = Offset(size.width*1/24, size.height*7/29),
size = Size((size.width/24)*9,(size.height/29)*4),
style = Stroke(4f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f,10f),0f))
)
//desciption box
drawRect(
color = White,
topLeft = Offset(size.width*1/24, size.height*17/29),
size = Size((size.width/24)*13,(size.height/29)*1),
style = Stroke(4f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f,10f),0f))
)
}
Button(
modifier = Modifier
.padding(bottom = 32.dp)
.align(Alignment.BottomCenter),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.buttonColors(
contentColor = White
),
border = BorderStroke(2.dp, Color(0x4DFFFFFF)),
onClick = {
viewModel.captureImage(cameraController)
if (capturedImage.value != null){
viewModel.cropExpediteurImage()
}
}
){
Text(text = "take picture",modifier = Modifier.padding(1.dp))
}
}
}
private fun setupCamera(
cameraController: LifecycleCameraController,
lifecycleOwner: LifecycleOwner,
previewView: PreviewView,
) {
cameraController.bindToLifecycle(lifecycleOwner)
previewView.controller = cameraController
}
and this is capture image funtion
fun captureImage(
cameraController: LifecycleCameraController,
onImageCaptured: (File) -> Unit
) {
val file = File(context.cacheDir, "${UUID.randomUUID()}.jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(file).build()
cameraController.takePicture(
outputOptions,
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
onImageCaptured(file)
}
override fun onError(exception: ImageCaptureException) {
println("Failed $exception")
}
}
)
}
I have tried so many proposed solutions in here but I the proportions have never been right, I always get small chunks of the picture, its like I am cropping the preview not the captured image.
Try this,
@Composable
private fun CameraScreenContent() {
Box(
contentAlignment = Alignment.BottomCenter,
modifier = Modifier
.fillMaxSize()
.background(Black)
) {
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
val cameraController: LifecycleCameraController =
remember { LifecycleCameraController(context) }
val previewView = remember { PreviewView(context) }
// val capturedImage = viewModel.capturedImage.collectAsState()
AndroidView(
modifier = Modifier
.align(Alignment.TopCenter)
.aspectRatio(1 / 1.414f)
.fillMaxSize()
.border(8.dp, Color(0x4A000000)),
factory = {
previewView.apply {
setupCamera(
cameraController = cameraController,
lifecycleOwner = lifecycleOwner,
previewView = previewView,
)
}
}
)
Canvas(
modifier = Modifier
.align(Alignment.TopCenter)
.aspectRatio(1 / 1.414f)
.fillMaxSize()
) {
drawRect(
color = White,
topLeft = Offset(size.width * 6 / 24, size.height * 3 / 29),
size = Size((size.width / 24) * 11, (size.height / 29) * 4),
style = Stroke(
4f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
)
)
//destinataire box
drawRect(
color = White,
topLeft = Offset(size.width * 1 / 24, size.height * 7 / 29),
size = Size((size.width / 24) * 9, (size.height / 29) * 4),
style = Stroke(
4f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
)
)
//desciption box
drawRect(
color = White,
topLeft = Offset(size.width * 1 / 24, size.height * 17 / 29),
size = Size((size.width / 24) * 13, (size.height / 29) * 1),
style = Stroke(
4f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
)
)
}
Button(
modifier = Modifier
.padding(bottom = 32.dp)
.align(Alignment.BottomCenter),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.buttonColors(
contentColor = White
),
border = BorderStroke(2.dp, Color(0x4DFFFFFF)),
onClick = {
captureImage(
cameraController = cameraController,
onImageCaptured = {
val bitmap = fileToBitmap(it) ?: return@captureImage
// I tried with the emulator and when it takes the image it is rotated so I had to set it right.
val rotatedBitmap = rotateBitmap(bitmap, 90f)
// Here you have images
processImage(rotatedBitmap).toMutableList()
},
context = context
)
},
content = { Text(text = "Capture") }
)
}
}
you also need these functions
fun rotateBitmap(bitmap: Bitmap, degrees: Float): Bitmap {
val matrix = Matrix()
matrix.postRotate(degrees)
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
fun cropBitmap(original: Bitmap, rect: Rect): Bitmap {
return Bitmap.createBitmap(original, rect.left, rect.top, rect.width(), rect.height())
}
fun processImage(original: Bitmap): List<Bitmap> {
val canvasWidth = original.width.toFloat()
val canvasHeight = original.height.toFloat()
val firstRect = Rect(
(canvasWidth * 6 / 24).toInt(),
(canvasHeight * 3 / 29).toInt(),
(canvasWidth * 17 / 24).toInt(),
(canvasHeight * 7 / 29).toInt()
)
val secondRect = Rect(
(canvasWidth * 1 / 24).toInt(),
(canvasHeight * 7 / 29).toInt(),
(canvasWidth * 10 / 24).toInt(),
(canvasHeight * 11 / 29).toInt()
)
val thirdRect = Rect(
(canvasWidth * 1 / 24).toInt(),
(canvasHeight * 17 / 29).toInt(),
(canvasWidth * 14 / 24).toInt(),
(canvasHeight * 18 / 29).toInt()
)
val firstBitmap = cropBitmap(original, firstRect)
val secondBitmap = cropBitmap(original, secondRect)
val thirdBitmap = cropBitmap(original, thirdRect)
return listOf(firstBitmap, secondBitmap, thirdBitmap)
}
fun fileToBitmap(file: File): Bitmap? {
return try {
val inputStream = FileInputStream(file)
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream.close()
bitmap
} catch (e: IOException) {
e.printStackTrace()
null
}
}