2013-11-24 58 views
-1

我一直在努力使这个节目的TGA图像转换为颜色分为黑色和白色。但我不知道如何去做。我对C很感兴趣,并且还没有掌握ubuntu的语法和适当的用法。问题转换TGA文件,以黑色和白色

我想我的问题是财产以后与THA TGA文件头不能被读取。因为我在tga文件上试用这个程序时得到的结果是一张没有高度的无法打开的图片。 “高度= 0”。

是否有一个基于C阅读了一些很好的联系?

#include <inttypes.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

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 < sizex * sizey; ++i) 
     { 
      struct pixel *pin = (struct pixel*) &input[i*4]; 
      struct pixel *pout = (struct pixel*) &output[i*4]; 

      float luma = 0.299 * pin->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); 
} 
+0

您使用读取模式“w”,fgets和fscanf,表明您认为TGA规范适用于* text *文件,而实际上它们是* binary *格式。不是每个图像都以相同的方式存储 - 看起来您正在假设TGA == PPM。要么开始尝试使用纯文本图像格式(如PPM),要么查阅如何读取二进制数据(以及TGA规格)。 – usr2564301

+1

我应该在阅读之前阅读莫言的东西。 – user3013923

+0

这永远不会伤害。尝试一步到位的方法 - 您的问题描述似乎是您已经写出转换后的TGA文件,而问题明显源于您的输入。向你的load_image函数中加入大量'printf'来持续检查你是否得到了你期望的数字。 – usr2564301

回答

1

(个人注意:阅读Why Stackoverflow sucks后一个较长的答案应该要求阅读为大家谁得到版主权限)

问题是你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标头描述得相当好here(随机选择Google结果)。

规范描述字节,所以首先要做的就是改变你的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),这2个字节得到存储在计算机内存中相同的顺序。字节以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(可以查看并使用位图编辑器进行编辑),但是如果您计划处理不知道它们是否有效的文件,则可能需要这样做。