问题转换TGA文件为黑色和白色 [英] Problems converting tga files to black and white

查看:211
本文介绍了问题转换TGA文件为黑色和白色的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力使这个节目的TGA图像转换为颜色分为黑色和白色。但我不知道如何去做。我柠新的C,还没有得到语法的窍门,甚至Ubuntu的正确使用。

我想我的问题是财产以后与THA TGA文件头部着被读取。因为结果对TGA文件尝试这一程序时,我得到的是一个unopenable画面,没有高度。 高度= 0。

有一些很好的联系为一体,以基于C读了?

 的#include< inttypes.h>
#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;结构像素{
    uint8_t有R,G,B,A;
};静态uint8_t有* load_image(字符*文件名,INT * SIZEX,为int * SIZEY)
{
    uint8_t有*形象;
    焦炭BUF [512];
    字符* bufptr;
    INT RET;    FILE *计划生育=的fopen(文件名,R);
    bufptr =与fgets(BUF,512,FP);
    RET =的fscanf(FP,%D \\ n,SIZEX,SIZEY);
    bufptr =与fgets(BUF,512,FP);    图像=的malloc(* SIZEX * * * SIZEY 4);    INT I;
    uint8_t有* PTR =图像;
    对于(i = 0; I< * SIZEX * SIZEY; ++ I){
        RET = FREAD(PTR,1,3,FP);
        PTR + = 4;
    }    FCLOSE(FP);
    返回形象;
}静态INT save_image(为const char *文件名,uint8_t有*形象,诠释SIZEX,诠释SIZEY)
{
    FILE *计划生育=的fopen(文件名,W);
    fprintf中(FPP6 \\ n%D \\ N255 \\ n,SIZEX,SIZEY);    INT I;
    uint8_t有* PTR =图像;
    对于(i = 0; I< SIZEX * SIZEY; ++ I){
        FWRITE(PTR,1,3,FP);
        PTR + = 4;
    }
    FCLOSE(FP);    返回1;
}无效convert_grayscale(uint8_t有*输入,uint8_t有*输出,INT SIZEX,诠释SIZEY)
{
    // Y = 0.299 * R + 0.587 * G + 0.114 * B    INT I;    对于(i = 0; I< SIZEX * SIZEY; ++ I)
        {
            结构像素*销=(结构*像素)及输入[我* 4];
            结构像素*噘嘴=(结构*像素)及输出[我* 4]。            浮动亮度= 0.299 *引脚> R + 0.587 *引脚> G + 0.114 *引脚> B;            如果(亮度> 255)
                亮度= 255;            uint8_t有intluma =(int)的亮度;            pout-> R = intluma;
            pout-> G = intluma;
            pout-> B = intluma;
            pout->一种= 255;
        }}诠释的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);
}


解决方案

