zoukankan      html  css  js  c++  java
  • RGB打水印在YUV图片上

    一、 概述

      将RGB图片打在YUV上需要注意的是, 字体之外应该透明, 否则背景也会被覆盖不好看,  所以RGB必须有透明度,  本测试格式为BMP ARGB8888(也即B是最低字节, A是最高字节),

    YUV格式为NV21(YUV420SP), 第一步是将ARGB转换成NV21, 然后小NV21打在打NV21上即可, 最后保存图片, 用7yuv.exe软件查看!

    二、 示例代码

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
    #include <stdlib.h>
    #include <linux/types.h>
    #include <linux/videodev2.h>
    #include <malloc.h>
    #include <math.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <assert.h>
    
    #define MAX_ICON_NUM    42
    
    #define SAVE_IMG_PATH    "./save_1280x720_nv21.yuv"
    #define WATERMARK_BMP_DIR    "./watermark"
    
    struct wm_icon
    {
        int id;
        int width;
        int height;
        unsigned char *y;
        unsigned char *c;
        unsigned char *alph;
    };
    
    struct wm_icon icon_arry[MAX_ICON_NUM];
    
    struct disp_content{
        int x;
        int y;
    #define MAX_CONTENT_LEN   32
        char content[MAX_CONTENT_LEN];
    };
    
    
    struct backgroud_info
    {
        int width;
        int height;
        unsigned char *y;
        unsigned char *c;
    };
    
    static void argb8888toyuv420sp(unsigned char *src_p, int width, int height, unsigned char *dest_y, unsigned char *dest_c, unsigned char *dest_alph)
    {
        int i,j;
    
        for(i = 0; i < (int)height; i++) {
            if((i&1) == 0) {
                for(j= 0; j< (int)width; j++) {
                    *dest_y = (299*src_p[2]+587*src_p[1]+114*src_p[0])/1000;
    
                    if((j&1) == 0) 
                        *dest_c++ = 128+(564*(src_p[0]-*dest_y)/1000); //cb
    
                    else
                        *dest_c++ = 128+(713*(src_p[2]-*dest_y)/1000); //cr
            
    
                    *dest_alph++ = src_p[3];
                    src_p +=4;
                    dest_y++;
                }
            } else {
                for(j= 0; j< (int)width; j++) {
                    *dest_y = (299*src_p[2]+587*src_p[1]+114*src_p[0])/1000;
                    *dest_alph++ = src_p[3];
                    src_p +=4;
                    dest_y++;
                }
            }
        }
    
        return;
    }
    
    int init_watermark()
    {
        FILE *fp = NULL;
        int i;
        int pic_width = 0, pic_height = 0;
        char filename[256];
        unsigned char *argb_buf = NULL;
    
    
        for(i = 0; i < MAX_ICON_NUM; i++) {
            sprintf(filename, "%s/argb8888_24x32_%d.bmp", WATERMARK_BMP_DIR, i);
            printf("open file %s
    ", filename);
            fp = fopen(filename, "r");
            if(NULL == fp) {
                printf("Fail to open file %s(%s)!", filename, strerror(errno));
                goto OPEN_FILE_ERR;
            }
            icon_arry[i].id = i;
    
            /* BMP格式解析说明: https://www.cnblogs.com/vedic/p/13608566.html */
            fseek(fp, 18, SEEK_SET);
            fread(&pic_width, 1, 4, fp);
            fread(&pic_height, 1, 4, fp);
            icon_arry[i].width = abs(pic_width);
            icon_arry[i].height = abs(pic_height);
            printf("real width=%d(%d), height=%d(%d)
    ", icon_arry[i].width, pic_width, icon_arry[i].height, pic_height);
    
            icon_arry[i].y = (unsigned char*)malloc(icon_arry[i].width * icon_arry[i].height*5/2);
            if(NULL == icon_arry[i].y) {
                printf("malloc picture yuv fail !
    ");
                fclose(fp);
                goto ALLOC_Y_ERR;
            }
            memset(icon_arry[i].y, 0xff, (icon_arry[i].width * icon_arry[i].height*5/2));
            icon_arry[i].alph = icon_arry[i].y + icon_arry[i].width * icon_arry[i].height;
            icon_arry[i].c = icon_arry[i].alph + icon_arry[i].width * icon_arry[i].height;
    
            argb_buf = (unsigned char *)malloc(icon_arry[i].width * icon_arry[i].height * 4);
            if (NULL == argb_buf) {
                printf("malloc bmp buf fail !
    ");
                fclose(fp);
                goto ALLOC_BUF_ERR;
            }
    
            fseek(fp, 54, SEEK_SET);
            fread(argb_buf, icon_arry[i].width * icon_arry[i].height * 4, 1, fp);
    
            argb8888toyuv420sp(argb_buf, icon_arry[i].width, icon_arry[i].height,
                       icon_arry[i].y, icon_arry[i].c, icon_arry[i].alph);
    
            fclose(fp);
            free(argb_buf);
        }
    
        return 0;
    
    ALLOC_BUF_ERR:
    ALLOC_Y_ERR:
    OPEN_FILE_ERR:
    
        for(i = 0; i < MAX_ICON_NUM; ++i) {
            if(icon_arry[i].y != NULL) {
                free(icon_arry[i].y);
                icon_arry[i].y = NULL;
            }
        }
        return -1;
    }
    
    static int get_word_num(char val)
    {
        int i;
        int cnt = 0;
    
        for(i=7; i>=0; i--) {
            if(!(val & (1<<i)))
                break;
            else
                cnt++;
        }
    
        if (cnt == 0)
            return 1;
    
        return cnt;
    }
    
    #define DEFAULT_NUM    36
    static int get_word_index(char *data, int wordlen)
    {
        int index = DEFAULT_NUM;
    
        if(wordlen == 1) {
            if (*data >= '0' && *data <= '9') {
                printf("choose numben 0 ~ 9
    ");
                index = (*data - 48); //落在数组下标0开始
            } else if (*data >= 'A' && *data <= 'Z') {
                printf("choose A~Z
    ");
                index = (*data - 55); //落在数组下标10开始
            } else if (*data == ' ') {
                printf("choose kong
    ");
                index = 36;
            } else if (*data == '-') {
                printf("choose -
    ");
                index = 37;
            } else if (*data == '.') {
                printf("choose .
    ");
                index = DEFAULT_NUM;
            } else if (*data == ':') {
                printf("choose eng :
    ");
                index = 38;
            } else if (*data == '/') {
                printf("choose /
    ");
                index = 39;
            } else {
                printf("not match %s , use DEFAULT_NUM
    ", data);
                index = DEFAULT_NUM;
            }
        } else if (wordlen == 3) {
            if(!memcmp(data, "", 3)) {
                printf("choose %s
    ", "");
                index = 40;
            } else if (!memcmp(data, "", 3)) {
                printf("choose %s
    ", "");
                index = 41;
            } else if (!memcmp(data, "", 3)) {
                printf("choose chn :
    ");
                index = 38;
            } else {
                printf("choose DEFAULT_NUM
    ");
                index = DEFAULT_NUM;
            }
        }
    
        printf("	wordlen -> %d, index -> %d
    ", wordlen, index);
        return index;
    }
    
    void yuv420sp_blending(unsigned char *bg_y, unsigned char *bg_c, int bg_width, int bg_height, int left, unsigned int top, int fg_width, int fg_height,
                           unsigned char *fg_y, unsigned char *fg_c, unsigned char *alph)
    {
        unsigned char *bg_y_p = NULL;
        unsigned char *bg_c_p = NULL;
        int i = 0;
        int j = 0;
    
        bg_y_p = bg_y + top * bg_width + left;
        bg_c_p = bg_c + (top >>1)*bg_width + left;
    
        for(i = 0; i<(int)fg_height; i++) {
            if((i&1) == 0) {
                for(j=0; j< (int)fg_width; j++) {
                    *bg_y_p = ((256 - *alph)*(*bg_y_p) + (*fg_y++)*(*alph))>>8;
                    *bg_c_p = ((256 - *alph)*(*bg_c_p) + (*fg_c++)*(*alph))>>8;
    
                    alph++;
                    bg_y_p++;
                    bg_c_p++;
                }
                bg_c_p = bg_c_p + bg_width - fg_width;
            } else {
                for(j=0; j< (int)fg_width; j++) {
                    *bg_y_p = ((256 - *alph)*(*bg_y_p) + (*fg_y++)*(*alph))>>8;
                    alph++;
                    bg_y_p++;
                }
            }
                bg_y_p = bg_y_p + bg_width - fg_width;
        }
    }
    
    int save_image(void *addr, int length)
    {
        FILE *fp = fopen(SAVE_IMG_PATH, "w");
      
        if(fp == NULL) {
            printf("Fail to fopen %s
    ", SAVE_IMG_PATH);
            exit(-1);
        }
        fwrite(addr, length, 1, fp);
        fflush(fp);
        usleep(500);
        fclose(fp);
        return 0;
    }
    
    int push_wm2bg(unsigned char *bg_yuv_buf, int bg_width, int bg_height, struct disp_content *single_wm)
    {
        int pos_x;
        struct backgroud_info bg_info;
        int buflen, wordlen;
        char *p_content;
        int pic_index;
        int i;
    
    
        bg_info.width = bg_width;
        bg_info.height = bg_height;
        bg_info.y = bg_yuv_buf;
        bg_info.c = bg_yuv_buf + bg_width * bg_height;
    
    
        p_content = single_wm->content;
        buflen = strlen(single_wm->content);
        pos_x = single_wm->x;
        printf("
    show: %s(%d Bytes):
    ", p_content, buflen);
        for (i = 0; i < strlen(single_wm->content); i++) {
            wordlen = get_word_num(p_content[0]);    
            buflen -= wordlen;
            if(buflen < 0) break;
            pic_index = get_word_index(p_content, wordlen);
            
    
            yuv420sp_blending(bg_info.y, bg_info.c, bg_info.width, bg_info.height, pos_x, single_wm->y,
                    icon_arry[pic_index].width, icon_arry[pic_index].height,
                    icon_arry[pic_index].y, icon_arry[pic_index].c,
                    icon_arry[pic_index].alph);
    
            pos_x += icon_arry[pic_index].width;
            p_content += wordlen;
        }
    
        return 0;
        }
    
    /* Note:
    *    input file 1280*720 NV21 format
    *    watermark ARGB8888
    */
    int main(int argc, char **argv)  
    {
        unsigned int bg_width = 1280;
        unsigned int bg_height = 720;
        unsigned int bg_nv21_size = bg_width * bg_height * 3/2;
        FILE *fp;
        char *bg_file = argv[1];
        unsigned char *bg_yuv_buf;
        /* 汉字编码用UTF-8, 建议用UTF-8编码 */
        struct disp_content single_wm = {200, 200, "0 123456789ABC京沪:-/"};
    
        if(bg_file == NULL) {
            printf("Pls input backgroud file !
    ");
            printf("	 eg: %s 1280x720_nv21.yuv
    ", argv[0]);
            exit(-1);
        }
    
        fp = fopen(bg_file, "rb");
        if(fp == NULL) {
            printf("open backgroud file(%s) fail !
    ", bg_file);
            exit(-1);        
        }
        
        bg_yuv_buf = (unsigned char *)malloc(bg_nv21_size);
        if(bg_yuv_buf == NULL){
            printf("malloc bg_yuv_buf fail!
    ");
            exit(-1);
        }
        fread(bg_yuv_buf, 1, bg_nv21_size, fp);    
    
        init_watermark();
        push_wm2bg(bg_yuv_buf, bg_width, bg_height, &single_wm);
        save_image(bg_yuv_buf, bg_nv21_size);
    
        return 0;
    }
    fzk@mdvr:push_wm2yuv$ ./a.out 1280x720_nv21.yuv 
    open file ./watermark/argb8888_24x32_0.bmp
    real width=32(32), height=48(48)
    open file ./watermark/argb8888_24x32_1.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_2.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_3.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_4.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_5.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_6.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_7.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_8.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_9.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_10.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_11.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_12.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_13.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_14.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_15.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_16.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_17.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_18.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_19.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_20.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_21.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_22.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_23.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_24.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_25.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_26.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_27.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_28.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_29.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_30.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_31.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_32.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_33.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_34.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_35.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_36.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_37.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_38.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_39.bmp
    real width=24(24), height=32(-32)
    open file ./watermark/argb8888_24x32_40.bmp
    real width=24(24), height=32(32)
    open file ./watermark/argb8888_24x32_41.bmp
    real width=24(24), height=32(32)
    
    show: 0 123456789ABC京沪:-/(25 Bytes):
    choose numben 0 ~ 9
            wordlen -> 1, index -> 0
    choose kong
            wordlen -> 1, index -> 36
    choose numben 0 ~ 9
            wordlen -> 1, index -> 1
    choose numben 0 ~ 9
            wordlen -> 1, index -> 2
    choose numben 0 ~ 9
            wordlen -> 1, index -> 3
    choose numben 0 ~ 9
            wordlen -> 1, index -> 4
    choose numben 0 ~ 9
            wordlen -> 1, index -> 5
    choose numben 0 ~ 9
            wordlen -> 1, index -> 6
    choose numben 0 ~ 9
            wordlen -> 1, index -> 7
    choose numben 0 ~ 9
            wordlen -> 1, index -> 8
    choose numben 0 ~ 9
            wordlen -> 1, index -> 9
    choose A~Z
            wordlen -> 1, index -> 10
    choose A~Z
            wordlen -> 1, index -> 11
    choose A~Z
            wordlen -> 1, index -> 12
    choose 京
            wordlen -> 3, index -> 40
    choose 沪
            wordlen -> 3, index -> 41
    choose chn :
            wordlen -> 3, index -> 38
    choose -
            wordlen -> 1, index -> 37
    choose /
            wordlen -> 1, index -> 39
    执行log

    三、注意事项

      1. BMP具体解析请查看上一篇博客: https://www.cnblogs.com/vedic/p/13608566.html

      2. 请用UTF-8格式查看, 否则中文会乱码

      3. 代码自动解析BMP头部图片的宽高, 所以不只是支持32x24

      4. 黑白图片没问题, 彩色会有问题

      5. 测试用的背景图片: https://files-cdn.cnblogs.com/files/vedic/1280x720_nv21.bmp  注意将后缀改成.yuv

      6. 测试用的水印图标: https://files.cnblogs.com/files/vedic/watermark.rar

      7. 最后展示:

  • 相关阅读:
    不吐不快之EJB演练——开篇概述
    URL重写:RewriteCond指令与RewriteRule 指令格式
    刚到公司有点压力山大,在此希望有大神给点正能量
    053第449题
    选择排序---简单选择排序 堆排序
    bzoj-1492 货币兑换Cash (2)——CDQ分治
    MySQL 提高Insert性能
    Codeforces Round #313 (Div. 2) 560C Gerald&#39;s Hexagon(脑洞)
    Matlab矩阵基础
    Android 带清除功能的输入框控件EditTextWithDel
  • 原文地址:https://www.cnblogs.com/vedic/p/13637747.html
Copyright © 2011-2022 走看看