这段时间,在做一个动态配置录相预览帧上的字符样式以及颜色等等的功能。因为要移植到几个不同的项目上,刚好这几个项目的camera原始预览数据格式,一个为yv12,一个yuv422,所以将这两种格式都做了送显的处理。先上一段传统的代码,也就是网上流行的给camera帧打上时间戳的代码:

DisplayClient::
addPreviewTimestamps(sp<StreamImgBuf>const& pCameraImgBuf)
{int width = pCameraImgBuf->getImgWidth();int height = pCameraImgBuf->getImgHeight();//ALOGD("timestamp videoSize  width : %d, height : %d",width,height);bool is1080P = width > 1280;int word_width = is1080P? digital_1080_d_width : digital_720_d_width;int word_height = is1080P? digital_1080_d_height : digital_720_d_height;int word_gap = is1080P? digital_1080_gap_d_width : gap_720_d_width;uint8_t* _ptr=(uint8_t *)pCameraImgBuf->getVirAddr();char isTimestampOffset[PROPERTY_VALUE_MAX];int    offset         = height - 100;property_get("com.spt.stampoffset.switch",isTimestampOffset,"0");if('1' == *isTimestampOffset){offset =  55;}int    margin_left      = width - 18 * word_width - 100;if ( NULL != _ptr ){char        dateTime[] = "2014-05-29 03:16:78";time_t          timer;struct tm     * t_tm;time( &timer );t_tm = localtime( &timer );memset( dateTime, 0, sizeof(dateTime) );sprintf( dateTime, "%4d-%02d-%02d %02d:%02d:%02d", t_tm->tm_year + 1900, t_tm->tm_mon + 1, t_tm->tm_mday, t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec );int digitalNums[10 + 8 + 1 + 1] = { -1 }; /* 10:-       11 ::      12:blank */memset( digitalNums, -1, sizeof(digitalNums) );for ( int i = 0; i < strlen( dateTime ); i++ ){char num = dateTime[i];if ( ('0' <= num) && (num <= '9') ){digitalNums[i] = num - '0';}else if ( num == '-' ){digitalNums[i] = 10;}else if ( num == ':' ){digitalNums[i] = 11;}else if ( num == ' ' ){digitalNums[i] = 12;}}for ( int j = 0; j < word_height; j++ ){for ( int k = 0; k < 10 + 1 + 8; k++ ){const unsigned char* str = (digitalNums[k] < 12 && digitalNums[k] != -1) ? (is1080P? DigitalArray_1080_d[digitalNums[k]] : DigitalArray_node_d[digitalNums[k]]) : NULL;if ( str != NULL ){for ( int h = 0; h < word_width; h++ ){if ( *(str + (word_height - 1 - j) * word_width + h) != 0x00 ){const int     offset_pixel     = offset * width + margin_left + j * width + k * (word_gap + word_width) + h;const int     offset_adr    = (int) (offset_pixel * 1);*(_ptr + offset_adr)        = 0xff;//*(_ptr + offset_adr + 1)      = 0xff;}}}}}//add by mcjerdy specified timestamp end}
}

这段代码的核心原理,就是从字符数组里取出编码成了yuv422或yv12的一个个字节,来替换对应位置的内容。我们现在要做的工作,也就是这样。只不过上面这段代码是没有加颜色的,也就是只画了Y(灰度)数据,所以算法很简单。而我们要将颜色也画上去,那么就还需要将对应的u、v分量也给画上去,算法自然也就不同了。

再来先讲下yuv数据的格式,YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV。

NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。

在YUV420中,一个像素点对应一个Y,一个4X4的小方块对应一个U和V。对于所有YUV420图像,它们的Y值排列是完全相同的,因为只有Y的图像就是灰度图像。YUV420sp与YUV420p的数据格式它们的UV排列在原理上是完全不同的。420p它是先把U存放完后,再存放V,也就是说UV它们是连续的。而420sp它是UV、UV这样交替存放的。(见下图) 有了上面的理论,我就可以准确的计算出一个YUV420在内存中存放的大小。 width * hight =Y(总和) U = Y / 4   V = Y / 4