(个人注释:阅读的为什么吸#1 ,这应该必读大家谁得到主持人的特权。)

问题是你的 load_image code似乎旨在阅读PPM(基于ASCII)图像:


  

每个PPM图像包括以下内容:
  1.幻数识别文件类型。一个PPM图像的幻数是两个字符P6。
  2.空格(空格,制表符,CRS,LFS)。
  3.宽度,格式为十进制ASCII字符。
  4.空白。
  5.高度,又在ASCII码。
  6.空白。
  7.最大颜色值(MAXVAL),又在ASCII码。必须小于65536和大于零。
  8,单个空格字符(通常是新行)。
  9.高度的行光栅[...]


- 您的第一个与fgets 读取,然后丢弃的幻数行,其次是阅读的宽度和高度,然后丢弃MAXVAL行。

它应该为PPM图像工作(你可以重命名这个程序 load_ppm_image ),如果不是一个重要的问题:毕竟是ASCII东西,你切换到 FREAD ,所以这里是警告#1

打开你的文件之前,决定是否要读的完全的ASCII文本,或的可能的需要读取二进制数据。

问题是,文本模式的W的转换的读取和写入到别人当某些字符。这是内置于所有常见的C库行为;它会尝试修复结束行字符的混乱,一个previous一代程序员给我们留下。现在,在文本模式下阅读文本文件得到了一个有点简单,但阅读二进制数据的无法的。你不能确定你有什么是在文件中。

让我们与警告#2:不是所有的文件格式都是一样的

上面的日常工作​​(大部分)PPM的图像,但它会在TGA由于其头部被不同组织失败。在TGA标题中描述相当不错这里(谷歌搜索结果的随机挑选)。

规范描述的字节的,这样做的第一件事就是改变你的的fopen

  FILE *计划生育=的fopen(文件名,RB);

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

 如果(FP == NULL)
{
   的printf(打开文件%s'失败\\ n,文件名);
   返回NULL;
}

然后你可以使用龟etc FREAD 读一个或多个字节。这里谈到的警告#3:用 FREAD 小心

FREAD 读取它们被存储在文件中的顺序多个字节,所以你会觉得它可以读取一个项目,如宽度高度 - 每个2字节整数值 - 在一个读操作。但 FREAD 不知道的为了的字节的系统(也不是文件本身),所以它可能是它读取LO -hi,在规范中我指出了,而你的电脑字节的整数的顺序是高低。为了澄清:如果文件中包含该

  80 00

和你读,然后存储,这与 FREAD(安培;宽度,1,2,FP),这2个字节得到存储在计算机内存中相同的顺序。该字节是大端顺序;在大字节结尾。但是,如果你的电脑恰好是一个little-endian顺序的系统,你不会得到值 0x0080 = 128 ,但为0x8000 = 32768 ,而不是!

绕过这个问题的方法是阅读的一个字节的一次:

  WIDTH =龟etc(FP)+(龟etc(FP)LT;< 8);

总是的读取正确的顺序数据:低,再高。只有总和被存储(在您的系统的命令,但现在无关!)。

通过上面的,我想我了警告。使用TGA规范为指导,你现在就可以打开该文件,一次读取头一个字节,直到你有公司所需的所有信息,并继续 FREAD 您的原始图象数据进入存储器。您的可以的安全使用 FREAD 在一个时间来阅读你的形象字节3,因为它们会出现在相同的顺序到内存中,因为他们读(他们不是整数或更大,因此,存储器命令不是问题)。

一个很好的方法,以确保您正在阅读的正确的信息是这样的:


  1. 在一次读一个字节,以prevent字节序的问题。

  2. 在code添加评论,详细说明它是什么

  3. 打印出来的值

  4. 检查与规范,如果值是允许的。

要你开始,在的fopen 行(和所需的检查,如果它的工作)后:

  INT idLength =龟etc(FP); / *头之后的ID字符串长度* /
的printf(ID长度:%u字节\\ n,idLength);
INT colorMapType =龟etc(FP); / * 0 = RGB * /
的printf(彩色地图类型:%U \\ N,colorMapType);
如果(colorMapType!= 0)
{
   的printf(意外彩色地图类型\\ n!);
   返回NULL;
}
INT IMAGETYPE =龟etc(FP); / * 0 =无,1 =索引,2 = RGB,3 =灰度* /

..等等。当整个头部已经阅读并没有遇到惊喜,你准备设立物读取实际的图像数据。需要有任何的变化,现有code应该只是罚款。


后期编辑:我看到我用

  INT colorMapType =龟etc(FP);

其中,彩色地图类型,其实是一个的字节的,不是的整数的。这是允许的带和吊带的方法。如果遇到文件的末尾,而你正在阅读的头球攻门,code,它龟etc 返回的是 EOF EOF 不能存储到字符,因为它是一个整数值: 0xFFFFFFFF的(更准确的:(INT)-1 )。如果你把它存储到一个字符,你不能从完全没问题值 0x000000FF (价值255)区分开来。

的皮带和吊杆的方法是检查每个单字节:

 如果(colorMapType == EOF)
{
   的printf(!遇到意外的文件结束\\ n);
   返回NULL;
}

矫枉过正,如果你是一个的的文件工作,你知道这是一个有效的TGA(您可以查看和使用位图编辑器编辑它),但是如果你曾经计划上的文件,其中你不知道它们是有效的,你可能需要这个。

I have been trying to make this program to convert a tga image for color into black and white. But i have no clue how to go about it. I am verry new to C and have yet to get the hang of the syntax and even proper usage of ubuntu.

I think my problem is somthing with tha tga files header cant be read. Because the result i get when trying this program on a tga file is an unopenable picture with no height. "height = 0".

Is there some good links for one to read up on 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);
}

