先放个效果图(原图来自知乎)
彩色图变灰度图用的是三通道平均值法,我上一篇文章就是写的这个BMP真彩图转灰度图
之所以这里还是彩色转像素风,而不是灰度转像素风,是因为我全部做完之后对代码做了一点整合,把很多冗余的部分提取成了函数,把代码简化了
转像素风的算法如下,依据知乎链接里的的内容,加上我自己的理解写的
上代码
宏的图像长宽是为了方便提取函数做的,可以根据自己的需求修改
#include <string.h>
#include"stdio.h"
#include"stdlib.h"
#define ORIFileName "1.bmp"
#define GrayFileName "interim.bmp"
#define DrawFileName "2.bmp"
#define NumberOfShade 5
#define FileHeight 390
#define FileWidth 390
#define BitCount 24
#define BytesPerLine ((FileWidth * BitCount + 31) / 32 * 4) //处理后的每行字节数,为4的倍数
#define Padding (BytesPerLine - FileWidth * 3) //每行补位的0的个数
#define ConversionFactor (255.0 / NumberOfShade) //被置零的最大灰度值
// 文件信息头结构体
typedef struct {
//unsigned short bfType; // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件
unsigned int bfSize; // 文件大小 以字节为单位(2-5字节)
unsigned short bfReserved1; // 保留,必须设置为0 (6-7字节)
unsigned short bfReserved2; // 保留,必须设置为0 (8-9字节)
unsigned int bfOffBits; // 从文件头到像素数据的偏移 (10-13字节)
} BitmapFileHeader;
//图像信息头结构体
typedef struct {
unsigned int biSize; // 此结构体的大小 (14-17字节)
long biWidth; // 图像的宽 (18-21字节)
long biHeight; // 图像的高 (22-25字节)
unsigned short biPlanes; // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1 (26-27字节)
unsigned short biBitCount; // 一像素所占的位数,一般为24 (28-29字节)
unsigned int biCompression; // 说明图象数据压缩的类型,0为不压缩。 (30-33字节)
unsigned int biSizeImage; // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
long biXPelsPerMeter; // 说明水平分辨率,用像素/米表示。一般为0 (38-41字节)
long biYPelsPerMeter; // 说明垂直分辨率,用像素/米表示。一般为0 (42-45字节)
unsigned int biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 (46-49字节)
unsigned int biClrImportant; // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
} BitmapInfoHeader;
void color_to_gray();//彩色图像转为灰度图
void to_draw();//灰度图转像素风
FILE **putInInfo(char *ori_file, char *tran_file);//写入BMP文件头信息
void putInGray(FILE *fp, FILE *fp_new, int choice);//写入灰度值信息。根据choice来决定写入的灰度的数值,choice = 1的时候,执行平均值取灰度;等于0的时候,执行灰度置零
int main() {
color_to_gray();
to_draw();
}
void color_to_gray() {
FILE **fp_list = putInInfo(ORIFileName, GrayFileName);
FILE *fp = fp_list[0], *fp_new = fp_list[1];
putInGray(fp, fp_new, 1);
fclose(fp);
fclose(fp_new);
free(fp_list);
}
void to_draw() {
FILE **fp_list = putInInfo(GrayFileName, DrawFileName);
FILE *fp = fp_list[0], *fp_new = fp_list[1];
putInGray(fp, fp_new, 0);
fclose(fp);
fclose(fp_new);
free(fp_list);
}
FILE **putInInfo(char *ori_file, char *tran_file) {
//文件读取
FILE *fp = fopen(ori_file, "rb"), *new_fp = fopen(tran_file, "wb");
if (fp == NULL) {
printf("Failed to open the original file!");
exit(-1);
}
unsigned short fileType;//单独把文件类型提出来是因为C语言结构体的对齐原则
fread(&fileType, sizeof(unsigned short), 1, fp);
//保证读出来的是BMP文件后再做下一步的读取
BitmapFileHeader fileHeader;
BitmapInfoHeader infoHeader;
if (fileType == 0x4d42) {
fread(&fileHeader, sizeof(BitmapFileHeader), 1, fp);
fread(&infoHeader, sizeof(BitmapInfoHeader), 1, fp);
} else {
printf("Not a BMP File!");
exit(-1);
}
//将BMP的文件头信息写入新创建的文件中
fwrite(&fileType, sizeof(unsigned short), 1, new_fp);
fwrite(&fileHeader, sizeof(BitmapFileHeader), 1, new_fp);
fwrite(&infoHeader, sizeof(BitmapInfoHeader), 1, new_fp);
//为存储文件指针的数组分配内存,避免函数执行完毕后被编译器释放
FILE **fp_list = (FILE **) malloc(sizeof(FILE *) * 2);
fp_list[0] = fp;
fp_list[1] = new_fp;
return fp_list;
}
void putInGray(FILE *fp, FILE *fp_new, int choice) {
unsigned char pixel[3] = {0}, gray = 0;
for (int i = 0; i < FileHeight; i++) {
for (int j = 0; j < FileWidth; j++) {
fread(&pixel, sizeof(unsigned char), 3, fp);
if (choice == 1) {
gray = (pixel[0] + pixel[1] + pixel[2]) / 3;
} else {
gray = (unsigned char) ((int) (pixel[0] / ConversionFactor) * ConversionFactor);
}
//灰度图中RGB的值相同
fwrite(&gray, sizeof(unsigned char), 1, fp_new);
fwrite(&gray, sizeof(unsigned char), 1, fp_new);
fwrite(&gray, sizeof(unsigned char), 1, fp_new);
}
unsigned char pad = 0;
for (int j = 0; j < Padding; j++) {//处理补位
fwrite(&pad, sizeof(unsigned char), 1, fp_new);
fseek(fp, 1, SEEK_CUR);
}
}
}