c++实现NV21(YUV420SP)转BMP

YUV420有很多种格式,此脚本是将 YUV420SP (也可以称为 NV21)格式转换成BMP格式并储存。
最好将脚本与文件路径放在一个盘下,比如我这都在D盘,当文件在C盘,脚本在D盘运行时,会出现无法获取文件名的情况

已经在windows10编译通过

#include   
#include  
#include   
#include 
#include   
#include  
#include 
//使用到vector库,下载后添加到环境变量中
//实现yuv420sp(NV21)格式转bmp格式
//可以实现多级文件夹内.yuv文件转同名.bmp文件
//脚本最好与代转换文件夹位于同一级文件目录,跨盘(脚本在C盘,文件夹在D盘)可能会无法搜寻到文件
//此脚本中yuv分辨率为1080p
using namespace std;

#define XSIZE 1920  
#define YSIZE 1080     

#define RGB_SIZE  XSIZE * YSIZE * 3
typedef unsigned char byte;
typedef unsigned char  	BYTE;
typedef unsigned short  WORD;
typedef unsigned int  	DWORD;
typedef int  		LONG;
 
#define BITS_PER_PIXCEL 24
#define FORMAT_RGBA 4
#define FORMAT_RGB  3
 
/** must needed. pack */
#pragma pack(1)
 
typedef struct
{
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
} BMP_FILE_HEADER;
 
typedef struct{
    DWORD      biSize;
    LONG       biWidth;
    LONG       biHeight;
    WORD       biPlanes;
    WORD       biBitCount;
    DWORD      biCompression;
    DWORD      biSizeImage;
    LONG       biXPelsPerMeter;
    LONG       biYPelsPerMeter;
    DWORD      biClrUsed;
    DWORD      biClrImportant;
} BMP_INFO_HEADER;
 
#pragma pack()
 //可以实现rgb/rgba转bitmap文件并储存,通过format切换
int rgbaToBmpFile(const char *pFileName, const char* pRgbaData, const int nWidth, const int nHeight, const int format)
{
    BMP_FILE_HEADER bmpHeader;
    BMP_INFO_HEADER bmpInfo;
 
    FILE* fp         = NULL;
    char* pBmpSource = NULL;
    char* pBmpData   = NULL;
 
    int i = 0, j=0;
 
    //4 bytes pack. must be 4 times per line。
    int bytesPerLine = (nWidth*BITS_PER_PIXCEL+31)/32*4;
    int pixcelBytes  = bytesPerLine*nHeight;
 
    bmpHeader.bfType        = 0x4D42;
    bmpHeader.bfReserved1   = 0;
    bmpHeader.bfReserved2   = 0;
    bmpHeader.bfOffBits     = sizeof(BMP_FILE_HEADER) + sizeof(BMP_INFO_HEADER);
    bmpHeader.bfSize        = bmpHeader.bfOffBits     + pixcelBytes;
 
    bmpInfo.biSize          = sizeof(BMP_INFO_HEADER);
    bmpInfo.biWidth         = nWidth;
    /** 这样图片才不会倒置 */
    bmpInfo.biHeight        = -nHeight; 
    bmpInfo.biPlanes        = 1;
    bmpInfo.biBitCount      = BITS_PER_PIXCEL;
    bmpInfo.biCompression   = 0;
    bmpInfo.biSizeImage     = pixcelBytes;
    bmpInfo.biXPelsPerMeter = 100;
    bmpInfo.biYPelsPerMeter = 100;
    bmpInfo.biClrUsed       = 0;
    bmpInfo.biClrImportant  = 0;
 
 
    /** convert in memort, then write to file. */
    pBmpSource = (char*)malloc(pixcelBytes);
    if (!pBmpSource)
    {
        return -1;
    }
 
    /** open file */
    fp = fopen(pFileName,"wb+");
    if (!fp)
    {
        return -1;
    }
 
    fwrite(&bmpHeader, sizeof(BMP_FILE_HEADER), 1, fp);
    fwrite(&bmpInfo,   sizeof(BMP_INFO_HEADER), 1, fp);
    /** Here you should consider color format. RGBA ? RGB? BGR?
        Param format is RGBA, format for file is BGR */
    pBmpData = pBmpSource;
    for (i=0; i<nHeight; i++)
    {
        for (j=0; j<nWidth; j++)
        {
           pBmpData[0] = pRgbaData[2];
           pBmpData[1] = pRgbaData[1];
           pBmpData[2] = pRgbaData[0];
           pRgbaData  += format;
           pBmpData   += FORMAT_RGB;
        }
        //pack for 4 bytes
        pBmpData +=(bytesPerLine - nWidth*FORMAT_RGB);
    }
    fwrite(pBmpSource, pixcelBytes, 1, fp);
 
    /** close and release。 */
    fclose(fp);
    free(pBmpSource);
 
    return 0;
}

//isRGB=true时,转换成rgb,false 转换成bgr
void NV212RGBorBGR(const uint8_t *input, int width, int height, uint8_t *output,bool isRGB)
{
	int nv_off = width * height;
	int  i, j, y_index = 0;
	int y, u, v;
	int r, g, b, nv_index = 0;
	for (i = 0; i < height; i++) {
		for (j = 0; j < width; j++,++y_index) {
			nv_index = i / 2 * width + j - j % 2;

			y = input[y_index];
			u = input[nv_off + nv_index];
			v = input[nv_off + nv_index + 1];

			r = y + ((351 * (v - 128)) >> 8);  //r
			g = y - ((179 * (v - 128) + 86 * (u - 128)) >> 8); //g
			b = y + ((443 * (u - 128)) >> 8); //b

			r = ((r>255) ? 255 : (r<0) ? 0 : r);
			g = ((g>255) ? 255 : (g<0) ? 0 : g);
			b = ((b>255) ? 255 : (b<0) ? 0 : b);
			if (isRGB) {
				output[y_index * 3 + 0] = (uint8_t)b;
				output[y_index * 3 + 1] = (uint8_t)g;
				output[y_index * 3 + 2] = (uint8_t)r;
			}
			else {
				output[y_index * 3 + 0] = (uint8_t)r;
				output[y_index * 3 + 1] = (uint8_t)g;
				output[y_index * 3 + 2] = (uint8_t)b;
			}
		}
	}
}


int endsWith(string s, string sub) {
	return s.rfind(sub) == (s.length() - sub.length()) ? 1 : 0;
}
// 该函数有两个参数,第一个为路径字符串(string类型,最好为绝对路径);  
// 第二个参数为文件夹与文件名称存储变量(vector类型,引用传递)。
// 第三个参数 筛选后缀名
bool TraverseDirectory(string path, vector<string>& vec_path, string postfix)
{
	__int64  Handle;
	struct __finddata64_t  FileInfo;
	string strFind = path + "\\*";

	if ((Handle = _findfirst64(strFind.c_str(), &FileInfo)) == -1L)
	{
		printf("没有找到匹配的项目\n");
		return false;
	}
	do
	{
		//判断是否有子目录
		if (FileInfo.attrib & _A_SUBDIR)
		{
           //cout << "有子目录" << endl;
			//判断是子文件夹
			//下面的判断条件很重要,过滤 . 和 ..
			if ((strcmp(FileInfo.name, ".") != 0) && (strcmp(FileInfo.name, "..") != 0))
			{
				string newPath = path + "\\" + FileInfo.name;
				TraverseDirectory(newPath, vec_path, postfix);
			}
		}
		else	//判断是文件
		{
           // cout << "是文件" << endl;
			string newPath = path + "\\" + FileInfo.name;
			//自定义操作
			if (postfix.size() == 0 || (postfix.size() > 0 && endsWith(newPath, postfix) && (postfix.c_str(), strchr(newPath.c_str(), '.'))))
			    vec_path.push_back(newPath);
		}
	} while (_findnext64(Handle, &FileInfo) == 0);

	_findclose(Handle);
	return true;
}



int  main(){
	int i, j, y, x;
    int idx = 0;
	long width, height;
	unsigned char *image;   //用于存放读取的yuv数据	
	unsigned char *image_rgb;

	width = XSIZE;
	height = YSIZE;
	long int bytePerLine = width * 3;
    string bmp_filename;
    string filename;
    
	//申请空间
	image = (unsigned char *)malloc(width * height * 3 / 2);
	image_rgb = (unsigned char *)malloc(width * height * 3);


	if ((NULL == image) || (image_rgb == NULL))
	{
		printf("faied to malloc the image\n");
		return -1;
	}

    //获取文件名,获取路径下下所有后缀名为.yuv的文件名
    string filePath = "D:\\Code\\c\\imatest";
    vector<string> files;    
	TraverseDirectory(filePath, files, ".yuv");
    //循环打开所有.yuv文件并在同级目录下转换成.bmp
	for ( ; idx < files.size(); idx++)
    {
        
	    FILE *fp_r;
        //读取yuv 文件
        fp_r = fopen(files[idx].c_str(), "rb"); //打开yuv 文件
        if (NULL == fp_r)
        {
            printf("failed to open the fp_r\n");
            return -1;
        }
        //将文件数据读到image buffer中
        fread(image, sizeof(unsigned char), XSIZE * YSIZE * 3 / 2, fp_r);
        fclose(fp_r);
        //提取文件名并修改成.bmp后缀
        filename = strtok((char *)files[idx].c_str(),(char *)".yuv") ;
        bmp_filename = filename + ".bmp";
        //将NV21转换成RGB
        NV212RGBorBGR(image,width,height,image_rgb,true);
        //将RGB转换成bmp
        int result = rgbaToBmpFile(bmp_filename.c_str(),(const char*)image_rgb,width,height , FORMAT_RGB);
        if(result == 0){
            cout << filename + ".yuv"  << "==============>"  + bmp_filename <<endl;
        }else{
            cout << "文件: "<< filename + ".yuv" << "转换失败" <<endl;
        }
        
    }
	//释放空间
	free(image);
    free(image_rgb);
	return(0);
}



编译 :
	g++  yuvsp2bmp.cpp   -o yuvsp2bmp

你可能感兴趣的:(c++实现NV21(YUV420SP)转BMP)