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

一、按像素点将图像镜像

图像可按水平镜像和垂直镜像。
假设有以下一张图像:

Pixel1  Pixel2  Pixel3  Pixel4

Pixel5  Pixel6  Pixel7  Pixel8

在**水平镜像**后,新的镜像数据内容将会是: 

Pixel4  Pixel3  Pixel2  Pixel1
Pixel8  Pixel7  Pixel6  Pixel5

也就是:

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

在垂直镜像后,新的镜像数据内容将会是:

Pixel5  Pixel6  Pixel7  Pixel8

Pixel1  Pixel2  Pixel3  Pixel4

也就是:

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

二、镜像BGR24 / RGB24

由于BGR数据是3个byte作为一个像素点,因此我们在拷贝每个像素时需要每次移动3个byte。

  • 水平镜像
void horizontalMirrorBgr24(char *bgr24, char *mirrorBgr24, int width, int height) {int lineStartIndex = 0;int lineDataSize = width * 3;for (int h = 0; h < height; h++) {for (int w = 0; w < lineDataSize; w += 3) {mirrorBgr24[lineStartIndex + w] = bgr24[lineStartIndex + lineDataSize - w - 3];mirrorBgr24[lineStartIndex + w + 1] = bgr24[lineStartIndex + lineDataSize - w - 2];mirrorBgr24[lineStartIndex + w + 2] = bgr24[lineStartIndex + lineDataSize - w - 1];}lineStartIndex += lineDataSize;}
}
  • 垂直镜像
void verticalMirrorBgr24(char *bgr24, char *mirrorBgr24, int width, int height) {int lineDataSize = width * 3;bgr24 += width * height * 3;for (int h = 0; h < height; h++) {memcpy(mirrorBgr24, bgr24, lineDataSize);mirrorBgr24 += lineDataSize;bgr24 -= lineDataSize;}
}

三、镜像NV21 / NV12

由于NV21和NV12的结构只是UV数据位置相反,因此镜像NV21的代码同样适用于镜像NV12。

  • 水平镜像
void horizontalMirrorNv21(char *nv21, char *mirrorNv21, int width, int height) {int yLineStartIndex = 0;int uvLineStartIndex = width * height;for (int h = 0; h < height; h++) {for (int w = 0; w < width; w += 2) {mirrorNv21[yLineStartIndex + w] = nv21[yLineStartIndex + width - w];mirrorNv21[yLineStartIndex + w + 1] = nv21[yLineStartIndex + width - w - 1];if ((h & 1) == 0) {mirrorNv21[uvLineStartIndex + w] = nv21[uvLineStartIndex + width - w];mirrorNv21[uvLineStartIndex + w + 1] = nv21[uvLineStartIndex + width - w - 1];}}yLineStartIndex += width;if ((h & 1) == 0) {uvLineStartIndex += width;}}
}
  • 垂直镜像
void verticalMirrorNv21(char *nv21, char *mirrorNv21, int width, int height) {int yLineDataSize = width;int nv21Index = 0, mirrorNv21Index = 0;nv21Index += width * height - yLineDataSize;//mirror yfor (int h = 0; h < height; h++) {memcpy(mirrorNv21 + mirrorNv21Index, nv21 + nv21Index, yLineDataSize);mirrorNv21Index += yLineDataSize;nv21Index -= yLineDataSize;}int uvLineDataSize = width;//mirror uvint uvHeight = height / 2;//point to final line of uvnv21Index += (width * height * 3 / 2 - uvLineDataSize);for (int h = 0; h < uvHeight; h++) {memcpy(mirrorNv21 + mirrorNv21Index, nv21 + nv21Index, uvLineDataSize);mirrorNv21Index += uvLineDataSize;nv21Index -= uvLineDataSize;}
}

四、镜像I420 / YV12

由于I420 和YV12的结构只是UV数据位置相反,因此镜像I420的代码同样适用于镜像YV12。

  • 水平镜像
