Resizing .bmp file and size of resulting file is wrong - c

I've been trying to write a program that resizes bitmap files, but cannot for the life of me figure out why my file size in the output file is off.
I tried resizing with another program and it resizes a file from 486 bytes to 1,9kb with a factor of 2, whereas my code results in a file of 3,5kb. Please have a look at my code and I hope someone can point me in the right direction as to what is making my file so bloated.
PS: The resulting file has the correct dimensions and also looks alright, its just the file size that's not scaling correctly...
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "bmp.h"
bool is_valid_count(int argc);
bool is_valid_range(int fact);
bool is_valid_file_type(char *filename);
int main(int argc, char *argv[])
{
char *n = argv[1];
int resize_factor = atoi(n);
// validate
if (!is_valid_count(argc) ||
!is_valid_range(resize_factor) ||
!is_valid_file_type(argv[2]) ||
!is_valid_file_type(argv[3]))
{
printf("USAGE: run resize <resize-factor> <infile.bmp> <outfile.bmp>\n");
return 1;
}
// remember filenames
char *infile = argv[2];
char *outfile = argv[3];
// open input file
FILE *open_in_file = fopen(infile, "r");
if (open_in_file == NULL)
{
fprintf(stderr, "Could not open %s.\n", infile);
return 2;
}
// open output file
FILE *open_out_file = fopen(outfile, "w");
if (open_out_file == NULL)
{
fclose(open_in_file);
fprintf(stderr, "Could not create %s.\n", outfile);
return 3;
}
// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bitmap_file_header;
fread(&bitmap_file_header, sizeof(BITMAPFILEHEADER), 1, open_in_file);
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bitmap_info_header;
fread(&bitmap_info_header, sizeof(BITMAPINFOHEADER), 1, open_in_file);
// ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bitmap_file_header.bfType != 0x4d42 ||
bitmap_file_header.bfOffBits != 54 ||
bitmap_info_header.biSize != 40 ||
bitmap_info_header.biBitCount != 24 ||
bitmap_info_header.biCompression != 0)
{
fclose(open_out_file);
fclose(open_in_file);
fprintf(stderr, "Unsupported file format.\n");
return 4;
}
int padding_old = (4 - (bitmap_info_header.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
LONG old_biWidth = bitmap_info_header.biWidth;
// resize bitmap
bitmap_info_header.biHeight *= resize_factor;
bitmap_info_header.biWidth *= resize_factor;
int padding_new = (4 - (bitmap_info_header.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bitmap_info_header.biSizeImage = ((sizeof(RGBTRIPLE) * bitmap_info_header.biWidth) + padding_new) * abs(bitmap_info_header.biHeight);
bitmap_file_header.bfSize = bitmap_info_header.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// write outfile's BITMAPFILEHEADER & BITMAPINFOHEADER
fwrite(&bitmap_file_header, sizeof(BITMAPFILEHEADER), 1, open_out_file);
fwrite(&bitmap_info_header, sizeof(BITMAPINFOHEADER), 1, open_out_file);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bitmap_info_header.biHeight); i < biHeight; i++)
{
for (int j = 0; j < resize_factor; j++)
{
// iterate over pixels in scanline
for (int k = 0; k < old_biWidth; k++)
{
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, open_in_file);
for (int l = 0; l < resize_factor; l++)
{
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, open_out_file);
}
}
// add padding
for (int m = 0; m < padding_new; m++)
{
fputc(0x00, open_out_file);
}
if (j < resize_factor - 1)
{
fseek(open_in_file, -(old_biWidth * (int)sizeof(RGBTRIPLE)), SEEK_CUR);
}
}
// skip over padding, if any
fseek(open_in_file, padding_old, SEEK_CUR);
}
fclose(open_in_file);
fclose(open_out_file);
// success
return 0;
}
bool is_valid_count(int argc)
{
if (argc != 4)
{
printf("Incorrect number of arguments provided. Must be 3!\n");
return false;
}
return true;
}
bool is_valid_range(int fact)
{
if (fact < 1 || fact > 100)
{
printf("Range is invalid.Resize-factor has to be between 1 and 100! Was: %i\n", fact);
return false;
}
return true;
}
bool is_valid_file_type(char *filename)
{
const char *dot = strrchr(filename, '.');
const char *extension = ".bmp";
if (strcmp(dot, extension) != 0)
{
printf("Filetype not recognized: %s!\n", dot);
return false;
}
return true;
}

//FILE *open_in_file = fopen(infile, "r");
FILE *open_in_file = fopen(infile, "rb");
//FILE *open_out_file = fopen(outfile, "w");
FILE *open_out_file = fopen(outfile, "wb");
//for (int i = 0, biHeight = abs(bitmap_info_header.biHeight); i < biHeight; i++)
for (int i = 0, biHeight = abs(bitmap_info_header.biHeight)/resize_factor; i < biHeight; i++)

Related

Resizing a BMP image (making it smaller)

It seems I need some help with resizing the BMP image when the zoom factor is less than 1. You can see the most crucial part of my code below. The variable f in the code is the zoom factor. It seems logical to me but it works improperly.
- this is the image I need to resize (to make it even smaller).
- and this is the result picture which doesn't look properly. I think I failed in uploading that here, but it looks like a small green square without any white center at all.
Moreover, I tried to resize one more image - this beautiful smiley:
And the result was rather unexpected:
This makes me think that there's a problem with the for-cycles, though it seems completely logical to me.
And this is how the BMP is organized.
// BMP-related data types based on Microsoft's own
#include <stdint.h>
// aliases for C/C++ primitive data types
// https://msdn.microsoft.com/en-us/library/cc230309.aspx
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
// information about the type, size, and layout of a file
// 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;
// information about the dimensions and color format
// 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;
// relative intensities of red, green, and blue
// https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
Does anybody know how to fix the code below?
// Copies a BMP file
#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 n infile outfile\n");
return 1;
}
// remember filenames
float f=atof(argv[1]);
int n=f;
if(f<=0||f>=1){
fprintf(stderr, "f, the resize factor, must be between 0 and 1.\n");
return 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;
}
BITMAPFILEHEADER bf_resize = bf;
BITMAPINFOHEADER bi_resize = bi;
bi_resize.biWidth = bi.biWidth * f;
bi_resize.biHeight = bi.biHeight * f;
int padding_resize = (4 - (bi_resize.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bi_resize.biSizeImage = (bi_resize.biWidth * sizeof(RGBTRIPLE) + padding_resize) * abs(bi_resize.biHeight);
bf_resize.bfSize = bi_resize.biSizeImage + sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
int fn = f * 10;
int x = 10 / fn;
int diff = x;
diff--;
int w = 0;
int h = 0;
// write outfile's BITMAPFILEHEADER
fwrite(&bf_resize, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi_resize, sizeof(BITMAPINFOHEADER), 1, outptr);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
if(h==0){
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
if(w==0){
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
w++;
}
else{
w++;
}
if(w==diff){
w=0;
}
}
}
h++;
if(h==diff){
h=0;
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// then add it back (to demonstrate how)
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}
I assume you want to shrink the image by skipping rows and columns using the variables w, h, and diff. For instance, if we set the
scaling factor f to 0.5, diff is assigned to 1, and every other
rows/columns will be skipped to scale the image by 0.5x.
Then there are two crutial issues in the loop with i and j:
You are resetting w in if(w==diff){ w=0; } just after w++;. Then
w keeps being 0 and no columns are skipped.
You are putting the if(h==0){ condition in outer block. Then the pixels
are not read while h==0. In order to shrink the image, you need to keep
on reading every pixels regardless of the condition, and write the pixel
if the conditions meet.
Then the loop will be improved as:
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) {
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++) {
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
if (w == 0 && h == 0) {
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
w++;
if (w > diff){
w = 0;
}
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// padding to the output file, if any
if (h == 0) {
for (int k = 0; k < padding_resize; k++) {
fputc(0x00, outptr);
}
}
h++;
if (h > diff){
h = 0;
}
}
It will work under limited conditions: f=0.5 and the image width and
height are even numbers. But in general conditions it still does not work well.
For instance, if we set f to 0.4,
bi_resize.biWidth and bi_resize.biHeight will be calculated with
multiplying original size by 0.4, while diff will be calculated to 1.
Then they cause conflicts between the header information and the
actual pixels.
Here is another hint to solve the problems:
The common approach to manipulate image is to store the entire pixels
into the memory at first.
Then you can random-access any pixels in the following
process and the code will be more straightforward.
Basic idea is to iterate over the destination coordinates
and retroject back to the coordinates of the source image to pick
the pixel values.
Your posted input image is too small to identify the problem, because
thin lines and small dots are easily collapsed just with filtering even
if the algorithm is appropriate.
Better to use larger images to evaluate.
Here is my rewrite based on your code:
#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 n infile outfile\n");
return 1;
}
// read the scaling factor
float f = atof(argv[1]);
if (f <= 0 || f > 1) {
fprintf(stderr, "f, the resize factor, must be between 0 and 1.\n");
return 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;
}
BITMAPFILEHEADER bf_resize = bf;
BITMAPINFOHEADER bi_resize = bi;
bi_resize.biWidth = bi.biWidth * f;
bi_resize.biHeight = bi.biHeight * f;
int padding = bi.biWidth % 4; // you can simplify the calculation
int padding_resize = bi_resize.biWidth % 4;
bi_resize.biSizeImage = (bi_resize.biWidth * sizeof(RGBTRIPLE) + padding_resize) * bi_resize.biHeight;
bf_resize.bfSize = bi_resize.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// allocate mamory for the rgb triplets of the original (input) image
RGBTRIPLE *pix = malloc(sizeof(RGBTRIPLE) * bi.biWidth * bi.biHeight);
if (pix == NULL) {
fprintf(stderr, "malloc failed.\n");
return 5;
}
// temporary storage
RGBTRIPLE triple;
// read the entire pixels of the original image and store into the memory
for (int i = 0; i < bi.biHeight; i++) {
for (int j = 0; j < bi.biWidth; j++) {
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
pix[i * bi.biWidth + j] = triple;
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
}
// write outfile's header
fwrite(&bf_resize, sizeof(BITMAPFILEHEADER), 1, outptr);
fwrite(&bi_resize, sizeof(BITMAPINFOHEADER), 1, outptr);
// write the pixels of destination (resized) image
for (int i = 0; i < bi_resize.biHeight; i++) {
for (int j = 0; j < bi_resize.biWidth; j++) {
// calculate the corresponding coorinates in the original image
int m = (i / f + 0.5); // +0.5 for rounding
if (m > bi.biHeight - 1) { // limit the value
m = bi.biHeight - 1;
}
int n = (j / f + 0.5);
if (n > bi.biWidth - 1) {
n = bi.biWidth - 1;
}
// pick the pixel value at the coordinate
triple = pix[m * bi.biWidth + n];
// write RGB triplet to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
// padding for the output image, if any
for (int j = 0; j < padding_resize; j++) {
fputc(0x00, outptr);
}
}
free(pix);
fclose(inptr);
fclose(outptr);
return 0;
}
Input image:
Output image with f=0.4:

Resizing bmp image

I'm trying to resize bmp image and it's almost works, but for some reason I get extra bytes filled with zeros. It kinda works, but I'd like to know where did those zeros come from.
I'm not sure if it's connected somehow with those zeros, but sometimes image depth (biBitCount) changes from 24 in original file to 32. I wasn't able to figure out why it does that. Unfortunately, I didn't save screenshots of that.
I'm sure, that I just did some stupid mistake I can't see. Help me, please.
original bmp
double sized bmp with extra zeros
My Code :
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "bmp.h"
float atoi2_0(char* S);
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 4)
{
fprintf(stderr, "Usage: ./resize f infile outfile\n");
return 1;
}
// remember filenames
float f = atoi2_0(argv[1]);
if(f == -1 || f <= 0.0 || f > 100)
{
printf("Usage: ./resize f infile outfile\n");
return 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;
}
int oldpadding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bi.biWidth *= f;
bi.biHeight *= f;
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bi.biSizeImage = ((sizeof(RGBTRIPLE) * bi.biWidth) + padding) * abs(bi.biHeight);
bf.bfSize = bi.biSizeImage + 54;
// write outfile's BITMAPFILEHEADER
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);
float df = f - (int)f;
int revdf = 1 / df;
int bbw = bi.biWidth / f;
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
RGBTRIPLE * oldtriple = (RGBTRIPLE*)calloc(bbw, sizeof(RGBTRIPLE));
if (oldtriple == NULL)
{
fclose(inptr);
fclose(outptr);
return 5;
}
fread(oldtriple, sizeof(RGBTRIPLE), bbw, inptr);
RGBTRIPLE * triple = (RGBTRIPLE*)calloc(bi.biWidth, sizeof(RGBTRIPLE));
if (triple == NULL)
{
free(oldtriple);
fclose(inptr);
fclose(outptr);
return 5;
}
for (int j = 0; j < bbw; j++)
{
int jxf = j * f;
for (int n = 0; n < f; n++)
{
*(triple + jxf + n) = *(oldtriple + j);
}
}
for (int j = 0; j < f; j++)
{
if (i % revdf && f - j == df) continue;
fwrite(triple, sizeof(RGBTRIPLE), bi.biWidth, outptr);
for (int n = 0; n < padding; n++)
{
fputc(0x00, outptr);
}
}
fseek(inptr, oldpadding, SEEK_CUR);
free(triple);
free(oldtriple);
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}
float atoi2_0(char* S)//kinda atoi for floats
{
int strl = strlen(S);
for (int i = 0; i < strl; i++)//input check
{
if ((S[i] < '0' || S[i] > '9') && (S[i] != '.'))
{
printf("Invalid input");
return -1;
}
}
float a = atoi(S);
char D[10] = { 0 };
for (int i = 0; i < strl; i++)
{
if (S[i] == '.')
{
++i;
for (int j = 0; i < strl; i++, j++)
{
D[j] = S[i];
}
}
}
a += (float)atoi(D) / pow(10, strlen(D));
return a;
}
BMP.h :
``` // BMP-related data types based on Microsoft's own
#include <stdint.h>
// aliases for C/C++ primitive data types
// https://msdn.microsoft.com/en-us/library/cc230309.aspx
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
// information about the type, size, and layout of a file
// 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;
// information about the dimensions and color format
// 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;
// relative intensities of red, green, and blue
// https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
Yeah, I fixed it myself. Problem was in cycle length.

CS50 Task Resize More I am unable to reduce the BMP file correctly

In this task(course CS50, PSET3, Resize (More cofortable), the part that enlarges the picture (f> = 1) works correctly. But the part of the problem that should reduce the picture at f <1 does not work correctly. I may have problems with skipping reading and writing horizontally and vertically. Please help me understand the reason. thanks
#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 f infile outfile\n");
return 1;
}
// read f
float f = atof (argv[1]);
if (f <= 0 || f > 100)
{
fprintf(stderr, "Usage: ./resize f infile outfile\n");
return 2;
}
// 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 3;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
fprintf(stderr, "Could not create %s.\n", outfile);
return 4;
}
// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf, bf1;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
bf1 = bf;
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi, bi1;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
bi1 = bi;
bi1.biWidth = floor (bi.biWidth*f);
if (bi.biHeight>=0)
{
bi1.biHeight = ceil (bi.biHeight*f);
}
else
{
bi1.biHeight = floor (bi.biHeight*f);
}
// 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 5;
}
// determine padding for scanlines
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
int padding1 = (4 - (bi1.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
// The new size, in bytes, of the image
bi1.biSizeImage = (bi1.biWidth * sizeof(RGBTRIPLE) + padding1) * abs (bi1.biHeight);
// The new size, in bytes, of the bitmap file
bf1.bfSize = bi1.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// write outfile's BITMAPFILEHEADER
fwrite(&bf1, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi1, sizeof(BITMAPINFOHEADER), 1, outptr);
if (f >= 1)
{
int n = floor (f);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
// iterate over pixels in scanline
for (int j = 0; j < n; j++)
{
for (int k = 0; k < bi.biWidth; k++)
{
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
// write RGB triple to outfile
for (int l = 0; l < n; l++)
{
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
}
// add padding
for (int m = 0; m < padding1; m++)
{
fputc (0x00, outptr);
}
if (j < n - 1)
{
fseek (inptr, -bi.biWidth * sizeof(RGBTRIPLE), SEEK_CUR);
}
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
}
}
else
{
int Dh = abs(bi.biHeight)/(abs(bi.biHeight) - abs(bi1.biHeight));
int Dw = bi.biWidth/(bi.biWidth - bi1.biWidth);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
if (i % Dh != 0)
{
// iterate over pixels in scanline
for (int k = 0; k < bi.biWidth; k++)
{
// temporary storage
RGBTRIPLE triple;
if (k % Dw != 0)
{
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
}
// add padding
for (int m = 0; m < padding1; m++)
{
fputc (0x00, outptr);
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
}
}
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}

Problem with bfSize (Pset3, task resize, CS50)

The task is to rezise a bmp image with a factor. My code works visually, it looks correct when I check the hexadeximal of the rezised picture and the size is correct, but check50 says the picture is too large. I checked the size by downloading the picture and looked at the hexadecimal via the CS50 IDE. I also tried other code from GitHub but that code was also rejected by Check50. What is the error? Thanks in advance!
// Copies a BMP file
#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 4)
{
printf("Usage: copy infile outfile\n");
return 1;
}
// remember filenames
int factor = atoi(argv[1]);
char *infile = argv[2];
char *outfile = argv[3];
if(factor < 0 || factor > 100)
{
printf("facor must be between 0 and 100\n");
return 15;
}
// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
printf("Could not open %s.\n", infile);
return 2;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
printf("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);
printf("Unsupported file format.\n");
return 4;
}
//adjust fileheader
BITMAPFILEHEADER bf_out = bf;
BITMAPINFOHEADER bi_out = bi;
//HEIGHT AND WIDTH
bi_out.biWidth = bi.biWidth * factor;
bi_out.biHeight = bi.biHeight * factor;
// determine padding for scanlines
int padding = (4 - (bi_out.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
//IMAGESIZE
bi_out.biSizeImage = bi_out.biHeight * bi_out.biWidth * sizeof(RGBTRIPLE)+ padding;
//BITMAPFILEHEADER
bf_out.bfSize = bi_out.biSizeImage - sizeof(BITMAPFILEHEADER) - sizeof(BITMAPINFOHEADER);
// write outfile's BITMAPFILEHEADER
fwrite(&bf_out, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi_out, sizeof(BITMAPINFOHEADER), 1, outptr);
printf("Original bfSize: %d\n", bf.bfSize);
printf("Resized bfSize: %d\n", bf_out.bfSize);
printf("Bitmapinfoheader: %lu\n", sizeof(BITMAPINFOHEADER));
printf("Bitmapfileheader: %lu\n", sizeof(BITMAPFILEHEADER));
printf("Original biSizeImage: %d\n", bi.biSizeImage);
printf("Resized biSizeImage: %d\n", bi_out.biSizeImage);
printf("Original biWidth: %d\n", bi.biWidth);
printf("Original biHeight: %d\n", bi.biHeight);
printf("Resized biWidth: %d\n", bi_out.biWidth);
printf("Resized biHeight: %d\n", bi_out.biHeight);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
// temporary storage
RGBTRIPLE triple[bi.biWidth];
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// read RGB triple from infile
fread(&triple[j], sizeof(RGBTRIPLE), 1, inptr);
}
for(int l = 0; l < factor; l++)
{
for(int o = 0; o < bi.biWidth; o++)
{
for(int g = 0; g < factor; g++)
{
fwrite(&triple[o], sizeof(RGBTRIPLE), 1, outptr);
}
}
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}
After looking for a solution online I finally managed to find a working code that I also understand. Maybe it will help someone with similar problems. This solution works with while loops instead of for loops and the padding is done outside of the inner loops but despite that the code is pretty much the same.
/**
* resize.c
*
* Computer Science 50
* Problem Set 4
*
* Copies a BMP piece by piece, but also resizes it, just because.
*/
#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"
int main(int argc, char* argv[])
{
// ensure proper usage
if (argc != 4)
{
printf("Usage: ./resize factor infile outfile\n");
return 1;
}
// remember filenames and resize factor
int factor = atoi(argv[1]);
char* infile = argv[2];
char* outfile = argv[3];
// check factor
if (factor < 1 || factor > 100)
{
printf("Factor must be in the range [1-100]\n");
return 1;
}
// open input file
FILE* inptr = fopen(infile, "r");
if (inptr == NULL)
{
printf("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;
BITMAPFILEHEADER bf_new;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
bf_new = bf;
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
BITMAPINFOHEADER bi_new;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
bi_new = bi;
// 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;
}
// set new width and height dimensions
bi_new.biWidth = bi.biWidth * factor;
bi_new.biHeight = bi.biHeight * factor;
// determine padding for scanlines
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
int new_padding = (4 - (bi_new.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
// set new file size
bf_new.bfSize = 54 + (bi_new.biWidth * sizeof(RGBTRIPLE) + new_padding) * abs(bi_new.biHeight);
bi_new.biSizeImage = bf_new.bfSize - 54;
// write outfile's BITMAPFILEHEADER
fwrite(&bf_new, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi_new, sizeof(BITMAPINFOHEADER), 1, outptr);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
// each row will be printed out factor times
int rowcounter = 0;
while (rowcounter < factor)
{
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// temporary storage
RGBTRIPLE triple;
// each pixel will be printed out factor times
int pixelcounter = 0;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
// write RGB triple to outfile
while (pixelcounter < factor)
{
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
pixelcounter++;
}
}
// add new padding
for (int k = 0; k < new_padding; k++)
fputc(0x00, outptr);
// seek back to the beginning of row in input file, but not after iteration of printing
if (rowcounter < (factor - 1))
fseek(inptr, -(bi.biWidth * sizeof(RGBTRIPLE)), SEEK_CUR);
rowcounter++;
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// that's all folks
return 0;
}
//BITMAPFILEHEADER
bf_out.bfSize = bi_out.biSizeImage - sizeof(BITMAPFILEHEADER) - sizeof(BITMAPINFOHEADER);
The file size is image size plus the size of the headers. Not minus.
And you can replace this
// temporary storage
RGBTRIPLE triple[bi.biWidth];
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// read RGB triple from infile
fread(&triple[j], sizeof(RGBTRIPLE), 1, inptr);
}
With just a single fread call. No need to loop.
RGBTRIPLE triple[bi.biWidth];
fread(triple, sizeof(RGBTRIPLE), bi.biWidth, inptr);
You also need to calculate padding for the original image and then seek past it after you read into the buffer.

CS50 pset 4 resize Vertical stretching gives weird output

#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;
}
// 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;
}
// write outfile's BITMAPFILEHEADER
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);
//Changes the Width and Height of the final image
bi.biWidth *= n;
bi.biHeight *= n;
// write outfile's BITMAPINFOHEADER
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
// iterate over infile's scanlines
for (int i = 1, biHeight = abs(bi.biHeight) + 1; i < biHeight; i++) {
//array where the last line will be stored
for (int curn = 0; curn < n; curn++) {
long int temp = sizeof(RGBTRIPLE);
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++) {
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
// write RGB triple to outfile
for (int k = 0; k < n; k++) {
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
}
fseek(inptr, -temp * bi.biWidth, SEEK_CUR);
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// then add it back (to demonstrate how)
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}
This is my long piece of code that I am using. I tried to understand other questions on the same topic but I was not able to correlate the answers to my own code. I personally think there is something wrong with the logic with the for n times because without it it prints a horizontally stretched image but I can't pinpoint what the solution is. My pseudo code is simply:
for each row
for n times
for each pixel in the row
read from inptr
for n times (second time)
write into outptr
end of n times (second time)
end of each pixel in the row
skip over padding
put the padding back
end of each row
This is what is supposed to be stretched Image1
and this is what it looks like when I use ./resize 4 smiley.bmp final.bmp Image3
Thanks for the help!
You haven't updated the biSizeImage and bfSize before writing the BITMAPFILEHEADER and
BITMAPINFOHEADER.
Also, the padding for the new file will change.
Hence, calculate the new padding, biSizeImage, and bfSize using
padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bi.biSizeImage = ((sizeof(RGBTRIPLE) * bi.biWidth + padding) * abs(bi.biHeight);
bi.bfSize = bi.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
Padding in fseek() for 'intptr' will be different than that for 'outptr'. Hence store both the paddings in different variables.

Resources