【音视频数据数据处理 2】【YUV篇】将YUV420P_I420数据旋转90°-180°-270°-镜像旋转

article/2025/9/29 7:21:19

【音视频数据数据处理 2】【YUV篇】将YUV420P_I420数据旋转90°

    • 一、理论分析(以yuv420p_i420格式为例)
    • 二、顺时针旋转90° 代码实现
    • 三、顺时针旋转180° 代码实现
    • 四、旋转90°-180°-270°-镜像旋转,完整代码实现
    • 五、运行结果

本文接着前文:
《【音视频数据数据处理 1】【YUV篇】分离YUV420P像素数据中的Y、U、V分量》


一、理论分析(以yuv420p_i420格式为例)

如下,以2x4的源图为例:
在这里插入图片描述
而,经过顺时针旋转90度后
在这里插入图片描述
取数据时,相当于下面黑色方向的箭头。
在这里插入图片描述

本文链接:《【音视频数据数据处理 2】【YUV篇】将YUV420P_I420数据旋转90°-180°-270°-镜像旋转》

二、顺时针旋转90° 代码实现

核心代码如下:

//  顺时针旋转90度 
// 格式为:YY YY YY YY ... UU...VV 
void Rotate_90_YUV420_I420(char *path, int width, int height)
{int i, j; FILE *file = fopen(path, "rb+");	//只读打开源文件FILE *file_r = fopen("yuv420p_i420_90.y", "wb+");	//可读可写创建 Y分量文件 // 申请 width * hight * 3 / 2 的内存unsigned char *p, *pic = (unsigned char *)malloc(sizeof(char) * width * height * 3 / 2);unsigned char *d, *pic_d = (unsigned char *)malloc(sizeof(char) * width * height * 3 / 2);size_t num=0;num = fread(pic, 1, width*height*3/2, file);	// 读取源图片的所有内容,每个数据块为1字节 printf("读取 %d 个字节的数据\n", num);			//旋转 Y分量 p = pic;d = pic_d;for(i = 0; i<width; i++)for(j = 0; j<height; j++)*d++ = *(p + i + (height-j)*width);  //p[i][height-j];printf("旋转Y分分量完毕 num=%d \n", d-pic_d);	// 旋转 U分量p += width * height; 			for(i = 0; i<width/2; i+=1)for(j = 0; j<height/2; j++){*d++ = *(p + i + (height/2 -j) * width/2);  //p[i][height/2-j];}printf("旋转U分分量完毕 num=%d \n", d-pic_d);// 旋转 V分量p += width * height / 4; 			for(i = 0; i<width/2; i+=1)for(j = 0; j<height/2; j++){*d++ = *(p + i + (height/2 -j) * width/2);  //p[i][height/2-j];}printf("旋转V分分量完毕 num=%d \n", d-pic_d);// 写入旋转后的图			num = fwrite(pic_d, 1, width * height *3 / 2, file_r); 	// 保存Y分量,pic[0, width*height] ,每个数据块写1字节 printf("写入 %d 个字节的数据\n", num);	free(pic);free(pic_d);fclose(file);fclose(file_r);
}

运行结果如下
在这里插入图片描述


三、顺时针旋转180° 代码实现

//  顺时针旋转180度 
// 格式为:YY YY YY YY ... UU...VV 
void Rotate_180_YUV420_I420(char *path, int width, int height)
{int i, j; FILE *file = fopen(path, "rb+");	//只读打开源文件FILE *file_r = fopen("yuv420p_i420_180.y", "wb+");	//可读可写创建 Y分量文件 // 申请 width * hight * 3 / 2 的内存unsigned char *p, *pic = (unsigned char *)malloc(sizeof(char) * width * height * 3 / 2);unsigned char *d, *pic_d = (unsigned char *)malloc(sizeof(char) * width * height * 3 / 2);size_t num=0;num = fread(pic, 1, width*height*3/2, file);	// 读取源图片的所有内容,每个数据块为1字节 printf("读取 %d 个字节的数据\n", num);			//旋转 Y分量 p = pic;d = pic_d;for(i = 0; i<height; i++)for(j = 0; j<width; j++)*d++ = *(p + width-j + (height-i)*width);  //p[heidht-i][widht-j];printf("旋转Y分分量完毕 num=%d \n", d-pic_d);	// 旋转 U分量p += width * height; 			for(i = 0; i<height/2; i++)for(j = 0; j<width/2; j++)*d++ = *(p + width/2-j + (height/2-i)*width/2);  //p[heidht-i][widht-j];printf("旋转U分分量完毕 num=%d \n", d-pic_d);// 旋转 V分量// 旋转 U分量p += width * height / 4; 			for(i = 0; i<height/2; i++)for(j = 0; j<width/2; j++)*d++ = *(p + width/2-j + (height/2-i)*width/2);  //p[heidht-i][widht-j];printf("旋转V分分量完毕 num=%d \n", d-pic_d);// 写入旋转后的图			num = fwrite(pic_d, 1, width * height *3 / 2, file_r); 	// 保存Y分量,pic[0, width*height] ,每个数据块写1字节 printf("写入 %d 个字节的数据\n", num);	free(pic);free(pic_d);fclose(file);fclose(file_r);
}

