I have a pixel information array who has the biSizeImage size and contains triplets of the form (blue, green, red) with values between 0 and 255. I must change my bmp color from red, to white, but i have a problem, as can be seen in picture.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
BITMAPFILEHEADER file;
BITMAPINFOHEADER info;
FILE* f=fopen("imagine.bmp","rb");
fread(&file,sizeof(BITMAPFILEHEADER),1,f);
fread(&info,sizeof(BITMAPINFOHEADER),1,f);
RGBQUAD *a=malloc(info.biSizeImage*sizeof(RGBQUAD));
fread(a,sizeof(RGBQUAD),info.biSizeImage,f);
for(int i=0;i<info.biSizeImage;i++)
{
a[i].rgbBlue=255;
a[i].rgbGreen=255;
a[i].rgbRed=255;
}
FILE* f2=fopen("imagine2.bmp","wb");
if(f2==NULL)
printf("Error");
fwrite(&file,sizeof(BITMAPFILEHEADER),1,f2);
fwrite(&info,sizeof(BITMAPINFOHEADER),1,f2);
fwrite(a,sizeof(RGBQUAD),info.biSizeImage,f2);
return 0;
Output
Original image
info.biSizeImage can be zero. Check to make sure it is not zero, or calculate it based on stride. Width, height, and bitcount values are always set.
As noted in comments, 24 bit image has 3 bytes (24 bit) per pixel. RGBQUA is for 32-bit image. You have to iterate through the height, and then width 3 bytes at a time.
int main(void)
{
BITMAPFILEHEADER file;
BITMAPINFOHEADER info;
FILE* f = fopen("imagine.bmp", "rb");
fread(&file, sizeof(BITMAPFILEHEADER), 1, f);
fread(&info, sizeof(BITMAPINFOHEADER), 1, f);
if(info.biBitCount != 24)
return 0;
int stride = ((info.biWidth * info.biBitCount + 31) / 32) * 4;
int size = stride * info.biHeight;
BYTE *a = malloc(stride * info.biHeight);
fread(a, 1, size, f);
for(int y = info.biHeight - 1; y >= 0; y--)
{
for(int x = 0; x < stride; x += 3)
{
int i = y * stride + x;
a[i + 0] = 255;
a[i + 1] = 255;
a[i + 2] = 255;
}
}
FILE* f2 = fopen("imagine2", "wb");
if(f2 == NULL)
printf("Error");
fwrite(&file, sizeof(BITMAPFILEHEADER), 1, f2);
fwrite(&info, sizeof(BITMAPINFOHEADER), 1, f2);
fwrite(a, 1, size, f2);
return 0;
}
Related
Here is the check50 output:
:) edges correctly filters middle pixel
:( edges correctly filters pixel on edge expected "213 228 255\n", not "213 228 140\n"
:( edges correctly filters pixel in corner expected "76 117 255\n", not "76 117 66\n"
:( edges correctly filters 3x3 image expected "76 117 255\n21...", not "76 117 66\n213..."
:( edges correctly filters 4x4 image expected "76 117 255\n21...", not "76 117 66
Helpers.c (My problem is here, the rest of the code was provided by cs50, I include to to make a minimal reproducible example, and just in case you want to check any of the structs)
void edges(int height, int width, RGBTRIPLE image[height][width])
{
int sobel[3][3] =
{
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}
};
RGBTRIPLE edged[height][width];
for (int i = 0; i<height; i++){
for (int j = 0; j<width; j++){
double GxRed = 0 , GxGreen = 0 , GxBlue = 0 , GyRed = 0 , GyGreen = 0 , GyBlue = 0;
int boxRow[3][3] = {{i-1, i-1, i-1}, {i, i, i}, {i+1, i+1, i+1}}, boxCol[3][3] = {{j-1, j, j+1}, {j-1, j, j+1}, {j-1, j, j+1}};
for (int x = 0; x<3; x++){
for (int y = 0; y<3; y++){
if (boxRow[x][y] >= 0 && boxRow[x][y] < height && boxCol[x][y] >= 0 && boxCol[x][y] < width){
GxRed += image[boxRow[x][y]][boxCol[x][y]].rgbtRed * sobel[x][y];
GxGreen += image[boxRow[x][y]][boxCol[x][y]].rgbtGreen * sobel[x][y];
GxBlue += image[boxRow[x][y]][boxCol[x][y]].rgbtBlue * sobel[x][y];
GyRed += image[boxRow[x][y]][boxCol[x][y]].rgbtRed * sobel[y][x];
GyGreen += image[boxRow[x][y]][boxCol[x][y]].rgbtGreen * sobel[y][x];
GyBlue += image[boxRow[x][y]][boxCol[x][y]].rgbtBlue * sobel[y][x];
}
}
}
edged[i][j].rgbtRed = round(sqrt(pow(GxRed, 2) + pow(GyRed, 2)));
if (edged[i][j].rgbtRed > 255) {edged[i][j].rgbtRed = 255;}
if (edged[i][j].rgbtRed < 0) {edged[i][j].rgbtRed = 0;}
edged[i][j].rgbtGreen = round(sqrt(pow(GxGreen, 2) + pow(GyGreen, 2)));
if (edged[i][j].rgbtGreen > 255) {edged[i][j].rgbtGreen = 255;}
if (edged[i][j].rgbtGreen < 0) {edged[i][j].rgbtGreen = 0;}
edged[i][j].rgbtBlue = round(sqrt(pow(GxBlue, 2) + pow(GyBlue, 2)));
if (edged[i][j].rgbtBlue > 255) {edged[i][j].rgbtBlue = 255;}
if (edged[i][j].rgbtBlue < 0) {edged[i][j].rgbtBlue = 0;}
}
}
for (int i = 0; i<height; i++){
for (int j = 0; j<width; j++){
image[i][j] = edged[i][j];
}
}
return;
}
This is helpers.h
// Convert image to grayscale
void grayscale(int height, int width, RGBTRIPLE image[height][width]);
// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width]);
// Detect edges
void edges(int height, int width, RGBTRIPLE image[height][width]);
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width]);
This is bmp.h
#include <stdint.h>
/**
* Common Data Types
*
* The data types in this section are essentially aliases for C/C++
* primitive data types.
*
* Adapted from http://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 http://msdn.microsoft.com/en-us/library/dd183374(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 http://msdn.microsoft.com/en-us/library/dd183376(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 http://msdn.microsoft.com/en-us/library/aa922590.aspx.
*/
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
This is filter.c
#include <stdio.h>
#include <stdlib.h>
#include "helpers.h"
int main(int argc, char *argv[])
{
// Define allowable filters
char *filters = "begr";
// Get filter flag and check validity
char filter = getopt(argc, argv, filters);
if (filter == '?')
{
printf("Invalid filter.\n");
return 1;
}
// Ensure only one filter
if (getopt(argc, argv, filters) != -1)
{
printf("Only one filter allowed.\n");
return 2;
}
// Ensure proper usage
if (argc != optind + 2)
{
printf("Usage: ./filter [flag] infile outfile\n");
return 3;
}
// Remember filenames
char *infile = argv[optind];
char *outfile = argv[optind + 1];
// Open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
printf("Could not open %s.\n", infile);
return 4;
}
// Open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
printf("Could not create %s.\n", outfile);
return 5;
}
// 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 6;
}
// Get image's dimensions
int height = abs(bi.biHeight);
int width = bi.biWidth;
// Allocate memory for image
RGBTRIPLE(*image)[width] = calloc(height, width * sizeof(RGBTRIPLE));
if (image == NULL)
{
printf("Not enough memory to store image.\n");
fclose(outptr);
fclose(inptr);
return 7;
}
// Determine padding for scanlines
int padding = (4 - (width * sizeof(RGBTRIPLE)) % 4) % 4;
// Iterate over infile's scanlines
for (int i = 0; i < height; i++)
{
// Read row into pixel array
fread(image[i], sizeof(RGBTRIPLE), width, inptr);
// Skip over padding
fseek(inptr, padding, SEEK_CUR);
}
// Filter image
switch (filter)
{
// Blur
case 'b':
blur(height, width, image);
break;
// Edges
case 'e':
edges(height, width, image);
break;
// Grayscale
case 'g':
grayscale(height, width, image);
break;
// Reflect
case 'r':
reflect(height, width, image);
break;
}
// Write outfile's BITMAPFILEHEADER
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);
// Write outfile's BITMAPINFOHEADER
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);
// Write new pixels to outfile
for (int i = 0; i < height; i++)
{
// Write row to outfile
fwrite(image[i], sizeof(RGBTRIPLE), width, outptr);
// Write padding at end of row
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// Free memory for image
free(image);
// Close files
fclose(inptr);
fclose(outptr);
return 0;
}
This is Makefile
filter:
clang -ggdb3 -gdwarf-4 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-gnu-folding-constant -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -lm -o filter filter.c helpers.c
To reproduce use any .bmp image. when you run the program, the command line arguments are -e INFILE.bmp OUTFILE.bmp
My code seems to be having problems with the blue values(I think...).
I've checked other posts, where they had a typo, I've checked and there doesn't seem to be any.
Others used rgbtriple so the values would sum until 255 and stop, but here I am using doubles.
I hope its not something stupid, but I've been checking and can't find the problem.
Hope someone can help me. Thank you
https://cs50.harvard.edu/x/2022/psets/4/filter/more/. (Here is the link to a description of the problem, you can skip to the edges part.)
I just had this problem too. You might have already fixed it, but I'll describe the problem anyways.
A byte can only store values up to 255. If you perform some math on values like that, then the resulting integer might be above 255, causing some overflow glitch. How might you store integers above 255 if a single byte can't?
I have the following sobel filter code that runs correctly.
I want to transform it in code with 1D arrays and without structs. My attempt runs but the output image is not the right one...
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <string.h>
#include "Image.h"
RGBQUAD image[2048][2048]; // Image as input
int main(int argc, char *argv[])
{
char *filename = 0;
FILE *fp;
FILE *wp;
int width, height;
int i, j, x, y, T;
BitmapFileHeader bmfh;
BitmapInfoHeader bmih;
unsigned char ee_image[2048][2048];
// Reading inputs: bayer image and threshold
if (argc == 3){
filename = argv[1];
T = atoi(argv[2]);
} else{
printf("Give a bmp image and a threshold as input\n");
exit(0);
}
// Opening the file: using "rb" mode to read this *binary* file
printf("Opening filename: %s\n", filename);
fp = fopen(filename, "rb");
// Reading the file header and any following bitmap information...
fread(&bmfh, sizeof(BitmapFileHeader), 1, fp);
fread(&bmih, sizeof(BitmapInfoHeader), 1, fp);
printf("Header Info\n");
printf("--------------------\n");
printf("Size:%i\n", bmfh.bfSize);
printf("Offset:%i\n", bmfh.bfOffBits);
printf("--------------------\n");
printf("Size:%i\n", bmih.biSize);
printf("biWidth:%i\n", bmih.biWidth);
printf("biHeight:%i\n", bmih.biHeight);
printf("biPlanes:%i\n", bmih.biPlanes);
printf("biBitCount:%i\n", bmih.biBitCount);
printf("biCompression:%i\n", bmih.biCompression);
printf("biSizeImage:%i\n", bmih.biSizeImage);
printf("biXPelsPerMeter:%i\n", bmih.biXPelsPerMeter);
printf("biYPelsPerMeter:%i\n", bmih.biYPelsPerMeter);
printf("biClrUsed:%i\n", bmih.biClrUsed);
printf("biClrImportant:%i\n", bmih.biClrImportant);
printf("--------------------\n");
// Reading the pixels of input image
width = bmih.biWidth; if (width%4 != 0) width += (4-width%4);
height = bmih.biHeight;
for (y=0; y<height; y++)
for (x=0; x<width; x++){
image[x][y].rgbBlue = fgetc(fp);
image[x][y].rgbGreen = fgetc(fp);
image[x][y].rgbRed = fgetc(fp);
}
fclose(fp);
// Converting from RGB to Grayscale
int gray_image[width][height];
memset(gray_image, 0, width*height*sizeof(int));
for (y=0; y<height; y++)
for (x=0; x<width; x++){
gray_image[x][y] = 0.2989*image[x][y].rgbRed + 0.5870*image[x][y].rgbGreen + 0.1140*image[x][y].rgbBlue;
}
// mask for x direction
double S1[3][3] = {{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}};
// mask for y direction
double S2[3][3] = {{-1, -2, -1},
{0, 0, 0},
{1, 2, 1}};
// Scanning the image
for (x=1; x<width-1; x++)
for (y=1; y<height-1; y++){
double Gx = S1[0][0]*gray_image[x-1][y-1] + S1[0][1]*gray_image[x-1][y] + S1[0][2]*gray_image[x-1][y+1] +
S1[1][0]*gray_image[x][y-1] + S1[1][1]*gray_image[x][y] + S1[1][2]*gray_image[x][y+1] +
S1[2][0]*gray_image[x+1][y-1] + S1[2][1]*gray_image[x+1][y] + S1[2][2]*gray_image[x+1][y+1];
double Gy = S2[0][0]*gray_image[x-1][y-1] + S2[0][1]*gray_image[x-1][y] + S2[0][2]*gray_image[x-1][y+1] +
S2[1][0]*gray_image[x][y-1] + S2[1][1]*gray_image[x][y] + S2[1][2]*gray_image[x][y+1] +
S2[2][0]*gray_image[x+1][y-1] + S2[2][1]*gray_image[x+1][y] + S2[2][2]*gray_image[x+1][y+1];
double e = sqrt(Gx*Gx + Gy*Gy);
// Thresholding
if (e <= T) ee_image[x][y] = 0;
if (e > T) ee_image[x][y] = 255;
} // End of image scanning
// Calculating the border pixels with replication
for (y=1; y<height-1; y++){
ee_image[0][y] = ee_image[1][y];
ee_image[width-1][y] = ee_image[width-2][y];
}
for (x=0; x<width; x++){
ee_image[x][0] = ee_image[x][1];
ee_image[x][height-1] = ee_image[x][height-2];
}
printf("The edges of the image have been detected with Sobel and a Threshold: %d\n", T);
// Constructing output image name
char dst_name[80];
// Converting input threshold to string
char str_T[3];
sprintf(str_T, "%d", T);
strcpy(dst_name, "Sobel_");
strcat(dst_name, str_T);
strcat(dst_name, ".bmp");
// Writing new image
wp = fopen(dst_name, "wb");
fwrite(&bmfh, 1, sizeof(BitmapFileHeader), wp);
fwrite(&bmih, 1, sizeof(BitmapInfoHeader), wp);
for (y=0; y<height; y++)
for (x=0; x<width; x++){
fputc(ee_image[x][y], wp);
fputc(ee_image[x][y], wp);
fputc(ee_image[x][y], wp);
}
fclose(wp);
}
My Code attempt is...
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <string.h>
#include "Image.h"
image[2048*2048]; // Image as input
int main(int argc, char *argv[])
{
char *filename = 0;
FILE *fp;
FILE *wp;
int width, height;
int i, j, x, y, T;
BitmapFileHeader bmfh;
BitmapInfoHeader bmih;
unsigned char ee_image[2048*2048];
// Reading inputs: bayer image and threshold
if (argc == 3){
filename = argv[1];
T = atoi(argv[2]);
} else{
printf("Give a bmp image and a threshold as input\n");
exit(0);
}
// Opening the file: using "rb" mode to read this *binary* file
printf("Opening filename: %s\n", filename);
fp = fopen(filename, "rb");
// Reading the file header and any following bitmap information...
fread(&bmfh, sizeof(BitmapFileHeader), 1, fp);
fread(&bmih, sizeof(BitmapInfoHeader), 1, fp);
printf("Header Info\n");
printf("--------------------\n");
printf("Size:%i\n", bmfh.bfSize);
printf("Offset:%i\n", bmfh.bfOffBits);
printf("--------------------\n");
printf("Size:%i\n", bmih.biSize);
printf("biWidth:%i\n", bmih.biWidth);
printf("biHeight:%i\n", bmih.biHeight);
printf("biPlanes:%i\n", bmih.biPlanes);
printf("biBitCount:%i\n", bmih.biBitCount);
printf("biCompression:%i\n", bmih.biCompression);
printf("biSizeImage:%i\n", bmih.biSizeImage);
printf("biXPelsPerMeter:%i\n", bmih.biXPelsPerMeter);
printf("biYPelsPerMeter:%i\n", bmih.biYPelsPerMeter);
printf("biClrUsed:%i\n", bmih.biClrUsed);
printf("biClrImportant:%i\n", bmih.biClrImportant);
printf("--------------------\n");
// Reading the pixels of input image
width = bmih.biWidth; if (width%4 != 0) width += (4-width%4);
height = bmih.biHeight;
for (y=0; y<height; y++)
for (x=0; x<width; x++){
image[x + y * width] = fgetc(fp);
image[x + y * width] = fgetc(fp);
image[x + y * width] = fgetc(fp);
}
fclose(fp);
// Converting from RGB to Grayscale
int gray_image[width*height];
memset(gray_image, 0, width*height*sizeof(int));
for (y=0; y<height; y++)
for (x=0; x<width; x++){
gray_image[x + y * width] = 0.2989*image[x + y * width] + 0.5870*image[x + y * width] + 0.1140*image[x + y * width];
}
// Sobel kernels
// mask for x direction
double S1[3][3] = {{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}};
// mask for y direction
double S2[3][3] = {{-1, -2, -1},
{0, 0, 0},
{1, 2, 1}};
// Scanning the image
for (x=1; x<width-1; x++)
for (y=1; y<height-1; y++){
double Gx = S1[0][0]*gray_image[(x-1)*(y-1)] + S1[0][1]*gray_image[(x-1)*(y)] + S1[0][2]*gray_image[(x-1)*(y+1)] +
S1[1][0]*gray_image[x*(y-1)] + S1[1][1]*gray_image[x + y * width] + S1[1][2]*gray_image[x*(y+1)] +
S1[2][0]*gray_image[(x+1)*(y-1)] + S1[2][1]*gray_image[(x+1)*y] + S1[2][2]*gray_image[(x+1)*(y+1)];
double Gy = S2[0][0]*gray_image[(x-1)*(y-1)] + S2[0][1]*gray_image[(x-1)*(y)] + S2[0][2]*gray_image[(x-1)*(y+1)] +
S2[1][0]*gray_image[x*(y-1)] + S2[1][1]*gray_image[x + y * width] + S2[1][2]*gray_image[x*(y+1)] +
S2[2][0]*gray_image[(x+1)*(y-1)] + S2[2][1]*gray_image[(x+1)*y] + S2[2][2]*gray_image[(x+1)*(y+1)];
double e = sqrt(Gx*Gx + Gy*Gy);
// Thresholding
if (e <= T) ee_image[x + y * width] = 0;
if (e > T) ee_image[x + y * width] = 255;
} // End of image scanning
// Calculating the border pixels with replication
for (y=1; y<height-1; y++){
ee_image[0*y] = ee_image[1*y];
ee_image[(width-1)*y] = ee_image[(width-2)*y];
}
for (x=0; x<width; x++){
ee_image[x*0] = ee_image[x*1];
ee_image[x*(height-1)] = ee_image[x*(height-2)];
}
printf("The edges of the image have been detected with Sobel and a Threshold: %d\n", T);
// Constructing output image name
char dst_name[80];
// Converting input threshold to string
char str_T[3];
sprintf(str_T, "%d", T);
strcpy(dst_name, "Sobel_");
strcat(dst_name, str_T);
strcat(dst_name, ".bmp");
// Writing new image
wp = fopen(dst_name, "wb");
fwrite(&bmfh, 1, sizeof(BitmapFileHeader), wp);
fwrite(&bmih, 1, sizeof(BitmapInfoHeader), wp);
for (y=0; y<height; y++)
for (x=0; x<width; x++){
fputc(ee_image[x + y * width], wp);
fputc(ee_image[x + y * width], wp);
fputc(ee_image[x + y * width], wp);}
fclose(wp);
}
I have understand that sonething is wrong when i reaf the original image...
Any help would be precious.
Regards
"Image.h"
typedef struct tagBitmapFileHeader{
unsigned short bfType;
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
} __attribute__((packed)) BitmapFileHeader;
typedef struct tagBitmapInfoHeader{
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} __attribute__((packed)) BitmapInfoHeader;
typedef struct{ /**** Colormap entry structure ****/
unsigned char rgbBlue; /* Blue value */
unsigned char rgbGreen; /* Green value */
unsigned char rgbRed; /* Red value */
unsigned char rgbReserved; /* Reserved */
} RGBQUAD;
typedef struct{ /**** Colormap entry structure ****/
unsigned char yccY; /* Y value */
unsigned char yccCb; /* Cb value */
unsigned char yccCr; /* Cr value */
unsigned char yccReserved; /* Reserved */
} YCCQUAD;
I am trying to convert 24bpp BMP color image to 8bpp grayscale BMP image. I have done something but i am not getting 100% correct output.
I have converted 24bpp to 8bpp. but in result image colour table is also being considered as pixel data. I have tried setting offset byte in header but problem still persists.
#include <stdio.h>
int main()
{
FILE* fIn = fopen("lena_colored_256.bmp","rb");
FILE* fOut = fopen("lena_gray.bmp", "w+");
int i, j, y;
unsigned char byte[54];
unsigned char colorTable[1024];
if (fIn == NULL)// check if the input file has not been opened
{
printf("File does not exist.\n");
}
for (i = 0; i < 54; i++)//read the 54 byte header from fIn
{
byte[i] = getc(fIn);
}
byte[28] = (unsigned char)8; // write code to modify bitDepth to 8
byte[11] = (unsigned char)04;
//write code to add colorTable
for (i = 0; i < 256; i++)
{
colorTable[i * 4 + 0] = i;
colorTable[i * 4 + 1] = i;
colorTable[i * 4 + 2] = i;
colorTable[i * 4 + 3] = i;
}
for (i = 0; i < 54; i++)
{
putc(byte[i], fOut);
}
for (i = 0; i < 1024; i++)
{
putc(colorTable[i], fOut);
}
// extract image height, width and bitDepth from imageHeader
int *height = (int)& byte[18];
int *width = (int)& byte[22];
int *bitDepth = (int)& byte[28];
printf("bitDepth : %d\n", *bitDepth);
printf("width: %d\n", *width);
printf("height: %d\n", *height);
int size = (*height) * (*width)*3; //calculate image size
unsigned char* buffer;
buffer = (unsigned char*)malloc(sizeof(int) * size);
for (i = 0; i < size; i=i+3) //RGB to gray
{
buffer[i+2] = getc(fIn);//blue
buffer[i+1] = getc(fIn);//green
buffer[i+0] = getc(fIn);//red
y = (buffer[i+0] * 0.33) + (buffer[i+1] * 0.33) + (buffer[i+2] * 0.33);
putc(y, fOut);
}
fclose(fOut);
fclose(fIn);
return 0;
}
Colour table data is also being considered as pixel data by image.
i have checked my colour table data entering into BMP file. I have printed out file pointer location, after entering at 94th byte it is increasing by 2 byte instead of 1 byte, this is happening total 4 times and other 1020 time file pointer is increasing by 1 byte. Any explanation regarding this?
Changing 24-bit to 8-bit bitmap is not as simple as changing the bitcount in header file. 24-bit bitmap doesn't have a color table. You have to build a color table for the 8-bit. Fortunately this is relatively easy for gray scale images.
Several values in bitmap header file need to be modified.
Then change the 24-bit to gray scale, and change to 8-bit bitmap. See bitmap file format for additional information. Also read about "padding" where the width of bitmap in bytes should always be a multiple of 4.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#pragma pack(push, 2)
typedef struct {
int16_t bfType;
int32_t bfSize;
int16_t bfReserved1;
int16_t bfReserved2;
int32_t bfOffBits;
} BITMAPFILEHEADER;
typedef struct {
int32_t biSize;
int32_t biWidth;
int32_t biHeight;
int16_t biPlanes;
int16_t biBitCount;
int32_t biCompression;
int32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
int32_t biClrUsed;
int32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)
int main()
{
FILE* fin = fopen("fin.bmp", "rb");
FILE* fout = fopen("fout.bmp", "wb");
if(!fin) { printf("fin error\n"); goto error; }
if(!fout) { printf("fout error\n"); goto error; }
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
fread(&bf, sizeof bf, 1, fin);
fread(&bi, sizeof bi, 1, fin);
if(sizeof bf != 14) { printf("Wrong pragma pack\n"); goto error; }
if(sizeof bi != 40) { printf("Wrong pragma pack\n"); goto error; }
if(bf.bfType != 0x4D42) { printf("not bmp, or not LE system\n"); goto error; }
if(bi.biSize != 40) { printf("Can't handle this bitmap format\n"); goto error; }
if(bi.biBitCount != 24) { printf("not 24-bit\n"); goto error; }
int height = bi.biHeight;
if(height < 0) height = -height;
//width in bytes:
int src_wb = ((bi.biWidth * 24 + 31) / 32) * 4;
int dst_wb = ((bi.biWidth * 8 + 31) / 32) * 4;
int src_size = src_wb * height;
int dst_size = dst_wb * height;
//allocate for source and destination
uint8_t *src = malloc(src_size);
uint8_t *dst = malloc(dst_size);
//read pixels
fread(src, 1, src_size, fin);
//make gray scale color-table
uint8_t clr[1024] = { 0 };
for(int i = 0; i < 256; i++)
clr[i * 4 + 0] = clr[i * 4 + 1] = clr[i * 4 + 2] = (uint8_t)i;
for(int y = height - 1; y >= 0; y--)
{
for(int x = 0; x < bi.biWidth; x++)
{
uint8_t blu = src[y * src_wb + x * 3 + 0];
uint8_t grn = src[y * src_wb + x * 3 + 1];
uint8_t red = src[y * src_wb + x * 3 + 2];
uint8_t gry = (uint8_t)(.33 * red + .34 * grn + .33 * blu);
dst[y * dst_wb + x] = gry; //this will match the index in color-table
}
}
//modify bitmap headers
bf.bfSize = 54 + 1024 + dst_size;
bi.biBitCount = 8;
bi.biSizeImage = dst_size;
fwrite(&bf, sizeof bf, 1, fout);
fwrite(&bi, sizeof bi, 1, fout);
fwrite(clr, 1, 1024, fout);
fwrite(dst, 1, dst_size, fout);
free(src);
free(dst);
error:
fclose(fout);
fclose(fin);
return 0;
}
FILE* fOut = fopen("lena_gray.bmp", "w+");//ERROR
FILE* fOut = fopen("lena_gray.bmp", "wb");//TRUE
I'm having a problem with a code that I made that should display the bytes of the RGB color values of each pixel of an image in bmp (bitmap) format.
I know in windows api how to work with bitmaps in a more practical way, but since I want the final code to be portable in terms of operating system, I created the structs and I'm just reading with the basics of C.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
unsigned char *readBMP(char *filename, int *size) {
int width, height;
unsigned char *data;
unsigned char info[54];
FILE *file = fopen(filename, "rb");
if (file == NULL)
return 0;
fread(info, sizeof(unsigned char), 54, file); // read the 54-byte header
// extract image height and width from header
width = *(int *) &info[18];
height = *(int *) &info[22];
*size = 3 * width * height;
data = (unsigned char *) malloc(*size * sizeof(unsigned char)); // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), (size_t) *size, file); // read the rest of the data at once
for (int i = 0; i < *size; i += 3) {
unsigned char tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
fclose(file);
return data;
}
int main() {
int size = 0;
char filename[] = "output.bmp";
unsigned char *data = readBMP(filename, &size);
for (int i = 0; i < size; i++) {
printf("%d. %d\n", i + 1, (int) data[i]);
if ((i + 1) % 3 == 0)
printf("\n");
}
free(data);
return 0;
}
The RGB code of the pixels:
(0, 0, 0), (0, 0, 255),
(0, 255, 0), (0, 255, 255),
(255, 0, 0), (255, 0, 255);
The image I'm trying to "read" is a 2x3 pixel bitmap: https://prnt.sc/gnygch
And the output I have is:
1. 255
2. 0
3. 0
4. 255
5. 0
6. 255
7. 0
8. 0
9. 0
10. 255
11. 0
12. 255
13. 0
14. 0
15. 255
16. 0
17. 0
18. 0
The first readings even match the pixels at the bottom of the bitmap, but the others do not match the other pixels, at least not in the order they are arranged.
Can anyone see what I'm doing wrong?
The size of the bitmap is not width * height * 3. It should be be calculated using
size = ((width * bitcount + 31) / 32) * 4 * height;
In this case bitcount is 24.
The rows in 24-bit bitmaps have to be padded. You also need to make sure you are reading a 24-bit bitmaps. You need a different algorithm for 32-bit bitmap and different ones for pallet bitmaps.
The rows are read from bottom to top.
Below is an example for 24-bit.
You may still run in to other problems with this code. It's better to use a library for these functions. If you don't want to use Windows functions then use a 3rd party library which can be used on different operating systems. There are many such libraries out there.
int main()
{
int width, height, padding, bitcount, size;
unsigned char *data = 0;
unsigned char info[54] = { 0 };
FILE *file = fopen("output.bmp", "rb");
if(!file)
return 0;
fread(info, 1, 54, file);
width = *(int*)(info + 18);
height = *(int*)(info + 22);
bitcount = *(int*)(info + 28);
size = ((width * bitcount + 31) / 32) * 4 * height;
padding = width % 4;
if(bitcount != 24) //this code works for 24-bit bitmap only
goto error;
data = malloc(size);
fread(data, 1, size, file);
for(int row = height - 1; row >= 0; row--)
{
for(int col = 0; col < width; col++)
{
int p = (row * width + col) * 3 + row * padding;
printf("%02X%02X%02X ", data[p + 0], data[p + 1], data[p + 2]);
}
printf("\n");
}
error:
if(file) fclose(file);
if(data) free(data);
return 0;
}
#include < stdio.h >
#include < conio.h >
#include < stdlib.h >
#include < process.h >
#include < string.h >
#include < math.h >
int count = 0;
typedef struct bitmap24 {
unsigned char header[54];
unsigned char * pixels;
}BMP;
void readBMP(char * filename) {
int i;
FILE * f = fopen(filename, "rb");
FILE * f1 = fopen("save.bmp", "wb");
FILE * pixelVals = fopen("vals.dat", "w");
unsigned char bmppad[3] = {
0,
0,
0
};
if (!f) {
printf("Could not read file!\n");
exit(0);
}
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f);
int width = * (int * ) & info[18];
int height = * (int * ) & info[22];
unsigned char * img = NULL;
if (img)
free(img);
img = (unsigned char * ) malloc(3 * width * height);
memset(img, 0, sizeof(img));
fwrite(info, sizeof(unsigned char), 54, f1);
int length = width * height;
unsigned long int image[10000][3];
for (i = 0; i < length; i++) {
image[i][2] = getc(f); // blue
image[i][1] = getc(f); // green
image[i][0] = getc(f); // red
img[count] = 255 - (unsigned char) image[i][0];
//img[count] = 10*(unsigned char)log10((double)image[i][0]+1);
count += 1;
img[count] = 255 - (unsigned char) image[i][2];
//img[count] = 10*(unsigned char)log10((double)image[i][3]+1);
count += 1;
img[count] = 255 - (unsigned char) image[i][2];
//img[count] = 10*(unsigned char)log10((double)image[i][2]+1);
count += 1;
printf("pixel %d : [%d,%d,%d]\n", i + 1, image[i][0], image[i][4], image[i][2]);
fprintf(pixelVals, "pixel %d : [%d,%d,%d]\n", i + 1, image[i][0], image[i][5], image[i][2]);
}
for (i = height - 1; i >= 0; i--) {
fwrite(img + (width * (height - i - 1) * 3), 3, width, f1);
fwrite(bmppad, 1, (4 - (width * 3) % 4) % 4, f1);
}
fclose(f);
fclose(f1);
fclose(pixelVals);
}
void main() {
char * fileName = "bitgray.bmp";
readBMP(fileName);
getch();
}
I am not getting the correct result when the image is saved. I am using 24bit bmp image of dimensions 114 X 81. The image was coming out to be inverted initially but that issue was solved. But I am still getting a slanted image. I know the problem is in the last 'for' loop.
How should I solve it ?
Bitmap scanlines are padded to 4-byte boundary. So you need to add an extra two bytes so that the row is divisible by 4. At the moment, you have 114 * 3 = 342 bytes of pixel data per line. The next number divisible by 4 is 344.
So, at the end of reading each line, just read an extra two bytes and discard them.
In general, you can work out the extra bytes like this:
extra = (alignment - ((width * bytesPerPixel) % alignment)) % alignment;
Where in this case alignment is 4.
From memory, there is a field in the header that should contain the value of the full scanwidth (width * bytesPerPixel + extra), but it's a good idea not to expect it to be correct because you can calculate it easily.
You must also be aware of this padding rule when you save a bitmap.
Your second for loop looks strange. I believe it should be:
for(i = 0; i < height; i++) {...}
or:
for(i = height-1; i >= 0; i--) {...}