所以YUV420 数据在内存中的长度是 width * hight * 3 / 2,

假设一个分辨率为8X4的YUV图像,它们的格式如下图:

YUV420sp格式如下图

YUV420p数据格式如下图

从上图可以看出,yuv420sp和yuv420p的存储方式,基本相同,只是yuv420sp的uv是交替存储的,而yuv420p的uv是分开存储的。我们要处理的yv12,就是属于yuv420p的一种,不过yv12是先存的全部Y,然后再存全部的V,最后再存全部的U,这个顺序不能弄乱了。

在yv12中,所有 Y 样例都会作为不带正负号的 char 值组成的数组首先显示在内存中。此数组后面紧接着所有 V (Cr) 样例。V 平面的跨距为 Y 平面跨距的一半,V 平面包含的行为 Y 平面包含行的一半。V 平面后面紧接着所有 U (Cb) 样例,它的跨距和行数与 V 平面相同, 见下图:

有了上面的基础,我们再来说说加yv12时间戳水印的事。因为我们camera出来的原始数据就是yv12的,所以我们要用来替换的数字图片数组,必定也是转成了yv12的无符号字符数组unsigned char ptr[]。也就是数且的前面w*h个字节,存储的是Y数据。后面紧接着从ptr[w*h - 1]开始,一共存储了w/2 * h/2个V字符。再从ptr[w*h + w/2 * h/2 -1]开始,存储剩下的w/2 * h/2个U字符。 以width=4, height=8为例,总大小为4*8*1.5=32*1.5=48个字节。 ptr[0]~ptr[31]存储的是Y数据, ptr[32]~ptr[39]存储的是V数据,ptr[40]~ptr[47]存储的是U数据。好了,接下来上画yv12的代码:

inline void DisplayClient::fill_yv12( int x,int y, unsigned char* camera_ptr,int cameraWidth,int cameraHeight,unsigned char* pic_ptr, int picWidth,int picHeight )
{int     offset_pixel = 0;int index = 0;for ( int j = 0; j < picHeight; j++ ){for ( int h = 0; h < picWidth; h++ ){offset_pixel     = y * cameraWidth + x + j * cameraWidth + h;index  = j*picWidth+h;*(camera_ptr + offset_pixel) = pic_ptr[index];                            }}
}

这个fill_yv12函数,每调一次,只单独画Y、U、V这三个分量中的一个。x, y是指从一帧图片的哪个座标开始画, camera_ptr是这一帧图片的起始地址,cameraWidth是一帧的宽度, cameraHeight是帧的高度,pic_ptr是用来替换帧像素的图片,比如对应的“0”、“1”等数据图片的地址, picWidth、picHeight是数字图片的宽高。

调用fill_yv12的代码如下:

uint8_t* _ptr=(uint8_t *)pCameraImgBuf->getVirAddr();
int half_height = picHieght/2;
int half_width = picWidth/2;
int half_camera_height = mCameraHeight/2;
int half_camera_widht = mCameraWidth/2;
int half_x = x/2;
int half_y = y/2;
int pic_start_pos = y * mCameraWidth + x;
uint8_t* v_start_ptr = _ptr+(mCameraWidth*mCameraHeight);
uint8_t* u_start_ptr = v_start_ptr + mCameraWidth/2 * mCameraHeight/2;
unsigned char* pic_v_ptr = prefix+(picWidth*picHieght);
unsigned char* pic_u_ptr = pic_v_ptr + half_width*half_height;
//画Y
fill_prefix_yv12(x, y, _ptr, mCameraWidth, mCameraHeight, prefix, picWidth, picHieght);
//画v
fill_prefix_yv12(half_x, half_y, v_start_ptr, half_camera_widht, half_camera_height, pic_u_ptr, half_width, half_height);
//画U
fill_prefix_yv12(half_x, half_y, u_start_ptr, half_camera_widht, half_camera_height, pic_v_ptr, half_width, half_height);

