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?
            }
        }
    )
}
Fábio Nascimento
  • 2,644
  • 1
  • 21
  • 27
KunBa
  • 235
  • 5
  • 12
  • Do you specifically want to do this using CameraX ? If not, you might use Glide, and its [Transformations](https://github.com/bumptech/glide/wiki/Transformations) for that. – Arthur Attout Jul 26 '19 at 15:13
  • I could also use Glide, yeah. I thought maybe there are some functions since CameraX seems pretty powerful. – KunBa Jul 26 '19 at 15:21
  • 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. – Arthur Attout Jul 26 '19 at 15:23
  • Ok Thank you very much Arthur! Helped a lot already. I can mark your answer if you write it down as an answer – KunBa Jul 26 '19 at 15:27
  • 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](https://tech.okcupid.com/cropping-bitmaps-with-custom-glide-transformations/) which explains how to achieve it. You might answer the question yourself afterwards. – Arthur Attout Jul 26 '19 at 15:56
  • any solution for this? I have some problem – R Rifa Fauzi Komara Jan 07 '20 at 12:03
  • Check my answer here [Answer Link](https://stackoverflow.com/a/65162873/10619147) – Wojuola Ayotola Dec 05 '20 at 23:02

4 Answers4

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
R Rifa Fauzi Komara
  • 1,915
  • 6
  • 27
  • 54
  • 1
    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 processing – Adam Burley Jun 22 '21 at 11:34
  • It is cropping wrong x and width, y and height seems fine. – Vishnuvardhan Feb 21 '22 at 07:28
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;
}
Anthony Garant
  • 614
  • 7
  • 13
  • When I use this function this line BitmapFactory.decodeByteArray(bytes, 0, bytes.length); returned null – Sebastianor Sep 27 '19 at 12:05
  • decodeByteArray returns null if the image cannot be decoded as mentioned here: https://developer.android.com/reference/android/graphics/BitmapFactory#public-methods_1. Can you validate that you have a proper image and that the bytes array was properly filled ? – Anthony Garant Sep 27 '19 at 16:50
  • The X and Y offsets are relative to the center of the image or what else? – rdxdkr Feb 04 '21 at 20:27
  • 1
    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 correctly – Adam Burley Jun 22 '21 at 11:32
  • 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. – Cornescu Cătălin Jul 27 '22 at 07:37
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)
Cash Lo
  • 1,052
  • 1
  • 8
  • 20
0

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()
Binh Ho
  • 3,690
  • 1
  • 31
  • 31