YUV图像合成原理

引言:在视频监控中最常用的就是图像拼接和字符叠加,25FPS的视频流,如果每隔40MS就从各个通道中取一幅图像来合成,则可以看到一个实时的合成视频。合成的过程也就是原始图像的拼接、缩放的过程,本文主要阐述UV分开存储的YUV420图像拼接的过程,实现下图的效果。

一、原图图像格式

1、图像常用的格式有两种RGB和YUV

(1)YUV是被欧洲电视系统所采用的一种颜色编码方法(属于PAL),是PAL和SECAM模拟彩色电视制式采用的颜色空间。在现代彩色电视系统中,通常采用三管彩色摄影机或彩色CCD摄影机进行取像,然后把取得的彩色图像信号经分色、分别放大校正后得到RGB,再经过矩阵变换电路得到亮度信号Y和两个色差信号R-Y(即U)、B-Y(即V),最后发送端将亮度和色差三个信号分别进行编码,用同一信道发送出去。这种色彩的表示方法就是所谓的YUV色彩空间表示。采用YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的。

(2)RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。

2、两者在存储上的区别

(1)YUV按照内存消耗量总体上分为YUV420、YUV422两种

YUV420-----其Y:U:V或者Y:UV或者Y:V:U的总量为4:2:0

YUV422-----其Y:U:V比例为4:2:2

(2)RGB内存比例为1:1:1

则可以看出 显示一个像素点 需要的内存字节数

YUV420=(4+2+0)/4=3/2BYTE

YUV422=(4+2+2)/4=2 BYTE

RGB=(1+1+1)/1=3 BYTE

现在有一副图像,宽W高H,那么显示一副图像所需内存

YUV420=W*H*3/2 BYTE

YUV422=W*H*2 BYTE

RGB=W*H*3 BYTE

3、YUV数据格式YUV420-UV分开存储

X代表Y分量 0代表U/V分量 420的常规打包格式如下:

也就是说在水平方向和锤子4个像素点Y分量公用一组U/V分量

在内存结构上U/V分量水平和垂直分量均为1/2 U=w/2*h/2  V=w/2*h/2

用表格数据得出来就是

二、图像合成过程

合成前的图像和需要合成到的目的图像如下图所示

需要进行采样缩放、贴图后就能实现图像合成

1、采样

这里不做详细介绍 只看采样后效果

2、贴图

贴图原理-采样后图像的Y分量直接memcpy到合成图像的对应区域,Y/V分量则需要注意下

U/V水平和垂直均1/2采样

隔行拷贝,每次拷贝采样后w/2长度。由于合成图像的U/V数据已经隔行-存储的时候是连续的,所以UV拷贝的时候连续拷贝采样后长度/2然后进入合成图像下一行的U/V然后再拷贝,拷贝的高度为采样后图像高度h/2