在这里插入图片描述


四、旋转90°-180°-270°-镜像旋转,完整代码实现

#include <stdio.h>
#include <stdlib.h>// 顺时针旋转 n*90 度 
// 格式为:YY YY YY YY ... UU...VV 
// path:源图地址  width,height 源图宽高
// rota: 希望旋转的度数 
void Rotate_YUV420_I420(char *path, int width, int height, int rota)
{int i, j, num; // 找开源文件 FILE *file_d, *file = fopen(path, "rb+");	//只读打开源文件// 申请 width * hight * 3 / 2 的内存unsigned char *p, *pic = (unsigned char *)malloc(sizeof(char) * width * height * 3 / 2);unsigned char *d, *pic_d = (unsigned char *)malloc(sizeof(char) * width * height * 3 / 2);num = fread(pic, 1, width*height*3/2, file);	// 读取源图片的所有内容,每个数据块为1字节 printf("读取 %d 个字节的数据\n", num);	// 根据旋转度数,计算switch( rota ){// 镜像旋转0度case 1:		// 创建旋转后的文件名 file_d = fopen("yuv420p_i420_0_mirror.yuv", "wb+");	//可读可写创建 Y分量文件 	//旋转 Y分量 p = pic;d = pic_d;for(i = 0; i<height; i++)for(j = 0; j<width; j++)*d++ = *(p + width-j + (i)*width); 	//p[height-j][i]printf("旋转Y分分量完毕 num=%d \n", d-pic_d);	// 旋转 U,分量p += width * height; 			for(i = 0; i<height/2; i++)for(j = 0; j<width/2; j++)*d++ = *(p + width/2-j + (i)*width/2); 	//p[height-j][i]printf("旋转U分分量完毕 num=%d \n", d-pic_d);// 旋转 V分量p += width * height / 4; 			for(i = 0; i<height/2; i++)for(j = 0; j<width/2; j++)*d++ = *(p + width/2-j + (i)*width/2); 	//p[height-j][i]printf("旋转V分分量完毕 num=%d \n", d-pic_d);break; 	// 90度case 2:		// 创建旋转后的文件名 file_d = fopen("yuv420p_i420_90.yuv", "wb+");	//可读可写创建 Y分量文件 //旋转 Y分量 p = pic;d = pic_d;for(i = 0; i<width; i++)for(j = 0; j<height; j++)*d++ = *(p + i + (height-j)*width); 	//p[height-j][i]printf("旋转Y分分量完毕 num=%d \n", d-pic_d);	// 旋转 U,分量p += width * height; 			for(i = 0; i<width/2; i++)for(j = 0; j<height/2; j++)*d++ = *(p + i + (height/2-j)*width/2); 	//p[height-j][i]printf("旋转U分分量完毕 num=%d \n", d-pic_d);// 旋转 V分量p += width * height / 4; 			for(i = 0; i<width/2; i++)for(j = 0; j<height/2; j++)*d++ = *(p + i + (height/2-j)*width/2); 	//p[height-j][i]printf("旋转V分分量完毕 num=%d \n", d-pic_d);break; // 镜像90度case 3:		// 创建旋转后的文件名 file_d = fopen("yuv420p_i420_90_mirror.yuv", "wb+");	//可读可写创建 Y分量文件 //旋转 Y分量 p = pic;d = pic_d;for(i = 0; i<width; i++)for(j = 0; j<height; j++)*d++ = *(p + width-i + (height-j)*width); 	//p[height-j][i]printf("旋转Y分分量完毕 num=%d \n", d-pic_d);	// 旋转 U,分量p += width * height; 			for(i = 0; i<width/2; i++)for(j = 0; j<height/2; j++)*d++ = *(p + width-i + (height/2-j)*width/2); 	//p[height-j][i]printf("旋转U分分量完毕 num=%d \n", d-pic_d);// 旋转 V分量p += width * height / 4; 			for(i = 0; i<width/2; i++)for(j = 0; j<height/2; j++)*d++ = *(p + width-i + (height/2-j)*width/2); 	//p[height-j][i]printf("旋转V分分量完毕 num=%d \n", d-pic_d);break; // 180度	case 4:		// 创建旋转后的文件名 file_d = fopen("yuv420p_i420_180.yuv", "wb+");	//可读可写创建 Y分量文件 //旋转 Y分量 p = pic;d = pic_d;for(i = 0; i<height; i++)for(j = 0; j<width; j++)*d++ = *(p + j + (height-i)*width); 	//p[height-i][j]printf("旋转Y分分量完毕 num=%d \n", d-pic_d);	// 旋转 U,分量p += width * height; 			for(i = 0; i<height/2; i++)for(j = 0; j<width/2; j++)*d++ = *(p + j + (height/2-i)*width/2); //p[height/2-i][j]printf("旋转U分分量完毕 num=%d \n", d-pic_d);// 旋转 V分量p += width * height / 4; 			for(i = 0; i<height/2; i++)for(j = 0; j<width/2; j++)*d++ = *(p + j + (height/2-i)*width/2); //p[height/2-i][j]printf("旋转V分分量完毕 num=%d \n", d-pic_d);break; // 旋转270度	case 5:		// 创建旋转后的文件名 file_d = fopen("yuv420p_i420_270.yuv", "wb+");	//可读可写创建 Y分量文件 	//旋转 Y分量 p = pic;d = pic_d;for(i = 0; i<width; i++)for(j = 0; j<height; j++)*d++ = *(p + width-i + (j)*width); 	//p[height-j][i]printf("旋转Y分分量完毕 num=%d \n", d-pic_d);	// 旋转 U,分量p += width * height; 			for(i = 0; i<width/2; i++)for(j = 0; j<height/2; j++)*d++ = *(p + width/2-i + (j)*width/2); 	//p[height-j][i]printf("旋转U分分量完毕 num=%d \n", d-pic_d);// 旋转 V分量p += width * height / 4; 			for(i = 0; i<width/2; i++)for(j = 0; j<height/2; j++)*d++ = *(p + width/2-i + (j)*width/2); 	//p[height-j][i]printf("旋转V分分量完毕 num=%d \n", d-pic_d);break; default:if(rota != 0)printf("不支持旋转%d度,当前不旋转\n", rota); // 创建旋转后的文件名 file_d = fopen("yuv420p_i420_0.yuv", "wb+");	//可读可写创建 Y分量文件 	// 写入旋转后的图			num = fwrite(pic, 1, width * height *3 / 2, file_d); 	// 保存Y分量,pic[0, width*height] ,每个数据块写1字节 printf("写入 %d 个字节的数据\n", num);	free(pic);free(pic_d);fclose(file);fclose(file_d);return;} // 写入旋转后的图			num = fwrite(pic_d, 1, width * height *3 / 2, file_d); 	// 保存Y分量,pic[0, width*height] ,每个数据块写1字节 printf("写入 %d 个字节的数据\n", num);	free(pic);free(pic_d);fclose(file);fclose(file_d);
}int main(void)
{char path[]="yuv420p_i420.yuv";//Split_YUV420_I420(path, 720, 480);Rotate_YUV420_I420(path,720, 480, 0);		// 旋转 0°Rotate_YUV420_I420(path,720, 480, 1);		// 镜像旋转 0°Rotate_YUV420_I420(path,720, 480, 2);		// 旋转90°Rotate_YUV420_I420(path,720, 480, 3);		// 镜像旋转90°Rotate_YUV420_I420(path,720, 480, 4);		// 旋转180°Rotate_YUV420_I420(path,720, 480, 5);		// 旋转270°return 0;	
}

