Android: How to crop images using CameraX?
Asked Answered
H

4

7

I want to take a photo and crop out a square of 25x25 dp from the center using CameraX. I have read that cropping is possible using ImageCapture but unfortunately there are almost no similar examples out there so far.

val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
    setTargetAspectRatio(Rational(1, 1))
    setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
}.build()

val imageCapture = ImageCapture(imageCaptureConfig)
btn_take_photo.setOnClickListener {
    imageCapture.takePicture(
        object : ImageCapture.OnImageCapturedListener() {
            override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) {
                super.onCaptureSuccess(image, rotationDegrees)

                // image manipulation here?
            }
        }
    )
}
Haddington answered 26/7, 2019 at 15:10 Comment(7)
Do you specifically want to do this using CameraX ? If not, you might use Glide, and its Transformations for that.Subdue
I could also use Glide, yeah. I thought maybe there are some functions since CameraX seems pretty powerful.Haddington
CameraX is mostly an API to interact with the camera(s) of the device. It's more hardware-related stuffs. Image manipulation isn't really something this API was designed to achieve.Subdue
Ok Thank you very much Arthur! Helped a lot already. I can mark your answer if you write it down as an answerHaddington
Unfortunately I wouldn't be able to answer the actual question. Cropping a fixed-size square out of an image is harder than I expected with Glide. You might take a look at this article which explains how to achieve it. You might answer the question yourself afterwards.Subdue
any solution for this? I have some problemJehol
Check my answer here Answer LinkSeisin
J
6

You can use this function to cropping the Image after capture the Image:

private fun cropImage(bitmap: Bitmap, frame: View, reference: View): ByteArray {
        val heightOriginal = frame.height
        val widthOriginal = frame.width
        val heightFrame = reference.height
        val widthFrame = reference.width
        val leftFrame = reference.left
        val topFrame = reference.top
        val heightReal = bitmap.height
        val widthReal = bitmap.width
        val widthFinal = widthFrame * widthReal / widthOriginal
        val heightFinal = heightFrame * heightReal / heightOriginal
        val leftFinal = leftFrame * widthReal / widthOriginal
        val topFinal = topFrame * heightReal / heightOriginal
        val bitmapFinal = Bitmap.createBitmap(
            bitmap,
            leftFinal, topFinal, widthFinal, heightFinal
        )
        val stream = ByteArrayOutputStream()
        bitmapFinal.compress(
            Bitmap.CompressFormat.JPEG,
            100,
            stream
        ) //100 is the best quality possibe
        return stream.toByteArray()
    }

Crop an image taking a reference a view parent like a frame and a view child like final reference

  • param bitmap image to crop
  • param frame where the image is set it
  • param reference frame to take reference for a crop the image
  • return image already cropped
Jehol answered 8/1, 2020 at 10:13 Comment(2)
That is if you already have a Bitmap; usually you won't have one and you will have an Image instead (as in the question); if you already had a Bitmap you could just use Bitmap.createBitmap to crop it and don't need to do byte-level processingJuetta
It is cropping wrong x and width, y and height seems fine.Hookworm
A
5

You can convert the image to a Bitmap and then crop it.

Bitmap cropImage(Image image, int rotationDegree, int xOffset, int yOffset, int cropWidth, int cropHeight) {
    // 1 - Convert image to Bitmap
    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

    // 2 - Rotate the Bitmap
    if(rotationDegree != 0) {
        Matrix rotationMatrix = new Matrix();
        rotationMatrix.postRotate(rotationDegree);
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), rotationMatrix, true);
    }

    // 3 - Crop the Bitmap
    bitmap = Bitmap.createBitmap(bitmap, xOffset, yOffset, cropWidth, cropHeight);

    return bitmap;
}
Abiogenesis answered 5/9, 2019 at 17:33 Comment(5)
When I use this function this line BitmapFactory.decodeByteArray(bytes, 0, bytes.length); returned nullEctomy
decodeByteArray returns null if the image cannot be decoded as mentioned here: developer.android.com/reference/android/graphics/…. Can you validate that you have a proper image and that the bytes array was properly filled ?Abiogenesis
The X and Y offsets are relative to the center of the image or what else?Antemortem
if your image is YUV_420_888 format (the default for CameraX) then you will have 3 planes and your code is only accessing the first one -> your cropped image is going to be monochrome at best, assuming you are interpreting the Y-plane data correctlyJuetta
The code works as intended. Most of the conversions lack the rotation part from Image to bitmap. Also, the cropping part could benefit from an out of bound check. After the rotation is done, the width and hight are swapped and you can crop outside the border, witch will return an exception.Panada
R
1

You can get the planes from the image and crop them manually:

private fun cropByteArray(array : ByteArray, cropRect: Rect): ByteArray {
    val croppedArray = ByteArray(cropRect.width()*cropRect.height())
    val imageWidth = 640
    var i = 0
    array.forEachIndexed { index, byte ->
        val x = index % imageWidth
        val y = index / imageWidth

        if (cropRect.left <= x && x < cropRect.right && cropRect.top <= y && y < cropRect.bottom) {
            croppedArray[i] = byte
            i++
        }
    }
    return croppedArray
}

......

val buffer = image.planes[0].buffer
val imageByteArray = buffer.toByteArray()
val data = cropByteArray(imageByteArray, cropRect)
Refreshment answered 20/8, 2019 at 16:6 Comment(0)
K
1

Tested. Simple crop bitmap at center function.

fun Bitmap.toSquare(): Bitmap {
    val srcBmp = this
    if (srcBmp.width >= srcBmp.height) {
        return Bitmap.createBitmap(
            srcBmp,
            srcBmp.width / 2 - srcBmp.height / 2,
            0,
            srcBmp.height,
            srcBmp.height
        );
    } else {
        return Bitmap.createBitmap(
            srcBmp,
            0,
            srcBmp.getHeight() / 2 - srcBmp.width / 2,
            srcBmp.width,
            srcBmp.width
        );
    }
}

Usage:

val squareBitmap = orgBitmap.toSquare()
Kloof answered 13/12, 2022 at 10:43 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.