【数字图像处理】YUV420转RGB并BMP存储<纯C++实现>

来源:互联网 发布:土木人的软件 编辑:程序博客网 时间:2024/06/12 15:31
1、读取akiyo_qcif.yuv YUV420文件,按帧读取,转RGB,并存储到BMP文件。
2、暂时实现读取一帧并存储一张BMP图片。 若要读取YUV序列,写成循环方式即可。
3、生成的BMP文件还有一点小问题:图像的倒立的,而且红色偏多。
YUV是qcif分辨率。
估计是YUV到RGB的计算公式有点问题。解决中。。。。
图像倒立问题已经解决。
红色不知道是不是公式变换和RGB数值区间限制的时候产生的。估计和UV的差值方式也有关系。
红色的问题已经解决,采用的变换公式不正确。 但是还是有一些横纹,不知道是怎么回事。
 
头文件Ex1.h
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG;
//位图文件头定义;
//其中不包含文件类型信息(由于结构体的内存结构决定,
//要是加了的话将不能正确读取文件信息)
typedef struct tagBITMAPFILEHEADER{
//WORD bfType;//文件类型,必须是0x424D,即字符“BM”
DWORD bfSize;//文件大小
WORD bfReserved1;//保留字
WORD bfReserved2;//保留字
DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数
}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//信息头大小
LONG biWidth;//图像宽度
LONG biHeight;//图像高度
WORD biPlanes;//位平面数,必须为1
WORD biBitCount;//每像素位数
DWORD biCompression; //压缩类型
DWORD biSizeImage; //压缩图像大小字节数
LONG biXPelsPerMeter; //水平分辨率
LONG biYPelsPerMeter; //垂直分辨率
DWORD biClrUsed; //位图实际用到的色彩数
DWORD biClrImportant; //本位图中重要的色彩数
}BITMAPINFOHEADER; //位图信息头定义
typedef struct tagRGBQUAD{
BYTE rgbBlue; //该颜色的蓝色分量
BYTE rgbGreen; //该颜色的绿色分量
BYTE rgbRed; //该颜色的红色分量
BYTE rgbReserved; //保留值
}RGBQUAD;//调色板定义
//像素信息
typedef struct tagIMAGEDATA
{
BYTE blue;///8位灰度图用其中1个
BYTE green;
BYTE red;
}IMAGEDATA;
源文件Ex1.cpp
#include "stdafx.h"
#include "Ex1.h"
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <iostream.h>
#define qcif
#ifdef qcif
#define Y_WIDTH 176
#define Y_HEIGHT 144
#define U_WIDTH 88
#define U_HEIGHT 72
#define V_WIDTH 88
#define V_HEIGHT 72
#endif
//变量定义
BITMAPFILEHEADER strHead; //位图文件头
BITMAPINFOHEADER strInfo; //位图信息头
unsigned char Y_space[Y_WIDTH*Y_HEIGHT];
unsigned char U_space[U_WIDTH*U_HEIGHT];
unsigned char V_space[V_WIDTH*V_HEIGHT];
unsigned char R_space[Y_WIDTH*Y_HEIGHT];
unsigned char G_space[Y_WIDTH*Y_HEIGHT];
unsigned char B_space[Y_WIDTH*Y_HEIGHT];
void ReadImage(unsigned char *pImage,char *cFileName,int nWidth,int nHeight,long offset);////读取YUV文件
int main(int argc, char* argv[])
{
long origin=0+Y_WIDTH*Y_HEIGHT+U_WIDTH*U_HEIGHT+V_WIDTH*V_HEIGHT;///YUV文件起始位置
int i,j,u_j,v_j;
int width,height;
width=Y_WIDTH;
height=Y_HEIGHT;
FILE *fpw;
WORD bfType_w=0x4d42;
IMAGEDATA *imagedata = NULL; //动态分配存储BMP图片的像素信息的二维数组
imagedata = (IMAGEDATA*)malloc(width*height* sizeof(IMAGEDATA)); //按IMAGEDATA结构体大小,分配存储BMP图片的存储空间
//初始化原始图片的像素数组
for( i = 0;i < height;++i)
{
for(int j = 0;j <width;++j)
{
(*(imagedata + i * width + j)).blue = 0;
(*(imagedata + i * width + j)).green = 0;
(*(imagedata + i * width + j)).red = 0;
}
}
//origin=0;///读第一帧
//for(i=0,origin=0;i<148;i++,origin+=(Y_WIDTH*Y_HEIGHT+U_WIDTH*U_HEIGHT+V_WIDTH*V_HEIGHT))
//{///读取第一帧图像
ReadImage(Y_space,"akiyo_qcif.yuv",Y_WIDTH,Y_HEIGHT,origin);//carphone001.yuv
ReadImage(V_space,"akiyo_qcif.yuv",V_WIDTH,V_HEIGHT,Y_WIDTH*Y_HEIGHT);///读取方法涉及YUV的存储顺序。 存储顺序是所有Y,随后是所有V,最后是所有U。
ReadImage(U_space,"akiyo_qcif.yuv",U_WIDTH,U_HEIGHT,Y_WIDTH*Y_HEIGHT+V_WIDTH*V_HEIGHT);
//}
///YUV转成RGB 176*144 88*72 width * height
u_j=0;v_j=0;
int y,cb,cr;//y u v
int r,g,b;
for(i = 0; i <Y_HEIGHT; i++)
{
for(j = 0; j <Y_WIDTH; j++)
{
u_j=j/2;///取倍数
v_j=j/2;///取倍数,U和V的宽度是Y的一半。
/////对应4:2:0 YUV 采样方式 参考文章:http://blog.csdn.net/shallon_luo/article/details/5544796
y = Y_space[j+i*Y_WIDTH];
cb = U_space[u_j+i*U_WIDTH];
cr = V_space[v_j+i*V_WIDTH];
/* R = Y + 1.14V,G = Y - 0.39U - 0.58V,B = Y + 2.03U YUV对应YCbCr
r = y + 1.14*cr;
g = y - 0.39*cb - 0.58*cr;
b = y + 2.03*cb; */
/* YCbCr与RGB的关系 */
r = 1.164*(y-16) + 1.596*(cr-128);
g = 1.164*(y-16) - 0.813*(cr-128) - 0.392*(cb-128);
b = 1.164*(y-16) + 2.017*(cb-128);
/* r=1.0*y+1.402*(cr-128) ;
g=1.0*y-0.34413*(cb-128)-0.71414*(cr-128);
b=1.0*y+1.772*(cb-128);*/
r = r > 255 ? 255 : r;
g = g > 255 ? 255 : g;
b = b > 255 ? 255 : b;
r = r < 0 ? 0 : r;
g = g < 0 ? 0 : g;
b = b < 0 ? 0 : b;
B_space[j+i*Y_WIDTH] = b;
G_space[j+i*Y_WIDTH] = g;
R_space[j+i*Y_WIDTH] = r;
}
}
for(i =0;i<Y_HEIGHT;++i)
{
for(int j = 0;j < Y_WIDTH;++j)
{
(*(imagedata + i * Y_WIDTH + j)).blue=B_space[j+i*Y_WIDTH];
(*(imagedata + i * Y_WIDTH + j)).green=G_space[j+i*Y_WIDTH];
(*(imagedata + i * Y_WIDTH + j)).red=R_space[j+i*Y_WIDTH];
}
}
///保存BMP图像
if((fpw=fopen("result033.bmp","wb"))==NULL)
{
cout<<"create the bmp file error!"<<endl;
return NULL;
}
fwrite(&bfType_w,1,sizeof(WORD),fpw);
strHead.bfSize=Y_WIDTH*Y_HEIGHT*3+54;//位图文件大小,说明文件的大小,用字节为单位
strHead.bfReserved1=0;
strHead.bfReserved2=0;
strHead.bfOffBits=54;//从文件头到实际位图数据的偏移字节数14+40,无调色板。
fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw);
strInfo.biWidth = Y_WIDTH;//指定图象的宽度,单位是象素。
strInfo.biHeight = Y_HEIGHT;//指定图象的高度,单位是象素。
strInfo.biBitCount=24;////指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)(
strInfo.biClrImportant=0;///指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。
strInfo.biClrUsed=0;///指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2的biBitCount次方。
strInfo.biCompression=0;//指定位图是否压缩。
strInfo.biPlanes=1;///平面数,必须是1
strInfo.biSize=40;///指定这个结构的长度,必须是40。
strInfo.biSizeImage=3*width*height;//指定实际的位图数据占用的字节数,似乎填什么都可以。
fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw);
//保存像素数据
for(i =Y_HEIGHT;i > 0;--i)///从上往下写。图像上下翻转
{
for(int j = 0;j < Y_WIDTH;++j)
//for(int j = Y_WIDTH;j > 0;--j)///从右往左写 图像左右对调
{
fwrite( &((*(imagedata + i * Y_WIDTH + j)).blue),1,sizeof(BYTE),fpw);
fwrite( &((*(imagedata + i * Y_WIDTH + j)).green),1,sizeof(BYTE),fpw);
fwrite( &((*(imagedata + i * Y_WIDTH + j)).red),1,sizeof(BYTE),fpw);
}
}
fclose(fpw);
//释放内存
delete[] imagedata;
return 0;
}
void ReadImage(unsigned char *pImage,char *cFileName,int nWidth,int nHeight,long offset)
{
int j,i;
unsigned char *pWork;
FILE *fp=0;
if ( fp=fopen(cFileName,"rb" ) ) //打开一幅图像
{
fseek(fp,offset,SEEK_SET); //文件定位
pWork=pImage; //指针指向
for ( j=0;j<nHeight;j++,pWork+=nWidth )
for ( i=0;i<nWidth;i++ )
fread(pWork+i,1,1,fp); //顺序读取,每次读一个字节存入pwork[]中
fclose(fp);
}
};
原创粉丝点击