在YUV图像上根据背景色实现OSD反色

所谓的OSD其实就是在视频图像上叠加一些字符信息,比如时间,地点,通道号等, 
在图像上叠加OSD通常有两种方式: 一种是在前端嵌入式设备上,在图像数据上叠加OSD, 
这样客户端这边只需解码显示数据即可。另一种是PC客户端在接收到前端设备图像,解码之后,进行叠加。这两种都是比较常见的方式。

OSD具有字符型(Font-Based)和位图型(Bit-Map)两种类型。

字符型OSD:为了节约显示缓存,早期及低成本的解决方案中使用字符型OSD发生器,其原理是将OSD中显示内容按照特定的格式(12×18、12×16等)进行分割成块,例如数字0-9、字母a-z、常用的亮度、对比度符号等,并把这些内容固化在ROM或Flash中,在显示缓存中仅存放对应的索引号,这样的“字典”结构可以大幅度减少显示缓存的需求。

位图OSD:通过对最终显示内容上特定区域的每个像素点进行改变,直接将OSD信息叠加到最终的显示画面上,其按像素进行控制的方式可以保证具有多色及足够的表现能力。 
最近做一个网络播放器, 有在播放器实时叠加OSD这个需求,正好借这个机会研究了一下位

最近做一个网络播放器, 有在播放器实时叠加OSD这个需求,正好借这个机会研究了一下。

先说下大体流程,

  1. 首先,播放SDK,通过网络模块接收前端视频流(经过压缩的数据),然后进行解压,得到一帧完整的YUV图像,
  2. 然后,我们在内存中创建一个设备无关的位图,并指定图像数据背景色为白色,字体为黑色。通过DrawTextW将字体画到内存DC上,
  3. 之后,通过GetDIBit将位图的二进制位复制到与设备无关的位图buffer里, 然后扫描此位图的每一个像素点,判断每个像素点的R,G,B三个分量之和 ,如果大于384 设置该像素为RGB(255,255,255), 否则设置为RGB(0,0,0),(384表示灰度)
  4. 然后根据图像的宽高,创建一个通明通道数组,通过遍历之前得到的设备无关位图buffer,获取每个像素点的R分量,如果R等于0,则设置通明通道数组中对应的值为1, 表示该像素点上需要绘制字体(换句话说,该像素点不是透明色) 
    这样我们就记住了临时图像上OSD文字每个像素的位置。
  5. 接下来,我们将构造出来的bmp位图数据进行转换,转换成YUV420数据,存储在 pOSDYuvBuffer中
  6. 下面这一步,就是最主要的地方, 即计算OSD反色的算法,

    我们遍历透明通道数组, 若值等于1, 则说明该像素点是字体,需要绘制, 那么,我们就在源图像(解码后的YUV图像)上找到位置想对应的点。并以该点为中心,计算出一个13*13的矩形区域,此区域作为背景参考区, 遍历该矩形区域 并计算该区域的 Y分量平均值,如果平均值大于128 说明该背景区是亮色,那么,我们设置pOSDYuvBuffer相应像素点的Y分量为1(背景亮,则osd字体为黑色,反之,若背景区为暗色,则设置osd字体像素点的Y为255)

  7. 这样扫描结束之后, 就实现了 pOSDYuvBuffer中的OSD字体颜色,根据背景色的反色。然后将我们构造出来的临时图像 叠加到源图像上即可。
  8. 至于叠加操作,其实很简单。 同样扫描通明通道数据,如果发现不是透明色,直接将pOSDYuvBuffer中的YUV复制到 源图像相应位置即可。

下面是流程:

反色计算算法 图

int posAx=0, posAy=0;
int posDx=0 ,posDy=0;
int nBKColor = 0;
for(i = 0; i < m_OSDHeigth; i++)
{
    for(j = 0; j < m_OSDWidth; j++)
    {
        if(  j+_dwStrPosX>=_VideoWidth || i+_dwStrPosY>=_VideoHeight )
            continue;
    // 找到字符 X
        if( m_palpha[i*m_OSDWidth + j] )// 找到x映射在在源图像上的13*13 背景块
        {
            posAx = _dwStrPosX+j - 3;   // 计算背景色块 A的坐标
            if ( posAx<0 )
                posAx=0;

            posAy = _dwStrPosY+i -3;
            if ( posAy<0 )
                posAy =0;

            posDx =  _dwStrPosX+j + 3;      // 计算背景色块 点D的坐标
            if ( posDx >_VideoWidth)
                posDx =_VideoWidth;

            posDy = _dwStrPosY+i +3;
            if ( posDy >_VideoHeight)
                posDy =_VideoHeight;

            // / 计算背景色块 长,宽
            int height_ = posDy-posAy;
            int width_  = posDx-posAx;

            UINT16* pTempDesY = (UINT16*)dest_buf + posAx + posAy*_VideoWidth;
                int nTemp = 0;
                for ( m=0; m< height_; m++ )//height
                {
                    for ( n=0; n<width_;n++ )
                    {
                        nTemp += ( (*pTempDesY) >>2 );
                        pTempDesY++;
                    }
                    pTempDesY = (UINT16*)dest_buf + posAx + posAy*_VideoWidth + m*_VideoWidth;
                }

                // 计算Y 的平均值
                nBKColor = nTemp/(height_*width_);
                if ( nBKColor >128 )
                    *pY = 0;
                else
                    *pY = ( 255<<2);
            }

            pDesY++;
                pY++;
        }//j
        pDesY = ((UINT16*)dest_buf + _dwStrPosX + _dwStrPosY*_VideoWidth) + i*_VideoWidth;
}//i

