2017-03-31 82 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 –