如何从CameraX获取Bitmap?现有方案问题及可行方案咨询
Hey there! Let's break down your questions based on the approaches you've tested and the issues you've run into:
Absolutely, this is a totally solid and practical solution—and honestly, it's one of the most reliable options for your use case:
- It skips the messy YUV-to-Bitmap conversion entirely, giving you a Bitmap that matches the preview's quality, dimensions, and rotation (thanks to setting
setTargetRotationin your Preview Builder, so no extra rotation logic is needed). - Performance is stable with no lag, since PreviewView is purpose-built by CameraX for preview display and includes optimized handling under the hood.
- Your null check for the Bitmap is a good touch—just make sure you call
getBitmap()only after the preview has started rendering content (youranalyzecallback is the right place for this).
Yes, there are several more reliable ways to convert ImageProxy to Bitmap if you ever need to avoid using PreviewView:
Option A: Use CameraX's built-in ImageProxy.toBitmap()
While you mentioned Google's experimental demo, CameraX now has a stable, built-in extension function for this. It handles YUV format variations across devices automatically:
// Make sure you have the latest CameraX Core dependency implementation "androidx.camera:camera-core:1.3.0" @RequiresApi(Build.VERSION_CODES.KITKAT) override fun analyze(imageProxy: ImageProxy) { val bitmap = imageProxy.toBitmap() val rotation = imageProxy.imageInfo.rotationDegrees // Process your bitmap here imageProxy.close() // Don't forget to close the ImageProxy to avoid resource leaks! }
This method is widely tested by developers and avoids the artifacts you saw with custom conversions.
Option B: Use AndroidX's YuvToRgbConverter
The AndroidX library provides a dedicated class for YUV_420_888 to Bitmap conversion, which handles plane strides and offsets correctly (something custom RenderScript implementations often mess up):
// Depend on either camera-core or exifinterface val converter = YuvToRgbConverter(context) val bitmap = Bitmap.createBitmap(imageProxy.width, imageProxy.height, Bitmap.Config.ARGB_8888) converter.yuvToRgb(imageProxy.image!!, bitmap) // Process your bitmap here imageProxy.close()
This eliminates the quality loss you experienced with your custom RenderScript converter.
Absolutely! Most issues stem from incorrect handling of YUV_420_888's plane strides, pixel strides, and device-specific format variations (like swapped U/V planes causing color blocks).
For example, your second approach (Image → JPEG) had red/blue blocks on the Mi A2 because the YUV_420_888toNV21 method copied U/V buffers directly without accounting for their strides. Here's a corrected version that fixes this:
@RequiresApi(api = Build.VERSION_CODES.KITKAT) private static byte[] YUV_420_888toNV21(Image image) { ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); int ySize = yBuffer.remaining(); byte[] nv21 = new byte[ySize + (image.getWidth() * image.getHeight() / 2)]; // Copy Y plane yBuffer.get(nv21, 0, ySize); int uvIndex = ySize; int rowStrideU = image.getPlanes()[1].rowStride; int pixelStrideU = image.getPlanes()[1].pixelStride; int rowStrideV = image.getPlanes()[2].rowStride; int pixelStrideV = image.getPlanes()[2].pixelStride; // Correctly copy V and U pixels (NV21 uses VU interleaved format) for (int y = 0; y < image.getHeight() / 2; y++) { for (int x = 0; x < image.getWidth() / 2; x++) { nv21[uvIndex++] = vBuffer.get(y * rowStrideV + x * pixelStrideV); nv21[uvIndex++] = uBuffer.get(y * rowStrideU + x * pixelStrideU); } } return nv21; }
This fixes the color block issue by properly iterating over each U/V pixel instead of copying the entire buffer.
Similarly, your custom RenderScript converter's quality loss came from incorrect ByteBuffer handling in imageToByteBuffer—using YuvToRgbConverter avoids this entirely.
内容的提问来源于stack exchange,提问作者Viktor Vostrikov

