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?
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];
}
}
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
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 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;
}