我们在使用Windows的画图软件,画一张图,然后保存成24位位图BMP图像后,要对这幅图像进行一系列的格式转换之前,需要先将BMP里的数据提取出来,然后再保存成BGR888的图像。
有两处细节要注意:
第一,我们直接保存成BMP图片,不知道为什么读取BMP信息很多字段为0,所以我们要自己手动设置;
第二,读取出的数据我们以为是bgr格式存放的,其实不对,其实是grb存放的,虽然我也不知道为什么会这样。
chenLeeCV.c
#include "chenLeeCV.h"
#include <stdio.h>
#include <stdlib.h>
ClImage* clLoadImage(char* path)
{
ClImage* bmpImg;
FILE* pFile;
unsigned short fileType;
ClBitMapFileHeader bmpFileHeader;
ClBitMapInfoHeader bmpInfoHeader;
int channels = 1;
int width = 0;
int height = 0;
int step = 0;
int offset = 0;
unsigned char pixVal;
ClRgbQuad* quad;
int i, j, k;
bmpImg = (ClImage*)malloc(sizeof(ClImage));
pFile = fopen(path, "rb");
if (!pFile)
{
free(bmpImg);
return NULL;
}
fread(&fileType, sizeof(unsigned short), 1, pFile);
if (fileType == 0x4D42)
{
printf("bmp file! \n");
fread(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
printf("==============================================\n");
printf("bmp:\n");
printf("bmpFileHeader.bfSize:%d \n", bmpFileHeader.bfSize);
printf("bmpFileHeader.bfReserved1:%d \n", bmpFileHeader.bfReserved1);
printf("bmpFileHeader.bfReserved2:%d \n", bmpFileHeader.bfReserved2);
printf("bmpFileHeader.bfOffBits:%d \n", bmpFileHeader.bfOffBits);
// printf("bmp文件头信息:\n");
// printf("文件大小:%d \n", bmpFileHeader.bfSize);
// printf("保留字:%d \n", bmpFileHeader.bfReserved1);
// printf("保留字:%d \n", bmpFileHeader.bfReserved2);
// printf("位图数据偏移字节数:%d \n", bmpFileHeader.bfOffBits);
fread(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
printf("==============================================\n");
printf("bmp\n");
printf("bmpInfoHeader.biSize:%d \n", bmpInfoHeader.biSize);
printf("bmpInfoHeader.biWidth:%d \n", bmpInfoHeader.biWidth);
printf("bmpInfoHeader.biHeight:%d \n", bmpInfoHeader.biHeight);
printf("bmpInfoHeader.biPlanes:%d \n", bmpInfoHeader.biPlanes);
printf("bmpInfoHeader.biBitCount:%d \n", bmpInfoHeader.biBitCount);
printf("bmpInfoHeader.biCompression:%d \n", bmpInfoHeader.biCompression);
printf("bmpInfoHeader.biSizeImage:%d \n", bmpInfoHeader.biSizeImage);
printf("bmpInfoHeader.biXPelsPerMeter:%d \n", bmpInfoHeader.biXPelsPerMeter);
printf("bmpInfoHeader.biYPelsPerMeter:%d \n", bmpInfoHeader.biYPelsPerMeter);
printf("bmpInfoHeader.biClrUsed:%d \n", bmpInfoHeader.biClrUsed);
printf("bmpInfoHeader.biClrImportant:%d \n", bmpInfoHeader.biClrImportant);
// printf("bmp文件信息头\n");
// printf("结构体长度:%d \n", bmpInfoHeader.biSize);
// printf("位图宽度:%d \n", bmpInfoHeader.biWidth);
// printf("位图高度:%d \n", bmpInfoHeader.biHeight);
// printf("位图平面数:%d \n", bmpInfoHeader.biPlanes);
// printf("颜色位数:%d \n", bmpInfoHeader.biBitCount);
// printf("压缩方式:%d \n", bmpInfoHeader.biCompression);
// printf("实际位图数据占用的字节数:%d \n", bmpInfoHeader.biSizeImage);
// printf("X方向分辨率:%d \n", bmpInfoHeader.biXPelsPerMeter);
// printf("Y方向分辨率:%d \n", bmpInfoHeader.biYPelsPerMeter);
// printf("使用的颜色数:%d \n", bmpInfoHeader.biClrUsed);
// printf("重要颜色数:%d \n", bmpInfoHeader.biClrImportant);
printf("==============================================\n");
// 不知道为什么这里读出来的数值是0,所以只能手动设置为24
bmpInfoHeader.biBitCount = 24;
if (bmpInfoHeader.biBitCount == 8)
{
printf("8bit\n\n");
// printf("该文件有调色板,即该位图为非真彩色\n\n");
channels = 1;
width = bmpInfoHeader.biWidth;
height = bmpInfoHeader.biHeight;
offset = (channels*width)%4;
if (offset != 0)
{
offset = 4 - offset;
}
//bmpImg->mat = kzCreateMat(height, width, 1, 0);
bmpImg->width = width;
bmpImg->height = height;
bmpImg->channels = 1;
bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*height);
step = channels*width;
quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad)*256);
fread(quad, sizeof(ClRgbQuad), 256, pFile);
free(quad);
for (i=0; i<height; i++)
{
for (j=0; j<width; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
bmpImg->imageData[(height-1-i)*step+j] = pixVal;
}
if (offset != 0)
{
for (j=0; j<offset; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
else if (bmpInfoHeader.biBitCount == 24)
{
printf("24bit\n\n");
// printf("该位图为位真彩色\n\n");
channels = 3;//channels也是需要手动设置,不知道为啥
// width = bmpInfoHeader.biWidth;
// height = bmpInfoHeader.biHeight;
width = 1920;
height = 1080;
bmpImg->width = width;
bmpImg->height = height;
bmpImg->channels = 3;
bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*3*height);
step = channels*width;
offset = (channels*width)%4;
if (offset != 0)
{
offset = 4 - offset;
}
for (i=0; i<height; i++)
{
for (j=0; j<width; j++)
{
for (k=0; k<3; k++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
bmpImg->imageData[(height-1-i)*step+j*3+k] = pixVal;
}
//kzSetMat(bmpImg->mat, height-1-i, j, kzScalar(pixVal[0], pixVal[1], pixVal[2]));
}
if (offset != 0)
{
for (j=0; j<offset; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
}
return bmpImg;
}
#if 0
//这是保存数据为BMP的代码,未验证
bool clSaveImage(char* path, ClImage* bmpImg)
{
FILE *pFile;
unsigned short fileType;
ClBitMapFileHeader bmpFileHeader;
ClBitMapInfoHeader bmpInfoHeader;
int step;
int offset;
unsigned char pixVal = '\0';
int i, j;
ClRgbQuad* quad;
pFile = fopen(path, "wb");
if (!pFile)
{
return false;
}
fileType = 0x4D42;
fwrite(&fileType, sizeof(unsigned short), 1, pFile);
if (bmpImg->channels == 3)//24位,通道,彩图
{
step = bmpImg->channels*bmpImg->width;
offset = step%4;
if (offset != 4)
{
step += 4-offset;
}
bmpFileHeader.bfSize = bmpImg->height*step + 54;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = 54;
fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
bmpInfoHeader.biSize = 40;
bmpInfoHeader.biWidth = bmpImg->width;
bmpInfoHeader.biHeight = bmpImg->height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 24;
bmpInfoHeader.biCompression = 0;
bmpInfoHeader.biSizeImage = bmpImg->height*step;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;
fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
for (i=bmpImg->height-1; i>-1; i--)
{
for (j=0; j<bmpImg->width; j++)
{
pixVal = bmpImg->imageData[i*bmpImg->width*3+j*3];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
pixVal = bmpImg->imageData[i*bmpImg->width*3+j*3+1];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
pixVal = bmpImg->imageData[i*bmpImg->width*3+j*3+2];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
if (offset!=0)
{
for (j=0; j<offset; j++)
{
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
else if (bmpImg->channels == 1)//8位,单通道,灰度图
{
step = bmpImg->width;
offset = step%4;
if (offset != 4)
{
step += 4-offset;
}
bmpFileHeader.bfSize = 54 + 256*4 + bmpImg->width;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = 54 + 256*4;
fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
bmpInfoHeader.biSize = 40;
bmpInfoHeader.biWidth = bmpImg->width;
bmpInfoHeader.biHeight = bmpImg->height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 8;
bmpInfoHeader.biCompression = 0;
bmpInfoHeader.biSizeImage = bmpImg->height*step;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 256;
bmpInfoHeader.biClrImportant = 256;
fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad)*256);
for (i=0; i<256; i++)
{
quad[i].rgbBlue = i;
quad[i].rgbGreen = i;
quad[i].rgbRed = i;
quad[i].rgbReserved = 0;
}
fwrite(quad, sizeof(ClRgbQuad), 256, pFile);
free(quad);
for (i=bmpImg->height-1; i>-1; i--)
{
for (j=0; j<bmpImg->width; j++)
{
pixVal = bmpImg->imageData[i*bmpImg->width+j];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
if (offset!=0)
{
for (j=0; j<offset; j++)
{
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
fclose(pFile);
return true;
}
#endif
chenLeeCV.h
#ifndef CHENLEECV_H
#define CHENLEECV_H
typedef struct
{
//unsigned short bfType;
unsigned long bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned long bfOffBits;
} ClBitMapFileHeader;
typedef struct
{
unsigned long biSize;
long biWidth;
long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} ClBitMapInfoHeader;
typedef struct
{
unsigned char rgbBlue; //该颜色的蓝色分量
unsigned char rgbGreen; //该颜色的绿色分量
unsigned char rgbRed; //该颜色的红色分量
unsigned char rgbReserved; //保留值
} ClRgbQuad;
typedef struct
{
int width;
int height;
int channels;
unsigned char* imageData;
}ClImage;
ClImage* clLoadImage(char* path);
//bool clSaveImage(char* path, ClImage* bmpImg);
#endif
main.c
//#include "stdafx.h"
#include <stdio.h>
#include "chenLeeCV.h"
unsigned char bgr_buffer[6220800];
unsigned char yuv444_buffer[6220800];
unsigned char nv12_buffer[3110400];
unsigned char b_buffer[2073600];
unsigned char g_buffer[2073600];
unsigned char r_buffer[2073600];
int process(char *path)
{
ClImage* img = clLoadImage(path);
char *bgrimage_fake = "bgr_fake.data";
FILE *fp_bgr_fake = fopen(bgrimage_fake,"a+");
if(NULL == fp_bgr_fake)
{
printf("open file[%s] failed!\n", bgrimage_fake);
return -1;
}
fwrite((void*)img->imageData, 1, 6220800, fp_bgr_fake);
fclose(fp_bgr_fake);
memset((void*)b_buffer,0,2073600);
memset((void*)g_buffer,0,2073600);
memset((void*)r_buffer,0,2073600);
int i = 0;
int j = 0;
int k = 0;
static int num = 1;
for(i = 0; i < 1080; i++)
{
for(j = 0; j < 1920; j++)
{
b_buffer[i*1920+j] = *((unsigned char*)img->imageData + k);
k++;
g_buffer[i*1920+j] = *((unsigned char*)img->imageData + k);
k++;
r_buffer[i*1920+j] = *((unsigned char*)img->imageData + k);
k++;
}
}
#if 0
char *bimage = "b.data";
FILE *fp_b = fopen(bimage,"a+");
if(NULL == fp_b)
{
printf("open file[%s] failed!\n", bimage);
return -1;
}
fwrite((void*)b_buffer, 1, 2073600, fp_b);
fclose(fp_b);
char *gimage = "g.data";
FILE *fp_g = fopen(gimage,"a+");
if(NULL == fp_g)
{
printf("open file[%s] failed!\n", gimage);
return -1;
}
fwrite((void*)g_buffer, 1, 2073600, fp_g);
fclose(fp_g);
char *rimage = "r.data";
FILE *fp_r = fopen(rimage,"a+");
if(NULL == fp_r)
{
printf("open file[%s] failed!\n", rimage);
return -1;
}
fwrite((void*)r_buffer, 1, 2073600, fp_r);
fclose(fp_r);
#endif
memset((void*)bgr_buffer,0,6220800);
/* 这张bmp图是按照grbgrb这么存放的,即我们认为的b其实是真实的g,g是r,r是b,虽然不知道为什么会这样 */
k = 0;
for(i = 0; i < 1080; i++)
{
for(j = 0; j < 1920;)
{
bgr_buffer[i * 1920 + j] = r_buffer[k];
bgr_buffer[i * 1920 + j + 1] = b_buffer[k];
bgr_buffer[i * 1920 + j + 2] = g_buffer[k];
j+=3;
k++;
}
}
char *bgrimage = "bgr.data";
FILE *fp_bgr = fopen(bgrimage,"a+");
if(NULL == fp_bgr)
{
printf("open file[%s] failed!\n", bgrimage);
return -1;
}
fwrite((void*)bgr_buffer, 1, 6220800, fp_bgr);
fclose(fp_bgr);
return 0;
}
int main(int argc, char* argv[])
{
int i = 0;
char photo_path[256];
for(i = 1; i < 61; i++)
{
sprintf(photo_path, "photo_test/%d.bmp",i);//这里是进行60张BMP图片处理
process(photo_path);
}
}