效果图:


扫描下方二维码。关注 【音视频开发训练营】

from:http://blog.csdn.net/machh/article/details/52840886

时间: 2025-01-16 01:39:11

在YUV图像上根据背景色实现OSD反色的相关文章

关于opencv在单通道图像上画矩形框

问题描述 关于opencv在单通道图像上画矩形框 rectangle(img r.tl() r.br()Scalar(00255)-180);这个函数是画3通道的矩形框,但是我的图像是红外图像,网上说是单通道的,不知道怎么在红外图像上画矩形框?求大神解答,灰常感谢!!

在DirectShow的视频图像上叠加线条和文字

在DirectShow的视频图像上叠加线条和文字 最近一直在从事工业测量方面的开发工作,难免会用到各种各样的相机,其中支持DX的USB相机开发起来比较方便,由于工作需要经常要在视频图像上叠加线条和文字,图1便是我最近一段时间写的一套工业检测系统,图像是从USB相机中实时获取的.看到网上有些帖子也在讨论这个问题,现在给出我的一个非常简单的思路并附上源代码(vc++6.0编译通过,需要连接USB相机,可用普通摄像头来代替.地址:http://xiaolang86.download.csdn.net/

代码-去除图像上的红色印章

问题描述 去除图像上的红色印章 请大神指教怎样编写一段可以去除图像上的红色印章的C#代码.因为有些图像上会盖有各种各样红色的印章,甚至有的还盖在了内容上.我需要将这些印章过滤掉,并且不损害内容,请问应该怎么编码? 解决方案 google opencv inpaint algorithm 解决方案二: 参考:http://docs.opencv.org/trunk/doc/py_tutorials/py_photo/py_inpainting/py_inpainting.html

图像处理-VC进行图像反色为什么只能处理部分区域。。(望指正代码哪里出错了)

问题描述 VC进行图像反色为什么只能处理部分区域..(望指正代码哪里出错了) 以下是OnPaint函数和反色函数的代码,结果如下图所示图片能完整的显示但只能对部分进行反色操作. void CPictureView::OnPaint() { CPaintDC dc(this); // device context for painting CRect rect; GetClientRect(rect); //得到文档指针 CPictureDoc* pDoc = GetDocument(); ASS

ios-iOS使用mapkit获取mapView上某一点的坐标反地理编码出来的地址不正确

问题描述 iOS使用mapkit获取mapView上某一点的坐标反地理编码出来的地址不正确 iOS使用mapkit获取mapView上某一点的坐标反地理编码出来的地址不正确.我在网上查了半天没有查到解决办法. 解决方案 你调用mapkit用的是哪个经纬度?你上面有3个经纬度. 解决方案二: 经纬度都对不上,应该是取错点了吧

RGB与YUV图像视频格式的相互转换

显示器图像显示概述: 我们知道普通彩色CRT显示器内部有三支电子枪,电子 枪去激活显示器屏幕的荧光粉,三种荧光粉发射出的光生成一个像素位置的颜色点,这就 是我们人眼能看到的一个像素.每个像素对应红.绿.蓝(R.G.B)三个强度等级,每 个像素占用24位,可以显示近1700 万种颜色,这就是我们所说的真彩色. 普通彩 色CRT显示器是基于电视技术的光栅扫描,电子束一次扫描一行,从顶到底依次扫描,整 个屏幕扫描一次(我们称它为1帧),电子束扫描完一帧后回到最初位置进行下一次扫描 . 电视图像显示概述

【转载】使用SDL播放YUV图像数据(转)

SDL提供了针对YUV格式数据的直接写屏操作.废话不多说,直接上代码吧 /** * file showyuv.c * author: rare * date: 2009/12/06 * email: dux003#163.com */ #include <stdlib.h>#include "SDL.h" int main(int argc , char* argv[]){    int i = 1;    int x, y;    int w = 176;    int

在java中如何获取在画布加载的图像上用鼠标点击的字符(图像上有很多字符)

问题描述 例如,我把一个小地图图像加载在canvas画布上,我鼠标点击小地图图像,怎么获取鼠标点击的地点(比如我点击的是火车站),java怎么知道我点的就是火车站,而不是其他地点的 解决方案 解决方案二:思路,给画布加入鼠标监听器addMouseListener,addMouseMotionListener,监听方法里event.getPoint()返回鼠标位置你的火车站,最起码要保留x,ywidthheight4个数据,可以建成Rectangle或其它形状(Shape的子类)Rectangle

ASP.NET treeview节点鼠标移上去时背景色变化问题

问题描述 属性设置中HoverStyle="background:#6079d2;color:#ffffff",SelectedStyle="background:#6079d2;color:#ffffff"节点选中时和鼠标移到节点时背景色设置成同样颜色,但实际效果中鼠标移上去的颜色要淡很多,SelectedStyle中#6079d2这个颜色生效了,色彩是对的,但是HoverStyle的颜色始终要淡,达不到我设置的颜色,求高人解答!!!!!!!! 解决方案 解决方案