从YUV_420_888图像获取RGB值

问题描述 投票:0回答:1

我正在尝试裁剪相机预览YUV_420_888图像的中心正方形部分。我想在处理每一帧时获取RGB值,而不是转换为位图然后获取值。

这里是我目前拥有的,但似乎无法获得有效的图像。我认为我错误地获取了YUV值。

planes: plane0 pixelStride:1 rowStride:1280 height:720 capacity:921600
planes: plane1 pixelStride:2 rowStride:1280 height:719 capacity:460799
planes: plane2 pixelStride:2 rowStride:1280 height:719 capacity:460799

_

fun processImage(image: Image) {
    val imageSize = 224
    val subImageWidth = imageSize/2
    val subImageHeight = imageSize/2

    val bb = ByteBuffer.allocateDirect(imageSize * imageSize * 3 * 4)
    bb.order(ByteOrder.nativeOrder())

    val plane0 = image.planes[0]
    val plane1 = image.planes[1]
    val plane2 = image.planes[2]

    val width = plane0.rowStride
    val height = plane0.buffer.capacity() / plane0.rowStride

    //Documentation says that plane 1&2 will have the same pixel stride and row stride
    val plane12Width = plane1.rowStride / plane1.pixelStride

    val xOffset = (width - subImageWidth) / 2
    val yOffset = (height - subImageHeight) / 2

    for (y in 0 until imageSize) {
        for (x in 0 until imageSize) {
            val yb = plane0.buffer[(x + xOffset) + width * (y + yOffset)]
            val ub = plane1.buffer[(x + xOffset)/2 + plane12Width * (y + yOffset)]
            val vb = plane2.buffer[(x + xOffset)/2 + plane12Width * (y + yOffset)]

            val rgb = yuvToRGB(yb, ub, vb)


            bb.putFloat(rgb[0])
            bb.putFloat(rgb[1])
            bb.putFloat(rgb[2])
        }
    }
    imageView.setBitmap(getOutputImage(bb))
}

fun yuvToRGB(y: Float, u: Float, v: Float): FloatArray {
    val rgb = FloatArray(3)

    val rTemp = ((y - 16) * 1.164 + (v - 128) * 1.596).toFloat()
    val gTemp = ((y - 16) * 1.164 - (u - 128) * 0.392 - (v - 128) * 0.813).toFloat()
    val bTemp = ((y - 16) * 1.164 + (u - 128) * 2.017).toFloat()

    if (rTemp > 255f) {
        rgb[0] = 255f
    } else if (rTemp < 0f) {
        rgb[0] = 0f
    } else {
        rgb[0] = rTemp
    }

    if (gTemp > 255f) {
        rgb[1] = 255f
    } else if (gTemp < 0f) {
        rgb[1] = 0f
    } else {
        rgb[1] = gTemp
    }

    if (bTemp > 255f) {
        rgb[2] = 255f
    } else if (bTemp < 0f) {
        rgb[2] = 0f
    } else {
        rgb[2] = bTemp
    }

    return rgb
}

private fun getOutputImage(output: ByteBuffer): Bitmap {
    val imageSize = 224
    output.rewind() // Rewind the output buffer after running.

    val bitmap = Bitmap.createBitmap(imageSize, imageSize, Bitmap.Config.ARGB_8888)
    val pixels = IntArray(imageSize * imageSize) // Set your expected output's height and width
    for (i in 0 until imageSize * imageSize) {
        val a = 0xFF
        val r: Float = output.float
        val g: Float = output.float
        val b: Float = output.float
        pixels[i] = a shl 24 or (r.toInt() shl 16) or (g.toInt() shl 8) or b.toInt()
    }
    bitmap.setPixels(pixels, 0, imageSize, 0, 0, imageSize, imageSize)

    return bitmap
}

center crop编辑:green image

android rgb yuv
1个回答
0
投票

修复#1:

您要确保击中彩色平面的正确区域,以获得UV值:

val ub = plane1.buffer[((x + xOffset)/2) * 2 + plane1.rowStride * ((y + yOffset)/2)].toFloat()
val vb = plane1.buffer[((x + xOffset)/2) * 2 + 1 + plane1.rowStride * ((y + yOffset)/2)].toFloat()

[我故意将plane1用作这两种技巧。在您的特定设备上,这些Imagesinterleaved色彩平面一起出现。 plane1plane2 本质上是相同的数组(有些警告)。它们都“指向” RAM中的同一区域。它们重叠了99.9%。并且它们包含这样的值:“ U / V / U / V / U / V”,这就是pixelStride为二的原因。我认为如果您这样做也可以:

val ub = plane1.buffer[((x + xOffset)/2) * 2 + plane1.rowStride * ((y + yOffset)/2)].toFloat()
val vb = plane2.buffer[((x + xOffset)/2) * 2 + plane2.rowStride * ((y + yOffset)/2)].toFloat()

...因为显然ByteBufferDirect接口已经抽象出了一个事实,即plane2plane1右边1个字节开始(在本机内存中)。 (如果您是从JNI进行本机操作,则看起来会有些不同。)

((x + xOffset)/2)*2校正是为了解决这种交错问题,而pixelStride((y + yOffset)/2)校正是要考虑以下事实:在4:2:0二次采样过程中,每隔一个像素行都会被跳过。 (其他所有column也会被跳过,但是由于每个像素都由相邻的2个字节(U,V)组成,因此我们需要使用* 2将偏移量增加一倍。整数数学;我们正在尝试制作偶数,因此参考偏移量始终在“ U”上)。

修复#2:

在此区域:

val r: Float = output.float * 255
val g: Float = output.float * 255
val b: Float = output.float * 255

...您不希望乘以255,因为您的值已经在0..255的范围内。

修复#3:

如您所述,数组值被解释为“有符号”;您的转换算法希望此符号是无符号的,因此最好将它们的范围设置为0..255。

推荐的改进

[yuvToRGB()如果对输入和输出使用整数数据类型而不是floats,效率会更高。

© www.soinside.com 2019 - 2024. All rights reserved.