原YUV格式的解析 Android NV21 视频采集

最近在搞视频分析类的项目,android默认视频采集格式是NV21,Y Cr Cb格式,4.2.0.方式采样
还有其他采样方式,默认使用NV21

Sets the image format for preview pictures.

If this is never called, the default format will be NV21, which uses the NV21 encoding format.

int JPEG Encoded formats.
int NV16 YCbCr format, used for video.
int NV21 YCrCb format used for images, which uses the NV21 encoding format.
int RGB_565 RGB format used for pictures encoded as RGB_565 see setPictureFormat(int).
int UNKNOWN  
int YUY2 YCbCr format used for images, which uses YUYV (YUY2) encoding format.
int YV12 Android YUV format: This format is exposed to software decoders and applications.
具体参见 http://developer.android.com/reference/android/graphics/ImageFormat.html
-----------------------------------------------------------------------------------------------------------
首先是其数据是怎样存储的
// 我们通过下列方式获取数据
class Buf implements Camera.PreviewCallback {
public byte[] yuv420sp =null;
public void onPreviewFrame(byte[] data, Camera camera) {
// 这里出来的data和tackPic出来的有区别,这边是NV21格式
yuv420sp=data;
}
}
420这种格式采样方式如下
X  X  X  X
O     O   
X  X  X  X

X  X  X  X
O     O   
X  X  X  X
即每4个Y对应一个U和一个V
Y0U0V0             Y1                 Y2U2V2                Y3
Y4                       Y5                 Y6                          Y7
Y8U8V8             Y9                 Y10U10V10         Y11
Y12                     Y13               Y14                       Y15
通过对采样数组的研究其存放规律是
YYYYY...(采样宽度width)...YYYYY
...
...(采样高度height)
...
YYYYY...(采样宽度width)...YYYYY
(vu)(vu)(vu)...(采样宽度)...(vu)(vu)(vu)
...(采样高度除以2,height/2,遇单加1)
(vu)(vu)(vu)...(采样宽度)...(vu)(vu)(vu)
即width=320,height=480,采样Y值长度320×480,采样VU长度320×480/2.yuv420sp=data;总长度为宽高乘积的一点五倍

// YUV转RGB rgb长度即width×heght
static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
        final int frameSize = width * height;
        
        for (int j = 0, yp = 0; j < height; j++) {
                int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
                for (int i = 0; i < width; i++, yp++) {
                        int y = (0xff & ((int) yuv420sp[yp])) - 16;
                        if (y < 0) y = 0;
                        if ((i & 1) == 0) {
                                v = (0xff & yuv420sp[uvp++]) - 128;
                                u = (0xff & yuv420sp[uvp++]) - 128;
                        }
                        
                        int y1192 = 1192 * y;
                        int r = (y1192 + 1634 * v);
                        int g = (y1192 - 833 * v - 400 * u);
                        int b = (y1192 + 2066 * u);
                        
                        if (r < 0) r = 0; else if (r > 262143) r = 262143;
                        if (g < 0) g = 0; else if (g > 262143) g = 262143;
                        if (b < 0) b = 0; else if (b > 262143) b = 262143;
                        
                        rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
                }
        }
}

以上是android自带的YUV420SP转RGB的函数,但是坑爹的问题在于函数处理数据的能力有限,我们有的时候并不需要全部转完毕再解析。
以下是更改后的方法,用于取值某点的RGB,在一些实时性要求的应用中可以用到
// 自定义RGB
static public int myDecodeYUV420SP(byte[] yuv420sp, int width, int height,
int mWidth, int mHeight) {
// Y矩阵长度frameSize , V和U矩阵第一位即frameSize
final int frameSize = width * height;
// yp为Y在矩阵中的位置,yph为所需要点的高mHeight-1,ypw为所需要点的宽mWidth
int yp, yph = mHeight - 1, ypw = mWidth;
yp = width * yph + ypw;
// uvp为
// uv在数组中的位置,V和U矩阵第一位即frameSize,yph>>1取值范围(0,0,1,1,2,2...)yph从0开始,即UV数组为Y数组的1/2.
int uvp = frameSize + (yph >> 1) * width, u = 0, v = 0;
// 获取Y的数值
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0)
y = 0;
if ((ypw & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp]) - 128;
} else {
u = (0xff & yuv420sp[uvp--]) - 128;
v = (0xff & yuv420sp[uvp]) - 128;
}

int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);

if (r < 0)
r = 0;
else if (r > 262143)
r = 262143;
if (g < 0)
g = 0;
else if (g > 262143)
g = 262143;
if (b < 0)
b = 0;
else if (b > 262143)
b = 262143;

return (0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff));

}

两套函数出来的数据int型需要转换成r/g/b。亮点就是转换方式

public static int red (int color)

Since:  API Level 1

Return the red component of a color int. This is the same as saying (color >> 16) & 0xFF

public static int green (int color)

Since:  API Level 1

Return the green component of a color int. This is the same as saying (color >> 8) & 0xFF

public static int blue (int color)

Since:  API Level 1

Return the blue component of a color int. This is the same as saying color & 0xFF

至此yuv取点rgb完成
1 需要代码的童鞋可以留下你的email
2 如果描述有误请指正
3 感谢 幸福的起点 博主,更多视频格式说明请参照 http://hi.baidu.com/joygogo0130/blog/item/04b707529efbec090df3e345.html
http://ai4work.blog.163.com/blog/static/189873306201171883316264/

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值