C语言读存处理BMP格式图片框架
注明:此文章有学习SCDN某博客的代码,因此有的变量命名可能一样,但是原代码在我电脑上完全跑不通,因此自己对其代码进行了调试并进行大改,下面是自己的工作成果。
注明:希望路人珍惜博主的努力成果,不要随意转载,如若转载请附上详细的链接,谢谢。如果希望博主更新指定文章,可私聊。
1.BMP格式详解参考以下博客
https://blog.csdn.net/nicholas_duan/article/details/90717599?ops_request_misc=&request_id=&biz_id=102&utm_source=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-3
我简要说明BMP图片的重点
(1)BMP图片含有文件头、信息头、调色板(可有可无)、数据
(2)BMP图片数据(RGB为例)是以BGR的格式在存储,同时数据存储左下角是原点
2.此篇文章代码可实现的功能
(1)读取BMP图片(8位、24位等)
(2)存储BMP图片(彩色、灰度等)
(3)彩色图片转灰度图片
(4)中间可穿插各种图像处理的算法
3.代码部分
代码部分共三个文件,每个文件对应下面的一段代码
(1)定义文件头、信息头、调色板、数据的结构体
#include <stdio.h>
#include <stdlib.h>
#ifndef BMP_H
#define BMP_H
typedef struct
{
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
代码解释:#ifndef和#endif是一起使用的,功能是防止此头文件被重复编译。同时此头文件命名为bmp.h。
(2)读取图片和存储图片的代码
#include "bmp.h"
#include <stdlib.h>
#pragma pack(4)
//用来载入bmp图片
ClImage* clLoadImage(char* path)
{
ClImage* bmpImg;
FILE* pFile;
unsigned short fileType;
ClBitMapFileHeader bmpFileHeader;
ClBitMapInfoHeader bmpInfoHeader;
int channels = 1;
long width = 0;
long 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);
printf("%X\n", fileType);
if (fileType == 0x4D42)
{
fread(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
//printf("%d\n", sizeof(ClBitMapFileHeader));
fread(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
//printf("%d\n", sizeof(ClBitMapInfoHeader));
printf("%d\n", bmpFileHeader.bfSize);
printf("%d\n", bmpFileHeader.bfReserved1);
printf("%d\n", bmpFileHeader.bfReserved2);
printf("%d\n", bmpFileHeader.bfoffBits);
printf("\n");
printf("%d\n", bmpInfoHeader.biBitCount);
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("%d\n", bmpInfoHeader.biXPelsPerMeter);
printf("%d\n", bmpInfoHeader.biYPelsPerMeter);
printf("%d\n", bmpInfoHeader.biClrUsed);
printf("%d\n", bmpInfoHeader.biClrImportant);
if (bmpInfoHeader.biBitCount == 8)
{
channels = 1;
width = bmpInfoHeader.biWidth;
height = bmpInfoHeader.biHeight;
offset = (channels * width) % 4;
if (offset!=0)
{
offset = 4 - offset;
}
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)
{
channels = 3;
bmpImg->width = bmpInfoHeader.biWidth;
bmpImg->height = bmpInfoHeader.biHeight;
bmpImg->channels = 3;
bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*(bmpImg->width)*3*(bmpImg->height));
step = channels * bmpImg->width;
offset = (channels *bmpImg->width) % 4;
if (offset!=0)
{
offset = 4 - offset;
}
int num=0;
for (i=0;i<bmpImg->height;i++)
{
for (j=0;j<bmpImg->width;j++)
{
for (k=0;k<3;k++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
bmpImg->imageData[(bmpImg->height-1-i)*(bmpImg->width*3)+j*3+k] = pixVal;
num+=1;
//printf("%d\n",bmpImg->imageData[(height-1-i)*step+j*3+k]);
}
}
if (offset != 0)
{
for (j=0; j<offset; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
printf("\n总共为:%d\n",num);
}
}
return bmpImg;
}
//用来保存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)
{
step = bmpImg->channels*bmpImg->width;
offset = step%4;
if (offset !=0 )
{
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<4-offset; j++)
{
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
//单通道
else if (bmpImg->channels == 1)
{
step = bmpImg->width;
offset = step % 4;
if (offset !=0)
{
step += 4 - offset;
}
bmpFileHeader.bfSize = 54 + 256*4 + bmpImg->height * step;
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 = 0;
bmpInfoHeader.biClrImportant = 0;
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);
int num=0;
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);
num+=1;
}
if (offset !=0)
{
for (j=0; j<4-offset; j++)
{
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
printf("\n总共调用:%d\n",num);
}
fclose(pFile);
return true;
}
此段代码里面的注释很少,但是你如果仔细看了结构体的内容,相信你一定能看的懂我写了什么东西。因为此文件就分为两部分一个载入图片,一个存取图片。而且这些代码的逻辑被我整理的很清晰。此文命名为imgprocess.c。
(3)算法部分
下面以模糊C均值聚类为例,综合上述的读取图片、存取图片的步骤
#include <stdio.h>
#include <math.h>
#include "bmp.h"
#include "imgprocess.cpp"
#define class_ 2
#define m 2
#define cycle 100
int main()
{
//下面从13行到 40行是固有代码,用于将彩色图转换为灰度图
int i, j, k;
//用于定义读取的图片
char *fileName1 = "F:\\fire.bmp";
//用于定于即将保存的图片
char *savefile = "F:\\fire_2.bmp";
ClImage* rainbow1 = clLoadImage(fileName1);
int width=rainbow1->width;
int height=rainbow1->height;
int channels = rainbow1->channels;
printf("\n%d--%d--%d\n", height, width, channels);
printf("\n我被执行了,要开始工作了\n");
//跟灰白图像开辟空间
ClImage* imgGray=(ClImage*)malloc(sizeof(ClImage));
imgGray->channels = 1;
imgGray->height = height;
imgGray->width = width;
imgGray->imageData = (unsigned char*)malloc(height*width);
unsigned char value;
//因为BMP格式图像是BGR,因此R在最后面,权重为0.299
for (i=0; i<height; i++)
{
for (j=0; j<width; j++)
{
int prior = (i*width+j)*3;
value = (unsigned char)(rainbow1->imageData[prior]*0.114 + rainbow1->imageData[prior+1]*0.587 + rainbow1->imageData[prior+2]*0.229);
imgGray->imageData[i*width+j]= value;
//printf("%d", imgGray->imageData[i]);
}
}
//下面是各种算法的正文,是可变部分,开头和结尾固定,不用修改。
//-----------------------------------------------------------------------
//读取图片数据,此处可以被替换
int *x = (int*)malloc(sizeof(int)*height*width);
for (i=0;i<height*width;i++)
{
x[i] = imgGray->imageData[i];
}
float u[class_];
float a[height * width][class_];
//初始化隶属度矩阵
for (i=0;i<height*width;i++)
{
for (j=0;j<class_;j++)
{
a[i][j] = 0.0;
}
}
//初始化类别中心
for (i=0;i<class_;i++)
{
u[i] = 255.0/(2*class_) + i*(255.0/class_);
printf("%f ", u[i]);
}
printf("\n");
int c=0;
//FCM均值聚类
double cusum=0;
float sum1=0;
float sum2=0;
for (c=0; c<cycle; c++)
{
//更新隶属度
for (j=0;j<height*width;j++)
{
cusum=0.0;
for (k=0;k<class_;k++)
{
cusum += pow(1/pow(x[j]-u[k], 2.0), 1/(m-1));
}
for (k=0;k<class_;k++)
{
a[j][k] = 1/(pow(pow(x[j]-u[k], 2.0), 1/(m-1))*cusum);
}
}
//更新每类中心
for (k=0; k<class_; k++)
{
sum1=0;
sum2=0;
for (i=0;i<height*width;i++)
{
sum1 += x[i]*pow(a[i][k], m);
sum2 += pow(a[i][k], m);
}
u[k] = sum1 / sum2;
printf("%f ",u[k]);
}
printf("\n");
}
//得到隶属度最大的类别
char *label;
label = (char*)malloc(sizeof(char) *height*width);
for (i=0;i<height*width;i++)
{
int maxlabel= 1;
float maxvalue = a[i][0];
for (k=1;k<class_;k++)
{
if (maxvalue < a[i][k])
{
maxlabel = k+1;
maxvalue = a[i][k];
}
}
label[i] = maxlabel;
}
//----------------------------------------------------------------------------------------
//将分类好的图片以BMP格式存入,此段也是固定代码
ClImage* saveimg;
saveimg = (ClImage*)malloc(sizeof(ClImage));
saveimg->width = width;
saveimg->height = height;
saveimg->channels = 1;
saveimg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*height*width);
for (i=0;i<height*width;i++)
{
saveimg->imageData[i]=label[i]*255/class_;
}
bool flag = clSaveImage(savefile, saveimg);
if (flag)
{
printf("\n灰度图生成成功..程序跑完了,see you lala\n");
}
return 0;
}
此文件可随意命名,因为main()在里面,以上代码在不同电脑上只需修改一下最后一个文件里的读取和存取图片路径即可,关于具体的逻辑此段代码里面的注释写的很清楚。
4.综合上述代码结果展示
以下分别是原图、2聚类、3聚类、5聚类的图像,以下图像均是由上述代码生成。