一、 按像素点旋转图像

假设有以下一张图像:

Pixel1  Pixel2  Pixel3  Pixel4

Pixel5  Pixel6  Pixel7  Pixel8

其图像分辨率是width x height

  1. 在顺时针旋转90度后,其内容将会变成:

Pixel5  Pixel1

Pixel6  Pixel2

Pixel7  Pixel3

Pixel8  Pixel4

分辨率变成了height x width

也就是:

  • 原始数据第 height - 1 行第 0 列的像素点将会变成目标数据第 0 行第 0 列的像素点
  • 原始数据第 height - 1 行第 1 列的像素点将会变成目标数据第 1 行第 0 列的像素点
  • 原始数据第 height - 1 行第 width - 1 列的像素点将会变成目标数据第 width - 1 行第 0 列的像素点
  • 原始数据第 1 行第 width - 1 列的像素点将会变成目标数据第 width - 1 行第 height - 2 列的像素点
  • 原始数据第 1 行第 width - 2 列的像素点将会变成目标数据第 width - 2 行第 height - 2 列的像素点
  • 原始数据第 1 行第 width - 3 列的像素点将会变成目标数据第 width - 3 行第 height - 2 列的像素点
  • 原始数据第 i 行第 j 列的像素点将会变成目标数据第 j 行第 height - 1 - i 列的像素点

2. 若旋转**180**度,其内容将会变成: >Pixel8  Pixel7  Pixel6  Pixel5
Pixel4  Pixel3  Pixel2  Pixel1

也就是

  • 原始数据第 0 行第 0 列的像素点将会变成目标数据第 height - 1 行第 width - 1 列的像素点
  • 原始数据第 1 行第 0 列的像素点将会变成目标数据第 height - 2 行第 width - 1 列的像素点
  • 原始数据第 height - 1 行第 0 列的像素点将会变成目标数据第 0 行第 width - 1 列的像素点
  • 原始数据第 height - 1 行第 1 列的像素点将会变成目标数据第 0 行第 width - 2 列的像素点
  • 原始数据第 i 行第 j 列的像素点将会变成目标数据第 height - 1 - i 行第 width - 1 - j 列的像素点

3. 若旋转**270**度,其内容将会变成: >Pixel4  Pixel8
Pixel3  Pixel7
Pixel2  Pixel6
Pixel1  Pixel5

也就是:

  • 原始数据第 0 行第 width - 1 列的像素点将会变成目标数据第 0 行第 0 列的像素点
  • 原始数据第 0 行第 width - 2 列的像素点将会变成目标数据第 1 行第 0 列的像素点
  • 原始数据第 0 行第 0 列的像素点将会变成目标数据第 width - 1 行第 0 列的像素点
  • 原始数据第 1 行第 width - 1 列的像素点将会变成目标数据第 0 行第 1 列的像素点
  • 原始数据第 1 行第 width - 2 列的像素点将会变成目标数据第 1 行第 1 列的像素点
  • 原始数据第 i 行第 j 列的像素点将会变成目标数据第 width - j - 1 行第 i 列的像素点

二、 旋转 BGR24 / RGB24 数据

BGR24 / RGB24 都是以3个byte作为一个像素,因此在旋转时需要将3个byte作为一个整体进行旋转。示例代码如下。

  • 旋转90度:
void rotateRgb24Degree90(char *rgb24, char *rotatedRgb24, int width, int height) {int lineDataSize = width * 3;int rotatedRgb24Index = 0;int finalLineStartIndex = (height - 1) * lineDataSize;for (int w = 0; w < lineDataSize; w += 3) {int bgr24StartIndex = finalLineStartIndex + w;int offset = 0;for (int h = 0; h < height; h++) {rotatedRgb24[rotatedRgb24Index++] = rgb24[bgr24StartIndex - offset];rotatedRgb24[rotatedRgb24Index++] = rgb24[bgr24StartIndex - offset + 1];rotatedRgb24[rotatedRgb24Index++] = rgb24[bgr24StartIndex - offset + 2];offset += lineDataSize;}}
}
  • 旋转180度
