PGM是存储和交换图像数据的简单文件格式之一。网上有许多博客都有介绍,不多做介绍;只做记录自己学习的过程;
链接信息
如果下面代码有什么问题请指出来;下面采用的是C语言
peek函数(C语言是没有这个函数,自己写的,类似于C++里面的peek函数)
int peek(FILE *fp) {
// todo 判空未做
// 保存当前文件指针的位置
fpos_t pos;
fgetpos(fp, &pos);
int ch;
// 读取文件指针当前指向的字符,文件指针的位置自动指向下一个位置
// 类似于其它高级语言里面的迭代器
ch = fgetc(fp);
fsetpos(fp, &pos);
return ch;
}
skip_comment函数(用来跳过换行和注释信息)
void skip_comment(FILE *fp) {
// 检测当前字符是注释开或者是换行,后面的判断是过滤多个空行
while (peek(fp) == '#'||peek(fp) == '\n') {
// 过滤注释信息,直到换行
while (fgetc(fp) != '\n');
}
}
swap_two_byte函数(用来交换双字节存放的值)
// 移位版
unsigned short swap_two_byte(unsigned short data) {
return ((0xff & data) << 8) | ((0xff00 & data) >> 8);
}
// 指针版
void swap_two_byte_pointer(unsigned short *ptr) {
// 取出第一个字节数据
unsigned char tmp = *((unsigned char *) ptr);
*((unsigned char *) ptr) = *((unsigned char *) ptr + 1);
*((unsigned char *) ptr + 1) = tmp;
}
封装的方法
void pgm_p2_write(const char *fileName, int width, int height, int maxValue) {
FILE *fp = fopen(fileName, "w");
if (fp == nullptr) exit(-1);
const char *comment = "#这是注释信息";
// fputs是一个函数,具有的功能是向指定的文件写入一个字符串(不自动写入字符串结束标记符‘\0’)
fputs("P2", fp);
fputc('\n', fp);
fprintf(fp,"%s\n",comment);
fprintf(fp, "%d %d\n", width, height);
// max value
fprintf(fp, "%d", maxValue);
fputc('\n', fp);
// 数据
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
fprintf(fp, "%d", (i + j) & maxValue);
}
fputc('\n', fp);
}
fclose(fp);
}
测试
int main(){
pgm_p2_write("pgm2.pgm", 30, 30, 50);
}
生成文件结果
封装方法
void pgm_p2_read(const char *fileName) {
FILE *fp = fopen(fileName, "r");
char buf[1024];
fgets(buf, 1024, fp);
printf("协议:%s", buf);
// 跳过空格或注释
skip_comment(fp);
int width, height;
fscanf(fp, "%d %d", &width, &height);
printf("%d %d\n", width, height);
// 跳过空格或注释
skip_comment(fp);
int max;
fscanf(fp, "%d", &max);
printf("%d\n", max);
// 跳过空格或注释
skip_comment(fp);
unsigned data[width][height];
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
fscanf(fp, "%d", &data[i][j]);
printf("%d ", data[i][j]);
}
printf("\n");
}
fclose(fp);
}
封装方法
void pgm_p5_write(const char *fileName, int width, int height, int maxValue) {
FILE *fp = fopen(fileName, "wb+");
if (fp == nullptr) exit(-1);
fprintf(fp, "P5\n");
fprintf(fp, "%d %d\n", width, height);
// max value
fprintf(fp, "%d\n", maxValue);
// 判断数据写入占用的字节数,只做1字节和2字节的
int dataSize = 1;
if (maxValue > UCHAR_MAX) {
dataSize = 2;
}
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
unsigned short value = (i + j) % maxValue == 0 ? maxValue : (i + j) % maxValue;
if (dataSize == 2) {
value = swap_two_byte(value);
}
// printf("%d ", value);
fwrite(&value, dataSize, 1, fp);
}
// printf("\n");
}
fclose(fp);
}
封装方法
void pgm_p5_read(const char *fileName) {
FILE *fp = fopen(fileName, "rb");
char buf[1024];
fscanf(fp, "%s", buf);
printf("%s\n", buf);
skip_comment(fp);
skip_comment(fp);
int width, height;
fscanf(fp, "%d %d", &width, &height);
printf("%d %d\n", width, height);
skip_comment(fp);
unsigned max = 0;
fscanf(fp, "%d", &max);
printf("%d\n", max);
if (peek(fp) == '\n') {
fgetc(fp);
}
// data
unsigned num;
if (max > UCHAR_MAX) {
num = 2;
} else {
num = 1;
}
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
unsigned short data;
fread(&data, num, 1, fp);
// if (num == 2) data = swap_two_byte(data); // 移位操作
if (num == 2) swap_two_byte_pointer(&data); // 地址操作
printf("%d ", data);
}
printf("\n");
}
fclose(fp);
}
测试代码
int main() {
pgm_p2_write("p2_file.pgm", 100, 200, 257);
pgm_p5_write("p5_file.pgm", 100, 200, 257);
}
结果