上代码

  1. //图像类型枚举
  2. typedef enum IMAGE_TYPE
  3. {
  4. IMAGE_YUV420,
  5. IMAGE_YUV422
  6. };
  7.  
  8. //YUV图像结构体
  9. typedef struct YUV_IMAGE
  10. {
  11. YUV_IMAGE(){ y = NULL; u = NULL; v = NULL; w = 0; h = 0; }
  12. ~YUV_IMAGE(){ y = NULL; u = NULL; v = NULL; w = 0; h = 0; }
  13. UCHAR *y;
  14. UCHAR *u;
  15. UCHAR *v;
  16. UINT w;
  17. UINT h;
  18. IMAGE_TYPE t;
  19. };

  

  1. /*从各个通道获取图像并缩放到合成图像上*/
  2. BOOL CDigitalCourt::GetMergeImg()
  3. {
  4. DWORD dwTimeBegin = GetTickCount();
  5. BOOL bRet = FALSE;
  6. YUV_IMAGE *pImg = NULL;
  7.  
  8. for (int i = ; i < MAX_CHANNEL_CNT; i++)
  9. {
  10. if (m_pChannel[i])
  11. {
  12. //将YUV图像进行拷贝
  13. if (m_pChannel[i]->GetYUVImage(&pImg))
  14. {
  15. //将拷贝后的YUV图像缩放到合成图像上
  16. MergeImage(pImg, &m_MergeImg, i);
  17.  
  18. bRet = TRUE;
  19. }
  20. }
  21. }
  22. //memcpy(m_MergeImg.y, src->y, 1920 * 1080 * 3 / 2);
  23. /*memset(m_MergeImg.y, m_nMergeFrmCnt % 255, m_MergeImg.w*m_MergeImg.h);
  24. memset(m_MergeImg.u, m_nMergeFrmCnt % 255 + 100, m_MergeImg.w*m_MergeImg.h / 2);
  25. */
  26. return bRet;
  27. }
  28.  
  29. //创建采样器
  30. SwsContext * CDigitalCourt::CreateSws(YUV_IMAGE *src, UINT nOrder, UINT w, UINT h)
  31. {
  32. if (NULL == src || nOrder >= MAX_CHANNEL_CNT || == w || == h)
  33. return NULL;
  34.  
  35. if (NULL == m_pSubSws[nOrder])
  36. {
  37. m_pSubSws[nOrder] = sws_getContext(src->w,
  38. src->h,
  39. AV_PIX_FMT_YUV420P,
  40. w,
  41. h,
  42. AV_PIX_FMT_YUV420P,
  43. SWS_BICUBIC,
  44. NULL,
  45. NULL,
  46. NULL);
  47. if (NULL == m_pSubSws)
  48. {
  49. LOG(LOG_ERROR, "CDigitalCourt::Create sws failed index=%d,w=%d,h=%d,mergeW=%d,mergeH=%d!", nOrder, src->w, src->h, m_nMergeW, m_nMergeH);
  50. return FALSE;
  51. }
  52. }
  53. return m_pSubSws[nOrder];
  54. }
  55.  
  56. //图像缩放--从源图像缩放到另外一个图像空间中去
  57. void CDigitalCourt::Scale(YUV_IMAGE *src, YUV_IMAGE *dst, SwsContext *pSws)
  58. {
  59. AVPicture pictsrc;
  60. AVPicture pictdst;
  61.  
  62. if (NULL == src || NULL == dst || NULL == pSws) return;
  63.  
  64. pictsrc.data[] = src->y;
  65. pictsrc.data[] = src->u;
  66. pictsrc.data[] = src->v;
  67. pictsrc.linesize[] = src->w;
  68. pictsrc.linesize[] = src->w / ;
  69. pictsrc.linesize[] = src->w / ;
  70.  
  71. pictdst.data[] = dst->y;
  72. pictdst.data[] = dst->u;
  73. pictdst.data[] = dst->v;
  74. pictdst.linesize[] = dst->w;
  75. pictdst.linesize[] = dst->w / ;
  76. pictdst.linesize[] = dst->w / ;
  77.  
  78. sws_scale(pSws, pictsrc.data, pictsrc.linesize, , src->h, pictdst.data, pictdst.linesize);
  79.  
  80. }
  81.  
  82. //将图像缩放到合成图像上 nOrder--图像位置0~5 对应于合成图像s1-s6
  83. //这里用了两个变量来存储缩放后的图像 m_mainMerge--位置0 m_subMerge--其它位置
  84. void CDigitalCourt::MergeImage(YUV_IMAGE *src, YUV_IMAGE *dst, UINT nOrder)
  85. {
  86.  
  87. UINT nOffY = , nOffx = ,w=,h=;
  88. SwsContext *pSws = NULL;
  89. YUV_IMAGE *pImgSmall = NULL;
  90.  
  91. if (NULL == src || NULL == dst || nOrder < || nOrder >= MAX_CHANNEL_CNT)
  92. {
  93. return;
  94. }
  95. //根据位置找出图像Y偏移量
  96. switch (nOrder)
  97. {
  98. case :
  99. nOffx = ;
  100. nOffY = ;
  101. w = dst->w * / ;
  102. h = dst->h * / ;
  103. pImgSmall = &m_mainMerge;
  104. break;
  105. case :
  106. nOffx = dst->w * / ;
  107. nOffY = ;
  108. w = dst->w / ;
  109. h = dst->h / ;
  110. pImgSmall = &m_subMerge;
  111. break;
  112. case :
  113. nOffx = dst->w * / ;
  114. nOffY = dst->h / ;
  115. w = dst->w / ;
  116. h = dst->h / ;
  117. pImgSmall = &m_subMerge;
  118. break;
  119. case :
  120. nOffx = dst->w * / ;
  121. nOffY = dst->h * / ;
  122. w = dst->w / ;
  123. h = dst->h / ;
  124. pImgSmall = &m_subMerge;
  125. break;
  126. case :
  127. nOffx = dst->w / ;
  128. nOffY = dst->h * / ;
  129. w = dst->w / ;
  130. h = dst->h / ;
  131. pImgSmall = &m_subMerge;
  132. break;
  133. case :
  134. nOffx = ;
  135. nOffY = dst->h * / ;
  136. w = dst->w / ;
  137. h = dst->h / ;
  138. pImgSmall = &m_subMerge;
  139. break;
  140. default:
  141. LOG(LOG_ERROR, "CDigitalCourt::MergeImage failed of image order error!");
  142. return;
  143. }
  144. //创建缩放器
  145. pSws = CreateSws(src, nOrder, w, h);
  146. //缩放
  147. Scale(src, pImgSmall, pSws);
  148. //图像粘贴
  149. MapImage(pImgSmall, &m_MergeImg, nOffx, nOffY);
  150. }
  151. //图像黏贴
  152. void MapImage(YUV_IMAGE *src, YUV_IMAGE *dst, UINT nOffX, UINT nOffY)
  153. {
  154. if (NULL == src || NULL == dst) return;
  155. if (src->w > dst->w || src->h > dst->h) return;
  156. if (NULL == src->y || NULL == src->u || NULL == src->v) return;
  157. if (NULL == dst->y || NULL == dst->u || NULL == dst->v) return;
  158.  
  159. UINT nOff = ;
  160. for (int i = ; i < src->h; i++)
  161. {
  162. nOff = dst->w*(nOffY + i) + nOffX;
  163. //逐行拷贝
  164. memcpy(dst->y + nOff, src->y + src->w*i, src->w);
  165. }
  166. UINT nUVOffX = nOffX / , nUVOffY = nOffY / ;
  167. UINT nUVSrcW = src->w / , nUVSrcH = src->h / ;
  168. UINT nUVDstW = dst->w / , nUVDstH = dst->h / ;
  169.  
  170. for (int j = ; j < nUVSrcH; j++)
  171. {
  172. nOff = nUVDstW*(nUVOffY + j) + nUVOffX;
  173. memcpy(dst->u + nOff, src->u + nUVSrcW*j, nUVSrcW);
  174. memcpy(dst->v + nOff, src->v + nUVSrcW*j, nUVSrcW);
  175. }
  176. }

