简体   繁体   中英

Cannot resize .bmp images properly (CS50 pset 4, resize, less comfortable)

I'm currently taking the CS50 course, and I'm completely stuck on the resize problem (less comfortable). The assignment is to make a program that takes an input .bmp file, scales it n times and writes that to an output .bmp file. But my code just kinda distorts the input image and doesn't change the size at all. This is the bmp that is supplied for testing (3x3 pixels, just zoomed in here): https://imgur.com/1v55tjz And when I attempt to resize it, it just turns to this (still 3x3): https://imgur.com/JAKQMfY

This is the code for resizing:

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

#include "bmp.h"

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

    // remember filenames
    int n = atoi(argv[1]);
    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;
    }

    // create file- and infoheader for output file
    BITMAPFILEHEADER outBF = bf;
    BITMAPINFOHEADER outBI = bi;

    //Scale width and height by n
    outBI.biWidth *= n;
    outBI.biHeight *= n;

    //Calculate padding for input and output file
    int outPadding = (4 - (outBI.biWidth * sizeof(RGBTRIPLE) % 4) % 4);
    int inPadding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

    //Calculate biSizeImage and bfSize for output file
    outBI.biSizeImage = ((sizeof(RGBTRIPLE) * outBI.biWidth) + outPadding * abs(outBI.biHeight));
    outBF.bfSize = outBI.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);

    //Instantiate two counters to keep track of whether we have reached the n - 1 times of the recopy method and to be able to fseek to the next line
    int countIterations = 1;
    int countPositionInFile = 0;


    // iterate over infile's scanlines
    for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
    {
        countIterations = 1;
        for(int j = 0; j < n; j++)
        {
            // iterate over pixels in scanline
            for (int k = 0; k < bi.biWidth; k++)
            {
                // temporary storage
                RGBTRIPLE triple;

                // read RGB triple from infile
                fread(&triple, sizeof(RGBTRIPLE), 1, inptr);

                // resize horizontally by writing every pixel in row n times
                for(int l = 0; l < n; l++)
                {
                    // write RGB triple to outfile
                    fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
                }
            }

            // skip over padding, if any
            fseek(inptr, inPadding, SEEK_CUR);

            //Add padding for this line
            for (int l = 0; l < outPadding; l++)
            {
            fputc(0x00, outptr);
            }

            if(countIterations == n)
            {
                countPositionInFile++;
            }

            int newPosition = (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (countPositionInFile * (bi.biWidth + inPadding)));

            // return the "cursor" to the start of the file
            fseek(inptr, newPosition, SEEK_SET);
            countIterations++;
        }

    }

    // close infile
    fclose(inptr);

    // close outfile
    fclose(outptr);

    // success
    return 0;
}    

And this is the bmp.h file:

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

I hope someone has experience with this assignment and can help, I feel pretty stuck right now tbh, but maybe I've just been staring at it for too long. Thanks in advance!

Files should be opened with binary flags:

FILE *inptr = fopen(infile, "rb");
FILE *outptr = fopen(outfile, "wb");

You are ignoring outBI and outBF , you are writing the old header in to new file:

fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);//old header
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);//old header

Two different methods is used here to calculate the padding:

int outPadding = (4 - (outBI.biWidth * sizeof(RGBTRIPLE)  % 4) % 4);
int inPadding =  (4 - (bi.biWidth *    sizeof(RGBTRIPLE)) % 4) % 4;
                                                        ^         ^

They can't both be right, except by accident. In this case we are using 24-bitmaps you can calculate padding as

int outPadding = outBI.biWidth % 4;
int inPadding = bi.biWidth % 4;

This will match the way you skip the padding with fseek(inptr, inPadding, SEEK_CUR); and for(...) fputc(0x00, outptr);

I couldn't figure out this code:

int newPosition = (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) 
    + (countPositionInFile * (bi.biWidth + inPadding)));<==remove
fseek(inptr, newPosition, SEEK_SET);<==remove

That's should be (bi.biWidth * 3) Or you could just remove that and use ftell to save the position before starting to read the line, and use fseek to go back to that position.

Bitmap height should be read from bottom to top, but in this case it doesn't make a difference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM