【Android音视频开发】【009】NV21旋转与翻转

NV21像素取样

NV21是YUV的一种子格式

它对每个像素的Y分量都取值,而UV分量则是每相邻的四个像素共享一组

因为相邻像素的UV分量值一般区别都是不大的,这样就压缩了像素存储时占用的字节大小

NV21像素排列和旋转翻转

注意,这里的行列只是为了方便人理解,对内存进行了换行显示,实际在内存中,像素是以数组形式存储的,而不是表格

在这里插入图片描述
旋转和翻转代码

有了示意图以后,参照示意图编写旋转翻转代码就容易多了

大家在写这种代码时,一定不要靠脑袋凭空想象,大多人是不具备这种空间想象力的

每个像素的转换规则都是一样的,我们对照示意图,找出单个像素的变换规则

再遍历所有像素,就可以得到整张变换后的图像了,非常简单

其实变换规则无非就是,行列互换,左右互换,上下互换这几种规则相互组合而已


	//旋转摄像头采集的NV21画面,得到推流画面,顺时针为正方向
	public static void rotateNV21(int face, byte[] src, byte[] reversedBytes, byte[] rotatedBytes, int width, int height, int rotation) {
	    if (rotation == 90)
	        rotateNV21_90(src, rotatedBytes, width, height);
	    else if (rotation == 180)
	        rotateNV21_180(src, rotatedBytes, width, height);
	    else if (rotation == 270)
	        rotateNV21_270(src, rotatedBytes, width, height);
	    else
	        rotateNV21_0(src, rotatedBytes, width, height);
	}
	
	//将NV21画面原样保留
	public static void rotateNV21_0(byte[] src, byte[] dst, int width, int height) {
	    System.arraycopy(src, 0, dst, 0, src.length);
	}
	
	//将NV21画面顺时针旋转90角度
	public static void rotateNV21_90(byte[] src, byte[] dst, int width, int height) {
	
	    //旋转90度后的像素排列:
	    //新的行数=原来的列数
	    //新的列数=原来的高度-1-原来的行数
	
	    //每相邻的四个Y分量,共享一组VU分量
	    //旋转前VU分量在左上角,旋转后VU分量在右上角
	
	    int index = 0;
	    //旋转Y分量,放入dst数组
	    for (int y = 0; y < width; y++)
	        for (int x = 0; x < height; x++) {
	            int oldY = (height - 1) - x;
	            int oldX = y;
	            int oldIndex = oldY * width + oldX;
	            dst[index++] = src[oldIndex];
	        }
	    //每四个点采集一组VU分量,共享右上角像素的VU分量
	    //根据Y分量,找到对应的VU分量,放入dst数组
	    for (int y = 0; y < width; y += 2)
	        for (int x = 0; x < height; x += 2) {
	            int oldY = (height - 1) - (x + 1);
	            int oldX = y;
	            int vuY = height + oldY / 2; //根据Y分量计算VU分量所在行
	            int vuX = oldX;
	            int vuIndex = vuY * width + vuX;
	            dst[index++] = src[vuIndex];
	            dst[index++] = src[vuIndex + 1];
	        }
	}
	
	//将NV21画面顺时针旋转180角度
	public static void rotateNV21_180(byte[] src, byte[] dst, int width, int height) {
	
	    //旋转180度后的像素排列:
	    //新的行数=原来的高度-1-原来的行数
	    //新的列数=原来的宽度-1-原来的列数
	
	    //每相邻的四个Y分量,共享一组VU分量
	    //旋转前VU分量在左上角,旋转后VU分量在右下角
	
	    int index = 0;
	    //旋转Y分量,放入dst数组
	    for (int y = 0; y < height; y++)
	        for (int x = 0; x < width; x++) {
	            int oldY = (height - 1) - y;
	            int oldX = (width - 1) - x;
	            int oldIndex = oldY * width + oldX;
	            dst[index++] = src[oldIndex];
	        }
	    //每四个点采集一组VU分量,共享右下角像素的VU分量
	    //根据Y分量,找到对应的VU分量,放入dst数组
	    for (int y = 0; y < height; y += 2)
	        for (int x = 0; x < width; x += 2) {
	            int oldY = (height - 1) - (y + 1);
	            int oldX = (width - 1) - (x + 1);
	            int vuY = height + oldY / 2; //根据Y分量计算VU分量所在行
	            int vuX = oldX;
	            int vuIndex = vuY * width + vuX;
	            dst[index++] = src[vuIndex];
	            dst[index++] = src[vuIndex + 1];
	        }
	}
	
	//将NV21画面顺时针旋转270角度
	public static void rotateNV21_270(byte[] src, byte[] dst, int width, int height) {
	
	    //旋转270度后的像素排列:
	    //新的行数=原来的宽度-1-原来的列数
	    //新的列数=原来的行数
	
	    //每相邻的四个Y分量,共享一组VU分量
	    //旋转前VU分量在左上角,旋转后VU分量在左下角
	
	    int index = 0;
	    //旋转Y分量,放入dst数组
	    for (int y = 0; y < width; y++)
	        for (int x = 0; x < height; x++) {
	            int oldY = x;
	            int oldX = width - 1 - y;
	            int oldIndex = oldY * width + oldX;
	            dst[index++] = src[oldIndex];
	        }
	    //每四个点采集一组VU分量,共享左下角像素的VU分量
	    //根据Y分量,找到对应的VU分量,放入dst数组
	    for (int y = 0; y < width; y += 2)
	        for (int x = 0; x < height; x += 2) {
	            int oldY = x;
	            int oldX = width - 1 - (y + 1);
	            int vuY = height + oldY / 2; //根据Y分量计算VU分量所在行
	            int vuX = oldX;
	            int vuIndex = vuY * width + vuX;
	            dst[index++] = src[vuIndex];
	            dst[index++] = src[vuIndex + 1];
	        }
	}
	
	//将NV21画面水平翻转
	public static void reverseNV21_H(byte[] src, byte[] dst, int width, int height) {
	
	    //水平翻转的像素排列:
	    //新的行数=原来的行数
	    //新的列数=原来的宽度-1-原来的列数
	
	    //每相邻的四个Y分量,共享一组VU分量
	    //翻转前VU分量在左上角,旋转后VU分量在右上角
	
	    int index = 0;
	    //旋转Y分量,放入dst数组
	    for (int y = 0; y < height; y++)
	        for (int x = 0; x < width; x++) {
	            int oldY = y;
	            int oldX = width - 1 - x;
	            int oldIndex = oldY * width + oldX;
	            dst[index++] = src[oldIndex];
	        }
	    //每四个点采集一组VU分量,共享右上角像素的VU分量
	    //根据Y分量,找到对应的VU分量,放入dst数组
	    for (int y = 0; y < height; y += 2)
	        for (int x = 0; x < width; x += 2) {
	            int oldY = y;
	            int oldX = width - 1 - (x + 1);
	            int vuY = height + oldY / 2; //根据Y分量计算VU分量所在行
	            int vuX = oldX;
	            int vuIndex = vuY * width + vuX;
	            dst[index++] = src[vuIndex];
	            dst[index++] = src[vuIndex + 1];
	        }
	}
	
	//将NV21画面竖直翻转
	public static void reverseNV21_V(byte[] src, byte[] dst, int width, int height) {
	
	    //竖直翻转的像素排列:
	    //新的行数=原来的高度-1-原来的行数
	    //新的列数=原来的列数
	
	    //每相邻的四个Y分量,共享一组VU分量
	    //翻转前VU分量在左上角,旋转后VU分量在左下角
	
	    int index = 0;
	    //旋转Y分量,放入dst数组
	    for (int y = 0; y < height; y++)
	        for (int x = 0; x < width; x++) {
	            int oldY = height - 1 - y;
	            int oldX = x;
	            int oldIndex = oldY * width + oldX;
	            dst[index++] = src[oldIndex];
	        }
	    //每四个点采集一组VU分量,共享左下角像素的VU分量
	    //根据Y分量,找到对应的VU分量,放入dst数组
	    for (int y = 0; y < height; y += 2)
	        for (int x = 0; x < width; x += 2) {
	            int oldY = height - 1 - (y + 1);
	            int oldX = x;
	            int vuY = height + oldY / 2; //根据Y分量计算VU分量所在行
	            int vuX = oldX;
	            int vuIndex = vuY * width + vuX;
	            dst[index++] = src[vuIndex];
	            dst[index++] = src[vuIndex + 1];
	        }
	}
	
	//向文件写入图像字节
	@SneakyThrows
	public static void writeImageBytesToFile(byte[] bytes, String path) {
	    File file = new File(path);
	    if (file.exists())
	        file.delete();
	    FileOutputStream fos = new FileOutputStream(file);
	    fos.write(bytes);
	    fos.flush();
	    fos.close();
	}

NV21格式示意图下载

上面图片的Excel版本,需要的可以自己下载

NV21格式示意图.zip

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值