2013-11-24 63 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(可以查看並使用位圖編輯器進行編輯),但是如果您計劃處理不知道它們是否有效的文件,則可能需要這樣做。