YUV NV21转换为RGB引起的困惑

35
根据http://developer.android.com/reference/android/graphics/ImageFormat.html#NV21,NV21是默认使用的格式。 网上有许多关于YUV NV21到RGB转换的代码。然而,当我看这些代码时,我对其正确性产生了怀疑。 V应该先于U排列,紧随其后 根据http://wiki.videolan.org/YUV#NV21NV21类似于NV12,但是U和V的顺序相反:它以V开头。 但是,当我检查代码实现时, R应位于最高有效位置 根据Color.java中的int argb实现,R应该处于最高有效位置。然而,我检查了以下代码实现 我想知道,他们是否犯了普遍的错误,还是我忽略了什么? 目前,我的实现如下。
public static void YUV_NV21_TO_RGB(int[] argb, byte[] yuv, int width, int height) {
    final int frameSize = width * height;

    final int ii = 0;
    final int ij = 0;
    final int di = +1;
    final int dj = +1;

    int a = 0;
    for (int i = 0, ci = ii; i < height; ++i, ci += di) {
        for (int j = 0, cj = ij; j < width; ++j, cj += dj) {
            int y = (0xff & ((int) yuv[ci * width + cj]));
            int v = (0xff & ((int) yuv[frameSize + (ci >> 1) * width + (cj & ~1) + 0]));
            int u = (0xff & ((int) yuv[frameSize + (ci >> 1) * width + (cj & ~1) + 1]));
            y = y < 16 ? 16 : y;

            int r = (int) (1.164f * (y - 16) + 1.596f * (v - 128));
            int g = (int) (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
            int b = (int) (1.164f * (y - 16) + 2.018f * (u - 128));

            r = r < 0 ? 0 : (r > 255 ? 255 : r);
            g = g < 0 ? 0 : (g > 255 ? 255 : g);
            b = b < 0 ? 0 : (b > 255 ? 255 : b);

            argb[a++] = 0xff000000 | (r << 16) | (g << 8) | b;
        }
    }
}

对我来说,你的代码完美地运行了,实际上它是唯一一个不改变任何图片属性(如亮度或颜色)的代码。 - ALiGOTec
1
在许多Android函数中,接受颜色参数或将颜色作为整数返回时,它们以R作为最高有效位,即0xAARRGGBB。然而,在使用JNI并直接访问C/C++字节的Android位图的实际内存布局中,情况正好相反,R是内存中的第一个字节,你会得到0xAABBGGRR。 - over_optimistic
2个回答

12

首先,我对图像编码并不是非常有经验(大约一年前有一些有限的接触)。因此,请对我的回答持保留态度。

然而,我认为你是正确的。我认为在他们的代码中, a)V 和 U 被翻转了 b)R 和 B 被颠倒了

我有一种感觉,当这两个东西都被翻转时,它将产生与它们没有被翻转时相同的结果。这就是为什么你可以在许多地方找到错误的代码(最初有人弄错了,之后它被复制到各个地方,因为生成的代码有效(但变量命名不正确))的原因。

这里有另一个代码示例(与你的代码功能相同): http://www.41post.com/3470/programming/android-retrieving-the-camera-preview-as-a-pixel-array


0
术语如“最高有效位位置”是不明确的,因为它取决于机器的字节顺序。
当所有数据类型都是8位时,有一个简单明确的规范:字节顺序。例如, unsigned char rgba [4]; 将把数据存储为 rgba [0] = r; rgba [1] = g; rgba [2] = b; rgba [3] = a;
或无论处理器的字节顺序是什么,都可以写成{r,g,b,a}。
如果你这样做:
int32 color = (r << 24) | (g << 16) | (b << 8) | (a << 0);
在big-endian系统上会得到{r,g,b,a},而在little-endian系统上会得到{a,r,g,b}。
你在处理具有异构处理器的系统吗?比如说你有一个CPU和一个GPU?他们怎么知道另一个正在使用的字节顺序?最好定义字节顺序。

1
请阅读问题链接的规范:指定了小端字节顺序。 - Jules
2
此外,该问题被标记为Android,并且所有支持的Android版本都使用小端硬件。 - Jules

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接