2017-03-31 22 views
0

新增至C.一直在用我的代碼調整24位未壓縮位圖的問題。我試圖將這幅圖像縮小n倍,我覺得我已經接近正常工作,但是我的輸出圖像仍然不正確。C調整BMP文件的N因子大小,我做錯了什麼?

我可以發佈輸入24位非壓縮BMP(small.bmp)的圖片我用於測試,我的程序輸出的圖片(resized.bmp),以及small.bmp縮放的正確圖像4的因子應該看起來像,如果這會有所幫助。請問。

resize.c

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

#include "bmp.h" 

int main(int argc, char *argv[]) 
{ 
    // ensure proper usage 
    if (argc != 4) 
    { 
     fprintf(stderr, "Usage: ./resize scale infile outfile\n"); 
     return 1; 
    } 

    int n = atoi(argv[1]); 
    // remember filenames 
    char *infile = argv[2]; 
    char *outfile = argv[3]; 

    // open input file 
    FILE *inptr = fopen(infile, "r"); 
    if (inptr == NULL) 
    { 
     fprintf(stderr, "Could not open %s.\n", infile); 
     return 2; 
    } 

    // open output file 
    FILE *outptr = fopen(outfile, "w"); 
    if (outptr == NULL) 
    { 
     fclose(inptr); 
     fprintf(stderr, "Could not create %s.\n", outfile); 
     return 3; 
    } 

    // read infile's BITMAPFILEHEADER 
    BITMAPFILEHEADER bf; 
    fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr); 


    // read infile's BITMAPINFOHEADER 
    BITMAPINFOHEADER bi; 
    fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr); 

    // ensure infile is (likely) a 24-bit uncompressed BMP 4.0 
    if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 || 
     bi.biBitCount != 24 || bi.biCompression != 0) 
    { 
     fclose(outptr); 
     fclose(inptr); 
     fprintf(stderr, "Unsupported file format.\n"); 
     return 4; 
    } 
    int oldpadding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; 
    bi.biWidth = bi.biWidth * n; 
    bi.biHeight = bi.biHeight * n; 

    // determine padding for scanlines 
    int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; 

bi.biSizeImage = ((sizeof(RGBTRIPLE) * bi.biWidth) + padding) * abs(bi.biHeight); 
bf.bfSize = bi.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); 
// write outfile's BITMAPFILEHEADER 
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr); 

    // write outfile's BITMAPINFOHEADER 
    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr); 


    // iterate over infile's scanlines 
    for (int i = 0, biHeight = abs(oldHeight); i < biHeight; i++) 
    { 

     // store scanline in an array pixel by pixel for vertical scaling. 
     RGBTRIPLE scanline[bi.biWidth - 1]; 

     // iterate over pixels in scanline 
     for (int j = 0; j < bi.biWidth; j++) 
     { 
      //check if we've hit padding in original bmp. 
      if (j % oldpadding == 0 && j != 0) 
      { 
       //skip the padding. 
       fseek(inptr, oldpadding, SEEK_CUR); 
      } 

      // temporary storage 
      RGBTRIPLE triple; 

      fread(&triple, sizeof(RGBTRIPLE), 1, inptr); 

      for (int h = 0; h < n; h++) 
      { 
       //scale horizontally, save each scanline pixel to our array. 
       scanline[j] = triple; 
      } 

     } 

     for (int x = 0; x < n; x++) 
     { 
      //write scanlines n - 1 times. 
      for (int y = 0; y < bi.biWidth; y++) 
      { 
        fwrite(&scanline[y], sizeof(RGBTRIPLE), 1, outptr); 
      } 

      //write padding if any for current scanline. 
      for (int z = 0; z < padding; z++) 
      { 
       fputc(0x00, outptr); 
      } 
     } 

    } 

    // close infile 
    fclose(inptr); 

    // close outfile 
    fclose(outptr); 

    // success 
    return 0; 
} 

bmp.h

/** 
* BMP-related data types based on Microsoft's own. 
*/ 

#include <stdint.h> 

