C/C++ YUV 文件叠加自定义符号

一、前言

        需要在图片文件上叠加文字,但是要在4M内存开发板上实现,实际内存不足1M,怎么实现?这个问题在网上查找的解决方案都需要使用第三方库文件,下载文字图像库,但是此开发板不能承受住这么大的内存,所以只能另辟蹊径,将图片文件还原为YUV文件,然后在YUV文件中计算分量坐标完成叠加文字的功能,这里就可以将需要的文字以十六进制数组的形式放在头文件中定义编译进程序,来解决内存不足的问题。然后针对YUV文件叠加文字这个问题查找,发现了这篇文章:

最原始的yuv图像叠加文字的实现--手动操作像素_yuv图片如何显示中文-CSDN博客

这篇文章写了YUV文件叠加文字的实现算法,但是这里的图案只有数字以及两个符号,如果需要扩展的话,需要自己添加。然后我就找生成点阵的软件,结果没有找到,只有LED屏点阵数组生成,一个是十六进制数组,一个是二进制点阵。那现在如果想要扩展两个选择:一是自己写程序来扩展图案数组;二是修改文字中的代码,使它可以使用二进制点阵。我选择第二种方式,因为它更简单,并且使内存占用更小。

二、YUV分量计算头文件

根据个人需求修改后的头文件,digital_led.h

#ifndef _DIGITAL_LED_H__
#define _DIGITAL_LED_H__#define BACKGROUND_BLACK 0  //白字,黑色背景
#define BACKGROUND_TRANSPARENT 1  //白字,透明背景
#define BACKGROUND_NULL 2  //半透明,无背景#define COLOR_YUV420p 0
#define COLOR_NV12 1 //420SP,Y平面,UV非平面:YYYYYYYYUVUV --- 没有经过测试,不知道是否存在问题
#define COLOR_YUV422p 2
#define COLOR_YUV444p 3
#define COLOR_Yp 9 //Y平面模型都小于这个值#define TAB_START_CHAR ' ' //字库起始字符
#define TAB_END_CHAR '~'   //字库结束字符static int digital_width = 16;
static int digital_height = 16;
static int gap_width = 0;//这里直接设置为黑背景
int overOSD(int colorformat, unsigned char *yuvOut, const unsigned char *osd, int osdW, int osdH, int yuvW, int yuvH , int x, int y);
//白字,透明背景 
int overOSD1(int colorformat, unsigned char *yuvOut, const unsigned char *osd, int osdW, int osdH, int yuvW, int yuvH , int x, int y);
//直接将Y分量取反,不改变UV分量,达到字体半透明的效果
int overOSD2(int colorformat, unsigned char *yuvOut, const unsigned char *osd, int osdW, int osdH, int yuvW, int yuvH , int x, int y);
//多字符写入
void overinfo(char*info, int colorformat, int overtype, unsigned char *yuvOut ,int yuvW, int yuvH , int x, int y);static const unsigned char tilde_sign[32] = {//~0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00111110, 0b00110000, 0b01110111, 0b01110000, 0b01110011, 0b11110000, 0b01110001, 0b11100000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000
};static const unsigned char* DigitalArray[] = {&space_sign[0], &exclamation_sign[0], &double_quotation[0], &pound_sign[0], &dollar_sign[0], &percent_sign[0], &ampersand_sign[0], &single_quotation[0], &left_bracket[0], &right_bracket[0],&asterisk_sign[0], &plus_sign[0], &omma_sign[0], &mimus_sign[0], &point_sign[0], &diagnal_bar[0], &digital_0[0], &digital_1[0], &digital_2[0], &digital_3[0],&digital_4[0], &digital_5[0], &digital_6[0], &digital_7[0], &digital_8[0], &digital_9[0], &colon_sign[0], &semico_sign[0], &less_than[0], &equal_than[0],&greater_than[0], &question_mark[0], &aite_symbol[0], &caplital_letter_A[0], &caplital_letter_B[0], &caplital_letter_C[0], &caplital_letter_D[0], &caplital_letter_E[0], &caplital_letter_F[0], &caplital_letter_G[0],&caplital_letter_H[0], &caplital_letter_I[0], &caplital_letter_J[0], &caplital_letter_K[0], &caplital_letter_L[0], &caplital_letter_M[0], &caplital_letter_N[0], &caplital_letter_O[0], &caplital_letter_P[0], &caplital_letter_Q[0],&caplital_letter_R[0], &caplital_letter_S[0], &caplital_letter_T[0], &caplital_letter_U[0], &caplital_letter_V[0], &caplital_letter_W[0], &caplital_letter_X[0], &caplital_letter_Y[0], &caplital_letter_Z[0], &square_brackets_l[0],&backslash_sign[0], &square_brackets_r[0], &power_sign[0], &bash_sign[0], &inverted_quotation[0], &small_letter_a[0], &small_letter_b[0], &small_letter_c[0], &small_letter_d[0], &small_letter_e[0],&small_letter_f[0], &small_letter_g[0], &small_letter_h[0], &small_letter_i[0], &small_letter_j[0], &small_letter_k[0], &small_letter_l[0], &small_letter_m[0], &small_letter_n[0], &small_letter_o[0],&small_letter_p[0], &small_letter_q[0], &small_letter_r[0], &small_letter_s[0], &small_letter_t[0], &small_letter_u[0], &small_letter_v[0], &small_letter_w[0], &small_letter_x[0], &small_letter_y[0],&small_letter_z[0], &left_brace[0], &or_sign[0], &right_brace[0], &tilde_sign[0]};#endif

