YUV图像合成原理<转>
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
上代码
//图像类型枚举
typedef enum IMAGE_TYPE
{
IMAGE_YUV420,
IMAGE_YUV422
}; //YUV图像结构体
typedef struct YUV_IMAGE
{
YUV_IMAGE(){ y = NULL; u = NULL; v = NULL; w = 0; h = 0; }
~YUV_IMAGE(){ y = NULL; u = NULL; v = NULL; w = 0; h = 0; }
UCHAR *y;
UCHAR *u;
UCHAR *v;
UINT w;
UINT h;
IMAGE_TYPE t;
};
/*从各个通道获取图像并缩放到合成图像上*/
BOOL CDigitalCourt::GetMergeImg()
{
DWORD dwTimeBegin = GetTickCount();
BOOL bRet = FALSE;
YUV_IMAGE *pImg = NULL; for (int i = ; i < MAX_CHANNEL_CNT; i++)
{
if (m_pChannel[i])
{
//将YUV图像进行拷贝
if (m_pChannel[i]->GetYUVImage(&pImg))
{
//将拷贝后的YUV图像缩放到合成图像上
MergeImage(pImg, &m_MergeImg, i); bRet = TRUE;
}
}
}
//memcpy(m_MergeImg.y, src->y, 1920 * 1080 * 3 / 2);
/*memset(m_MergeImg.y, m_nMergeFrmCnt % 255, m_MergeImg.w*m_MergeImg.h);
memset(m_MergeImg.u, m_nMergeFrmCnt % 255 + 100, m_MergeImg.w*m_MergeImg.h / 2);
*/
return bRet;
} //创建采样器
SwsContext * CDigitalCourt::CreateSws(YUV_IMAGE *src, UINT nOrder, UINT w, UINT h)
{
if (NULL == src || nOrder >= MAX_CHANNEL_CNT || == w || == h)
return NULL; if (NULL == m_pSubSws[nOrder])
{
m_pSubSws[nOrder] = sws_getContext(src->w,
src->h,
AV_PIX_FMT_YUV420P,
w,
h,
AV_PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL,
NULL,
NULL);
if (NULL == m_pSubSws)
{
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);
return FALSE;
}
}
return m_pSubSws[nOrder];
} //图像缩放--从源图像缩放到另外一个图像空间中去
void CDigitalCourt::Scale(YUV_IMAGE *src, YUV_IMAGE *dst, SwsContext *pSws)
{
AVPicture pictsrc;
AVPicture pictdst; if (NULL == src || NULL == dst || NULL == pSws) return; pictsrc.data[] = src->y;
pictsrc.data[] = src->u;
pictsrc.data[] = src->v;
pictsrc.linesize[] = src->w;
pictsrc.linesize[] = src->w / ;
pictsrc.linesize[] = src->w / ; pictdst.data[] = dst->y;
pictdst.data[] = dst->u;
pictdst.data[] = dst->v;
pictdst.linesize[] = dst->w;
pictdst.linesize[] = dst->w / ;
pictdst.linesize[] = dst->w / ; sws_scale(pSws, pictsrc.data, pictsrc.linesize, , src->h, pictdst.data, pictdst.linesize); } //将图像缩放到合成图像上 nOrder--图像位置0~5 对应于合成图像s1-s6
//这里用了两个变量来存储缩放后的图像 m_mainMerge--位置0 m_subMerge--其它位置
void CDigitalCourt::MergeImage(YUV_IMAGE *src, YUV_IMAGE *dst, UINT nOrder)
{ UINT nOffY = , nOffx = ,w=,h=;
SwsContext *pSws = NULL;
YUV_IMAGE *pImgSmall = NULL; if (NULL == src || NULL == dst || nOrder < || nOrder >= MAX_CHANNEL_CNT)
{
return;
}
//根据位置找出图像Y偏移量
switch (nOrder)
{
case :
nOffx = ;
nOffY = ;
w = dst->w * / ;
h = dst->h * / ;
pImgSmall = &m_mainMerge;
break;
case :
nOffx = dst->w * / ;
nOffY = ;
w = dst->w / ;
h = dst->h / ;
pImgSmall = &m_subMerge;
break;
case :
nOffx = dst->w * / ;
nOffY = dst->h / ;
w = dst->w / ;
h = dst->h / ;
pImgSmall = &m_subMerge;
break;
case :
nOffx = dst->w * / ;
nOffY = dst->h * / ;
w = dst->w / ;
h = dst->h / ;
pImgSmall = &m_subMerge;
break;
case :
nOffx = dst->w / ;
nOffY = dst->h * / ;
w = dst->w / ;
h = dst->h / ;
pImgSmall = &m_subMerge;
break;
case :
nOffx = ;
nOffY = dst->h * / ;
w = dst->w / ;
h = dst->h / ;
pImgSmall = &m_subMerge;
break;
default:
LOG(LOG_ERROR, "CDigitalCourt::MergeImage failed of image order error!");
return;
}
//创建缩放器
pSws = CreateSws(src, nOrder, w, h);
//缩放
Scale(src, pImgSmall, pSws);
//图像粘贴
MapImage(pImgSmall, &m_MergeImg, nOffx, nOffY);
}
//图像黏贴
void MapImage(YUV_IMAGE *src, YUV_IMAGE *dst, UINT nOffX, UINT nOffY)
{
if (NULL == src || NULL == dst) return;
if (src->w > dst->w || src->h > dst->h) return;
if (NULL == src->y || NULL == src->u || NULL == src->v) return;
if (NULL == dst->y || NULL == dst->u || NULL == dst->v) return; UINT nOff = ;
for (int i = ; i < src->h; i++)
{
nOff = dst->w*(nOffY + i) + nOffX;
//逐行拷贝
memcpy(dst->y + nOff, src->y + src->w*i, src->w);
}
UINT nUVOffX = nOffX / , nUVOffY = nOffY / ;
UINT nUVSrcW = src->w / , nUVSrcH = src->h / ;
UINT nUVDstW = dst->w / , nUVDstH = dst->h / ; for (int j = ; j < nUVSrcH; j++)
{
nOff = nUVDstW*(nUVOffY + j) + nUVOffX;
memcpy(dst->u + nOff, src->u + nUVSrcW*j, nUVSrcW);
memcpy(dst->v + nOff, src->v + nUVSrcW*j, nUVSrcW);
}
}
http://blog.csdn.net/zwz1984/article/details/50403150
YUV图像合成原理<转>的更多相关文章
- YUV图像合成原理
http://blog.csdn.net/zwz1984/article/details/50403150 http://zhongcong386.blog.163.com/blog/static/1 ...
- YUV格式分析
转自:http://www.cnblogs.com/armlinux/archive/2012/02/15/2396763.html Andrew Huang <bluedrum@163.com ...
- Atitit rgb yuv hsv HSL 模式和 HSV(HSB) 图像色彩空间的区别
Atitit rgb yuv hsv HSL 模式和 HSV(HSB) 图像色彩空间的区别 1.1. 色彩的三要素 -- 色相.明度.纯度1 1.2. YUV三个字母中,其中"Y&quo ...
- Android高效内存:让图片占用尽可能少的内存
Android高效内存:让图片占用尽可能少的内存 一.让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336 小图:220 * 168 小图的高宽都是大图的1/2--> ...
- Android高效内存2:让图片占用尽可能少的内存
Android高效内存:让图片占用尽可能少的内存 一.让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336 小图:220 * 168 资源目录:xhdpi 小图的高宽都是 ...
- YUV格式介绍
原文链接:http://www.cnblogs.com/azraelly/archive/2013/01/01/2841269.html YUV格式有两大类:planar和packed.对于plana ...
- 关于yuv格式
首先,内存分布 1:YUV420 (1):I420: YYYYYYYY UU VV =>YUV420P (2): ...
- 【如何快速的开发一个完整的iOS直播app】(原理篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的 ...
- Atitit 图像处理 灰度图片 灰度化的原理与实现
Atitit 图像处理 灰度图片 灰度化的原理与实现 24位彩色图与8位灰度图 首先要先介绍一下24位彩色图像,在一个24位彩色图像中,每个像素由三个字节表示,通常表示为RGB.通常,许多24位彩色图 ...
随机推荐
- wait() ,notify() ,notifyAll(),synchronized 和同步方法锁,对象锁的联系,关系,区别;
一直不明白一个问题,因为在书上关于生产者和消费者的例子里看到一段这样的代码,估计很多人都和我一样迷惑 public synchronized void set(String name, String ...
- PCA最小平方误差理论推导
PCA最小平方误差理论推导 PCA求解其实是寻找最佳投影方向,即多个方向的标准正交基构成一个超平面. 理论思想:在高维空间中,我们实际上是要找到一个d维超平面,使得数据点到这个超平面的距离平方和最小 ...
- bzoj 2850 巧克力王国
bzoj 2850 巧克力王国 钱限题.题面可以看这里. 显然 \(x\) \(y\) 可以看成坐标平面上的两维,蛋糕可以在坐标平面上表示为 \((x,y)\) ,权值为 \(h\) .用 \(kd- ...
- BZOJ2705 SDOI2012 Longge的问题 【欧拉函数】
BZOJ2705 SDOI2012 Longge的问题 Description Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数N,你需要求出∑gcd(i, ...
- Django 打印
转自:http://bbs.chinaunix.net/archiver/tid-1227401.html fentin 发表于 2008-07-28 17:52:44 请教Django Python ...
- JavaScript中字符串截取函数slice()、substring()、substr()
在js中字符截取函数有常用的三个slice().substring().substr()了,下面我来给大家介绍slice().substring().substr()函数在字符截取时的一些用法与区别吧 ...
- PHP 生成类似 SqlServer NEWID() 全局唯一标识符
最近在对接SqlServer中对方有一个字段要求 SqlServer 中 有一个 NEWID() 就是生成36位的 唯一标识符 -> 8CBD3198-297D-4037-A859-B27BB ...
- Appium+python(1)简单的介绍环境搭建
环境搭建其实并不难,只不过安装的东西有点多,要加的环境变量有点多. 链接:https://pan.baidu.com/s/1nwLhNIT 密码:56wn 这个压缩包里要用的都有了,只需要下载,然后安 ...
- ckeditor使用教程
ckeditor 的官方网站是 http://ckeditor.com/ 一.使用方法: 1.在页面<head>中引入ckeditor核心文件ckeditor.js <script ...
- [LeetCode系列]链表环探测问题II
给定一个链表头, 探测其是否有环, 如果没有返回NULL, 如果有返回环开始的位置. 环开始的位置定义为被两个指针指向的位置. 算法描述: 1. 快慢指针遍历, 如果到头说明无环返回NULL, 如果相 ...