I'm looking for a way to convert a BMP image of 256 grey levels to a 1-bit BMP image. The conversion rule is that the bit should be 0 for grey levels from 0 - 127, and 1 from 128 - 255. So far I have attempted to make a program in C. The problem is even though I changed the bits per pixel in the information header to 1 BBP, the output is a black image (I'm using Lena as the input file btw) as if the colour depth is still 8-bit (while in Properties it says 1-bit).
I tried to open the BMP file in a hex editor and saw that the pixel data had been changed to 0 and 1 as expected and the headers of the input and output files were the same except the colour depth. Am I missing anything? What changes do I need to make for the image to show what is expected?
Here is my program:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#pragma pack(1) //avoid padding in a struct
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
typedef struct
{
WORD Signature;
DWORD fSize;
WORD fReserved_1;
WORD fReserved_2;
DWORD fOffset;
} BITMAP_HEADER;
typedef struct
{
DWORD Size;
LONG Width;
LONG Height;
WORD Planes;
WORD BitsPerPixel;
DWORD Compression;
DWORD imgSize;
LONG ResX;
LONG ResY;
DWORD Color;
DWORD ImpColor;
} INFO_HEADER;
typedef struct
{
BYTE rgbtBlue; //since I'm only dealing with 8-bit colour depth
//so only one value is needed I guess?
// BYTE rgbtGreen;
// BYTE rgbtRed;
} RGBTRIPLE;
void BinaryConvert(DWORD height, DWORD width, RGBTRIPLE img[height][width]);
int main()
{
FILE* fp, *cp;
fp = fopen("Resources/test.bmp", "rb"); //bmp is binary file, therefore use "rb" permission
cp = fopen("Resources/output.bmp", "wb");
BITMAP_HEADER fHeader;
INFO_HEADER fInfo;
//read headers
fread(&fHeader, sizeof(BITMAP_HEADER), 1, fp);
fread(&fInfo, sizeof(INFO_HEADER), 1, fp);
int width = fInfo.Width;
int height = fInfo.Height;
int padding = (4 - (width * sizeof(RGBTRIPLE)) % 4) % 4;
RGBTRIPLE (*image)[width] = calloc(height, width * sizeof(RGBTRIPLE));
//read pixel data
for (int i = 0; i < height; i++)
{
fread(image[i], sizeof(RGBTRIPLE), width, fp);
fseek(fp, padding, SEEK_CUR); //skip over padding
}
//coonvert grey levels into 0 and 1
BinaryConvert(height, width, image);
//modify file header
fInfo.noBitsPerPixel = 1;
//write headers into output file
fseek(cp, 0, SEEK_SET);
fwrite(&fHeader, sizeof(BITMAP_HEADER), 1, cp);
fwrite(&fInfo, sizeof(INFO_HEADER), 1, cp);
//write pixel data into output file
for (int i = 0; i < height; i++)
{
fwrite(image[i], sizeof(RGBTRIPLE), width, cp);
for (int k = 0; k < padding; k++)
{
fputc(0x00, cp);
}
}
fclose(cp);
fclose(fp);
}
void BinaryConvert(DWORD height, DWORD width, RGBTRIPLE img[height][width])
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (img[i][j].rgbtBlue <= 127 && img[i][j].rgbtBlue >=0)
{
img[i][j].rgbtBlue = 0;
}
else if (img[i][j].rgbtBlue <= 255 && img[i][j].rgbtBlue >= 128)
img[i][j].rgbtBlue = 1;
}
}
}
Input file (lena.bmp). (257 KB)
Result from program (257 KB)
You need to use 255 instead of 1. That is the color for white
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 months ago.
Improve this question
I do not understand why my code does not work. It returns a very strange image, with VERY dark colors. I read someone else's code on a similar question (https://stackoverflow.com/a/70203062/19299155), and to me it looks like a very similar concept and approach. If someone could explain to me why mine doesn't work I'd be very grateful.
One thing I do not understand about the other guy's code is why he updates the pixel value when he does (//assigning new pixel values) because it would mean that the average of other pixels would be using the new value of the now updated pixel instad of the original. (I hope I am explaining myself correctly).
I also read this post (cs50 - pset4 - blur) this guy seems to have the same problem as me, and very similar code but the answers people gave him don't seem to apply to me.
Here is my code:
helpers.c
void blur(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE blurred[height][width];
for (int i = 0; i<height; i++){
for (int j = 0; j<width; j++){
float colorCount = 0.0;
blurred[i][j].rgbtBlue = 0;
blurred[i][j].rgbtGreen = 0;
blurred[i][j].rgbtRed = 0;
for (int count = -1; count<2; count++){
if (((count+i) >= 0) && ((count+i) < height)){
for (int count2 = -1; count2<2; count2++){
if (((count2+j) >= 0) && ((count2+j) < width)){
blurred[i][j].rgbtBlue += image[i+count][j+count2].rgbtBlue;
blurred[i][j].rgbtGreen += image[i+count][j+count2].rgbtGreen;
blurred[i][j].rgbtRed += image[i+count][j+count2].rgbtRed;
colorCount += 1.0;
}
}
}
}
blurred[i][j].rgbtBlue = round(blurred[i][j].rgbtBlue/colorCount);
blurred[i][j].rgbtGreen = round(blurred[i][j].rgbtGreen/colorCount);
blurred[i][j].rgbtRed = round(blurred[i][j].rgbtRed/colorCount);
}
}
for (int i = 0; i<height; i++){
for (int j = 0; j<width; j++){
image[i][j].rgbtBlue = blurred[i][j].rgbtBlue;
image[i][j].rgbtGreen = blurred[i][j].rgbtGreen;
image[i][j].rgbtRed = blurred[i][j].rgbtRed;
}
}
return;
}
I tried 2 nested for loops starting at -1 so it would go through the 3x3 adjacent pixels as -1 0 +1. for count = -1 it would go count2 =-1 then count2=0 then count2 = 1. then count = 0 and count2 =-1 then count2=0 then count2 = 1. and so on.
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
Searching for the definition of RGBTRIPLE online (since you did not provide it) yields this post where the definition is:
typedef uint8_t BYTE;
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
This poses an issue for your current blur kernel calculation, because you are using a RGBTRIPLE value to accumulate the sum of all pixels in the 3x3 kernel. Each color channel in the pixel uses the entire range, and so the moment you add data from another pixel to this, you risk exceeding the maximum possible value that can be stored. This will discard the carry bit from such an operation and wrap the result to remain in the 0-255 range.
The reason your output is "dark" is precisely because for most of your image the maximum possible value for a channel is round(255.0 / 9), which is 28. On the edges of your image, that will be brighter, but still never better than round(255.0 / 4), which is 64. Within this narrow range, you're also likely to see jagged artifacts or banding where the color channels wrapped after discarding overflow bits.
Even the answers you linked to point out the same thing, which you mistakenly assert "don't seem to apply to me". That is incorrect. There is absolutely no way that this isn't a problem for you. Allow me to fix your code, and maybe just rename some variables along the way to improve readability:
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int count = 0;
float sumRed = 0.0f, sumGreen= 0.0f, sumBlue = 0.0f;
for (int dy = -1; dy <= 1; dy++) {
int ky = y + dy;
if (ky >= 0 && ky < height) {
for (int dx = -1; dx <= 1; dx++) {
int kx = x + dx;
if (kx >= 0 && kx < width) {
sumBlue += (float)image[ky][kx].rgbtBlue;
sumGreen += (float)image[ky][kx].rgbtGreen;
sumRed += (float)image[ky][kx].rgbtRed;
count++;
}
}
}
}
blurred[y][x].rgbtBlue = round(sumBlue / count);
blurred[y][x].rgbtGreen = round(sumGreen / count);
blurred[y][x].rgbtRed = round(sumRed / count);
}
}
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
image[y][x] = blurred[y][x];
}
}
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 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 have able to read Bmp Header of Image as per below properties.
Image Bit-depth : 1
Image Attributes : 1
I want to scroll/Navigate in between Black and White Image (Bit Depth 1). Without Closing and reopening it (bmp image).
1) Like if input == f or F , Pixel Data goes one by one Forward Direction
Y0,Y1,Y2,Y3,Y4,....Yn
2) Like if input == r or R, Pixel Data goes one by one Backward Direction
Y123,Y122,Y121,Y120,Y119, ......Y0
3) Also I don't want to read pixels from starting (On Every Read) .
I want to read in between (Vertical Only).
Below is my code .
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct BitMap {
short Type;
long Size;
short Reserve1;
short Reserve2;
long OffBits;
long biSize;
long biWidth;
long biHeight;
short biPlanes;
short biBitCount;
long biCompression;
long biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
long biClrUsed;
long biClrImportant; } Header;
unsigned char DummyValueRead ; unsigned char pixelValue =0 ;
int Horizontal= 0 ,Vertical = 0 ;
long myPalette[2]; long HeaderBytes;
unsigned char bPadding ; long wBytesPerRow ; unsigned char
bAdditionalBitsPerRow ; FILE *BMPFile; void SendPixelValues();
unsigned char input ;
int main( void ) {
BMPFile = fopen ("mul.bmp", "r");
if (BMPFile == NULL)
{
printf("Image Not Found !!!!!!");
return -1;
}
fread(&Header.Type, sizeof(Header.Type), 1, BMPFile);
fread(&Header.Size, sizeof(Header.Size), 1, BMPFile);
fread(&Header.Reserve1, sizeof(Header.Reserve1) , 1, BMPFile);
fread(&Header.Reserve2, sizeof(Header.Reserve2) , 1, BMPFile);
fread(&Header.OffBits, sizeof(Header.OffBits), 1, BMPFile);
fread(&Header.biSize, sizeof(Header.biSize), 1, BMPFile);
fread(&Header.biWidth, sizeof(Header.biWidth), 1, BMPFile);
fread(&Header.biHeight, sizeof(Header.biHeight) , 1, BMPFile);
fread(&Header.biPlanes, sizeof(Header.biClrUsed), 1, BMPFile);
fread(&Header.biBitCount, sizeof(Header.biBitCount), 1, BMPFile);
fread(&Header.biCompression, sizeof(Header.biCompression), 1, BMPFile);
fread(&Header.biSizeImage, sizeof(Header.biSizeImage), 1, BMPFile);
fread(&Header.biXPelsPerMeter, sizeof(Header.biXPelsPerMeter), 1, BMPFile);
fread(&Header.biYPelsPerMeter, sizeof(Header.biYPelsPerMeter), 1, BMPFile);
fread(&Header.biClrUsed, sizeof(Header.biClrUsed), 1, BMPFile);
fread(&Header.biClrImportant, sizeof(Header.biClrImportant), 1, BMPFile);
fseek(BMPFile,Header.OffBits,SEEK_SET) ;
printf("\nType:%hd and Type in %x\n", Header.Type,Header.Type);
printf("Size:%ld\n", Header.Size);
printf("Reserve1:%hd\n", Header.Reserve1);
printf("Reserve2:%hd\n", Header.Reserve2);
printf("OffBits:%ld\n", Header.OffBits);
printf("biSize:%ld\n", Header.biSize);
printf("Width: %ld\n", Header.biWidth);
printf("Height: %ld\n", Header.biHeight);
printf("biPlanes:%hd\n", Header.biPlanes);
printf("biBitCount:%hd\n", Header.biBitCount);
printf("biCompression:%ld\n", Header.biCompression);
printf("biSizeImage:%ld\n", Header.biSizeImage);
printf("biXPelsPerMeter:%ld\n", Header.biXPelsPerMeter);
printf("biYPelsPerMeter:%ld\n", Header.biYPelsPerMeter);
printf("biClrUsed:%ld\n", Header.biClrUsed);
printf("biClrImportant:%ld\n\n", Header.biClrImportant);
wBytesPerRow =Header.biWidth/8;
bAdditionalBitsPerRow = Header.biWidth % 8;
bPadding = (4 - ((wBytesPerRow + (bAdditionalBitsPerRow?1:0) ) % 4 ) ) %4;
HeaderBytes = Header.biWidth/8 ;
for(Vertical = 0 ; Vertical < Header.biHeight ; Vertical ++)
{
printf("Sr. No. %d \n",Vertical) ;
scanf("%c",&input) ;
if(input =='r' || input =='R' ) // Reverse Direction
{
// fseek(BMPFile,((4*968) + 3), SEEK_SET );
SendPixelValues() ;
}
if(input =='f' || input =='F' ) // Forward Direction
SendPixelValues() ;
printf("\n") ;
}
fclose(BMPFile);
return 0;
}
unsigned int bAdditionalBitsPerRowCount =
0,bPaddingCount=0; void SendPixelValues() {
for(Horizontal = 0 ; Horizontal < HeaderBytes ; Horizontal++)
{
fread(&pixelValue, sizeof(pixelValue), 1, BMPFile);
printf("0x%x ",pixelValue) ;
}
if(bAdditionalBitsPerRow > 0)
{
fread(&DummyValueRead , sizeof(DummyValueRead ), 1, BMPFile);
bAdditionalBitsPerRow++;
printf("bAdditionalBitsPerRowCount %d",bAdditionalBitsPerRowCount);
}
for(Horizontal = 0 ; Horizontal < bPadding; Horizontal++)
{
fread(&DummyValueRead , sizeof(DummyValueRead ), 1, BMPFile);
bPaddingCount++;
}
printf("bPaddingCount = %d",bPaddingCount) ;
}
Thanks
Karan
1-bit bitmap format has 8 bytes for its palette, you have to read those 8 bytes before reading the pixels.
There are 8 pixels packed in to one byte. You have to separate the bits and write them accordingly. Bitmap can have width padding, you have to calculate the padding for the input file as well as output file.
Make sure to read/write the file in binary (it matters in Windows).
Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(push, 1)
struct BITMAPFILEHEADER {
short bfType;
int bfSize;
short bfReserved1;
short bfReserved2;
int bfOffBits;
};
struct BITMAPINFOHEADER {
int biSize;
int biWidth;
int biHeight;
short biPlanes;
short biBitCount;
int biCompression;
int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
int biClrUsed;
int biClrImportant;
};
#pragma pack(pop)
int main(void)
{
//set X and Y offset:
int x0 = 10;
int y0 = 10;
if(sizeof(short) != 2 ||
sizeof(int) != 4 ||
sizeof(struct BITMAPFILEHEADER) != 14 ||
sizeof(struct BITMAPINFOHEADER) != 40)
{
printf("Error, wrong structure size...");
return -1;
}
FILE *fin = fopen("mul.bmp", "rb");
FILE *fout = fopen("output.bmp", "wb");
if(fin == NULL)
{
printf("Image Not Found !!!!!!");
return -1;
}
struct BITMAPFILEHEADER header;
struct BITMAPINFOHEADER info;
fread(&header, sizeof(header), 1, fin);
fread(&info, sizeof(info), 1, fin);
if (info.biBitCount != 1)
{
printf("Error, not 1-bit bitmap\n");
return -1;
}
int old_width = info.biWidth;
int old_height = info.biHeight;
int bitcount = info.biBitCount;
int width_in_bytes = ((old_width * bitcount + 31) / 32) * 4;
char palette[8];
fread(palette, 1, sizeof(palette), fin);
int new_width = old_width - x0;
int new_height = old_height - y0;
//convert to 24-bit bitmap for output file
info.biBitCount = 24;
info.biWidth = new_width;
info.biHeight = new_height;
info.biSizeImage = ((info.biHeight * bitcount + 31) / 32) * info.biWidth;
header.bfSize = 54 + info.biSizeImage;
header.bfOffBits = 54;
fwrite(&header, 1, sizeof(header), fout);
fwrite(&info, 1, sizeof(info), fout);
char *bytes_read = malloc(width_in_bytes);
char black[3] = { 0 };
char white[3] = { 255,255,255 };
for(int y = 0; y < old_height; y++)
{
fread(bytes_read, 1, width_in_bytes, fin);
int width_index = 0;
for(int i = 0; i < width_in_bytes; i++)
{
//8 pixels are packed in to 1 byte
//separate the bits, write them as bytes in 24-bit format
for(int j = 0; j < 8; j++)
{
width_index = i * 8 + j;
if(width_index < x0) continue;
if(width_index - x0 >= new_width) break;
int pixel = (bytes_read[i] & (1 << (8 - j)));
if(pixel)
fwrite(white, 1, 3, fout);
else
fwrite(black, 1, 3, fout);
}
}
//add padding to output file
int m = width_index % 4;
if(m)
fwrite(black, 1, m, fout);
}
fclose(fin);
fclose(fout);
return 0;
}
im trying to copy pixel data from a bmp file to a text file in c (white will be 0, every other color will be 1), but for some reason the text file comes out trippled (three areas of 1) and in the wrong direction, i cant figure out why. anyway this is my scipt:
#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>
#pragma pack(1)
typedef struct {
unsigned char type1; /* Magic identifier */
unsigned char type2;
unsigned int size; /* File size in bytes */
unsigned int reserved;
unsigned long offset; /* Offset to image data, bytes */
} Header;
#pragma pack(2)
typedef struct {
unsigned int size; /* Header size in bytes */
int width,height; /* Width and height of image */
unsigned short int planes; /* Number of colour planes */
unsigned short int bits; /* Bits per pixel */
unsigned int compression; /* Compression type */
unsigned int imagesize; /* Image size in bytes */
unsigned int xresolution,yresolution; /* Pixels per meter */
unsigned int ncolours; /* Number of colours */
unsigned int importantcolours; /* Important colours */
} Infoheader;
typedef struct
{ unsigned char Red, Green, Blue,Reserved;
} Pixel;
void main()
{
int n;
FILE *fin;
Header *fileheader;
Infoheader *information;
Pixel *ppixel,**pImage;
fin=fopen("TEST_web.bmp","rb");
if (fin==NULL)
{
printf("Error reading image\n");
exit(1);
}
int i,j;
fileheader=(Header *)malloc(sizeof(Header));
information=(Infoheader *)malloc(sizeof(Infoheader));
ppixel=(Pixel *)malloc(sizeof(Pixel));
fread(fileheader,sizeof(Header),1,fin);
if ((*fileheader).type1!='B'&&(*fileheader).type2!='M')
{
printf("Not a bmp file\n");
exit(1);
}
n=fread(information, sizeof(Infoheader),1,fin);
if(n!=1)
{
printf("Error reading image information\n");
exit(1);
}
pImage = (Pixel **)malloc(sizeof(Pixel *) * information->height);
for(i = 0; i < information->height; i++)
{ pImage[i] = (Pixel *)malloc(sizeof(Pixel) * information->width);
}
fseek(fin,fileheader->offset,SEEK_SET);
for(i = 0; i < information->width; i++)
{ for(j = 0; j < information->height; j++)
{ fread(&(*ppixel).Red, sizeof(unsigned char), 1, fin);
fread(&(*ppixel).Green, sizeof(unsigned char), 1, fin);
fread(&(*ppixel).Blue, sizeof(unsigned char), 1, fin);
pImage[i][j] = *ppixel;
}
}
char data[information->width][information->height];
for(i = 0; i <information->width; i++)
{ for(j= 0; j<information->height; j++)
{
if((pImage[i][j]).Red==255&&(pImage[i][j]).Green==255&& Image[i][j]).Blue==255)
data[i][j]='0';
else
data[i][j]='1';
}
}
FILE *fout;
fout=fopen("text.txt","wt");
for(i = 0; i < information->width; i++)
{ for(j = 0; j < information->width ; j++)
{ fputc(data[i][j],fout);
}
fputs("\n",fout);
}
free(fileheader);
free(information);
free(ppixel);
for(i = 0; i < information->height; i++)
free(pImage[i]);
fclose(fin);
}
Im working for now with this picture
Instead of this:
for(i = 0; i < information->width; i++)
{
for(j = 0; j < information->height; j++)
use something like this:
for(y = 0; y < information->height; y++)
{
for(x = 0; x < information->width; x++)
(and of course do the same when you are scanning the bitmap.)