《音视频:给图片添加黑色边框》
创始人
2024-06-02 19:49:29
0

目录

  • 一、前言
  • 二、libjpeg-turbo解码jpeg图片
  • 三、Yuv420编码格式
  • 四、生成目标形式
  • 五、libjpeg-turbo编码yuv生成jpeg

一、前言

本文旨在给一个jpg图片添加一个黑色边框,比如原jpg图片的分辨率为128 * 128,需要用它生成一张1280 * 720分辨率的图片,该生成的图片样式为jpg图片放置在正中间、周围填充黑色背景。类似于给图片添加黑色边框的效果。涉及的知识有,yuv420p格式,YUV分量的对应关系,以及YUV在内存中的存储格式,libjpeg-turbo库的使用。

二、libjpeg-turbo解码jpeg图片

读出jpeg图片数据后,使用libjpeg-turbo接口对其进行解码,生成为YUV格式数据。

int UserHeadImage::decode2Yuv()
{QByteArray jpeg;QByteArray yuv;int yuv_type;readImage(jpeg);tjhandle handle = NULL;int subsample, colorspace;int flags = 0;int padding = 1;int ret = 0;handle = tjInitDecompress();tjDecompressHeader3(handle, (unsigned char*)jpeg.data(), jpeg.size(), &yuvWidth, &yuvHeight, &subsample, &colorspace);qDebug("w: %d h: %d subsample: %d color: %d\n", yuvWidth, yuvHeight, subsample, colorspace);flags |= 0;yuv_type = subsample;yuvBufferSize = tjBufSizeYUV2(yuvWidth, padding, yuvHeight, subsample);if(yuvBuffer){free(yuvBuffer);yuvBuffer = NULL;}yuvBuffer = (char *)malloc(yuvBufferSize);if (yuvBuffer == NULL){qDebug("malloc buffer for rgb failed.\n");return -1;}ret = tjDecompressToYUV2(handle, (unsigned char*)jpeg.data(), jpeg.size(), (unsigned char*)yuvBuffer, yuvWidth,padding, yuvHeight, flags);if (ret < 0){qDebug("compress to jpeg failed: %s\n", tjGetErrorStr());}tjDestroy(handle);return ret;
}

三、Yuv420编码格式

yuv420P格式 Y、U、V分量的对应关系,如下图所示。

  • 四个Y共用一组UV。

  • 四个Y由两个相邻的行组成。
    在这里插入图片描述
    YUV分量在内存中的存放方式,如下图所示。

  • Y、U、V三个分量数据分开存放。

  • 先存放Y分量数据,长度为w*h。

  • 然后存放U分量数据,长度为w*h/4。

  • 最后存放V分量数据,长度为w*h/4。
    在这里插入图片描述

四、生成目标形式

对解码后的Y、U、V数据进行处理,生成目标形式。目标图像宽高为VIDEO_TRANS_WIDTH * VIDEO_TRANS_HEIGHT。

  • 计算出源图像在目标图像中的位置,然后以像素点扫描目标图像,每一个像素点对应一组YUV数据。
  • 然后根据上面的Y、U、V分量的对应关系和在内存中存放的位置,给目标图像填充Y、U、V数据。
  • 如果扫描到的像素点为源图像在目标图像中的位置区域时,拷贝源图像的Y、U、V数据到相应位置;如果不是,则在相应位置处填充“黑色”背景的Y、U、V数据。

比如目标图像的大小为1280 * 720,源图像大小为128 * 128。

  • 源图像需要存放在目标图像的居中位置,则其存放区域(x0,y0,x1,y1)为((1280-128)/ 2,(720-128)/ 2,(1280-128)/ 2 + 128,(1280-128)/ 2 + 128)。
  • 对应Y分量,扫描到以上区域后拷贝源图像数据到目标位置,其他区域上的填充0x00(黑色)。
  • 对应UV分量,在一行上每两个Y写入一组UV,每两行执行一次写入。扫描到以上区域后拷贝源图像数据到目标位置,其他区域上的填充0x80。

源图像
在这里插入图片描述
目标图像
在这里插入图片描述

void UserHeadImage::addBlackBorder()
{// yuvBuffer -> transmissionData// 添加黑色边框,yuv原始大小(128*128)转为  VIDEO_TRANS_WIDTH*VIDEO_TRANS_HEIGHT 大小char *sY = yuvBuffer;char *sU = sY + yuvWidth*yuvHeight;char *sV = sU + yuvWidth*yuvHeight/4;char *dY = transmissionData;char *dU = dY + VIDEO_TRANS_WIDTH*VIDEO_TRANS_HEIGHT;char *dV = dU + VIDEO_TRANS_WIDTH*VIDEO_TRANS_HEIGHT/4;int srcYIndex = 0;int srcUVIndex = 0;int dstYIndex = 0;int dstUVIndex = 0;for (int j = 0; j < VIDEO_TRANS_HEIGHT; j++) {for (int k = 0; k < VIDEO_TRANS_WIDTH; k++) {if ( (j >= ((VIDEO_TRANS_HEIGHT)-yuvHeight)/2 && j < (((VIDEO_TRANS_HEIGHT)-yuvHeight)/2 + yuvHeight)) && (k >= ((VIDEO_TRANS_WIDTH)-yuvWidth)/2 && k < (((VIDEO_TRANS_WIDTH)-yuvWidth)/2 + yuvWidth)) ){// 读取src图像的Y数据dY[dstYIndex] = sY[srcYIndex];srcYIndex++;dstYIndex++;                }else{dY[dstYIndex] = 0x00;dstYIndex++;                            }// 写入 u v 数据,每两个y写入一组u vif(dstYIndex % 2 == 0){   // 隔行存放if((j+1)%2 == 0){continue;}if ( (j >= ((VIDEO_TRANS_HEIGHT)-yuvHeight)/2 && j < (((VIDEO_TRANS_HEIGHT)-yuvHeight)/2 + yuvHeight)) && (k >= ((VIDEO_TRANS_WIDTH)-yuvWidth)/2 && k < (((VIDEO_TRANS_WIDTH)-yuvWidth)/2 + yuvWidth)) ){dU[dstUVIndex] = sU[srcUVIndex];              dV[dstUVIndex] = sV[srcUVIndex];srcUVIndex++;dstUVIndex++;}else{dU[dstUVIndex] = 0x80;dV[dstUVIndex] = 0x80;dstUVIndex++;}}}}
}

五、libjpeg-turbo编码yuv生成jpeg

int UserHeadImage::tyuv2jpeg(unsigned char* yuv_buffer, int yuv_size, int width, int height, int subsample, unsigned char** jpeg_buffer, unsigned long* jpeg_size, int quality)
{tjhandle handle = NULL;int flags = 0;int padding = 1;int need_size = 0;int ret = 0;handle = tjInitCompress();flags |= 0;need_size = tjBufSizeYUV2(width, padding, height, subsample);if (need_size != yuv_size){qDebug("detect yuv size: %d, give: %d, check again.\n", need_size, yuv_size);return 0;}ret = tjCompressFromYUV(handle, yuv_buffer, width, padding, height, subsample, jpeg_buffer, jpeg_size, quality, flags);if (ret < 0){qDebug("compress to jpeg failed: %s\n", tjGetErrorStr());}tjDestroy(handle);return ret;
}

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...