void rotateRgb24Degree180(char *rgb24, char *rotatedRgb24, int width, int height) {int lineDataSize = width * 3;int rotatedRgb24Index = 0;int rgb24StartIndex = lineDataSize * height - 3;for (int h = height - 1; h >= 0; h--) {for (int w = lineDataSize - 3; w >= 0; w -= 3) {rotatedRgb24[rotatedRgb24Index++] = rgb24[rgb24StartIndex];rotatedRgb24[rotatedRgb24Index++] = rgb24[rgb24StartIndex + 1];rotatedRgb24[rotatedRgb24Index++] = rgb24[rgb24StartIndex + 2];rgb24StartIndex -= 3;}}
}
  • 旋转270度
void rotateRgb24Degree270(char *rgb24, char *rotatedRgb24, int width, int height) {int lineDataSize = width * 3;int rotatedRgb24Index = 0;int finalColumnStartIndex = lineDataSize;for (int w = 0; w < lineDataSize; w += 3) {finalColumnStartIndex -= 3;int offset = 0;for (int h = 0; h < height; h++) {rotatedRgb24[rotatedRgb24Index++] = rgb24[finalColumnStartIndex + offset];rotatedRgb24[rotatedRgb24Index++] = rgb24[finalColumnStartIndex + offset + 1];rotatedRgb24[rotatedRgb24Index++] = rgb24[finalColumnStartIndex + offset + 2];offset += lineDataSize;}}
}

三、旋转NV21、NV12数据

对于NV21或NV12数据,其排列是width * height个Y连续存储,接下来是 height / 2 行的UV数据,每一行的UV数据是 width / 2 个U和 width / 2 个V交叉存储(NV21是VU VU VU VU…,NV12是UV UV UV UV …),Y数据的大小刚好就是像素数,可以直接进行旋转,对于U和V数据,需要考虑下标的跳跃情况(由于NV21和NV12的U和V只是刚好位置相反,因此旋转NV21的代码也同样适用于旋转NV12)。示例代码如下。

  • 旋转90度
void rotateNv21Degree90(char *nv21, char *rotatedNv21, int width, int height) {int yFinalLineStartIndex = (height - 1) * width;int rotatedYIndex = 0;//rotate yfor (int w = 0; w < width; w++) {int yStartIndex = yFinalLineStartIndex + w;int offset = 0;for (int h = 0; h < height; h++) {rotatedNv21[rotatedYIndex++] = nv21[yStartIndex - offset];offset += width;}}//rotate uvint uvFinalLineStartIndex = width * height * 3 / 2 - width;int rotatedVIndex = width * height;int rotatedUIndex = width * height + 1;for (int w = 0; w < width; w += 2) {int uvStartIndex = uvFinalLineStartIndex + w;int offset = 0;for (int h = 0; h < height; h += 2) {rotatedNv21[rotatedVIndex] = nv21[uvStartIndex - offset];rotatedNv21[rotatedUIndex] = nv21[uvStartIndex - offset + 1];offset += width;rotatedVIndex += 2;rotatedUIndex += 2;}}
}
  • 旋转180度
void rotateNv21Degree180(char *nv21, char *rotatedNv21, int width, int height) {int yIndex = width * height - 1;int rotatedYIndex = 0;//rotate yfor (int h = height - 1; h >= 0; h--) {for (int w = width - 1; w >= 0; w--) {rotatedNv21[rotatedYIndex++] = nv21[yIndex];yIndex--;}}int uvIndex = width * height * 3 / 2 - 2;int rotatedVIndex = width * height;int rotatedUIndex = width * height + 1;//rotate uvfor (int h = height - 1; h >= 0; h -= 2) {for (int w = width - 1; w >= 0; w -= 2) {rotatedNv21[rotatedVIndex] = nv21[uvIndex];rotatedNv21[rotatedUIndex] = nv21[uvIndex + 1];uvIndex -= 2;rotatedVIndex += 2;rotatedUIndex += 2;}}
}
  • 旋转270度
