将tga文件转换为黑白文件时出现问题

我一直试图让这个程序将tga图像转换成黑白色。 但我不知道如何去做。 我是C的新手,并且尚未掌握语法,甚至正确使用ubuntu。

我认为我的问题是与tga文件头不能读取的东西。 因为在tga文件上尝试这个程序时得到的结果是一张没有高度的无法打开的图片。 “身高= 0”。

是否有一些很好的链接可供人阅读C?

#include  #include  #include  #include  struct pixel { uint8_t r, g, b, a; }; static uint8_t *load_image(char *filename, int *sizex, int *sizey) { uint8_t *image; char buf[512]; char *bufptr; int ret; FILE *fp = fopen(filename, "r"); bufptr = fgets(buf, 512, fp); ret = fscanf(fp, "%d %d\n", sizex, sizey); bufptr = fgets(buf, 512, fp); image = malloc(*sizex * *sizey * 4); int i; uint8_t *ptr = image; for (i=0; i<*sizex * *sizey; ++i) { ret = fread(ptr, 1, 3, fp); ptr += 4; } fclose(fp); return image; } static int save_image(const char *filename, uint8_t *image, int sizex, int sizey) { FILE *fp = fopen(filename, "w"); fprintf(fp, "P6\n%d %d\n255\n", sizex, sizey); int i; uint8_t *ptr = image; for (i=0; i<sizex * sizey; ++i) { fwrite(ptr, 1, 3, fp); ptr += 4; } fclose(fp); return 1; } void convert_grayscale(uint8_t *input, uint8_t *output, int sizex, int sizey) { // Y = 0.299 * R + 0.587 * G + 0.114 * B int i; for (i = 0; i r + 0.587 * pin->g + 0.114 * pin->b; if (luma > 255) luma = 255; uint8_t intluma = (int) luma; pout->r = intluma; pout->g = intluma; pout->b = intluma; pout->a = 255; } } int main() { uint8_t *inputimg, *outputimg; int sizex, sizey; inputimg = load_image("image.tga", &sizex, &sizey); outputimg = malloc(sizex * sizey * 4); convert_grayscale(inputimg, outputimg, sizex, sizey); save_image("output.tga", outputimg, sizex, sizey); } 

(个人注意事项:阅读之后为什么Stackoverflow会糟透了 。对于获得版主权限的所有人来说,这应该是必读的。)

问题是你的load_image代码似乎是为了读取PPM(基于ASCII的)图像而设计的:

每个PPM图像由以下内容组成:1。用于标识文件类型的“幻数”。 ppm图像的幻数是两个字符“P6”。 2.空白(空白,TAB,CR,LF)。 3.宽度,格式为十进制的ASCII字符。 4.空白。 5.高度,再次以ASCII十进制表示。 6.空白。 7.最大颜色值(Maxval),再次以ASCII十进制表示。 必须小于65536且大于零。 8.单个空白字符(通常是换行符)。 9.高度行的光栅[…]

– 你的第一个fgets读取,然后丢弃,“幻数”行,然后读取宽度和高度,然后丢弃“maxval”行。

它应该适用于PPM图像(并且你可以重命名这个例程load_ppm_image )如果它不是一个重要的问题:在所有的ASCII东西之后,你切换到fread ,所以这里是警告#1

在打开文件之前,请确定您是要读取ASCII文本,还是需要读取二进制数据。

问题是’文本模式’“w”在读写别人时会转换某些字符。 这是所有常见C库中的内置行为; 它试图修复上一代程序员留给我们的行尾字符混乱。 现在,以文本模式读取文本文件变得更简单,但是读取二进制数据是不可能的 。 你不能确定你得到了文件中的确切内容。

让我们继续警告#2:并非所有文件格式都相同。

上述例程(主要)适用于PPM图像,但它会在TGA上失败,因为它的标题组织方式不同。 TGA标题在这里描述得相当好(谷歌搜索结果随机选择)。

规范描述了字节 ,所以首先要做的是将你的fopen行更改为

 FILE *fp = fopen(filename, "rb"); 

顺便说一句,一个好的做法是测试它是否成功:

 if (fp == NULL) { printf ("Opening the file '%s' failed\n", filename); return NULL; } 

然后你可以使用fgetcfread来读取一个或多个字节。 警告#3:小心使用fread

fread按照它们存储到文件中的顺序读取多个字节,因此您可以认为它可以在一个“读取”操作中读取widthheight等项目 – 每个字节为2字节整数值。 但是fread并不知道系统中字节的顺序 (也不知道文件本身的顺序 ),所以它可能是“lo-hi”,就像我指出的规范一样,而在你的计算机中则是字节的顺序整数是“hi-lo”。 澄清:如果文件包含此内容

 80 00 

你用fread (&width,1,2, fp)读取,然后存储,这两个字节以相同的顺序存储到计算机内存中。 字节采用Big-Endian顺序; 最后的“大”字节。 但是如果你的计算机恰好是Little-Endian订单系统,你就不会得到值0x0080 = 128而是0x8000 = 32768

避免这种情况的方法是一次读取一个字节

 width = fgetc(fp) + (fgetc(fp)<<8); 

始终以正确的顺序读取数据:先低,然后高。 只存储总和(按照系统的顺序存储,但现在无关紧要!)。

有了上述内容,我想我已经没有警告了。 使用TGA规范作为指导,您现在可以打开文件,一次读取一个字节的标题,直到您拥有所需的所有信息,并继续将原始图像数据放入内存。 您可以安全地使用fread读取三个图像字节,因为它们在读取时将以相同的顺序显示在内存中(它们不是整数或更大,因此“内存顺序”不是问题)。

确保您正在阅读正确信息的好方法是:

  1. 一次读取一个字节,以防止字节序问题。
  2. 在代码中添加注释,详细说明它是什么
  3. 打印出值
  4. 如果允许值,请检查规格。

为了让你开始,在fopen行之后(以及所需的检查是否有效):

 int idLength = fgetc(fp); /* length of id string after header */ printf ("id length: %u bytes\n", idLength); int colorMapType = fgetc(fp); /* 0 = RGB */ printf ("color map type: %u\n", colorMapType); if (colorMapType != 0) { printf ("unexpected color map type!\n"); return NULL; } int imageType = fgetc(fp); /* 0 = None, 1 = Indexed, 2 = RGB, 3 = Greyscale */ 

.. 等等。 当读取整个标题并且您没有遇到意外时,您就可以设置要读取实际图像数据的内容了。 不需要进行任何更改,您现有的代码应该可以正常工作。


编辑后:我看到我用过了

 int colorMapType = fgetc(fp); 

其中'颜色映射类型'实际上是一个字节 ,而不是整数 。 这是为了让腰带和吊带接近。 如果在读取标题时遇到文件末尾,则fgetc返回的代码为EOFEOF不能存储到char ,因为它是一个整数值: 0xFFFFFFFF (更准确地说: (int)-1 )。 如果将它存储到char ,则无法将其与完全正常的值0x000000FF (值255)区分开来。

腰带和吊带方法是检查每个字节:

 if (colorMapType == EOF) { printf ("encountered unexpected end of file!\n"); return NULL; } 

如果您使用的是已知文件,并且您知道它是有效的TGA(您可以使用位图编辑器查看和编辑它),则会出现问题,但是如果您计划处理您不知道它们是否有效的文件,你可能需要这个。