三、YUV分量计算的源文件

根据个人需求修改后的源文件,digital_led.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "digital_led.h"//这里直接设置为黑背景
int overOSD(int colorformat,unsigned char *yuvOut , const unsigned char *osd, int osdW, int osdH, int yuvW, int yuvH , int x, int y)
{if((x + osdW) > yuvW || (y + osdH) > yuvH){printf("pram err\n");return 0;}if(yuvW%2 != 0 || yuvH%2 != 0){printf("[%s%d]pram err\n",__FUNCTION__,__LINE__ );return 0;}    if(colorformat == COLOR_NV12){//NV12//y:int i=0,j=0;for (i = 0; i < osdH * osdW / 8; i++){unsigned char osddata = osd[i];for (j = 0; j < 8; j++){if(((osddata << j) & 0x80) == 0x80){yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = 0xff;}else{yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = 0x00;}}}//uv 这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率yuvOut+=yuvW*yuvH;for(i=0;i<osdH/2;i++){for(j=0;j<osdW;j++){yuvOut[yuvW *(i+y/2) + j+x] = 0x80;}}}else if(colorformat == COLOR_YUV420p){//yuv420p//y: 这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率int i = 0, j = 0;for (i = 0; i < osdH * osdW / 8; i++){unsigned char osddata = osd[i];for (j = 0; j < 8; j++){if(((osddata << j) & 0x80) == 0x80){yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = 0xff;}else{yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = 0x00;}}}//u //这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率//需要保证w和h是偶数yuvOut+=yuvW*yuvH;for(i=0;i<osdH/2;i++){for(j=0;j<osdW/2;j++){yuvOut[yuvW/2 *(i+y/2) + j+x/2] = 0x80;}}//v 这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率yuvOut+=yuvW*yuvH/4;for(i=0;i<osdH/2;i++){for(j=0;j<osdW/2;j++){yuvOut[yuvW/2 *(i+y/2) + j+x/2] = 0x80;}}}return 1;
}//白字,透明背景 
int overOSD1(int colorformat,unsigned char *yuvOut , const unsigned char *osd, int osdW, int osdH, int yuvW, int yuvH , int x, int y)
{if((x + osdW) > yuvW || (y + osdH) > yuvH){printf("pram err\n");return 0;}if(colorformat == COLOR_NV12){//NV12//y:int i=0,j=0;for (i = 0; i < osdH * osdW / 8; i++){unsigned char osddata = osd[i];for (j = 0; j < 8; j++){if(((osddata << j) & 0x80) == 0x80){yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = 0xff;}else{yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = 0x00;}}}//uv 这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率yuvOut += yuvW * yuvH;for (i = 0; i < osdH * osdW / 8; i++)//{unsigned char osddata = osd[i];for (j = 0; j < 8; j++){if(((osddata << j) & 0x80) == 0x80){int index_y = yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x);yuvOut[(index_y / yuvW) / 2 * (yuvW / 2) * 2 + (index_y % yuvW)] = 0x80;//uyuvOut[(index_y / yuvW) / 2 * (yuvW / 2) * 2 + (index_y % yuvW) + 1] = 0x80;//v}}}}else if(colorformat == COLOR_YUV420p){//YUV420p//y:int i=0,j=0;for (i = 0; i < osdH * osdW / 8; i++){unsigned char osddata = osd[i];for (j = 0; j < 8; j++){if(((osddata << j) & 0x80) == 0x80){yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = 0xff;}}}//u 这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率yuvOut += yuvW * yuvH;for (i = 0; i < osdH * osdW / 8; i ++){unsigned char osddata = osd[i];for (j = 1; j < 8; j ++){if(((osddata << j) & 0x80) == 0x80){int index_y = yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x);if((index_y / yuvW) % 2 == 0)yuvOut[(index_y / yuvW) / 2 * (yuvW / 2) + (index_y % yuvW) / 2] = 0x80;}}}//v 这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率yuvOut += yuvW * yuvH / 4;for (i = 0; i < osdH * osdW / 8; i ++){unsigned char osddata = osd[i];for (j = 1; j < 8; j ++){if(((osddata << j) & 0x80) == 0x80){int index_y = yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x);if((index_y / yuvW) % 2 == 0)yuvOut[(index_y / yuvW) / 2 * (yuvW / 2) + (index_y % yuvW) / 2] = 0x80;}}}}return 1;
}//直接将Y分量取反,不改变UV分量
int overOSD2(int colorformat, unsigned char *yuvOut, const unsigned char *osd, int osdW, int osdH, int yuvW, int yuvH , int x, int y)
{if((x + osdW) > yuvW || (y + osdH) > yuvH){printf("pram err\n");return 0;}if(colorformat <= COLOR_Yp){//yint i = 0, j = 0;for (i = 0; i < osdH * osdW / 8; i++){unsigned char osddata = osd[i];for (j = 0; j < 8; j++){if(((osddata << j) & 0x80) == 0x80){yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)] = ~yuvOut[yuvW * (i / (osdW / 8) + y) + (j + (i%(osdW/8)) * 8 + x)];}}}}return 1;
}//多字符写入
void overinfo(char*info,int colorformat,int overtype,unsigned char *yuvOut ,int yuvW, int yuvH , int x, int y)
{int infolen = strlen(info);printf("infolen = %d\n", infolen);int i = 0;int postionX = x;int postionY = y;for(i = 0; i < infolen; i++){if(info[i] == '\n'){postionX = x;postionY += digital_height;}else if(TAB_START_CHAR <= info[i] && info[i] <= TAB_END_CHAR){int ret = 1;if(overtype == BACKGROUND_BLACK){ret = overOSD(colorformat, yuvOut, DigitalArray[info[i] - TAB_START_CHAR], digital_width, digital_height, yuvW, yuvH, postionX, postionY);}else if(overtype == BACKGROUND_TRANSPARENT){ret = overOSD1(colorformat, yuvOut, DigitalArray[info[i] - TAB_START_CHAR], digital_width, digital_height, yuvW, yuvH, postionX, postionY);}else if(overtype == BACKGROUND_NULL){ret = overOSD2(colorformat, yuvOut, DigitalArray[info[i] - TAB_START_CHAR], digital_width, digital_height, yuvW, yuvH, postionX, postionY);}else{printf("error: overtype unknown definition %d\n", overtype);return;}if(ret == 0){//坐标超过,换行写入postionX = x;postionY += digital_height;i --;}else if(ret == 1){postionX += digital_width;}else{//未知返回printf("error: unknown return %d\n", ret);}if((postionY + digital_height) > yuvH){printf("stop drawing at \'%c\'! postionY is greter than yuvH!\n", info[i]);return;}}else{printf("an unknown symbol has been skipped: %c\n", info[i]);}}
}

四、测试说明

看代码,在多字符写入的函数中有判断换行符和溢出,调用测试:

overinfo("1\n234567890\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$^*(){}[] :;%\\|/><.,`qweroiugsdmnxcvzvjsqodasjdfaoiugfkajhOWEYRAHDFAYEWFAJDSLAKWEORYFASDFJALKSDFWQOURTYASGDF", COLOR_YUV420p, BACKGROUND_NULL, buf, w, h, x + 10, y + (digital_height * 2));

原图片和处理后的图片:

      

五、字符图案扩展

这里我使用的是在线软件,地址如下:
点阵生成软件|字模提取(支持中文) - LED、OLED、LCD、单片机在线取模、中文点阵取模软件、在线显示屏取模 - Arduino|STM32|STM8 - 文字或图片点阵生成软件 (zhetao.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/60606.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio 学习——整体框架和概念

一、创建新项目 选择File-->New-->New Project 这里可以随便选&#xff0c;我一般选择Empty Activity&#xff0c;不同的选择&#xff0c;只是界面不同而已。然后静静的等待安装就可以了 二、框架结构 1&#xff09;manifests manifests文件是一个XML文件&#xff0c;…

「JVM详解」

JVM JVM概述 基本介绍 JVM&#xff1a;全称 Java Virtual Machine&#xff0c;即 Java 虚拟机&#xff0c;一种规范&#xff0c;本身是一个虚拟计算机&#xff0c;直接和操作系统进行交互&#xff0c;与硬件不直接交互&#xff0c;而操作系统可以帮我们完成和硬件进行交互的…

Serverless架构在实时数据处理中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Serverless架构在实时数据处理中的应用 Serverless架构在实时数据处理中的应用 Serverless架构在实时数据处理中的应用 引言 Ser…

【Android、IOS、Flutter、鸿蒙、ReactNative 】标题栏

Android 标题栏 参考 Android Studio版本 配置gradle镜像 阿里云 Android使用 android:theme 显示标题栏 添加依赖 dependencies {implementation("androidx.appcompat:appcompat:1.6.1")implementation("com.google.android.material:material:1.9.0")…

pytorch量化训练

训练时量化&#xff08;Quantization-aware Training, QAT&#xff09;是一种在模型训练过程中&#xff0c;通过模拟低精度量化效应来增强模型对量化操作的鲁棒性的技术。与后训练量化不同&#xff0c;QAT 允许模型在训练过程中考虑到量化引入的误差&#xff0c;从而在实际部署…

使用Java绘制图片边框,解决微信小程序map组件中marker与label层级关系问题,label增加外边框后显示不能置与marker上面

今天上线的时候发现系统不同显示好像不一样&#xff0c;苹果手机打开的时候是正常的&#xff0c;但是一旦用安卓手机打开就会出现label不置顶的情况。尝试了很多种办法&#xff0c;也在官方查看了map相关的文档&#xff0c;发现并没有给label设置zIndex的属性&#xff0c;只看到…

Redisson的可重入锁

初始状态&#xff1a; 表示系统或资源在没有线程持有锁的情况下的状态&#xff0c;任何线程都可以尝试获取锁。 线程 1 获得锁&#xff1a; 线程 1 首次获取了锁并进入受保护的代码区域。 线程 1 再次请求锁&#xff1a; 在持有锁的情况下&#xff0c;线程 1 再次请求锁&a…

三周精通FastAPI:37 包含 WSGI - Flask,Django,Pyramid 以及其它

官方文档&#xff1a;https://fastapi.tiangolo.com/zh/advanced/wsgi/ 包含 WSGI - Flask&#xff0c;Django&#xff0c;其它 您可以挂载多个 WSGI 应用&#xff0c;正如您在 Sub Applications - Mounts, Behind a Proxy 中所看到的那样。 为此, 您可以使用 WSGIMiddlewar…

Swagger UI

Swagger UI 是一个开源工具&#xff0c;用于可视化、构建和交互式地探索 RESTful API。 它是 Swagger 生态系统的一部分&#xff0c;Swagger 是一套用于描述、生成、调用和可视化 RESTful Web 服务的工具和规范。 Swagger UI 可以自动生成 API 文档&#xff0c;并提供一个交互…

thinkphp6 --数据库操作 增删改查

一、数据库连接配置 如果是本地测试&#xff0c;它会优先读取 .env 配置&#xff0c;然后再读取 database.php 的配置&#xff1b; 如果禁用了 .env 配置&#xff0c;则会读取数据库连接的默认配置&#xff1a; # .env文件&#xff0c;部署服务器&#xff0c;请禁用我 我们可以…

【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-最大的数

CL13 最大的数(20 分) 输入一个有 n 个无重复元素的整数数组 a&#xff0c;输出数组中最大的数。提示&#xff1a;如使用排序库函数 sort()&#xff0c;需要包含头文件#include 。输入&#xff1a; 第一行是一个正整数 n(2<n<20)&#xff1b; 第二行包含 n 个不重复的整…

让Git走代理

有时候idea提交代码或者从github拉取代码&#xff0c;一直报错超时或者:Recv failure: Connection was reset,下面记录一下怎么让git走代理从而访问到github。 1.打开梯子 2.打开网络和Internet设置 3.设置代理 记住这个地址和端口 4.打开git bash终端 输入以下内容 git c…

vivo 游戏中心包体积优化方案与实践

作者&#xff1a;来自 vivo 互联网大前端团队- Ke Jie 介绍 App 包体积优化的必要性&#xff0c;游戏中心 App 在实际优化过程中的有效措施&#xff0c;包括一些优化建议以及优化思路。 一、包体积优化的必要性 安装包大小与下载转化率的关系大致是成反比的&#xff0c;即安装…

Struts扫盲

Struts扫盲 这里的struts是struts1。以本文记录我的那些复习JavaEE的痛苦并快乐的晚上 Struts是什么 框架的概念想必大家都清楚&#xff0c;框架即“半成品代码”&#xff0c;是为了简化开发而设计的。一个项目有许多分层&#xff0c;拿一个MVC架构的Web应用来说&#xff0c;有…

【AiPPT-注册/登录安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

自动驾驶系列—从数据采集到存储:解密自动驾驶传感器数据采集盒子的关键技术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

【月之暗面kimi-注册/登录安全分析报告】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

时序预测 | 改进图卷积+informer时间序列预测,pytorch架构

时序预测 | 改进图卷积informer时间序列预测&#xff0c;pytorch架构 目录 时序预测 | 改进图卷积informer时间序列预测&#xff0c;pytorch架构预测效果基本介绍参考资料 预测效果 基本介绍 改进图卷积informer时间序列预测代码 CTR-GC卷积,informer&#xff0c;CTR-GC 图卷积…

从入门到精通:一文掌握 Dockerfile 的用法!(多阶段构建与缓存优化)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Dockerfile基础用法 📒📝 什么是 Dockerfile?📝 Dockerfile 的常见指令🔖 构建指令🔖 命令指令🎈 完整示例:构建一个 Python Flask 应用🔖 1. 项目结构🔖 2. 编写 Dockerfile🔖 3. 构建和运行 Docker 镜像�…

Go语言开发基于SQLite数据库实现用户表修改接口(四)

背景 上一章 Go语言开发基于SQLite数据库实现用户表查询详情接口(三) 这一章我们实现用户表的修改接口 代码实现 mapper层 type UserMapper interface {UpdateById(user *model.User, id uint64) error}type userMapper struct { }func (m *userMapper) UpdateById(user *m…