解决方案

(Personal note: A longer answer after reading Why Stackoverflow sucks. That should be Required Reading for everyone who gets Moderator privileges.)

The problem is your load_image code seems designed to read PPM (ASCII-based) images:

Each PPM image consists of the following: 1. A "magic number" for identifying the file type. A ppm image's magic number is the two characters "P6". 2. Whitespace (blanks, TABs, CRs, LFs). 3. A width, formatted as ASCII characters in decimal. 4. Whitespace. 5. A height, again in ASCII decimal. 6. Whitespace. 7. The maximum color value (Maxval), again in ASCII decimal. Must be less than 65536 and more than zero. 8. A single whitespace character (usually a newline). 9. A raster of Height rows [...]

-- your first fgets reads, then discards, the "magic number" line, followed by reading the width and height, and then discarding the "maxval" line.

It ought to work for PPM images (and you could rename this routine load_ppm_image) were it not for a single important issue: after all that ASCII stuff, you switch to fread, and so here is Warning #1.

Before opening your file, decide whether you are going to read exclusively ASCII text, or might need to read binary data.

The problem is that 'text mode' "w" converts certain characters when reading and writing into others. That's built-in behavior in all common C libraries; it attempts to fix the end-of-line characters mess that a previous generation of programmers left us with. Now, reading text files in text mode got a bit simpler, but reading binary data is impossible. You can't be sure you got exactly what was in the file.

Let's get on with Warning #2: not all file formats are the same.

The above routine works (mostly) for PPM images, but it will fail on TGA because its header is organized differently. The TGA header is described rather well here (a random pick of Google results).

The specification describes bytes, so first thing to do is change your fopen line to

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

and, by the way, a good practice is to test if it was successful:

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

Then you can use fgetc or fread to read one or more bytes. Here comes Warning #3: use fread with care.

fread reads multiple bytes in the order in which they are stored into the file, and so you would think it may read an item such as width and height -- each a 2-byte integer value -- in one 'read' operation. But fread does not know the order of the bytes in your system (nor in the file itself), and so it could be it reads "lo-hi", as in the specification I pointed to, while in your computer the order of bytes in an integer is "hi-lo". To clarify: if the file contains this

80 00

and you read, then store, this with fread (&width,1,2, fp), these 2 bytes get stored into computer memory in that same order. The bytes are in Big-Endian order; the "large" byte is at the end. But if your computer happens to be a Little-Endian order system, you would not get the value 0x0080 = 128 but 0x8000 = 32768 instead!

The way to circumvent this is to read one byte at a time:

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

will always read the data in the correct order: low first, then high. Only the sum gets stored (in the order for your system, but that's now irrelevant!).

With the above, I think I'm out of warnings. Using the TGA specifications as a guide, you can now open the file, read the header one byte at a time until you have all information that's needed, and continue to fread your raw image data into memory. You can safely use fread to read your image bytes three at a time because they will appear in the same order into memory as they were read (they are not integers or larger, so the "memory order" is not an issue).

A good approach to ensure you are reading the correct information is this:

  1. Read one byte at a time, to prevent endianness issues.
  2. Add a comment in your code detailing what it is
  3. Print out the value
  4. Check with the specifications if the value is allowed.

To get you started, after the fopen line (and the required check if it worked):

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 */

.. and so on. When the entire header has been read and you didn't encounter surprises, you are ready to set up things to read the actual image data. No changes needed there, your existing code should work just fine.


Post-edit: I see I used

int colorMapType = fgetc(fp);

where the 'color map type' is in fact a byte, not an integer. That is to allow a belt-and-suspenders approach. If you encounter the end of the file while you are reading the header, the code that fgetc returns is EOF. EOF cannot be stored into a char, because it is an integer value: 0xFFFFFFFF (more accurately: (int)-1). If you store it into a char, you cannot distinguish it from the perfectly okay value 0x000000FF (the value 255).

The belt-and-suspender approach is to check each and every single byte:

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

Overkill if you are working with a known file, and you know it's a valid TGA (you can view and edit it with bitmap editors), but if you ever plan to work on files of which you don't know if they are valid, you might need this.

这篇关于问题转换TGA文件为黑色和白色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