5

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,479
  • 1
  • 20
  • 26
KunBa
  • 205
  • 4
  • 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

3 Answers3

5

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,452
  • 5
  • 20
  • 41
  • 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
4

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
  • 576
  • 6
  • 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
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
  • 872
  • 1
  • 7
  • 18