日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NV12图像格式叠加(水印原理演示)

發布時間:2023/12/14 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NV12图像格式叠加(水印原理演示) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概述

圖像疊加與音頻疊加(混音)不同,人耳可以同時聽到兩種聲音,因此混音時需要將兩種信號都保留;但視頻不同,圖像一旦疊加,那么疊加區域人眼就只能看到最上層的圖像。本文僅闡述對原始圖像數據做修改的疊加方式,不涉及多個plane實現顯示級別的圖像疊加的知識。因此本文所述的圖像疊加基本原理就是:頂層圖像的像素直接替換掉底層圖像的像素,從而新的圖像會顯示出疊加效果。

本文針對NV12像素格式圖像的疊加(兩張圖片均為NV12格式),如果想擴展疊加其他圖片,則需先將其他圖片轉為NV12,然后再進行疊加。如下提供bmp格式的頂圖轉換路徑:

頂圖(bmp) —> RGB24 —> NV12 —> 執行疊加操作

NV12格式闡述

nv12像素的存儲格式可參考:https://blog.csdn.net/lyy901135/article/details/97934892
如下紅框地方則為需要替換的區域,只要將如下兩個紅框內的值替換為對應“頂圖”的值就完成了圖像的疊加。那么首先就要確認兩個紅框內的每個像素的位置和紅框的大小。

  • Y數據內的紅框
    頂圖的寬高為144x48,根據NV12的像素組織特點可知,(x,y)對應的紅框大小也為144x48,此處的(x,y)對應代碼中的(pos_x, pos_y)。代碼中rect_y表示Y數據中矩形框起始地址,
    rect_y = 底圖基址(對應坐標原點) + w*y + x,對應代碼rect_y = param->bm_buff + param->pos_y * param->bm_w + param->pos_x;
    矩形框內每個像素點的位置關系:

    /* i 遍歷行,j遍歷列, w=1920, h=1080*/ for (i=0; i < 48; i++) {/* i 行對應的基址 */rect_tmp_y = rect_y + i*w;for (j=0; j < 144; j++) {Y(x+j, y+i) = rect_tmp_y + j;} }
  • UV數據內的紅框
    頂圖的寬高為144x48,根據NV12的像素組織特點可知,(x’,y’)對應的紅框大小則為144x24(每兩行Y對應一行UV,所以高度減半:48 -> 24)。代碼中rect_u表示UV數據中矩形框起始地址,
    rect_u = 底圖基址(對應坐標原點)+ wh + yw/2 + x,對應代碼rect_u = param->bm_buff + param->bm_w * param->bm_h + param->pos_y * param->bm_w / 2 + param->pos_x; 正是由于每兩行Y才對應一行UV數據的特性,因此再計算偏移時累加的是w的一半,對應上述公式中+ y*w/2。
    矩形框內每個像素點的位置關系:

    /* i 遍歷行,j遍歷列, w=1920, h=1080*/ for (i=0; i < 48; i++) {/* i 行對應的uv基址,每兩行才更新一次uv基址 */if (i % 2 == 0) {rect_tmp_u = rect_u + i * param->bm_w / 2;rect_tmp_v = rect_tmp_u + 1;}/* 每兩行Y對應一行UV,Y的偶數行對應UV行的U數據,并且U還要求在偶數列位置才取值;* Y的奇數行對應UV行的V數據,并且V還要求在奇數列才取值。 */for (j=0; j < 144; j++) {if ((i % 2 == 0) && (j % 2 == 0)) {/* U(x+j, y+i)的位置, 偶數行,偶數列 */rect_tmp_u += 2;} else if ((i % 2 == 1) && (j % 2 == 1)) {/* V(x+j, y+i)的位置, 奇數行,奇數列 */rect_tmp_v += 2;}} }

代碼

