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

Android中如何将Bitmap正确转换为YUV420格式?

Android Bitmap转YUV420(YUV420P/NV21)正确实现方法

现有代码的核心问题

  • 错误将Bitmap的RGB像素字节当作YUV420SP数据处理,跳过了RGB到YUV的色彩空间转换,导致后续操作基于错误数据源。
  • convertYUV420SPtoYUV420P函数逻辑不匹配输入数据,因为输入根本不是YUV420SP格式。

方法一:手动RGB转YUV420P(逐像素计算)

YUV420P格式为:Y分量(width*height字节)→ U分量((width*height)/4字节)→ V分量((width*height)/4字节)。采用BT.601色彩转换标准(Android通用):

fun bitmapToYuv420P(bitmap: Bitmap): ByteArray {
    val width = bitmap.width
    val height = bitmap.height
    // YUV420要求宽高为偶数,做对齐处理
    val alignedWidth = if (width % 2 != 0) width - 1 else width
    val alignedHeight = if (height % 2 != 0) height - 1 else height

    val pixels = IntArray(alignedWidth * alignedHeight)
    bitmap.getPixels(pixels, 0, alignedWidth, 0, 0, alignedWidth, alignedHeight)

    val yuvSize = alignedWidth * alignedHeight * 3 / 2
    val yuvArray = ByteArray(yuvSize)
    val ySize = alignedWidth * alignedHeight
    val uvSize = ySize / 4

    var yIndex = 0
    var uIndex = ySize
    var vIndex = ySize + uvSize

    for (y in 0 until alignedHeight) {
        for (x in 0 until alignedWidth) {
            val pixel = pixels[y * alignedWidth + x]
            val r = (pixel shr 16) and 0xFF
            val g = (pixel shr 8) and 0xFF
            val b = pixel and 0xFF

            // 计算Y分量
            val yVal = (0.299 * r + 0.587 * g + 0.114 * b).toInt()
            yuvArray[yIndex++] = yVal.coerceIn(0, 255).toByte()

            // 每2x2像素采样一组U、V分量(YUV420的4:2:0采样规则)
            if (y % 2 == 0 && x % 2 == 0) {
                val uVal = (-0.14713 * r - 0.28886 * g + 0.436 * b + 128).toInt()
                val vVal = (0.615 * r - 0.51499 * g - 0.10001 * b + 128).toInt()
                yuvArray[uIndex++] = uVal.coerceIn(0, 255).toByte()
                yuvArray[vIndex++] = vVal.coerceIn(0, 255).toByte()
            }
        }
    }
    return yuvArray
}

方法二:RenderScript高效实现(适合大分辨率Bitmap)

RenderScript是Android高性能计算框架,图像转换效率远高于手动循环,推荐用于大尺寸Bitmap转换。

步骤1:启用RenderScript

在模块级build.gradle中添加配置:

android {
    defaultConfig {
        renderscriptTargetApi 21
        renderscriptSupportModeEnabled true
    }
}

步骤2:实现转换函数

默认输出NV21格式(Android相机常用YUV420变体,Y后UV交错排列):

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

fun bitmapToYuv420NV21(rs: RenderScript, bitmap: Bitmap): ByteArray {
    val width = bitmap.width
    val height = bitmap.height
    val alignedWidth = if (width % 2 != 0) width - 1 else width
    val alignedHeight = if (height % 2 != 0) height - 1 else height

    // 创建Bitmap输入Allocation
    val inputAlloc = Allocation.createFromBitmap(
        rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT
    )
    // 创建YUV输出Allocation(NV21格式尺寸为width*height*3/2)
    val outputType = Type.Builder(rs, Element.U8(rs))
        .setX(alignedWidth * alignedHeight * 3 / 2)
        .create()
    val outputAlloc = Allocation.createTyped(rs, outputType)

    // 执行RGB转YUV转换
    val yuvScript = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
    yuvScript.setInput(inputAlloc)
    yuvScript.forEach(outputAlloc)

    // 提取转换后的字节数组
    val yuvArray = ByteArray(outputAlloc.bytesSize)
    outputAlloc.copyTo(yuvArray)

    // 释放资源
    inputAlloc.destroy()
    outputAlloc.destroy()
    yuvScript.destroy()

    return yuvArray
}

如果需要YUV420P格式,可在得到NV21后将UV分量分离重组,逻辑简单且性能影响极小。


内容的提问来源于stack exchange,提问作者Mustafa Maden

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 19:30:56