五、运行结果

图片太多,我就放一起显示了:
在这里插入图片描述




《视音频数据处理入门:RGB、YUV像素数据处理》
《图文详解YUV420数据格式》
《YUV420图像旋转90算法的优化》


http://chatgpt.dhexx.cn/article/Ay1nWcGi.shtml

相关文章

YUV420->RGB888格式转换的CPP实现

git地址&#xff1a; GitHub - Evayangelion/EYUVConverter: A YUV converter implemented by cpp 上一次学习了YUV各种采样和存储方式的区别&#xff08;YUV知识存档&#xff09; 接下来一起学习一下RGB格式的知识和YUV到RGB的转换方式 RGB色彩模式的分类 RGB的分类相较YUV来说…

YUV444,YUV420P,YUV420SP,YUV422P,YUV422SP,NV12,NV21,NV16,NV61等格式区分与存储简介

一、基础知识&#xff1a; 概述 YUV是顏色編碼方式&#xff0c;常用於視頻及圖像處理中。 其中的YUV是三個分量。“Y”表示明亮度&#xff08;Luminance或Luma&#xff09;&#xff0c;也就是灰度值。“U”和“V” 表示的是彩色信息&#xff0c;分別爲色度和濃度&#xff08;C…

OpenGL渲染YUV420P