void rotateNv21Degree270(char *nv21, char *rotatedNv21, int width, int height) {int rotatedYIndex = 0;int yFinalColumnStartIndex = width;//rotate yfor (int w = 0; w < width; w++) {int offset = 0;for (int h = 0; h < height; h++) {rotatedNv21[rotatedYIndex++] = nv21[yFinalColumnStartIndex + offset];offset += width;}yFinalColumnStartIndex--;}//rotate uvint uvFinalColumnStartIndex = width * height + width;int rotatedVIndex = width * height;int rotatedUIndex = width * height + 1;for (int w = 0; w < width; w += 2) {uvFinalColumnStartIndex -= 2;int offset = 0;for (int h = 0; h < height; h += 2) {rotatedNv21[rotatedVIndex] = nv21[uvFinalColumnStartIndex + offset];rotatedNv21[rotatedUIndex] = nv21[uvFinalColumnStartIndex + offset + 1];offset += width;rotatedVIndex += 2;rotatedUIndex += 2;}}
}

四、旋转I420、YV12数据

对于I420、YV12数据,其排列是width * height个Y连续存储,接下来是连续的U和V或连续的V和U(I420是UUUUUUUU…VVVVVVVV…,YV12是VVVVVVVV…UUUUUUUU…),Y数据的大小刚好就是像素数,可以直接进行旋转;因为U和V的宽高都只有Y的宽高的一半,所以宽高的循环数各只有Y的一半(由于I420和YV12的U和V只是刚好位置相反,因此旋转I420的代码也同样适用于旋转YV12)。示例代码如下。

  • 旋转90度
void rotateI420Degree90(char *i420, char *rotatedI420, int width, int height) {int halfWidth = width / 2;int yFinalLineStartIndex = (height - 1) * width;int rotatedYIndex = 0;//rotate yfor (int w = 0; w < width; w++) {int yStartIndex = yFinalLineStartIndex + w;int offset = 0;for (int h = 0; h < height; h++) {rotatedI420[rotatedYIndex++] = i420[yStartIndex - offset];offset += width;}}//rotate uvint uFinalLineStartIndex = width * height * 5 / 4 - halfWidth;int vFinalLineStartIndex = width * height * 3 / 2 - halfWidth;int rotatedUIndex = width * height;int rotatedVIndex = width * height * 5 / 4;for (int w = 0; w < width; w += 2) {int uStartIndex = uFinalLineStartIndex + w / 2;int vStartIndex = vFinalLineStartIndex + w / 2;int offset = 0;for (int h = 0; h < height; h += 2) {rotatedI420[rotatedUIndex++] = i420[uStartIndex - offset];rotatedI420[rotatedVIndex++] = i420[vStartIndex - offset];offset += halfWidth;}}
}
  • 旋转180度
void rotateI420Degree180(char *i420, char *rotatedI420, int width, int height) {int yIndex = width * height - 1;int rotatedYIndex = 0;//rotate yfor (int h = height - 1; h >= 0; h--) {for (int w = width - 1; w >= 0; w--) {rotatedI420[rotatedYIndex++] = i420[yIndex];yIndex--;}}int uIndex = width * height * 5 / 4 - 1;int vIndex = width * height * 3 / 2 - 1;int rotatedUIndex = width * height;int rotatedVIndex = width * height * 5 / 4;//rotate uvfor (int h = height - 1; h >= 0; h -= 2) {for (int w = width - 1; w >= 0; w -= 2) {rotatedI420[rotatedUIndex++] = i420[uIndex--];rotatedI420[rotatedVIndex++] = i420[vIndex--];}}
}
  • 旋转270度
void rotateI420Degree270(char *i420, char *rotatedI420, int width, int height) {int halfWidth = width / 2;int yFinalColumnStartIndex = width;int rotatedYIndex = 0;//rotate yfor (int w = 0; w < width; w++) {int offset = 0;for (int h = 0; h < height; h++) {rotatedI420[rotatedYIndex++] = i420[yFinalColumnStartIndex + offset];offset += width;}yFinalColumnStartIndex--;}//rotate uvint uFinalColumnStartIndex = width * height + width;int vFinalColumnStartIndex = width * height * 5 / 4 + width;int rotatedUIndex = width * height;int rotatedVIndex = width * height * 5 / 4;for (int w = 0; w < width; w += 2) {uFinalColumnStartIndex--;vFinalColumnStartIndex--;int offset = 0;for (int h = 0; h < height; h += 2) {rotatedI420[rotatedUIndex++] = i420[uFinalColumnStartIndex + offset];rotatedI420[rotatedVIndex++] = i420[vFinalColumnStartIndex + offset];offset += halfWidth;}}
}