void horizontalMirrorI420(char *i420, char *mirrorI420, int width, int height) {int yLineStartIndex = 0;int uLineStartIndex = width * height;int vLineStartIndex = width * height * 5 / 4;for (int h = 0; h < height; h++) {for (int w = 0; w < width; w += 2) {mirrorI420[yLineStartIndex + w] = i420[yLineStartIndex + width - w];mirrorI420[yLineStartIndex + w + 1] = i420[yLineStartIndex + width - w - 1];if ((h & 1) == 0) {mirrorI420[uLineStartIndex + (w >> 1)] = i420[uLineStartIndex + ((width - w) >> 1)];mirrorI420[vLineStartIndex + (w >> 1) + 1] = i420[vLineStartIndex +((width - w) >> 1) - 1];}}yLineStartIndex += width;if ((h & 1) == 0) {uLineStartIndex += width >> 1;vLineStartIndex += width >> 1;}}
}
  • 垂直镜像
void verticalMirrorI420(char *i420, char *mirrorI420, int width, int height) {int halfWidth = width / 2;int yLineDataSize = width;int i420Index = 0, mirrorI420Index = 0;i420Index += width * height - yLineDataSize;//mirror yfor (int h = 0; h < height; h++) {memcpy(mirrorI420 + mirrorI420Index, i420 + i420Index, yLineDataSize);mirrorI420Index += yLineDataSize;i420Index -= yLineDataSize;}int uvLineDataSize = halfWidth;//mirror uvint uHeight = height / 2;//point to final line of ui420Index += (width * height * 5 / 4 + yLineDataSize - uvLineDataSize);for (int h = 0; h < uHeight; h++) {memcpy(mirrorI420 + mirrorI420Index, i420 + i420Index, uvLineDataSize);mirrorI420Index += uvLineDataSize;i420Index -= uvLineDataSize;}//point to final line of vi420Index += width * height / 2;int vHeight = height / 2;for (int h = 0; h < vHeight; h++) {memcpy(mirrorI420 + mirrorI420Index, i420 + i420Index, uvLineDataSize);mirrorI420Index += uvLineDataSize;i420Index -= uvLineDataSize;}
}

五、镜像YUYV

YUYV的共用关系是每2个Y共用1组U和V,因此在水平镜像时需要每4个Y替换其中两个Y的顺序。而垂直镜像时,整行处理即可。

  • 水平镜像
void horizontalMirrorYuyv(char *yuyv, char *mirrorYuyv, int width, int height) {int lineStartIndex = 0;int lineDataSize = width * 2;for (int h = 0; h < height; h++) {for (int w = 0; w < lineDataSize; w += 4) {mirrorYuyv[lineStartIndex + w] = yuyv[lineStartIndex + lineDataSize - w - 2];mirrorYuyv[lineStartIndex + w + 1] = yuyv[lineStartIndex + lineDataSize - w - 3];mirrorYuyv[lineStartIndex + w + 2] = yuyv[lineStartIndex + lineDataSize - w - 4];mirrorYuyv[lineStartIndex + w + 3] = yuyv[lineStartIndex + lineDataSize - w - 1];}lineStartIndex += lineDataSize;}
}
  • 垂直镜像
void verticalMirrorYuyv(char *yuyv, char *mirrorYuyv, int width, int height) {int lineDataSize = width * 2;yuyv += width * height * 2;for (int h = 0; h < height; h++) {memcpy(mirrorYuyv, yuyv, lineDataSize);mirrorYuyv += lineDataSize;yuyv -= lineDataSize;}
}