为了让大家有个更直观的理解,再上一个从yuv字符数组里取uv分量的函数:


// 获取 UV 分量
typedef unsigned char UCHAR, BYTE, *PUCHAR, *PBYTE;
VOID CRawImage::GetUV(PBYTE pbX, PBYTE *ppbU, PBYTE *ppbV)
{_Assert(ppbU && ppbV);if (m_csColorSpace == CS_YV12){*ppbV = pbX + m_uWidth * m_uHeight;*ppbU = *ppbV + m_uWidth/2 * m_uHeight / 2;}
}

总之一句话,画yuv字符时,先画y的值,然后盏V、U的值。 画V、U的值的时候,对应的x、y坐标,以及宽高都为y的一半。

好了,上面入是画yv12的代码。 下面再说一下画yuv422的的方法,准确来说,是YUYV,它是Y1U0, Y2V0, Y3U1, Y4U1这样yuv交替存储的, 相邻的两个Y共用其相邻的两个U、V。对应的还有yuv422p,YUV422P也属于YUV422的一种,它是一种Plane模式,即平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量,YUV422占用内存空间 = w * h * 2。

有了上面这些概念,再来上画yuv422,也即yuyv的代码:

inline void fill_yuv422(uint8_t* camera_ptr, unsigned char* pic_ptr, int y, int x, int bitsPerPixe)
{int index = 0;for ( int j = 0; j < mWord_height; j++ ){for ( int h = 0; h < mPrefixWidth; h++ ){const int     offset_pixel     = y * mCameraWidth + x + j * mCameraWidth + h;const int     offset_adr    = (int) (offset_pixel * bitsPerPixe);index = j*mPrefixWidth*2+h*2;*(camera_ptr + offset_adr) = pic_ptr[index];if(index+3 >= mWord_height*mPrefixWidth*2){//如果颜色显示正常,就用下面这条代码*(camera_ptr + offset_adr + 1) = pic_ptr[index+1];}else{//如果颜色反了,则可以用下的代码,将u和v分量的位置换一下。*(camera_ptr + offset_adr + 1) = pic_ptr[index+3];}                               }}
}

fill_yuv422的参数camera_ptr,是指帧图片的地址,  pic_ptr是数字图片的地址, y, x是要画的数字图片的座标, bitsPerPixe是指每一个像素点占几个字节。当为yuv422时,每一个像素点占两个字节。 *(camera_ptr + offset_adr) = pic_ptr[index];这一行是画Y数据。 下面的是画U和V

YUV420之YV12格式以及yuv422格式的显示相关推荐

  1. MATLAB读取一张RGB图片转成YUV420格式、YUV422格式、YUV444格式

    转:https://www.cnblogs.com/hythink/p/5421720.html 1.读入照片 控制输出的标志定义 1 2 3 4 5 6 7 8 9 10 11 clc;close  ...

  2. YUV422格式信号格式(以备学习之用)

    YUV信号有很多种,一般YUV420和YUV422用的比较多, YUV422格式,又分为很多小类,按照U.V的排列可以有YUYV,YVYU,UYVY,VYUY四种,其中,YUYVY一般又称作yuv2格 ...

  3. 图片YUV格式与RGB格式的转换

    YUV格式与RGB格式的转换 YUV格式介绍 YUV420.YUV422.YUV444 (1) YUV4:2:0 (2) YUV4:2:2 (3) YUV4:4:4 内存排列方式 YUV与RGB转换 ...

  4. YUV视频格式到RGB32格式转换的速度优化 上篇(转)

    YUV视频格式到RGB32格式转换的速度优化 上篇                     HouSisong@GMail.com    2007.10.30   tag: YUV,YCbCr,YUV ...

  5. YUV 格式与 RGB 格式的相互转换公式总结(C++版)

    YUV 格式与 RGB 格式的相互转换公式 最近在用的一个工业相机,输出的图像格式是 YUY2 格式.而在电脑上显示时需要 RGB 格式,所以就花了些时间在网上查了些相关的资料.说实话,网上关于 YU ...

  6. 基于RDKit的Python脚本:SDF格式转SMILES格式

    RDKit: Open-Source Cheminformatics Software http://www.rdkit.org/ 简化分子线性输入规范(SMILES)是一种用ASCII字符串明确描述 ...

  7. livechart 只显示 y 值_基于Python语言的SEGY格式地震数据读取与显示编程

    敬请关注<地学新视野> 摘要:本文简单介绍了SEG-Y地震数据文件格式,以及如何用Python语言编写读写SEG-Y格式的地震数据并绘制地震剖面,其中用到了Segyio和matplotli ...

  8. Python使用scipy包将稀疏矩阵保存为Mtx格式和npz格式文件实战

    Python使用scipy包将稀疏矩阵保存为Mtx格式和npz格式文件实战 目录 Python将稀疏矩阵保存为Mtx格式和npz格式文件实战 #导入包和仿真数据

  9. R语言ggplot2可视化:应用pivot_longer函数将数据从宽格式转换为长格式、为dataframe的每一列绘制密度图和直方图(堆叠)

    R语言ggplot2可视化:应用pivot_longer函数将数据从宽格式转换为长格式.为dataframe的每一列绘制密度图和直方图(堆叠) 目录 R语言ggplot2可视化:应用pivot_lon ...

  10. Convert PLY to VTK Using PCL 1.6.0 or PCL 1.8.0 使用PCL库将PLY格式转为VTK格式

    PLY格式是比较流行的保存点云Point Cloud的格式,可以用MeshLab等软件打开,而VTK是医学图像处理中比较常用的格式,可以使用VTK库和ITK库进行更加复杂的运算处理.我们可以使用Par ...

最新文章

  1. 分治、动态规划、贪婪 之 算法分析
  2. linux 查看 全部网卡 虚拟网卡 物理网卡
  3. C++实现教学信息管理系统
  4. 群里别人问的杂七杂八的问题
  5. 您在2016年会做什么? Apache Spark,Kafka,Drill等
  6. 计算机的定点运算器原理,计算机组成原理定点运算器的组成及结构.doc
  7. C# Replace函数与JS replace函数
  8. 康宁玻璃ct值计算公式_防眩光玻璃的硬度及强度
  9. Bootstrap3 Font Awesome 字体图标带边框的图标
  10. RQNOJ PID379 / 约会计划 -并查集
  11. Pentium的指令系统(1)——Pentium的寻址方式
  12. Java连接并操作SQLServer数据库
  13. Ubuntu16.04 pip下载安装tensorflow(GPU版)
  14. 微信加人的108种方法
  15. c语言 牛顿方法计算平方根,sqrt()平方根计算函数的实现2——牛顿迭代法
  16. Oracle 11g 通过透明网关访问瀚高数据库
  17. 14期 《星星之火,可以燎原》4月刊
  18. 如何在 JavaScript 中使用对象解构
  19. 苹果召回MacBook Air 内地官网声明用英文遭指责
  20. 如何做好微信朋友圈推广?

热门文章

  1. C++编写COM组件 ATL工程
  2. 优雅降级实现IE8的transform平移属性
  3. Tomcat6 无法登陆Tomcat Manager
  4. jeb配置java环境_jeb 提示 java help space
  5. VS2013编译最简单的PPAPI插件
  6. python怎么返回上一行代码_如何返回循环Python中的第一行代码
  7. python自动抓取局域网文件_python使用tcp实现局域网内文件传输
  8. 刘铎 计算机学院,离散数学及应用 [刘铎 编著] 2013年版
  9. 天线接口 IPEX接口 SMA接口 U.FL、IPX 天线的工作原理 天线的种类
  10. android 识别车牌颜色,Android、ios移动端车牌识别sdk / 车牌识别API