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;而操作系统可以帮我们完成和硬件进行交互的…

两种鼠标hover切换对应图片方法对比

方法一&#xff1a;鼠标hover时使用JS给元素添加类名达到切换图片效果 <!-- hover元素 --> <div class"hover-div"><ul><li class"hover-div-item" data-index"1">当鼠标hover我切换对应的图片1</li><li class…

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

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

day14|static关键字和const关键字的作用、常量指针和指针常量之间有什么区别、结构体和类之间有什么区别

day14|C重难点之 static关键字和const关键字的作用、常量指针和指针常量之间有什么区别、结构体和类之间有什么区别 37.static关键字和const关键字的作用38.常量指针和指针常量之间有什么区别39.结构体和类之间有什么区别 37.static关键字和const关键字的作用 1. static 关键字…

【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;从而在实际部署…

docker--工作目录迁移

前言 安装docker&#xff0c;默认的情况容器的默认存储路径会存储系统盘的 /var/lib/docker 目录下&#xff0c;系统盘一般默认 50G&#xff0c;容器输出的所有的日志&#xff0c;文件&#xff0c;镜像&#xff0c;都会存在这个地方&#xff0c;时间久了就会占满系统盘。 一、…

开发效率工具链全解析

&#x1f6e0; 开发效率工具链全解析&#xff1a;从入门到精通 在现代前端开发中&#xff0c;高效的工具链对于提升开发效率至关重要。本文将全方位剖析项目脚手架、包管理工具以及构建工具的深度集成与实战应用。 &#x1f4d1; 内容导航 工具链概述项目脚手架包管理工具常见…

[ 网络安全介绍 3 ] 网络安全事件相关案例有哪些?

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

【Unity基础】Unity中碰撞及触发类物理交互应用场景说明

一、碰撞类回调方法 在Unity中&#xff0c;碰撞类回调方法是用于处理物体间碰撞的逻辑。这些方法常用于 MonoBehaviour 脚本中&#xff0c;以便在物体发生碰撞时进行响应。以下是最常用的三个碰撞类回调方法的详细说明&#xff1a; 1. OnCollisionEnter(Collision collision)…

【MySQL】MySQL中的函数之REGEXP_SUBSTR

在 MySQL 中&#xff0c;REGEXP_SUBSTR() 函数用于从字符串中提取与正则表达式匹配的子串。这个函数也是从 MySQL 8.0 开始引入的。下面是一些关于如何使用 REGEXP_SUBSTR() 的详细说明和示例。 基本语法 REGEXP_SUBSTR(str, pat [, position [, occurrence [, match_type ]]…

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

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

arm64架构的linux 配置vm_page_prot方式

在 ARM64 架构上&#xff0c;通过 vm_page_prot 属性可以修改 UIO 映射内存的访问权限及缓存策略&#xff0c;常见的有非缓存&#xff08;Non-cached&#xff09;、写合并&#xff08;Write Combine&#xff09;等。下面是 ARM64 常用的 vm_page_prot 设置及其对应的操作方式。…

Redisson的可重入锁

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

探秘Spring Boot中的@Conditional注解

文章目录 1. 什么是Conditional注解&#xff1f;2. 为什么需要Conditional注解&#xff1f;3. 如何使用Conditional注解&#xff1f;4. Conditional注解的高级用法5. 注意事项6. 结语推荐阅读文章 在Spring Boot的世界里&#xff0c;配置的灵活性和多样性是至关重要的。有时候&…

三周精通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;请禁用我 我们可以…

WPF中MVVM工具包 CommunityToolkit.Mvvm

CommunityToolkit.Mvvm&#xff0c;也称为MVVM工具包&#xff0c;是Microsoft Community Toolkit的一部分。它是一个轻量级但功能强大的MVVM&#xff08;Model-View-ViewModel&#xff09;库&#xff0c;旨在帮助开发者更容易地实现MVVM设计模式。 特点 独立于平台和运行时&a…