yuv格式是相机的原始格式,一般都需要压缩处理,减少体积,yuv根据不同的排列方式有不同的格式,下面列举常用的格式转换。
可以使用libjpeg库来完成压缩工作,编译好的库请移步这里下载:
download.csdn.net/download/lp…
下面是使用的demo,需要定义三个类YuvToJpegUtil.h 、YuvToJpegUtil.cpp 、main.cpp 和一个配置文件CMakeList.txt。
其中 YuvToJpegUtil.h 如下:
#include "turbojpeg.h"
typedef unsigned char BYTE;
class YuvToJpegUtil {
public:
bool convertYuvToJpeg(unsigned char *yuvBuffer,
int yuvSize,
int type,
int width,
int height,
int padding,
int quality,
unsigned char **jpgBuffer,
int &jpgSize);
private:
/*
* YUV420P:YV12 -> YU12,because libjpeg only surport YU12
*/
bool yv12ToYu12(BYTE* yuv_buffer, int yuv_size, int width, int height, int padding);
/*
* NV12 -> YU12
*/
bool nv12ToYu12(BYTE* yuv_buffer, int yuv_size, int width, int height, int padding);
/**
* NV21 -> YU12
*/
int nv21ToYu12(BYTE *in, int width, int height);
/**
* YUYV -> YUV422P
*/
int yuyvToYuv422P(BYTE *in, int width, int height);
/**
* YU12 -> Jpeg
* @param yuv_buffer:yuv数据区
* @param yuv_size:yuv大小
* @param width:yuv宽度
* @param height:yuv高度
* @param quality:jpg压缩质量 (1-100)
* @out jpg_buffer:输出的jpg数据
* @out jpg_size :输出的jpg大小
* @return :0为成功
*/
int yu12ToJpeg(BYTE* yuv_buffer, int yuv_size, int width, int height, int padding, int quality,
BYTE** jpg_buffer, int& jpg_size, TJSAMP = TJSAMP_420);
};
其中 YuvToJpegUtil.cpp 如下:
#include <cstring>
#include <cstdint>
#include "YuvToJpegUtil.h"
#include "turbojpeg.h"
bool YuvToJpegUtil::convertYuvToJpeg(unsigned char *yuvBuffer, int yuvSize, int type, int width, int height, int padding,
int quality, unsigned char **jpgBuffer, int &jpgSize) {
// yu12 -> jpeg
if (type == 0) {
return yu12ToJpeg(yuvBuffer, yuvSize, width, height, padding, quality, jpgBuffer,
jpgSize);
}
// yv12 -> yu12
// yu12 -> jpeg
if (type == 1) {
yv12ToYu12(yuvBuffer, yuvSize, width, height, padding);
return yu12ToJpeg(yuvBuffer, yuvSize, width, height, padding, quality, jpgBuffer,
jpgSize);
}
// nv12 -> yu12
// yu12 -> jpeg
if (type == 2) {
nv12ToYu12(yuvBuffer, yuvSize, width, height, padding);
return yu12ToJpeg(yuvBuffer, yuvSize, width, height, padding, quality, jpgBuffer,
jpgSize);
}
// nv21 -> yu12
// yu12 -> jpeg
if (type == 3) {
nv21ToYu12(yuvBuffer, width, height);
int result = yu12ToJpeg(yuvBuffer, yuvSize, width, height, padding, quality,
jpgBuffer,
jpgSize);
return result;
}
// yuyv -> yuv422p
// yu12 -> jpeg
if (type == 4) {
yuyvToYuv422P(yuvBuffer, width, height);
int result = yu12ToJpeg(yuvBuffer, yuvSize, width, height, padding, quality,
jpgBuffer,
jpgSize, TJSAMP_422);
return result;
}
}
/**
* YUV420P:YV12 -> YU12
*/
bool YuvToJpegUtil::yv12ToYu12(BYTE *yuvBuffer, int yuvSize, int width, int height, int padding) {
int uvLength = (yuvSize - width * height) / 2;
BYTE *tempCache = new BYTE[uvLength];
memcpy(tempCache, yuvBuffer + width * height, uvLength);
memcpy(yuvBuffer + width * height, yuvBuffer + width * height + uvLength, uvLength);
memcpy(yuvBuffer + width * height + uvLength, tempCache, uvLength);
delete[] tempCache;
tempCache = nullptr;
return true;
}
/**
* NV12 -> YU12
*/
bool YuvToJpegUtil::nv12ToYu12(BYTE *yuvBuffer, int yuvSize, int width, int height, int padding) {
// 将yyyyyyuvuvuv 分离为 yyyuuuvvv
// u 、v各占 1/6
// y 独占 2/3
// 创建存放UV的缓存
int size = width * height * 3 / 2;
BYTE *tempVBuffer = new BYTE[size / 6];
// 第一个V的位置
int uvIndex = 0;
int vIndex = 0;
int yEndPos = size * 2 / 3 + 1;
for (int index = yEndPos; index < size; ++index) {
// 偶数为 u
if (index % 2 == 0) {
// 当前的U 要替换 的位置
int newUPos = yEndPos + uvIndex / 2;
yuvBuffer[newUPos] = yuvBuffer[index];
} else {
*(tempVBuffer + vIndex) = yuvBuffer[index];
vIndex++;
}
uvIndex++;
}
// 将v拼接到后面
memcpy(yuvBuffer + size * 5 / 6, tempVBuffer, size / 6);
delete[](tempVBuffer);
return true;
}
/**
* NV21 -> YU12
*/
int YuvToJpegUtil::nv21ToYu12(BYTE *in, int width, int height) {
// 将yyyyyyvuvuvuvuvu 分离为 yyyuuuvvv
// u 、v各占 1/6
// y 独占 2/3
// 创建存放UV的缓存
int size = width * height * 3 / 2;
BYTE *tempVBuffer = new BYTE[size / 6];
// 第一个V的位置
int uvIndex = 0;
int vIndex = 0;
int yEndPos = size * 2 / 3 + 1;
for (int index = yEndPos; index < size; ++index) {
// 偶数为 v
if (index % 2 != 0) {
// 当前的U 要替换 的位置
int newUPos = yEndPos + uvIndex / 2;
in[newUPos] = in[index];
} else {
*(tempVBuffer + vIndex) = in[index];
vIndex++;
}
uvIndex++;
}
// 将v拼接到后面
memcpy(in + size * 5 / 6, tempVBuffer, size / 6);
delete[](tempVBuffer);
return 0;
}
/**
* YU12 -> Jpeg
*/
int YuvToJpegUtil::yu12ToJpeg(BYTE *yuvBuffer, int yuvSize, int width, int height, int padding,
int quality,
BYTE **jpgBuffer, int &jpgSize, TJSAMP TJSAMP_TYPE) {
int subsample = TJSAMP_TYPE;
tjhandle handle = NULL;
int flags = 0;
int need_size = 0;
int ret = 0;
handle = tjInitCompress();
flags |= 0;
need_size = tjBufSizeYUV2(width, padding, height, subsample);
if (need_size != yuvSize) {
return -1;
}
unsigned long retSize = 0;
ret = tjCompressFromYUV(handle, yuvBuffer, width, padding, height, subsample,
jpgBuffer, &retSize, quality, flags);
jpgSize = retSize;
if (ret < 0) {
// Log::info("压缩jpeg失败,错误信息:%s", tjGetErrorStr());
}
tjDestroy(handle);
return ret;
}
/**
* YUYV -> Yuv422P
*/
int YuvToJpegUtil::yuyvToYuv422P(BYTE *in, int width, int height) {
// 将yuyv 分离为 yyyuuuvvv
// 创建存放UV的缓存
int size = width * height * 2;
int halfOfSize = size / 2;
BYTE *tempUVBuffer = new BYTE[halfOfSize];
// 第一个U的位置
BYTE *UBuffer = tempUVBuffer;
// 第一个V的位置
BYTE *VBuffer = tempUVBuffer + halfOfSize / 2;
bool isU = true;
int uIndex = 0;
int vIndex = 0;
for (int index = 0; index < size; ++index) {
// 偶数为 Y
if (index % 2 == 0) {
// 当前的Y 要替换 index/2 的位置
in[index / 2] = in[index];
} else {
// U、V交替
if (isU) {
isU = false;
*(UBuffer + uIndex) = in[index];
uIndex++;
} else {
*(VBuffer + vIndex) = in[index];
isU = true;
vIndex++;
}
}
}
// 将uv拼接到后面
memcpy(in + halfOfSize, tempUVBuffer, halfOfSize);
delete[](tempUVBuffer);
return 0;
}
接下来,是调用方使用,代码如下:
#include "YuvToJpegUtil.h"
void testYuvToJpeg(int quality){
YuvToJpegUtil *jpgUtil = new YuvToJpegUtil();
uint8_t *jpgBuffer = new uint8_t[1024 * 1024];
int jpgSize = 1024 * 1024;
int yuvSize = 1920 * 1080 * 3 / 2;
unsigned char *yuvBuffer = new unsigned char[yuvSize];
FILE *fp_in = fopen(
"/sdcard/1614138720028814.yuv",
"rb+");
if (fp_in != NULL) {
fread(yuvBuffer, yuvSize, 1, fp_in);
} else {
std::cout<<"找不到yuv文件"<<std::endl;
return;
}
fclose(fp_in);
jpgUtil->convertYuvToJpeg(yuvBuffer, yuvSize, 0, 1920, 1080, 4, quality, &jpgBuffer, jpgSize);
std::string path = "/sdcard/1614138720028814_";
path = path+ std::to_string(quality)+".jpg";
FILE *fp_out = NULL;
if ((fp_out = fopen(
path.c_str(),
"wb+")) == NULL) {
std::cout<<"文件创建失败"<<std::endl;
} else {
fwrite(jpgBuffer, jpgSize, 1, fp_out);
fclose(fp_out);
std::cout<<"文件创建成功"<<std::endl;
}
delete[] jpgBuffer;
delete[] yuvBuffer;
}
int main() {
testYuvToJpeg(80);
testYuvToJpeg(100);
return 0;
}
配置文件 CMakeList.txt 如下:
...
# 第一步
include_directories(
...
thirdParty/libjpeg-turbo/Headers
)
# 第二步
add_subdirectory("thirdParty/libjpeg-turbo")
# 第三步, LibJpeg_turbo_static 请参照下载文件中编译好的静态库
target_link_libraries(
demo
LibJpeg_turbo_static
)