You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ImageView中显示Camera2实时YV12格式图像并与Drawable图像对比

Got it, let's break this down step by step—since you're working with YV12 from Camera2 and need to display it plus compare with a Drawable, here's how to pull it off:

1. Convert YV12 Data to Bitmap (Required for ImageView)

ImageView can only render Bitmaps or Drawables, so converting your YV12 feed is non-negotiable. YV12 is a 4:2:0 YUV format, while Android Bitmaps use ARGB8888. I’ll share two approaches: one optimized for real-time performance, and another to help you understand the conversion logic.

Method 1: RenderScript (Best for Real-Time Camera Feeds)

RenderScript is Android’s built-in high-performance computing framework—perfect for processing camera frames without dropping FPS. Here’s how to use it:

import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicYuvToRGB

fun yv12ToBitmap(rs: RenderScript, yv12Data: ByteArray, width: Int, height: Int): Bitmap {
    // Define the YV12 format structure for RenderScript
    val yuvType = Type.Builder(rs, Element.U8(rs))
        .setX(width)
        .setY(height)
        .setYuvFormat(ImageFormat.YV12)
        .create()

    // Allocate memory for input YV12 data and output Bitmap
    val inputAlloc = Allocation.createTyped(rs, yuvType)
    inputAlloc.copyFrom(yv12Data)
    
    val outputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val outputAlloc = Allocation.createFromBitmap(rs, outputBitmap)

    // Run the YUV-to-RGB conversion
    val yuvToRgbScript = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
    yuvToRgbScript.setInput(inputAlloc)
    yuvToRgbScript.forEach(outputAlloc)

    // Copy converted data back to Bitmap and clean up resources
    outputAlloc.copyTo(outputBitmap)
    inputAlloc.destroy()
    outputAlloc.destroy()
    yuvToRgbScript.destroy()

    return outputBitmap
}

Note: Get your RenderScript instance with RenderScript.create(context) in your Activity/Fragment. Always pass the correct width/height of your camera frame.

Method 2: Manual Conversion (For Learning Purposes)

If you want to understand how YUV translates to RGB under the hood, here’s a manual implementation. Warning: This is slower than RenderScript and may cause frame drops for high-resolution feeds:

fun yv12ToBitmapManual(yv12Data: ByteArray, width: Int, height: Int): Bitmap {
    val frameSize = width * height
    val uvSize = frameSize / 4
    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val pixels = IntArray(frameSize)

    // Split YV12 data into Y, V, U planes
    val yBuffer = yv12Data.copyOfRange(0, frameSize)
    val vBuffer = yv12Data.copyOfRange(frameSize, frameSize + uvSize)
    val uBuffer = yv12Data.copyOfRange(frameSize + uvSize, frameSize + 2 * uvSize)

    // Convert each YUV pixel to ARGB
    for (y in 0 until height) {
        for (x in 0 until width) {
            val yIndex = y * width + x
            val yValue = yBuffer[yIndex].toInt() and 0xFF

            val uvIndex = (y / 2) * (width / 2) + (x / 2)
            val uValue = uBuffer[uvIndex].toInt() and 0xFF
            val vValue = vBuffer[uvIndex].toInt() and 0xFF

            // Standard YUV to RGB conversion formula
            val r = yValue + 1.370705f * (vValue - 128)
            val g = yValue - 0.698001f * (vValue - 128) - 0.337633f * (uValue - 128)
            val b = yValue + 1.732446f * (uValue - 128)

            // Clamp values to valid RGB range (0-255)
            val rInt = clamp(r.toInt(), 0, 255)
            val gInt = clamp(g.toInt(), 0, 255)
            val bInt = clamp(b.toInt(), 0, 255)

            pixels[yIndex] = -0x1000000 or (rInt shl 16) or (gInt shl 8) or bInt
        }
    }

    bitmap.setPixels(pixels, 0, width, 0, 0, width, height)
    return bitmap
}

private fun clamp(value: Int, min: Int, max: Int): Int {
    return if (value < min) min else if (value > max) max else value
}
2. Display the Bitmap in ImageView

Once you have your converted Bitmap, setting it to an ImageView is straightforward. Just make sure to update the UI on the main thread:

// Assume you have an ImageView with id ivCameraFeed
val cameraBitmap = yv12ToBitmap(renderScriptInstance, yv12Data, imageWidth, imageHeight)
ivCameraFeed.post {
    ivCameraFeed.setImageBitmap(cameraBitmap)
}