http://blog.csdn.net/zwz1984/article/details/50403150

YUV图像合成原理<转>的更多相关文章

  1. YUV图像合成原理

    http://blog.csdn.net/zwz1984/article/details/50403150 http://zhongcong386.blog.163.com/blog/static/1 ...

  2. YUV格式分析

    转自:http://www.cnblogs.com/armlinux/archive/2012/02/15/2396763.html Andrew Huang <bluedrum@163.com ...

  3. Atitit  rgb yuv  hsv HSL 模式和 HSV(HSB) 图像色彩空间的区别

    Atitit  rgb yuv  hsv HSL 模式和 HSV(HSB) 图像色彩空间的区别 1.1. 色彩的三要素 -- 色相.明度.纯度1 1.2. YUV三个字母中,其中"Y&quo ...

  4. Android高效内存:让图片占用尽可能少的内存

    Android高效内存:让图片占用尽可能少的内存 一.让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336    小图:220 * 168 小图的高宽都是大图的1/2--> ...

  5. Android高效内存2:让图片占用尽可能少的内存

    Android高效内存:让图片占用尽可能少的内存 一.让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336    小图:220 * 168 资源目录:xhdpi 小图的高宽都是 ...

  6. YUV格式介绍

    原文链接:http://www.cnblogs.com/azraelly/archive/2013/01/01/2841269.html YUV格式有两大类:planar和packed.对于plana ...

  7. 关于yuv格式

    首先,内存分布        1:YUV420          (1):I420:              YYYYYYYY UU VV    =>YUV420P          (2): ...

  8. 【如何快速的开发一个完整的iOS直播app】(原理篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的 ...

  9. Atitit 图像处理 灰度图片 灰度化的原理与实现

    Atitit 图像处理 灰度图片 灰度化的原理与实现 24位彩色图与8位灰度图 首先要先介绍一下24位彩色图像,在一个24位彩色图像中,每个像素由三个字节表示,通常表示为RGB.通常,许多24位彩色图 ...

随机推荐

  1. HDU - 6214:Smallest Minimum Cut(最小割边最小割)

    Consider a network G=(V,E) G=(V,E) with source s s and sink t t . An s-t cut is a partition of nodes ...

  2. threejs 画二维圆(圆弧)

    画圆: var radius = 40, segments = 64, material = new THREE.LineBasicMaterial({ color: 0x0000ff }), geo ...

  3. POJ2891 Strange Way to Express Integers【扩展中国剩余定理】

    题目大意 就是模板...没啥好说的 思路 因为模数不互质,所以直接中国剩余定理肯定是不对的 然后就考虑怎么合并两个同余方程 \(ans = a_1 + x_1 * m_1 = a_2 + x_2 * ...

  4. 20179223《Linux内核原理与分析》第十周学习笔记

    课本第17.19和20章内容学习 关于设备驱动和设备管理,Linux主要有四种内核成分 设备类型:在所有Unix系统中为了统一普通设备的操作所采用的分类. 模块:Linux内核中用于按需加载和卸载目标 ...

  5. Nginx——安装Nginx(二)

    安装所需环境 Nginx 是 C语言 开发,建议在 Linux 上运行,当然,也可以安装 Windows 版本,本篇则使用 CentOS 7 作为安装环境. gcc 安装 安装 nginx 需要先将官 ...

  6. win32窗口样式GWL_EXSTYLE

    Private Const GWL_STYLE = (-16)             '窗口样式 '窗口风格Private Const WS_CAPTION = &HC00000       ...

  7. dda的fpga实现(转载)

    The general approach using DDAs will be to simulate a system of first-order differential equations, ...

  8. 洛谷P1309 瑞士轮

    传送门 题目大意: 2*n个人,有初始的比赛分数和实力值. 每次比赛前总分从大到小排序,总分相同编号小的排在前面. 每次比赛是1和2比,3和4比,5和6比. 实力值大的获胜得1分. 每次比赛前排序确定 ...

  9. Python tarfile模块解压报错 invalid mode ('wb') or filename

    问题原因 在使用tarfile模块解压一份Linux服务器上的打包文件时, 出现了错误提示: IOError: [Errno 22] invalid mode ('wb') or filename. ...

  10. php用smtp方式发送邮件

    http://www.daixiaorui.com/read/16.html 2个比较经典的PHP加密解密函数分享 http://www.jb51.net/article/51706.htm php5 ...