一 、YUV420P数据格式二 、GLFW渲染YUV420P2.1 定义顶点数据2.2 创建YUV三张纹理2.3上行YUV420数据2.4 渲染纹理2.5 着色器 三、代码地址以及存在的问题四 、解决存在的问题 一 、YUV420P数据格式 图片来源于谷歌 结合上图可以看出YUV420P的特点如下&#xff1a; ①无论在横向还…

详解 YUV 格式(I420/YUV420/NV12/NV12/YUV422)

转载自&#xff1a;划水型派大星 YUV &#xff08;YCbCr&#xff09;是一种像素格式&#xff0c;常见于视频编码与静态图像。与 RGB 格式&#xff08;红-绿-蓝&#xff09;相反&#xff0c;YUV 分别由一个称为 Y&#xff08;相当于灰度&#xff09;的“亮度”分量&#xff08;…

ffmpeg学习笔记(2)-YUV420算法原理

ffmpeg学习笔记(2)——YUV420算法原理 一、YUV像素格式 YUV的组成格式如下图所示 Y:明亮度&#xff0c;即灰度值 U、V&#xff1a;颜色 YUV格式将亮度信息和色彩信息分离&#xff0c;没有色彩信息依然能够显示图像&#xff08;没有色彩信息&#xff09;&#xff0c;很好的解…

音视频编解码: YUV存储格式中的YUV420P,YUV420SP,NV12, NV21理解(转)

概述 之前介绍了YUV码流的采样格式&#xff0c;下面分析下YUV码流的存储格式&#xff0c;YUV码流的存储格式与采样格式息息相关。总的来讲&#xff0c;YUV存储格式主要分为两种&#xff1a; planar 平面格式 指先连续存储所有像素点的 Y 分量&#xff0c;然后存储 U 分量&…

图像编码YUV420格式

目录 一、YUV简介 二、RGB 到 YUV 的转换 三、YUV采样 YUV 4:4:4 YUV 4:2:2 YUV 4:2:0 四、YUV 存储格式 基于 YUV 4:2:2 采样的格式 YUYV 格式 UYVY 格式 YUV 422P 格式 基于 YUV 4:2:0 采样的格式 YU12 和 YV12 格式 NV12 和 NV21 格式 一、YUV简介 YUV&#x…

YUV420数据格式详解

YUV简介 YUV格式有两大类&#xff1a;planar和packed。 对于planar的YUV格式&#xff0c;先连续存储所有像素点的Y&#xff0c;紧接着存储所有像素点的U&#xff0c;随后是所有像素点的V。 对于packed的YUV格式&#xff0c;每个像素点的Y,U,V是连续交叉存储的。 YUV分为三个分…

python 生产者消费者模式

