赞
踩
视频相关领域(监控、LED大屏)很多场合可能会涉及到在一幅画面上进行文字或时间的叠加,常规做法都是在后端实现,即先渲染图像,然后叠加OSD文字信息,这种方法简单且高效。但是有些场合必须要求在前端进行叠加,比如监控领域中视频名称信息、时间信息等这些都需要在前端图像编码的时候就已经叠加到图像上,以防止用户修改和验伪。如果你有这种想法,那么这篇文章就算是帮助到你了,算是抛砖引玉的作用吧。
示例中采用jpeg图像作为底图,所以需要借助libjpeg将图像解码还原RGB原始图像数据,叠加OSD文字信息需借助freetype生成点阵RGB图像数据,然后对2个图像进行像素合并或重写。
- // JpegOSD.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
- //
-
- #define _CRT_SECURE_NO_WARNINGS
- #include <iostream>
- #include <fstream>
- #include <string>
- #include <windows.h>
-
- #include "./libjpeg-turbo-win/jpeglib.h"
- #include "./libjpeg-turbo-win/jerror.h"
-
- #ifdef _DEBUG
- #pragma comment(lib, "./x64/debug/libjpeg-turbo-win.lib")
- #else
- #pragma comment(lib, "./x64/release/libjpeg-turbo-win.lib")
- #endif // _DEBUG
-
- #include "ft2build.h"
- #include FT_FREETYPE_H
- #pragma comment(lib, "./freetype/freetype.lib")
-
- // 将图像数据编码为.jpeg图像文件
- bool saveJPEG(const char* filename, const unsigned char* image, int width, int height, int channels, int quality) {
- struct jpeg_compress_struct cinfo;
- struct jpeg_error_mgr jerr;
-
- FILE* file = fopen(filename, "wb");
- if (!file) {
- std::cout << "无法创建文件:" << filename << std::endl;
- return false;
- }
-
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_compress(&cinfo);
- jpeg_stdio_dest(&cinfo, file);
-
- cinfo.image_width = width;
- cinfo.image_height = height;
- cinfo.input_components = channels;
- cinfo.in_color_space = JCS_RGB;
-
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo, quality, TRUE);
-
- jpeg_start_compress(&cinfo, TRUE);
-
- JSAMPROW row_pointer[1];
- while (cinfo.next_scanline < cinfo.image_height) {
- row_pointer[0] = const_cast<JSAMPROW>(&image[cinfo.next_scanline * width * channels]);
- jpeg_write_scanlines(&cinfo, row_pointer, 1);
- }
-
- jpeg_finish_compress(&cinfo);
- fclose(file);
- jpeg_destroy_compress(&cinfo);
-
- return true;
- }
-
- int main()
- {
- const char* filename = "D:/1.jpg";
- FILE* file = fopen(filename, "rb");
- if (!file) {
- std::cout << "打开文件失败: " << filename << std::endl;
- return -1;
- }
-
- unsigned long long dw1 = GetTickCount64();
-
- jpeg_decompress_struct cinfo;
- jpeg_error_mgr jerr;
-
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_decompress(&cinfo);
-
- // 加载图像文件
- jpeg_stdio_src(&cinfo, file);
-
- // 获取图像压缩信息
- jpeg_read_header(&cinfo, TRUE);
- jpeg_start_decompress(&cinfo);
-
- // 获取图像信息
- int width = cinfo.output_width;
- int height = cinfo.output_height;
- int channels = cinfo.output_components;
-
- // 申请图像数据内存
- unsigned char* image = new unsigned char[width * height * channels];
-
- while (cinfo.output_scanline < cinfo.output_height) {
- unsigned char* row = image + (cinfo.output_scanline * width * channels);
- jpeg_read_scanlines(&cinfo, &row, 1);
- }
-
- //加载FreeType字体库
- FT_Library ftLibrary;
- if (FT_Init_FreeType(&ftLibrary)) {
- std::cout << "Failed to initialize FreeType library" << std::endl;
- return -1;
- }
-
- FT_Face ftFace;
- if (FT_New_Face(ftLibrary, "font.ttf", 0, &ftFace)) {
- std::cout << "Failed to load font file" << std::endl;
- FT_Done_FreeType(ftLibrary);
- return -1;
- }
-
- // 设置字符大小
- int fontSize = 48;
- FT_Set_Pixel_Sizes(ftFace, 0, fontSize);
-
- // 叠加OSD字符信息
- const char* osdString = "Hello World!";
-
- // OSD颜色
- unsigned char textColor[4] = { 255, 0, 0, 255 }; // 红色
-
- // OSD叠加位置
- int posX = 100;
- int posY = 200;
-
- // 水印叠加
- for (int i = 0; i < strlen(osdString); ++i) {
- if (FT_Load_Char(ftFace, osdString[i], FT_LOAD_RENDER))
- continue;
-
- FT_GlyphSlot ftGlyph = ftFace->glyph;
-
- int glyphWidth = ftGlyph->bitmap.width;
- int glyphHeight = ftGlyph->bitmap.rows;
- unsigned char* glyphBitmap = ftGlyph->bitmap.buffer;
-
- for (int y = 0; y < glyphHeight; ++y) {
- for (int x = 0; x < glyphWidth; ++x) {
- if (glyphBitmap[y * glyphWidth + x] != 0x00)//透明叠加
- {
- int imgX = posX + ftGlyph->bitmap_left + x;
- int imgY = posY - ftGlyph->bitmap_top + y;
-
- if (imgX >= 0 && imgX < width && imgY >= 0 && imgY < height) {
- int pixelIndex = (imgY * width + imgX) * channels;
-
- for (int c = 0; c < channels; ++c) {
- image[pixelIndex + c] = textColor[c];
- }
- }
- }
- }
- }
-
- posX += ftGlyph->advance.x >> 6; // 移动到下一个字符的位置
- }
-
- unsigned long long dw2 = GetTickCount64();
- std::cout << "水印叠加时间:" << (dw2 - dw1) << "ms" << std::endl;
-
- // 将修改后的图像数据编码为.jpeg图像文件
- const char* outputFilename = "D:/watermarked_image.jpg";
- int quality = 90;
- if (!saveJPEG(outputFilename, image, width, height, channels, quality)) {
- std::cout << "Failed to save JPEG file: " << outputFilename << std::endl;
- }
- else {
- unsigned long long dw3 = GetTickCount64();
- std::cout << "水印图像已保存至:" << outputFilename << ",保存时间:" << (dw3 - dw2) << "ms" << std::endl;
- }
-
- // 释放内存
- delete[] image;
- FT_Done_Face(ftFace);
- FT_Done_FreeType(ftLibrary);
- jpeg_finish_decompress(&cinfo);
- jpeg_destroy_decompress(&cinfo);
- fclose(file);
-
- return 0;
- }
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。