FFmpeg之yuv镜像(十八)相关推荐

  1. FFmpeg之yuv旋转(十九)

    一. 按像素点旋转图像 假设有以下一张图像: Pixel1  Pixel2  Pixel3  Pixel4 Pixel5  Pixel6  Pixel7  Pixel8 其图像分辨率是width x  ...

  2. opencv镜像_DX200操作要领—PAM与镜像平移变换(三十八)

    6.4 PAM功能 6.4.1 PAM功能 再现中的位置修改功能 (PAM 功能:Position Adjustment Manual) ,可在查看机器人动作状况的同时,在不停止机器人的情况下,通过简 ...

  3. OpenCV学习笔记(四十六)——FAST特征点检测features2D OpenCV学习笔记(四十七)——VideoWriter生成视频流highgui OpenCV学习笔记(四十八)——PCA算

    OpenCV学习笔记(四十六)--FAST特征点检测features2D 特征点检测和匹配是计算机视觉中一个很有用的技术.在物体检测,视觉跟踪,三维常年关键等领域都有很广泛的应用.这一次先介绍特征点检 ...

  4. OpenCV学习笔记(十六)——CamShift研究 OpenCV学习笔记(十七)——运动分析和物体跟踪Video OpenCV学习笔记(十八)——图像的各种变换(cvtColor*+)imgproc

    OpenCV学习笔记(十六)--CamShift研究 CamShitf算法,即Continuously Apative Mean-Shift算法,基本思想就是对视频图像的多帧进行MeanShift运算 ...

  5. [Python从零到壹] 五十八.图像增强及运算篇之图像锐化Sobel、Laplacian算子实现边缘检测

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

  6. ComicEnhancerPro 系列教程十八:JPG文件长度与质量

    作者:马健 邮箱:stronghorse_mj@hotmail.com 主页:http://www.comicer.com/stronghorse/ 发布:2017.07.23 教程十八:JPG文件长 ...

  7. 机器学习之MATLAB代码--CEEMDAN+EEMD+EMD+VMD+IMF重构络(十八)

    机器学习之MATLAB代码--CEEMDAN+EEMD+EMD+VMD+IMF重构络(十八) 压缩分量的EEMD代码 压缩分量的EEMD数据 压缩分量的EEMD结果 CEEMDAN代码 CEEMDAN ...

  8. [Python从零到壹] 三十八.图像处理基础篇之图像几何变换(平移缩放旋转)

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

  9. Windows 8的十八项特点

    一.支持ARM架构 在年初的CES 2011上,微软正式宣布,Windows 8将支持ARM芯片架构.即来自Intel.AMD.NVIDIA.高通和德州仪器等芯片厂商的ARM系统都将一并提供支持.微软 ...

最新文章

  1. 非标自动化转行机器人_机器人与非标自动化这两个有什么区别?可以从结构,功能等方面谈谈吗?...
  2. 达内软件测试证书是什么证书,达内软件测试培训让我拥有了实际工作经验
  3. SharePoint2013开发环境搭建(完整版:图文并茂)
  4. java aop execution_Spring AOP -- execution表达式
  5. Not Equal on a Segment CodeForces - 622C
  6. AWK 批量杀进程号,好记性不如烂笔头
  7. javascript基础-ajax
  8. sendgrid java_java – SendGrid电子邮件API,发送电子邮件附件
  9. [转载] OpenCV-Python图像位与运算bitwise_and函数详解
  10. 全选、取消全选、单选
  11. 【原】unity3D之Draw Call
  12. 制作U盘macos系统
  13. excel把多个工作表合并
  14. java图形用户界面基础
  15. linux wifi音箱,基于Orangpi Zero和Linux ALSA实现WIFI无线音箱(三)
  16. 使用定积分计算三角形面积
  17. 计算机的复数形式英语,名词的复数形式大全
  18. 《手机传感器》参数与选择
  19. Win10电脑开机后黑屏只有鼠标怎么办?
  20. text改为longtext

热门文章

  1. uva 11732 strcmp() Anyone?
  2. 高并发系统数据幂等性
  3. wince BindingSource
  4. memcached部署安装文档
  5. c#获取应用程序路径的方法
  6. 关于.NET CF的底层资料(幻灯片)
  7. BPMN 2.0 流程设计
  8. 【机器人】项目疑难杂症
  9. oracle改表结构非空字段类型,oracle 表结构的非完全复制
  10. Python玩转简书钻,简述钻是否对文章权重有影响?结果确实有!