五、 旋转YUYV数据

由于YUYV的排列方式是(YUYV YUYV YUYV …),其共用关系是每2个横向相邻的Y会使用同一组U和V,因此,在旋转180度时,YUV的共用关系可以不被打破,只是更改每4个byte中的2个Y的顺序;但是在旋转90度或270度时,由于原来横向的Y将被修改为纵向,YUV的共用关系也将被打破。示例代码如下。

  • 旋转90度
void rotateYuyvDegree90(char *yuyv, char *rotatedYuyv, int width, int height) {int lineDataSize = width * 2;int rotatedLineDataSize = height * 2;int rotatedYuyvIndex = 0;int finalLineStartIndex = (height - 2) * lineDataSize;for (int w = 0; w < lineDataSize; w += 4) {int yuyvStartIndex = finalLineStartIndex + w;int offset = 0;for (int h = 0; h < height; h += 2) {/*** y1 u1 y2 v2   y3 u2 y4 v2*                              ->    旋转后的画面脑补下* y5 u3 y6 v3   y7 u4 y8 v4*///y5rotatedYuyv[rotatedYuyvIndex] = yuyv[yuyvStartIndex - offset + lineDataSize];//u3rotatedYuyv[rotatedYuyvIndex + 1] = yuyv[yuyvStartIndex - offset + lineDataSize + 1];//y1rotatedYuyv[rotatedYuyvIndex + 2] = yuyv[yuyvStartIndex - offset];//v3rotatedYuyv[rotatedYuyvIndex + 3] = yuyv[yuyvStartIndex - offset + lineDataSize + 3];//y6rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize] = yuyv[yuyvStartIndex + lineDataSize - offset + 2];//u1rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 1] = yuyv[yuyvStartIndex - offset + 1];//y2rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 2] = yuyv[yuyvStartIndex - offset + 2];//v1rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 3] = yuyv[yuyvStartIndex - offset + 3];rotatedYuyvIndex += 4;offset += lineDataSize * 2;}rotatedYuyvIndex += rotatedLineDataSize;}
}
  • 旋转180度
void rotateYuyvDegree180(char *yuyv, char *rotatedYuyv, int width, int height) {int lineDataSize = width * 2;int yuyvIndex = lineDataSize * height - 4;int rotatedIndex = 0;//rotatefor (int h = height - 1; h >= 0; h--) {for (int w = lineDataSize - 4; w >= 0; w -= 4) {rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex + 2];rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex + 1];rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex];rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex + 3];yuyvIndex -= 4;}}
}
  • 旋转270度
void rotateYuyvDegree270(char *yuyv, char *rotatedYuyv, int width, int height) {int lineDataSize = width * 2;int rotatedLineDataSize = height * 2;int rotatedYuyvIndex = 0;int finalColumnStartIndex = lineDataSize - 4;for (int w = 0; w < lineDataSize; w += 4) {int offset = 0;for (int h = 0; h < height; h += 2) {/*** y1 u1 y2 v1   y3 u2 y4 v2*                              ->    旋转后的画面脑补下* y5 u3 y6 v3   y7 u4 y8 v4*///y4rotatedYuyv[rotatedYuyvIndex] = yuyv[finalColumnStartIndex + offset + 2];//u2rotatedYuyv[rotatedYuyvIndex + 1] = yuyv[finalColumnStartIndex + offset + 1];//y8rotatedYuyv[rotatedYuyvIndex + 2] = yuyv[finalColumnStartIndex + offset + lineDataSize +2];//v2rotatedYuyv[rotatedYuyvIndex + 3] = yuyv[finalColumnStartIndex + offset + 3];//y3rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize] = yuyv[finalColumnStartIndex + offset];//u4rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 1] = yuyv[finalColumnStartIndex + lineDataSize + offset + 1];//y7rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 2] = yuyv[finalColumnStartIndex + lineDataSize + offset];//v4rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 3] = yuyv[finalColumnStartIndex + lineDataSize + offset + 3];rotatedYuyvIndex += 4;offset += lineDataSize * 2;}finalColumnStartIndex -= 4;rotatedYuyvIndex += rotatedLineDataSize;}
}

FFmpeg之yuv旋转(十九)相关推荐

  1. FFmpeg之yuv镜像(十八)

    在开发相机相关程序时,由于人们习惯于看镜子,因此开发者们经常会遇到镜像显示预览数据的需求.各个手机厂家也了解这一点,因此一般手机打开相机切换到前置摄像头看到的画面都是镜像的.本文提供了一些常见YUV. ...

  2. 经典算法题每日演练——第十九题 双端队列

    经典算法题每日演练--第十九题 双端队列 原文:经典算法题每日演练--第十九题 双端队列 话说大学的时候老师说妹子比工作重要~,工作可以再换,妹子这个...所以...这两个月也就一直忙着Fall in ...

  3. ROS探索总结(十六)(十七)(十八)(十九)——HRMRP机器人的设计 构建完整的机器人应用系统 重读tf 如何配置机器人的导航功能

    ROS探索总结(十六)--HRMRP机器人的设计 1. HRMRP简介         HRMRP(Hybrid Real-time Mobile Robot Platform,混合实时移动机器人平台 ...

  4. WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形

    原文:WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形 说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘> ...

  5. Android开发笔记(七十九)资源与权限校验

    硬件资源 因为移动设备的硬件配置各不相同,为了防止使用了不存在的设备资源,所以要对设备的硬件情况进行检查.一般情况下,前置摄像头.部分传感器在低端手机上是没有的,像SD卡也可能因为用户没插卡使得找不到 ...

  6. 几何画板画椭圆_几何画板降龙十九式视频教程每天只要十分钟

    少侠,请先看几张宝图 图片来自百度搜索做这样神奇的动画难?NononoSoEasy!!!来西偶得一绝世武功秘籍:几何画板[遥想当年,上大学的时候,去上电脑课,得穿了鞋套进机房,机房里蹲着一排排呆头呆脑 ...

  7. 【正点原子FPGA连载】第三十九章OV7725摄像头RGB-LCD显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  8. [Python从零到壹] 五十九.图像增强及运算篇之图像锐化Scharr、Canny、LOG实现边缘检测

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  9. 研究生周报(第十九周)

    研究生周报(第十九周) 学习目标 Transformer LAS CTC RNN-T Language-Model 学习时间 9.11 ~ 9.17 学习产出 Transformer Embeddin ...

最新文章

  1. 大转盘完整源码附效果图,可以下载直接用
  2. SQLAlchemy实现插入数据(使用前端页面)
  3. 四、设计模式——策略模式
  4. STL源码分析-bitset
  5. WPF数据绑定(1-简单数据绑定)
  6. java矩阵类_java矩阵类,矩阵的乘法
  7. Ubuntu“无法解析或打开软件包的列表或是状态文件”的解决办法。
  8. Hbase之过滤器的使用
  9. 教你如何使用automake生成Makefile文件
  10. 爬取行政区划(改版)
  11. 贾跃亭个人破产内幕曝光 差一点获得中东土豪投资
  12. jsDoc的使用文档
  13. Golang http server 跨域问题与解决办法
  14. Lenovo 使用BoMC工具制作微码升级U盘刷新System x
  15. Java Excel文件内容替换
  16. 考博英语题型及难度分析
  17. 转自:如何自学Android(强烈推荐)
  18. @ZBBIX集成LDAP功能实现用户统一登录认证
  19. win10一键优化禁用缓存,禁用组件,
  20. 全网唯一 | 互联网公司微信支付宝服务商入驻流程图文指南

热门文章

  1. Heartbeat集群配置实例
  2. foreman架构的引入6-整合puppetmaster
  3. PXE安装报错:Cant' write to /dev/sda ,because it is opened read-only
  4. Quartus II调用modelsim ALTEA 的软件使用及问题
  5. SCOM 2007 R2安装部署各组件支持的操作系统详细列表
  6. 联通3G用户破千万 建成全球规模最大WCDMA网络
  7. HTTP请求字符限制和HTTP状态码
  8. 10个用好模糊效果的超实用设计技巧
  9. python网页开发实现本地上传_树莓派 python 如何将本地文件上传到指定的服务器页面上...
  10. Jquery第一章表格新增功能课后练习第二节2/2