【问题标题】:Android: How to crop images using CameraX?Android:如何使用 CameraX 裁剪图像?
【发布时间】:2020-01-08 18:13:04
【问题描述】:

我想拍照并使用 CameraX 从中心裁剪出 25x25 dp 的正方形。我已经读到使用 ImageCapture 可以进行裁剪,但不幸的是,到目前为止几乎没有类似的例子。

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?
            }
        }
    )
}

【问题讨论】:

  • 您是否特别想使用 CameraX 执行此操作?如果没有,您可以使用 Glide 及其 Transformations
  • 我也可以使用 Glide,是的。我想也许有一些功能,因为 CameraX 看起来很强大。
  • CameraX 主要是一个与设备的摄像头交互的 API。这是更多与硬件相关的东西。图像处理并不是这个 API 旨在实现的。
  • 好的,非常感谢亚瑟!已经帮了大忙了。如果你把它写下来作为答案,我可以标记你的答案
  • 不幸的是,我无法回答实际问题。使用 Glide 从图像中裁剪出固定大小的正方形比我预期的要难。你可以看看this article,它解释了如何实现它。之后你可以自己回答这个问题。

标签: android android-camerax


【解决方案1】:

您可以使用此功能在捕获图像后裁剪图像:

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()
    }

裁剪图像以引用父视图(如框架)和视图子视图(如最终参考)

  • 参数bitmap 要裁剪的图像
  • 参数frame设置图片的地方
  • param reference 帧作为裁剪图像的参考
  • return 图片已裁剪

【讨论】:

  • 如果你已经有一个Bitmap;通常你不会有一个,而是有一个Image(如问题所示);如果你已经有了Bitmap,你可以使用Bitmap.createBitmap 来裁剪它,不需要进行字节级处理
  • 裁剪错误的 x 和宽度,y 和高度似乎很好。
【解决方案2】:

您可以将图像转换为位图,然后对其进行裁剪。

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;
}

【讨论】:

  • 当我使用这个函数时这一行 BitmapFactory.decodeByteArray(bytes, 0, bytes.length);返回空
  • decodeByteArray 如果无法按照此处所述解码图像,则返回 null:developer.android.com/reference/android/graphics/…。你能验证你有一个正确的图像并且字节数组被正确填充吗?
  • X 和 Y 偏移量是相对于图像的中心还是其他什么?
  • 如果您的图像是 YUV_420_888 格式(CameraX 的默认格式),那么您将拥有 3 个平面,并且您的代码只能访问第一个 -> 假设您的裁剪图像充其量是单色的正确解释 Y 平面数据
【解决方案3】:

您可以从图像中获取平面并手动裁剪它们:

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)

【讨论】:

  • buffer.toByteArray() 抛出了一个未解析的引用。
猜你喜欢
  • 1970-01-01
  • 2023-01-18
  • 1970-01-01
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 2013-09-26
  • 2020-08-30
  • 2021-07-24
相关资源
最近更新 更多