/** author: francis.fan@rock-chip.com* date: 2019-8-2*/typedef struct _overlayParam {unsigned char *bm_buff; //base map buffint bm_w; //base map widthint bm_h; //base map heightunsigned char *top_buff; //top overlay buffint top_w;int top_h;int pos_x; //position x [0, bm_w-top_w]int pos_y; }overlayParam;void dump_overlayparam(overlayParam *param) {printf("=== overlayParam ===\n");printf("\tbm_buff:%p\n", param->bm_buff);printf("\tbm_w:%d\n", param->bm_w);printf("\tbm_h:%d\n\n", param->bm_h);printf("\ttop_buff:%p\n", param->top_buff);printf("\ttop_w:%d\n", param->top_w);printf("\ttop_h:%d\n\n", param->top_h);printf("\tpos_x:%d\n", param->pos_x);printf("\tpos_y:%d\n", param->pos_y); }/* 功能:nv12圖像疊加 */ int pixel_format_nv12_overlay(overlayParam *param) {unsigned char *rect_y = NULL;unsigned char *rect_u = NULL;unsigned char *rect_v = NULL;unsigned char *rect_tmp_y = NULL;unsigned char *rect_tmp_u = NULL;unsigned char *rect_tmp_v = NULL;unsigned char *top_y = NULL;unsigned char *top_u = NULL;unsigned char *top_v = NULL;int i, j;dump_overlayparam(param);if (!param || !(param->bm_buff) || !(param->top_buff) ||((param->bm_w * param->bm_h) <= 0) || ((param->top_w * param->top_h) <= 0) ||(param->pos_x < 0) || (param->pos_y < 0)) {printf("ERROR: %s input args invalid!\n", __func__);return -EINVAL;}/* Position align */param->pos_x = (param->pos_x / 2) * 2;param->pos_y = (param->pos_y / 2) * 2;if (((param->pos_x + param->top_w) >= param->bm_w) ||(((param->pos_y + param->top_h) >= param->bm_h))) {printf("ERROR: %s overlay img size invalid!\n", __func__);return -EINVAL;}/** 以(pos_x, pos_y)為起始點分別向右(x軸)和向下(y軸)畫一個矩形框(圖片左上角為原點),* 矩形框的款高就是頂圖的寬高。該矩形框便是需要替換成頂圖的區域。*/rect_y = param->bm_buff + param->pos_y * param->bm_w + param->pos_x;rect_u = param->bm_buff + param->bm_w * param->bm_h + param->pos_y * param->bm_w / 2 + param->pos_x;rect_v = rect_u + 1;top_y = param->top_buff;top_u = param->top_buff + param->top_w * param->top_h;top_v = top_u + 1;for (i = 0; i < param->top_h; i++) {rect_tmp_y = rect_y + i * param->bm_w;if (i % 2 == 0) {rect_tmp_u = rect_u + i * param->bm_w / 2;rect_tmp_v = rect_tmp_u + 1;}for (j = 0; j < param->top_w; j++) {/* Replace y value */*(rect_tmp_y + j) = *(top_y + i * param->top_w + j);if ((i % 2 == 0) && (j % 2 == 0)) {*rect_tmp_u = *top_u;top_u += 2;rect_tmp_u += 2;} else if ((i % 2 == 1) && (j % 2 == 1)) {*rect_tmp_v = *top_v;top_v += 2;rect_tmp_v += 2;}}}return 0; }

工程

工程地址(包含Makefile和測試使用的NV12圖片):
https://download.csdn.net/download/lyy901135/11467723

運行命令:./nv12_add_nv12 images/base_map.nv12 images/top.nv12 images/output.nv12

查看頂圖命令:ffplay -s 144x48 -f rawvideo -pixel_format nv12 images/top.nv12

查看疊加后底圖命令:ffplay -s 1920x1080 -f rawvideo -pixel_format nv12 images/output.nv12

總結

以上是生活随笔為你收集整理的NV12图像格式叠加(水印原理演示)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。