概述
最近学习雷神的博客,深受启发。后面的代码或者借鉴原作的代码,或者修改自原作。一般的图片播放器无法显示rgb和yuv格式的图片,为了方便查看rgb和yuv图片,雷神修改了一个网上的YUV/RGB播放器,既支持YUV又支持RGB的播放器。不过要求图片名字类似于test_320x420.yuv,320x420是图片的宽和高。
首先要了解rgb24和yuv420p的存储方式,yuv420p是先存y再存u最后是v分量。废话少说,直接上代码。
#include
#include
#include
#include
unsigned char clip_value(unsigned char x,unsigned char min_val,unsigned char max_val){
if(x>max_val){
return max_val;
}else if(x> 8) + 16 ;
u = (unsigned char)( ( -38 * r - 74 * g + 112 * b + 128) >> 8) + 128 ;
v = (unsigned char)( ( 112 * r - 94 * g - 18 * b + 128) >> 8) + 128 ;
*(ptrY++) = clip_value(y,0,255);
if (j%2==0&&i%2 ==0){
*(ptrU++) =clip_value(u,0,255);
}
else{
if (i%2==0){
*(ptrV++) =clip_value(v,0,255);
}
}
}
}
return true;
}
int simplest_rgb24_to_yuv420(const char *url_in, int w, int h,int num,const char *url_out){
FILE *fp=fopen(url_in,"rb+");
FILE *fp1=fopen(url_out,"wb+");
unsigned char *pic_rgb24=(unsigned char *)malloc(w*h*3);
unsigned char *pic_yuv420=(unsigned char *)malloc(w*h*3/2);
for(int i=0;i
原rgb图与生成的yuv420p利用上面提到的播放器显示,如下:
可见,两幅图几乎看不出差别(若rgb图片宽高分别是w,h,则总像素个数是wh3,对应的yuv总像素个数是wh3/2,这么看图像信息还是有缺失的)。
另外,在用上述播放器显示rgb图片时,一定要选择正确的像素格式(播放器没那么智能,知道你的文件是什么格式,默认yuv420),否则就会出错,像下面这样。在播放器工具栏单击‘像素格式’选择‘rgb24格式’就能正常显示了。
这里是雷神将rgb24封装为bmp格式的代码。开始直接用雷神的代码,发现生成的bmp图片无法显示,后来查阅bmp的结构才发现,雷神的结构体有点问题。bmp文件头应该是54字节,按照雷神的结构体,文件头占了106字节,因此修改了结构体和代码中的有关文件头的部分,然后输出的结果可以正常显示了。该转换需要注意两点:(1)BMP的存储方式是小端存储,低字节放低地址,高字节放高地址,而RGB的存储方式是大端存储;(2)结构体在内存中的对齐准则,(a)从第二个数据成员开始,数据成员存储的起始地址要从该成员大小的整数倍开始;(b)最后检查总存储单元是否为所有元素中最宽元素的整数倍,若不是,则需要补齐。若包含指针元素,指针占四个字节,与类型无关。
#include
#include
#include
#include
int simplest_rgb24_to_bmp(const char *rgb24path, int width, int height, const char *bmppath) {
typedef struct
{
unsigned int imageSize;
unsigned int blank;
unsigned int startPosition;
}BmpHead;
typedef struct
{
unsigned int Length;
int width;
int height;
unsigned short colorPlane;
unsigned short bitColor;
unsigned int zipFormat;
unsigned int realSize;
int xPels;
int yPels;
unsigned int colorUse;
unsigned int colorImportant;
}InfoHead;
int i = 0, j = 0;
BmpHead m_BMPHeader = { 0 };
InfoHead m_BMPInfoHeader = { 0 };
char bfType[2] = { 'B','M' };
int header_size = 54;
unsigned char *rgb24_buffer = NULL;
FILE *fp_rgb24 = NULL, *fp_bmp = NULL;
if ((fp_rgb24 = fopen(rgb24path, "rb")) == NULL) {
printf("Error: Cannot open input RGB24 file.\n");
return -1;
}
if ((fp_bmp = fopen(bmppath, "wb")) == NULL) {
printf("Error: Cannot open output BMP file.\n");
return -1;
}
rgb24_buffer = (unsigned char *)malloc(width*height * 3);
fread(rgb24_buffer, 1, width*height * 3, fp_rgb24);
m_BMPHeader.imageSize = 3 * width*height + header_size;
m_BMPHeader.startPosition = header_size;
m_BMPInfoHeader.Length = 40;
m_BMPInfoHeader.width = width;
//BMP storage pixel data in opposite direction of Y-axis (from bottom to top).
m_BMPInfoHeader.height = -height;
m_BMPInfoHeader.colorPlane = 1;
m_BMPInfoHeader.bitColor = 24;
m_BMPInfoHeader.realSize = 3 * width*height;
fwrite(bfType, 1, sizeof(bfType), fp_bmp);
fwrite(&m_BMPHeader, 1, sizeof(m_BMPHeader), fp_bmp);
fwrite(&m_BMPInfoHeader, 1, sizeof(m_BMPInfoHeader), fp_bmp);
//BMP save R1|G1|B1,R2|G2|B2 as B1|G1|R1,B2|G2|R2
//It saves pixel data in Little Endian
//So we change 'R' and 'B'
for (j = 0;j < height;j++) {
for (i = 0;i < width;i++) {
char temp = rgb24_buffer[(j*width + i) * 3 + 2];
rgb24_buffer[(j*width + i) * 3 + 2] = rgb24_buffer[(j*width + i) * 3 + 0];
rgb24_buffer[(j*width + i) * 3 + 0] = temp;
}
}
fwrite(rgb24_buffer, 3 * width*height, 1, fp_bmp);
fclose(fp_rgb24);
fclose(fp_bmp);
free(rgb24_buffer);
printf("Finish generate %s!\n", bmppath);
return 0;
return 0;
}
int main() {
//Test
simplest_rgb24_to_bmp("../process_100jpg/pic_rgb/15_1024x640.rgb",1024,640,1,"15_1024x640.bmp");
return 0;
}
转成的bmp格式可以直接用普通的播放器显示。
这个转换更简单,去掉bmp头就是rgb24。但是这里同样要注意两个问题:注意:(1)BMP的存储方式是小端存储,低字节放低地址,高字节放高地址,而RGB的存储方式是大端存储;(2)结构体在内存中的对齐准则。
解决方法:定义结构体解析BMP文件头,获取图片宽高信息;自下而上读取BMP文件的位图数据,调换R、B数据位置。代码如下:
#include
#include
#include
#include
#include
#pragma pack(2)
//下面两个结构是位图的结构
typedef struct BITMAPFILEHEADER
{
u_int16_t bfType;
u_int32_t bfSize;
u_int16_t bfReserved1;
u_int16_t bfReserved2;
u_int32_t bfOffBits;
}BITMAPFILEHEADER;
typedef struct BITMAPINFOHEADER
{
u_int32_t biSize;
u_int32_t biWidth;
u_int32_t biHeight;
}BITMAPINFODEADER;
//bmp to rgb
int bmp_to_rgb(unsigned char *bmpBuf,int w,int h,unsigned char *rgbBuf)
{
unsigned char*ptr_bmp, *ptr_rgb;
char r,g,b;
memset(rgbBuf,0,w*h*3);
ptr_bmp= bmpBuf+54;
ptr_rgb= rgbBuf;
int j,i;
for ( j = h-1; j>=0;j--){
ptr_bmp = bmpBuf+54+ w*j*3 ;
for (i = 0;i
yuv420与nv21最大的区别就是uv分量的存储方式,nv21与nv12的区别在于nv分量的存储先后顺序。最初我以为rgb24转NV21相较于rgb24转yuv420p,只要把存储方式转换一下就行了,其实不然。nv21存储格式如下,四个y对应一对uv。只看y分量,可以发现,图像的宽或者高不可能为奇数。所以对于宽或者高是奇数的rgb图像,可以使用舍弃行、列,或者增加行、列的方式将生成的nv21图片的宽高成为偶数。这里使用舍弃行rgb行或列的方法,对于宽是奇数的rgb图像,读取rgb数据时,最后一列舍弃;高是奇数的话,最后一行舍弃。例如,对于图8_1024x683.rgb,它的高为683,在读取rgb图时舍弃最后一行。
代码如下:
#include
#include
#include
#include
unsigned char clip_value(unsigned char x,unsigned char min_val,unsigned char max_val){
if(x>max_val){
return max_val;
}else if(x> 8) + 16 ;
u = (unsigned char)( ( -38 * r - 74 * g + 112 * b + 128) >> 8) + 128 ;
v = (unsigned char)( ( 112 * r - 94 * g - 18 * b + 128) >> 8) + 128 ;
*(ptrY++) = clip_value(y,0,255);
if (j%2==0&&i%2 ==0){
*(ptrV++) =clip_value(v,0,255);ptrV++;
}
else{
if (i%2==0){
*(ptrU++) =clip_value(u,0,255);ptrU++;
}
}
}
}
return 0;
}
int simplest_rgb24_to_yuv420(const char *url_in, int w, int h,int num,const char *url_out){
FILE *fp=fopen(url_in,"rb+");
FILE *fp1=fopen(url_out,"wb+");
unsigned char *pic_rgb24=(unsigned char *)malloc(w*h*3);
unsigned char *pic_yuv420=(unsigned char *)malloc(w*(h-1)*3/2);
fread(pic_rgb24,1,w*h*3,fp);
RGB24_TO_YUV420(pic_rgb24,w,h,pic_yuv420);
fwrite(pic_yuv420,1,w*(h-1)*3/2,fp1);
free(pic_rgb24);
free(pic_yuv420);
fclose(fp);
fclose(fp1);
return 0;
}
int main(){
//Test
simplest_rgb24_to_yuv420("./pic_rgb/8_1024x683.rgb",1024,683,1,"./8_1024x682.yuv");
return 0;
}
开始在做图像格式转换时,只有雷神的一张lena的rgb图,网上搜了许久也没找到,就用libjpeg库将网上下载的jpg图像转为bmp,然后又将bmp利用上面的代码转为rgb。事实上,libjpeg库包含将jpg解压为rgb的方法,只不过转过来是错的!所以我只能先转bmp然后再转rgb。转换过程,下一篇再介绍,下面是我把100张jpg图片转成的rgb格式图片的链接。图片命名为类似1_500x352.rgb,序号_图片宽x图片高.jpg。上面代码中用到的rgb格式图片选自该文件夹。
rgb图片下载链接: link.
参考博客:
播放器链接: link.
雷神链接: link.