4


I'm now doing the pset4 resize more version as I have already done the 'less version' with successfull check50.

For my 'more version' check50 indicates 3 errors although visually my outputs look good.

Here is output of check50:
check50 output

So I focus on resizing 1x1 with factors 3, 4 and 5 (name: onepix.bmp). I have created 1x1 pixel bmp file (24 bits) and use it as a source for resizing in order to be on the same line with check50. The intension was to compare files created by my code and the staff code (using programs peek, xxd and diff). Firstly I have created files for comparison like so:
~/workspace/pset4/resizeMORE/ $ ./resize 4 onepix.bmp student.bmp ~/workspace/pset4/resizeMORE/ $ ~cs50/hacker4/resize 4 onepix.bmp staff.bmp

Comparing headers with PEEK

using:

~cs50/pset4/peek student.bmp staff.bmp
peek output

Comparing with XXD (headers including)

using:

~/workspace/pset4/resizeMORE/ $ xxd -c 24 -g 3 staff.bmp and
~/workspace/pset4/resizeMORE/ $ xxd -c 24 -g 3 student.bmp
xxd output

Comparison with DIFF

I have also tried diff program but there wasn't found any difference between compared two files.

diff output

Source code

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "bmp.h"

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

// remember filenames
char *infile = argv[2];
char *outfile = argv[3];

//n has to be positive integer
float factor = atof(argv[1]);
if (factor <= 0 || factor > 100)
{
    fprintf(stderr, "factor in range (0.0, 100.0].\n");
    return 1;
}

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

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

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

// temp headers for resized image
BITMAPFILEHEADER newBf;
BITMAPINFOHEADER newBi;

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

//change headers
newBi = bi;
newBi.biWidth = round(bi.biWidth * factor); // padding excluding
if(bi.biHeight < 0)
    newBi.biHeight = round(fabsf(bi.biHeight * factor)) * -1;
else
    newBi.biHeight = round(bi.biHeight * factor);

newPadding = (4 - (newBi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
newBi.biSizeImage = (newBi.biWidth * sizeof(RGBTRIPLE) + newPadding) * abs(newBi.biHeight);

newBf = bf;
newBf.bfSize = newBi.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // headers, pixels, padding including

// write outfile's BITMAPFILEHEADER
fwrite(&newBf, sizeof(BITMAPFILEHEADER), 1, outptr);

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

// remember sizes for seek position in the loop
long tripleSize = sizeof(RGBTRIPLE);
long BMfileSize = sizeof(BITMAPFILEHEADER);
long BMinfoSize = sizeof(BITMAPINFOHEADER);
int row = 0;
int col = 0;

// iterate over outfile's scanlines
for (int i = 0, biHeight = abs(newBi.biHeight); i < biHeight; i++)
{
    // corresponding row in infile
    row = round((float)i / (abs(newBi.biHeight) - 1) * (abs(bi.biHeight) - 1));

    // iterate over pixels in scanline
    for (int j = 0; j < newBi.biWidth; j++)
    {
        // temporary storage
        RGBTRIPLE triple;

        // corresponding column in infile
        col = round((float)j / (newBi.biWidth - 1) * (bi.biWidth - 1));

        //set proper position in infile
        long position = BMfileSize + BMinfoSize + (row * (tripleSize * bi.biWidth + padding)) + (col * tripleSize);
        fseek(inptr, position, SEEK_SET);

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

        // write RGB triple to outfile
        fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
    }

    // add new padding
    for (int k = 0; k < newPadding; k++)
    {
        fputc(0x00, outptr);
    }
}


// close infile
fclose(inptr);

// close outfile
fclose(outptr);


// success
return 0;

}

After these tests I am at the end of my limited knowledge/possibilites and ask for your help. I see two fields where the error could be:
1. Bug in my code - which is probably the case :-)
2. Something in check50 which is not obvious and check more than just resulting files.

As I am not very experienced I would probably bet on bug in my code - please help me. I don't necessarily need clean check50 output but I would like to know what is wrong with my code just to learn from that mistake.

Thank very much in advance.

2 Answers 2

1

Based on your formulae, I'd assume your code should fail on any of the target dimensions being 1, as then you divide by 0. Maybe something like

    row = round((i + 0.5) / abs(newBi.biHeight) * abs(bi.biHeight) - 0.5);

and

        col = round((j + 0.5) / newBi.biWidth * bi.biWidth - 0.5);

would work. +0.5 shifts the index to the center of the pixel, then I scale, and shift the target position from center-of-pixel back to index.

Not sure whether that helps with the 1x1 pixel input, though.

3
  • Hello Blauelf, thank you very much for having look at my problem. You are right, in case of newBi.biHeight or newBi.biWidth == 1, deviding by zero would occure (will try to fix it). As you wrote - resizing from 1x1 with factor greater than 1 won't be probably affected, so I am still looking for some other bug. Anyway, you helped a lot.
    – Poki
    Feb 13, 2017 at 15:38
  • With my suggested changes, it passes all tests. Not exactly sure what's the reason, it should not be a division by 0 in that case.
    – Blauelf
    Feb 13, 2017 at 16:02
  • Once again, thank for your time and the effort! There is probably some other aspect which check50 takes into account. If there should be an error caused by zero-division (where the newBi.biWidth or newBi.biHeight == 1) the :( red line should be at this line of check50 output: 9) resizes 2x2-pixel BMP to 1x1 correctly when f is 0.5; but there isn't - really interesting. I will try to figure it out
    – Poki
    Feb 14, 2017 at 7:51
0

Based on Blauelf's answer I have revised the definition of row and col which determinatanes the way of how pixels are handled. I have improved it a bit and came up with this:

temprow = round((float)i / abs(newBi.biHeight) * abs(bi.biHeight) - 0.5);
row = max(0, temprow);

and

tempcol = round((float)j / newBi.biWidth * bi.biWidth - 0.5); 
col = max(0, tempcol);

where max function is defined as a helper method as there is no definition in standard header files.

int max(int a, int b)
{
if(a > b)
    return a;
else
    return b;
}
2
  • Did this work correctly? Jun 19, 2017 at 10:34
  • Yes, it did. At least for me.
    – Poki
    Jun 27, 2017 at 9:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .