对视频图像进行OSD叠加

    频叠加就是将图片和文字信息叠加到视频信号中,如电视台的LOGO,电影的字幕,电视机的菜单,都是通过视频叠加的形式显示在视频图像中的。区别于用于专业影视编辑行业的字幕叠加器,字符叠加器这一名称现在更加习惯用于各类监控系统中使用的价格相对低廉的具备基本的视频字幕叠加能力的电子设备。 
    字符叠加器按照功能分型可分为 动态字符叠加器 静态字符叠加器  
    动态字符叠加器指与微机或其他智能设备配合,可显示随现场情况变化的字 符信 息,字符信息与现场视频信号相结合,为监控者提供更为详尽准确的信息。 

    静态字符叠加器指只在视频信号上显示相对固定字符信息的设备,主要用于在视频信号上叠加摄像头位置信息。价格低廉是此类静态字符叠加器的特点之一。

    接下来给大家介绍我在hi3516的平台实现的OSD叠加的测试代码,我选择在VI通道进行osd叠加,具体的初始化操作和通道的配置会在下一篇文件会介绍,本文给大家介绍的是通过自己设置一个汉字模或者ASSIC模,然后通过二进制表示,最后读取字模并绘制在相应的图像区域,经过编码后即可显示叠加的内容。

    先来了解一下字库的表示格式  

一般我们使用16*16的点阵宋体字库,所谓16*16,是每一个汉字在纵、横各16点的区域内显示的。不过后来又有了HZK12、HZK24,HZK32和HZK48字库及黑体、楷体和隶书字库。虽然汉字库种类繁多,但都是按照区位的顺序排列的。前一个字节为该汉字的区号,后一个字节为该字的位号。每一个区记录94个汉字,位号则为该字在该区中的位置。
   因此,汉字在汉字库中的具体位置计算公式为: 94*(区号-1)+位号-1 。减1是因为数组是以0为开始而区号位号是以1为开始的。这仅为以汉字为单位该汉字在汉字库中的位置,那么,如何得到以字节为单位得到该汉字在汉字库中的位置呢?只需乘上一个汉字字模占用的字节数即可,即:(94*(区号-1)+位号-1)*一个汉字字模占用字节数,而按每种汉字库的汉字大小不同又会得到不同的结果。
以16*16点阵字库为例,
计算公式则为:(94*(区号-1)+(位号-1))*32。汉字库文该从该位置起的32字节信息即记录了该字的字模信息。
以32*32点阵字库为例,
计算公式则为:(94*(区号-1)+(位号-1))*128。汉字库文该从该位置起的128字节信息即记录了该字的字模信息。

先来看一个32为的字模 自己也可以通过excel绘制一个的一个 “一”
 
对应的二进制代码:
unsigned char ZT[128] =
{
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,

0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,
0x07, 0xFF,0xFF, 0x00,

0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00,
0x00, 0x00,0x00, 0x00
};
取字模
memcpy(p, &ZT, 128); //测试使用

把取出来分字模绘制成对应的位图
static inline void do_blit(char *p, char *origin, unsigned int BitbmpPix_X, int x, int y, int font)
{
char *pixel;
int row, col;
int y_next = y;
int j,k;
if(font == hz_font)
{
for(row = 0; row < 32; row++)
{
pixel = origin + (BitbmpPix_X * 2 * (y_next)) + x;
for(j = 0; j < 4; j++)
{
for(col = 0; col < 8; col++)
{
if(bitmap[row*4+j] &(0x80 >> col))
{
pixel[(col+j*8)*2] = 0x7f;
pixel[(col+j*8)*2+1] = 0xff;
}
else
{
pixel[(col+j*8)*2] = 0x00;
pixel[(col+j*8)*2+1] = 0x00;
}
}
}
y_next++; /* Next line in bitmap */
}
}
else
{
for(row = 0; row < 32; row++)
{
pixel = origin + (BitbmpPix_X * 2 * (y_next)) + x;
for(k = 0;k < 2;k++)
{
for(col = 0; col < 8; col++)
{
if(bitmap[row*2+k] &(0x80 >> col))
{
pixel[(col+k*8)*2] = 0x7f;
pixel[(col+k*8)*2+1] = 0xff;
}
else
{
pixel[(col+k*8)*2] = 0x00;
pixel[(col+k*8)*2+1] = 0x00;
}
}
}
y_next++; /* Next line in bitmap */
}
}
}