/** 
* Common Data Types 
* 
* The data types in this section are essentially aliases for C/C++ 
* primitive data types. 
* 
* Adapted from https://msdn.microsoft.com/en-us/library/cc230309.aspx. 
* See http://en.wikipedia.org/wiki/Stdint.h for more on stdint.h. 
*/ 
typedef uint8_t BYTE; 
typedef uint32_t DWORD; 
typedef int32_t LONG; 
typedef uint16_t WORD; 

/** 
* BITMAPFILEHEADER 
* 
* The BITMAPFILEHEADER structure contains information about the type, size, 
* and layout of a file that contains a DIB [device-independent bitmap]. 
* 
* Adapted from https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx. 
*/ 
typedef struct 
{ 
    WORD bfType; 
    DWORD bfSize; 
    WORD bfReserved1; 
    WORD bfReserved2; 
    DWORD bfOffBits; 
} __attribute__((__packed__)) 
BITMAPFILEHEADER; 

/** 
* BITMAPINFOHEADER 
* 
* The BITMAPINFOHEADER structure contains information about the 
* dimensions and color format of a DIB [device-independent bitmap]. 
* 
* Adapted from https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx. 
*/ 
typedef struct 
{ 
    DWORD biSize; 
    LONG biWidth; 
    LONG biHeight; 
    WORD biPlanes; 
    WORD biBitCount; 
    DWORD biCompression; 
    DWORD biSizeImage; 
    LONG biXPelsPerMeter; 
    LONG biYPelsPerMeter; 
    DWORD biClrUsed; 
    DWORD biClrImportant; 
} __attribute__((__packed__)) 
BITMAPINFOHEADER; 

/** 
* RGBTRIPLE 
* 
* This structure describes a color consisting of relative intensities of 
* red, green, and blue. 
* 
* Adapted from https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx. 
*/ 
typedef struct 
{ 
    BYTE rgbtBlue; 
    BYTE rgbtGreen; 
    BYTE rgbtRed; 
} __attribute__((__packed__)) 
RGBTRIPLE; 
+0

刪除了我用於測試的一些不必要的行。 –

+0

[SO:this question](http://stackoverflow.com/questions/41861274/scaling-up-an-image-using-nearest-neighbor)似乎有同樣的錯誤(儘管它有點簡單)。當掃描一行時:'j CristiFati

+0

@CristiFati將條件更改爲你所提到的,仍然不是正確的輸出。效率<這種情況下的正確性也是。而檢查函數返回碼也超出了我的實際問題範圍。 –

回答

1

您的代碼有兩種技術用於縮放的混合物:1)在陣列中縮放和然後寫出來,2)讀出原始大小的數組並將其寫出。然而,你的第一技術的實施實際上並沒有做任何縮放:

for (int h = 0; h < n; h++) 
{ 
    //scale horizontally, save each scanline pixel to our array. 
    scanline[j] = triple; 
} 

因爲你不增加Ĵ,你只是覆蓋相同的三層到同一位置n次。它根本不做任何縮放。我建議放棄第一種技術,只關注第二種技術。對於初學者來說,解決您的數組聲明:

RGBTRIPLE scanline[bi.biWidth]; 

然後,當你讀,只寫三到正確的位置沒有任何循環:

scanline[j] = triple; 

當你寫出來,你有循環繞錯誤的方向。您想循環遍歷像素並將每個像素寫出n次,而不是在圖像上循環n次並寫出所有像素(這將重複該圖像n次,而不是將其縮放n)。然後將其全部包裝在一個循環中以輸出每一行n次。

for (int y = 0; y < n; y++) // repeat each row n times 
{ 
    for (int x = 0; x < bi.biWidth; x++) // iterate over pixels 
    { 
     for (int r = 0; r < n; r++) // repeat each pixel n times 
      fwrite(&scanline[x], sizeof(RGBTRIPLE), 1, outptr); 
    } 
    // write padding if any for current scanline. 
    for (int z = 0; z < padding; z++) 
    { 
     fputc(0x00, outptr); 
    } 
} 

這是縮放圖像的基本算法。您可以通過執行塊I/O讀取和寫入來提高速度,而不是在循環中調用fwrite,但正如您在評論中指出的那樣,正確性>效率。你可能也有一些填充問題,我沒有解決這個問題。

+0

感謝您的詳細回覆。我還發現,我並沒有增加j,這只是我忽略的一件愚蠢的事情。我想你有時會失敗學習:P –