位置:首页 > C > C/C++ YUV文件叠加自定义符号与内存优化方案

C/C++ YUV文件叠加自定义符号与内存优化方案

时间:2026-08-27  |  作者:穿越地图的猫  |  阅读:0

目录

  1. 一、前言
  2. 二、YUV分量计算头文件
  3. 三、YUV分量计算的源文件
  4. 四、测试说明
  5. 五、字符图案扩展
一、前言 对应的技术说明图
一、前言概括一、前言的核心概念、关键要点与实践提示。

前言

在4M内存的开发板中实现图片文字叠加极具挑战,因可用内存不足1M,常规依赖第三方库的方案难以胜任。为此,通过将图片还原为YUV文件并计算分量坐标完成文字叠加,将文字以十六进制数组形式编译进程序,有效解决内存瓶颈。本文基于CSDN算法,针对现有工具仅支持LED点阵数组的局限,通过修改算法支持二进制点阵,实现更简单且内存占用更小的扩展方案,为嵌入式设备提供高效叠加策略。

C/C++ YUV文件叠加自定义符号与内存优化方案 的核心流程信息图
C/C++ YUV文件叠加自定义符号与内存优用简体中文信息图概括C/C++ YUV文件叠加自定义符号与内存优的核心流程、关键规则与实践要点。

一、前言

在4M内存的开发板上实现图片文件文字叠加面临严峻挑战,实际可用内存不足1M。常规方案依赖第三方库,但内存无法承受。为此,采用将图片还原为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], £_sign[0], &dollar_sign[0], &percent_sign[0], &ersand_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
#include
#include
#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 errn");
        return 0;
    }
    if(yuvW%2 != 0 || yuvH%2 != 0){
        printf("[%s%d]pram errn",__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;i2;i++){
            for(j=0;j2) + 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;i2;i++){
            for(j=0;j2;j++){
                yuvOut[yuvW/2 *(i+y/2) + j+x/2] = 0x80;
            }
        }
        //v 这里的坐标计算尽量保持原本的意思,看起来比较冗余,可以简化提供效率
        yuvOut+=yuvW*yuvH/4;
        for(i=0;i2;i++){
            for(j=0;j2;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 errn");
        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;//u
                    yuvOut[(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 errn");
        return 0;
    }

    if(colorformat <= COLOR_Yp){
        //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)] = ~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 = %dn", 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 %dn", overtype);
                return;
            }
            if(ret == 0){//坐标超过,换行写入
                postionX = x;
                postionY += digital_height;
                i --;
            }else if(ret == 1){
                postionX += digital_width;
            }else{//未知返回
                printf("error: unknown return %dn", 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: %cn", info[i]);
        }
    }
}

四、测试说明

代码中多字符写入函数包含判断换行符和溢出逻辑,调用测试如下:

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

原图片和处理后的图片对比展示如下:

五、字符图案扩展

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

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多