Pro Tip: Recycle unused Bitmaps with cameraBitmap.recycle() to avoid out-of-memory errors.

3. Compare with a Drawable Image

You have two main options for comparison: visual side-by-side viewing, or programmatic pixel/feature matching.

Option 1: Visual Side-by-Side Comparison

The simplest way to let users compare is to display both images side-by-side in your layout:

<!-- In your activity/fragment layout -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:weightSum="2">

    <ImageView
        android:id="@+id/ivCameraFeed"
        android:layout_width="0dp"
        android:layout_height="250dp"
        android:layout_weight="1"
        android:scaleType="centerCrop"/>

    <ImageView
        android:id="@+id/ivReferenceDrawable"
        android:layout_width="0dp"
        android:layout_height="250dp"
        android:layout_weight="1"
        android:src="@drawable/your_reference_image"
        android:scaleType="centerCrop"/>
</LinearLayout>

Option 2: Programmatic Pixel-Level Comparison

If you need to automatically detect differences, convert your Drawable to a Bitmap first, then compare pixel values:

First, convert Drawable to Bitmap:

fun drawableToBitmap(drawable: Drawable): Bitmap {
    if (drawable is BitmapDrawable) {
        return drawable.bitmap
    }

    // Create empty Bitmap and draw the Drawable onto it
    val bitmap = Bitmap.createBitmap(
        drawable.intrinsicWidth,
        drawable.intrinsicHeight,
        Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)
    return bitmap
}

Then compare the two Bitmaps (scale to match sizes first if needed):

fun compareBitmaps(bitmap1: Bitmap, bitmap2: Bitmap): Boolean {
    // Resize bitmaps to match dimensions if needed
    if (bitmap1.width != bitmap2.width || bitmap1.height != bitmap2.height) {
        val scaledBitmap2 = Bitmap.createScaledBitmap(bitmap2, bitmap1.width, bitmap1.height, true)
        return compareBitmaps(bitmap1, scaledBitmap2)
    }

    // Check each pixel for matches
    for (y in 0 until bitmap1.height) {
        for (x in 0 until bitmap1.width) {
            if (bitmap1.getPixel(x, y) != bitmap2.getPixel(x, y)) {
                // Pixels differ - return false or log the position
                return false
            }
        }
    }
    return true
}

Note: For non-exact matches (e.g., lighting differences), add a threshold to allow small RGB value variations.

Option 3: Feature-Based Comparison (Robust for Transformations)

If your images might be scaled, rotated, or distorted, use feature matching (like ORB or SIFT) via libraries like OpenCV. This is more complex but handles real-world variations better than pixel-level checks.

Quick Bonus: Directly Handle Camera2 Image Objects

If you’re using ImageReader with Camera2 (format ImageFormat.YUV_420_888), you don’t need to manually assemble YV12 byte arrays. Use this snippet to convert the Image object directly to a Bitmap:

fun imageToBitmap(rs: RenderScript, image: Image): Bitmap {
    val yuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
    val yuvType = Type.Builder(rs, Element.U8(rs))
        .setX(image.width)
        .setY(image.height)
        .setYuvFormat(ImageFormat.YUV_420_888)
        .create()
    val inputAlloc = Allocation.createTyped(rs, yuvType)
    inputAlloc.copyFrom(imageToYv12ByteArray(image))
    
    val outputBitmap = Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888)
    val outputAlloc = Allocation.createFromBitmap(rs, outputBitmap)
    
    yuvToRgb.setInput(inputAlloc)
    yuvToRgb.forEach(outputAlloc)
    outputAlloc.copyTo(outputBitmap)
    
    inputAlloc.destroy()
    outputAlloc.destroy()
    yuvToRgb.destroy()
    return outputBitmap
}

fun imageToYv12ByteArray(image: Image): ByteArray {
    val planes = image.planes
    val yBuffer = planes[0].buffer
    val uBuffer = planes[1].buffer
    val vBuffer = planes[2].buffer
    
    val ySize = yBuffer.remaining()
    val uSize = uBuffer.remaining()
    val vSize = vBuffer.remaining()
    
    val byteArray = ByteArray(ySize + uSize + vSize)
    yBuffer.get(byteArray, 0, ySize)
    vBuffer.get(byteArray, ySize, vSize) // YV12 order: Y → V → U
    uBuffer.get(byteArray, ySize + vSize, uSize)
    return byteArray
}

内容的提问来源于stack exchange,提问作者Ahsan Azwar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:15:33