YUV图像合成原理[通俗易懂]

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

大家好,又见面了,我是你们的朋友全栈君。

YUV图像合成原理

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

YUV图像合成原理[通俗易懂]

一、原图图像格式

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

YUV图像合成原理[通俗易懂]

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

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

YUV图像合成原理[通俗易懂]

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

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

用表格数据得出来就是

YUV图像合成原理[通俗易懂]

二、图像合成过程

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

YUV图像合成原理[通俗易懂]

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

1、采样

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

YUV图像合成原理[通俗易懂]

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 = 0; 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 || 0 == w || 0 == 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[0] = src->y;
	pictsrc.data[1] = src->u;
	pictsrc.data[2] = src->v;
	pictsrc.linesize[0] = src->w;
	pictsrc.linesize[1] = src->w / 2;
	pictsrc.linesize[2] = src->w / 2;

	pictdst.data[0] = dst->y;
	pictdst.data[1] = dst->u;
	pictdst.data[2] = dst->v;
	pictdst.linesize[0] = dst->w;
	pictdst.linesize[1] = dst->w / 2;
	pictdst.linesize[2] = dst->w / 2;

	sws_scale(pSws, pictsrc.data, pictsrc.linesize, 0, 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 = 0, nOffx = 0,w=0,h=0;
	SwsContext *pSws = NULL;
	YUV_IMAGE *pImgSmall = NULL;

	if (NULL == src || NULL == dst || nOrder < 0 || nOrder >= MAX_CHANNEL_CNT)
	{
		return;
	}
	//根据位置找出图像Y偏移量
	switch (nOrder)
	{
		case 0:
			nOffx = 0;
			nOffY = 0;
			w = dst->w * 2 / 3;
			h = dst->h * 2 / 3;
			pImgSmall = &m_mainMerge;
			break;
		case 1:
			nOffx = dst->w * 2 / 3;
			nOffY = 0;
			w = dst->w  / 3;
			h = dst->h / 3;
			pImgSmall = &m_subMerge;
			break;
		case 2:
			nOffx = dst->w * 2 / 3;
			nOffY = dst->h / 3;
			w = dst->w / 3;
			h = dst->h / 3;
			pImgSmall = &m_subMerge;
			break;
		case 3:
			nOffx = dst->w * 2 / 3;
			nOffY = dst->h * 2 / 3;
			w = dst->w / 3;
			h = dst->h / 3;
			pImgSmall = &m_subMerge;
			break;
		case 4:
			nOffx = dst->w / 3;
			nOffY = dst->h * 2 / 3;
			w = dst->w / 3;
			h = dst->h / 3;
			pImgSmall = &m_subMerge;
			break;
		case 5:
			nOffx = 0;
			nOffY = dst->h * 2 / 3;
			w = dst->w / 3;
			h = dst->h / 3;
			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 = 0;
	for (int i = 0; i < src->h; i++)
	{
		nOff = dst->w*(nOffY + i) + nOffX;
		//逐行拷贝
		memcpy(dst->y + nOff, src->y + src->w*i, src->w);
	}
	UINT nUVOffX = nOffX / 2, nUVOffY = nOffY / 2;
	UINT nUVSrcW = src->w / 2, nUVSrcH = src->h / 2;
	UINT nUVDstW = dst->w / 2, nUVDstH = dst->h / 2;

	for (int j = 0; 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);
	}
}

引用文献:

【1】http://baike.baidu.com/view/189685.htm?fr=aladdin

【2】http://blog.csdn.net/searchsun/article/details/2443867

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/160261.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Go语言生成二维码是如此简单

    Go语言生成二维码是如此简单

  • 图像检索评价指标:PR曲线的计算与绘制

    图像检索评价指标:PR曲线的计算与绘制#@filename:test2.py#@brief:如何绘制PR曲线#@author:liupc#@date:2021/8/2importnumpyasnpfromtqdmimporttqdmimportmatplotlib.pyplotasplt#计算汉明距离。有几位不同,距离就为几。defCalcHammingDist(B1,B2):q=B2.shape[1]distH=.

  • 四旋翼飞行器的飞控实现「建议收藏」

    尝试制作这个四旋翼飞控的过程,感触颇多,整理了思绪之后,把重要的点一一记下来;这个飞控是基于STM32,整合了MPU6050,即陀螺仪和重力加速计,但没有融合电子罗盘; 另外,四旋翼飞行器的运动方式请百度百科,不太复杂,具体不再赘述; 这是飞控程序的控制流程(一个执行周期):   比较重要的地方:1.i2c通信方式;

  • StrictMode 详解「建议收藏」

    StrictMode 详解「建议收藏」StrictMode类是Android2.3(API9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题。比如,如果你在UI线程中进行了网络或者磁盘操作,StrictMode就会通过Log(logcat)或者对话框的方式把信息提示给你,因为让你的UI线程处理这里操作会被认为是不规范的做法,可能会让你的应用变得比较卡顿。官网文档:http://developer.an

  • potplayer怎么设置清晰度_potplayer输出分辨率设置

    potplayer怎么设置清晰度_potplayer输出分辨率设置今天因为要看视频,所以用到了播放器,之前就下载过potplayer和kmplayer这两个播放器,一开始发现kmp的清晰度明显高于potplayer播放器,但随之发现kmplayer有几个缺点①在使用倍速播放的过程中有频繁的卡顿现象②设置较为繁琐③网上的教程不多,维护的人少(这两个软件作者相同,potplayer是作者跳槽之后开发的版本)。 所以还是准备使用potplayer,…

  • Tomcat:第二章:Tomcat日志文件分析

    Tomcat:第二章:Tomcat日志文件分析Tomcat下载地址:https://tomcat.apache.org/download-80.cgitomcat源码下载:tomcat目录结构:打开Tomcat的日志目录,也就是Tomcat安装目录下的logs目录。Tomcat的日志信息分为两类:一是运行日志,它主要记录运行过程中的一些信息,尤其是一些异常错误日志信息; 二是访问日志,它记录访问的时间、IP地址、访问的路径等相关信息。日志文件类型分析:catalina.***.log:主

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号