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