生产者消费者模式概述 在并发编程中使用生产者和消费者模式能够解决大不多的并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么使用生产者消费者模式 在线程世界里&#xff0c;生产者就是生产数据的线程&#xff0c;消费者就是…

生产者-消费者模式

概述 生产者-消费者模式在系统交互方面,有几个特点: 1、系统解耦 2、解决并发问题 3、不需要关心对方系统何时处理数据,处理结果如何 下面用几个简单例子逐步说明。 简单例子 假设有两个系统,A系统和B系统,B系统需要依赖A系统产生的数据,也就是说,A系统产生数据后,必须把数…

生产者消费者模式及简单的运用场景

先考虑一个问题&#xff1a;服务端接受多个客户端提交的视频文件进行转码的操作&#xff0c;应该怎么设计&#xff1f; 由于转码比较花费时间&#xff0c;所以我们排除同步的想法。而转码需要用到的外部软件&#xff08;exe文件&#xff09;&#xff0c;不能同时被多个线程用到…

Java多线程系列—生产者消费者模式的多种实现(04)

生产者消费者模式 我们先来看看什么是生产者消费者模式,生产者消费者模式是程序设计中非常常见的一种设计模式,被广泛运用在解耦、消息队列等场景。 在现实世界中,我们把生产商品的一方称为生产者,把消费商品的一方称为消费者,有时生产者的生产速度特别快,但消费者的消…

生产者消费者模式的Java实现

一.概述 1. 什么是“生产者消费者模式”&#xff1f; 生产线程负责生产&#xff0c;消费线程负责消费 生产线程和消费线程要达到均衡。 生产满了就不能继续生产了&#xff0c;必须让消费线程进行消费 消费完了就不能再消费了&#xff0c;必须让生产线程进行生产 这是一种特殊的…

实现生产者消费者模式的三种方式

什么是生产者消费者模式 简单来说&#xff0c;生产者消费者模式就是缓冲区。 那么这么做有两个好处&#xff0c;一个是解耦&#xff0c;第二个是平衡生产能力和消费能力的差&#xff0c;因为生产者和消费者的速度是不一样的&#xff0c;有了这个缓冲区就可以平衡这样一个落差&…

生产者消费者模式最佳实践

测试环境&#xff1a;ubuntu18.04opencv4.2Qt 一个生产者-消费者模式下的视频处理框架。基础结构&#xff1a;视频读取类线程不断读取视频帧&#xff0c;处理类线程对图像进行处理&#xff0c;之后通过信号与槽机制在主线程中显示。特点&#xff1a;视频读取、处理为独立线程&a…

生产者、消费者模式

架构设计&#xff1a;生产者/消费者模式[0]&#xff1a;概述 今天打算来介绍一下“生产者&#xff0f;消费者模式”&#xff0c;这玩意儿在很多开发领域都能派上用场。由于该模式很重要&#xff0c;打算分几个帖子来介绍。今天这个帖子先来扫盲一 把。如果你对这个模式已经比较…

生产者和消费者模式详解

★简介 生产者消费者模式并不是GOF提出的23种设计模式之一&#xff0c;23种设计模式都是建立在面向对象的基础之上的&#xff0c;但其实面向过程的编程中也有很多高效的编程模式&#xff0c;生产者消费者模式便是其中之一&#xff0c;它是我们编程过程中最常用的一种设计模式。…

生产者消费者模式+代码实现

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么要使用生产者和消费者模式 在线程世界里&#xff0c;生产者就是生产数据的线程&#xff0c;消费者就是消费数据的线程。在…

生产者消费者模式详解(转载)

★简介 在实际的软件开发过程中&#xff0c;经常会碰到如下场景&#xff1a;某个模块负责产生数据&#xff0c;这些数据由另一个模块来负责处理&#xff08;此处的模块是广义的&#xff0c;可以是类、函数、线程、进程等&#xff09;。产生数据的模块&#xff0c;就形象地称为生…

多线程之生产者消费者模式

文章目录 基本组成阻塞队列有界队列与无界队列ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue 流量控制与信号量(Semaphore)双缓冲与Exchanger 基本组成 生产者&#xff1a;生产者的任务是生产产品&#xff0c;产品可以是数据&#xff0c;也可以是任务。(将产品存入传…