<think>我们被要求使用OpenCV在MP4视频文件上叠加OSD(屏幕显示信息)。根据提供的引用,我们可以总结出以下关键点:1.引用[1]展示了在视频固定位置叠加另一个视频或图片的代码示例。它通过读取两个视频,将其中一个视频(girl.mp4)叠加到另一个视频(rabbit.mp4)的指定位置(左上角)。叠加操作使用了`addWeighted`函数,但这里实际上是将第二个视频完全覆盖在第一个视频的指定区域(因为第一个视频的权重为0,第二个视频的权重为1)。2.引用[2]解释了OSD叠加的两种方式:上方叠加和下方叠加。这实际上是指图层的叠加顺序,即OSD信息可以显示在视频的上层(覆盖在视频之上)或下层(但通常OSD都是显示在视频上层的)。3.引用[3]介绍了使用Gstreamer实现视频OSD,但我们的任务是使用OpenCV。因此,我们的目标是在视频的每一帧上绘制一些信息(如文本、图形等),然后将处理后的帧写入新的视频文件或直接显示。步骤:1.读取输入视频文件。 2.对于每一帧,在帧的指定位置绘制OSD信息(例如:文本、矩形、圆形等)。3.将处理后的帧写入输出视频文件(或显示)。 注意:由于我们要处理MP4文件,需要确保OpenCV编译时支持对应的编解码器(如H.264)。如果遇到无法写入MP4的问题,可以尝试先保存为AVI,或者安装额外的编码器(如OpenH264)。下面我们将实现一个示例,在视频的左上角显示当前帧的时间戳和一个自定义的文本信息,并在右上角显示一个矩形框,在视频底部中央显示一条文本。 具体步骤:1.使用`VideoCapture`打开输入视频文件。 2.获取视频的帧率、尺寸等参数。3.创建`VideoWriter`对象用于保存处理后的视频。4.循环读取每一帧。 5.在帧上绘制OSD信息(使用OpenCV的绘图函数,如`putText`,`rectangle`等)。 6.将帧写入输出视频。7.释放资源。代码示例: ```cpp#include <opencv2/opencv.hpp>using namespacecv;int main() {//1.打开视频文件VideoCapture cap("input.mp4");if (!cap.isOpened()) {std::cerr <<"Error: Couldnot openvideo file." <<std::endl;return-1;}//2.获取视频参数doublefps =cap.get(CAP_PROP_FPS);int width= cap.get(CAP_PROP_FRAME_WIDTH);intheight =cap.get(CAP_PROP_FRAME_HEIGHT);//定义输出视频VideoWriter writer("output.mp4", VideoWriter::fourcc('a','v','c','1'), fps, Size(width,height));if(!writer.isOpened()){std::cerr<< "Error:Could notcreate outputvideo."<< std::endl;return -1;}//3.循环处理每一帧Mat frame;while(cap.read(frame)) {//4.在帧上绘制OSD信息//左上角:显示当前帧的时间戳(以秒为单位)doubletimestamp= cap.get(CAP_PROP_POS_MSEC) /1000.0;std::stringtimeText= "Time:" +std::to_string(timestamp) +"s";putText(frame,timeText, Point(20,40),FONT_HERSHEY_SIMPLEX,1,Scalar(0,255,0),2);//右上角:画一个矩形rectangle(frame, Point(width -100,20),Point(width-20,100), Scalar(0,0,255),2);//底部中央:显示自定义文本Stringtext ="OpenCV OSD Example";int baseline=0;SizetextSize= getTextSize(text,FONT_HERSHEY_SIMPLEX,2,2,&baseline);PointtextOrg((width- textSize.width) /2, height-20);putText(frame, text, textOrg,FONT_HERSHEY_SIMPLEX,2, Scalar(255,255,255),2);//5.写入输出视频writer.write(frame);//可选:显示处理后的帧imshow("Frame",frame);if(waitKey(1)==27)//按ESC退出break;}//6.释放资源cap.release();writer.release();destroyAllWindows();return0;} ```注意:-上述代码中,我们使用了`VideoWriter::fourcc('a','v','c','1')`来指定H.264编码。如果您的OpenCV不支持,可以尝试使用`'M','J','P','G'`(Motion-JPEG)或者`'X','V','I','D'`(Xvid)等。另外,保存为MP4时,有时使用`fourcc('m','p','4','v')`也可以。-在绘制文本时,我们使用了`putText`函数,参数包括位置、字体、大小、颜色和线宽等。 -为了在视频底部中央显示文本,我们计算了文本的尺寸,然后调整位置使其居中。 如果需要在视频叠加图片(例如一个Logo),可以参考引用[1]中的方法,使用`addWeighted`或者直接使用ROI区域复制。例如:```cpp//假设我们有一个logo图像(尺寸要合适) Matlogo =imread("logo.png");if (logo.empty()) {//处理错误} //将logo放置在左上角Rectroi_rect =Rect(0,0, logo.cols, logo.rows); Matroi= frame(roi_rect);//将logo复制到roi区域,也可以使用addWeighted进行透明叠加logo.copyTo(roi);``` 如果希望叠加透明图片,可以使用`addWeighted`进行混合。请根据实际需求调整OSD的内容和位置。 相关问题: 1.如何在OpenCV中给视频添加水印?2.OpenCV中如何实现透明图片的叠加? 3.如何将处理后的视频保存为MP4格式?4. OpenCV中如何获取视频的编解码器信息?</think>### 使用OpenCV在MP4视频叠加OSD信息 在OpenCV中实现视频OSD(屏幕显示信息)叠加,核心是通过图像混合技术在视频帧的指定位置绘制文本、图形或图片。以下是详细实现步骤: #### 1. 基础实现流程 ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { // 1. 读取视频文件 VideoCapture cap("input.mp4"); if (!cap.isOpened()) return -1; // 2. 获取视频参数 double fps = cap.get(CAP_PROP_FPS); int width = cap.get(CAP_PROP_FRAME_WIDTH); int height = cap.get(CAP_PROP_FRAME_HEIGHT); // 3. 创建输出视频 VideoWriter out("output.mp4", VideoWriter::fourcc('a','v','c','1'), fps, Size(width, height)); // 4. 逐帧处理 Mat frame; while (cap.read(frame)) { // 5. 添加OSD信息(示例:文本+矩形) putText(frame, "Time: " + std::to_string(cap.get(CAP_PROP_POS_MSEC)/1000) + "s", Point(20, 40), FONT_HERSHEY_SIMPLEX, 1, Scalar(0,255,0), 2); // 绿色文本 rectangle(frame, Rect(50, 60, 200, 80), Scalar(0,0,255), 2); // 红色矩形框 // 6. 写入处理后的帧 out.write(frame); } // 7. 释放资源 cap.release(); out.release(); return 0; } ``` #### 2. 关键操作详解 1. **OSD位置控制**: - 使用`Rect(x, y, width, height)`定义叠加区域 - 坐标原点在左上角,例如`Rect(0,0,100,100)`表示左上角区域 2. **文本叠加**: ```cpp putText(frame, "OSD TEXT", // 文本内容 Point(x, y), // 文本左下角坐标 FONT_HERSHEY_SIMPLEX, // 字体类型 1.0, // 字体大小 Scalar(B,G,R), // 颜色(BGR格式) 2); // 线宽 ``` 3. **图片叠加**(如Logo): ```cpp Mat logo = imread("logo.png"); resize(logo, logo, Size(100, 100)); // 调整大小 Rect roi(10, height-110, 100, 100); // 右下角位置 logo.copyTo(frame(roi)); // 直接复制 ``` 4. **透明叠加**(使用权重混合): ```cpp Mat osdLayer = Mat::zeros(frame.size(), CV_8UC3); // 在osdLayer上绘制OSD内容... addWeighted(frame, 0.7, osdLayer, 0.3, 0, frame); // 70%原帧 + 30%OSD层 ``` #### 3. 高级技巧 - **动态OSD**:基于帧属性动态生成内容 ```cpp double timestamp = cap.get(CAP_PROP_POS_MSEC) / 1000.0; std::string dynamicText = "Frame: " + std::to_string(cap.get(CAP_PROP_POS_FRAMES)); ``` - **抗闪烁处理**:使用双缓冲机制避免OSD闪烁 - **性能优化**: - 预生成静态OSD元素 - 使用GPU加速(`cv::cuda`模块) - 限制高频率更新操作 #### 4. 注意事项 1. 编解码器支持:确保OpenCV编译时包含H.264支持(`VideoWriter::fourcc('a','v','c','1')`) 2. 坐标系统:OpenCV使用**左上角原点**坐标系 3. 透明度处理:纯复制会覆盖原内容,需用`addWeighted`实现透明效果 4. 文字渲染:中文字符需使用FreeType扩展(`cv::freetype::createFreeType2()`) > 引用说明:OSD叠加原理是通过在视频帧指定区域混合图像实现的[^2],OpenCV的`addWeighted()`和ROI操作是关键技术[^1],类似Gstreamer的OSD实现原理但使用不同工具链[^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值