CS50 pset4 Blur [closed] - c

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

Related

Cs50 pset4 edges

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?

Difficulties converting 8-bit greyscale .BMP images to 1-bit .BMP Images

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

converting 24bpp bmp colour image to 8bpp grayscale bmp image in c

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

Reading pattern from file and create a bmp image of that in C

I want to read an text file Using C Language.Here's the file:-
You see there is some pattern in the text content on the file.
0 means nothing. 9 means black. so there is coloring scheme from 0 to 9.
I have to create a bitmap image of this and the color are according to the values in the pattern. You have to adjust it with the 0-256 color scheme.
And the final output for this is shown below
Now see the pattern in the content of the text file is opposite to the final output bitmap file (not necessary). The darkness of the colors in bitmap image is according to the values in the pattern of the text content.
Anyone will tell me how I achieve this in C language.
I am able to create a BMP file but not according to the pattern in text file.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char bitmap[1900];
// -- FILE HEADER -- //
// bitmap signature
bitmap[0] = 0x42;
bitmap[1] = 0x4d;
// file size
bitmap[2] = 58; // 40 + 14 + 12
bitmap[3] = 0;
bitmap[4] = 0;
bitmap[5] = 0;
int i=0;
// reserved field (in hex. 00 00 00 00)
for(i = 6; i < 10; i++) bitmap[i] = 0;
// offset of pixel data inside the image
for(i = 10; i < 14; i++) bitmap[i] = 0;
// -- BITMAP HEADER -- //
// header size
bitmap[14] = 40;
for(i = 15; i < 18; i++) bitmap[i] = 0;
// width of the image
bitmap[18] = 4;
for(i = 19; i < 22; i++) bitmap[i] = 0;
// height of the image
bitmap[22] = 1;
for(i = 23; i < 26; i++) bitmap[i] = 0;
// reserved field
bitmap[26] = 1;
bitmap[27] = 0;
// number of bits per pixel
bitmap[28] = 24; // 3 byte
bitmap[29] = 0;
// compression method (no compression here)
for(i = 30; i < 34; i++) bitmap[i] = 0;
// size of pixel data
bitmap[34] = 12; // 12 bits => 4 pixels
bitmap[35] = 0;
bitmap[36] = 0;
bitmap[37] = 0;
// horizontal resolution of the image - pixels per meter (2835)
bitmap[38] = 0;
bitmap[39] = 0;
bitmap[40] = 0b00110000;
bitmap[41] = 0b10110001;
// vertical resolution of the image - pixels per meter (2835)
bitmap[42] = 0;
bitmap[43] = 0;
bitmap[44] = 0b00110000;
bitmap[45] = 0b10110001;
// color pallette information
for(i = 46; i < 50; i++) bitmap[i] = 0;
// number of important colors
for(i = 50; i < 54; i++) bitmap[i] = 0;
// -- PIXEL DATA -- //
for(i = 54; i < 66; i++) bitmap[i] = 255;
FILE *file;
file = fopen("bitmap.bmp", "w+");
for(i = 0; i < 66; i++)
{
fputc(bitmap[i], file);
}
fclose(file);
return 0;
}
Here is a working code. I do not check some case of error (input file badly formatted, for example). I put some commentaries, though the code itself is not difficult to understand.
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <ctype.h>
#define BMP_ASSERT(condition, message) \
do { \
if (! (condition)) { \
fprintf(stderr, __FILE__ ":%d: %s: Assertion `" #condition \
"` failed.\n\t: %s\n", __LINE__, __func__, message); \
exit(EXIT_FAILURE); \
} \
} while (false)
#define BMP_COLORMAP_SIZE 10
////////////////////////////////////////////////////////////////////////////////
// STRUCTS
typedef struct __attribute__((__packed__))
bmp_color
{
uint8_t r,
g,
b;
uint8_t :8;
} bmp_color_t;
typedef struct __attribute__((__packed__))
bmp_header_infos
{
uint32_t const info_size;
uint32_t width;
uint32_t height;
uint16_t const planes;
uint16_t const bpp;
uint32_t const compression;
uint32_t img_size;
uint32_t const horz_resolution;
uint32_t const vert_resolution;
uint32_t const n_colors;
uint32_t const n_important_colors;
} bmp_header_infos_t;
typedef struct __attribute__((__packed__))
bmp_header
{
/* Header */
char const signature[2];
uint32_t file_size;
uint32_t const :32; // reserved
uint32_t const img_offset;
/* Infos */
bmp_header_infos_t infos;
/* Color map */
bmp_color_t colormap[BMP_COLORMAP_SIZE];
} bmp_header_t;
typedef struct bmp_file_datas
{
size_t width;
size_t height;
/* Bit map */
uint8_t *bitmap;
} bmp_file_datas_t;
////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/* Give a header with the right magic values */
bmp_header_t *
bmp_get_header(void)
{
static bmp_header_t _header = {
{'B', 'M'},
0u,
sizeof(_header),
{ // struct info
sizeof(_header.infos),
0u, 0u, // width, height
1u, // planes
8u, // bpp
0u, // no compression
0u, // img size
0u, 0u, // resolution
BMP_COLORMAP_SIZE,
BMP_COLORMAP_SIZE, // important colors
},
{{0u}}
};
bmp_header_t *header = malloc(sizeof(_header));
size_t i,
color;
assert(header != NULL);
memcpy(header, &_header, sizeof(*header));
// setting the scale of greys
for (i = 0 ; i < BMP_COLORMAP_SIZE ; ++i)
{
color = (i * 255) / BMP_COLORMAP_SIZE;
header->colormap[i] = (bmp_color_t){color, color, color};
}
return header;
}
/* Take all the file content and store it in a buffer */
char *
get_file_content(char const *filename)
{
FILE *file_handler = fopen(filename, "r");
size_t file_len;
char *buff;
BMP_ASSERT(file_handler != NULL, strerror(errno));
fseek(file_handler, 0, SEEK_END);
file_len = ftell(file_handler);
fseek(file_handler, 0, SEEK_SET);
buff = malloc(file_len + 1);
assert(buff != NULL);
fread(buff, file_len, 1, file_handler);
buff[file_len] = '\0';
fclose(file_handler);
return buff;
}
/* Get the greatest multiple of 4 that is >= size */
static inline size_t
multiple_of_four(size_t size)
{
while (size % 4)
++size;
return size;
}
/* Get the informations from buffer: size of line, number of lines */
void
get_file_infos(char *buff, bmp_header_t *header, bmp_file_datas_t *datas)
{
/* width & height */
header->infos.width = strchr(buff, '\n') - buff;
header->infos.height = strlen(buff) / (header->infos.width + 1);
// + 1 for the terminating '\n'
datas->width = multiple_of_four(header->infos.width);
datas->height = header->infos.height;
printf("File size: %u, %u\n", header->infos.width, header->infos.height);
/* image size & bitmap allocation */
header->infos.img_size = datas->width * datas->height;
datas->bitmap = malloc(header->infos.img_size * sizeof(*datas->bitmap));
assert(datas->bitmap != NULL);
header->file_size = header->img_offset + header->infos.img_size;
}
/* Take the informations from the buffer and store them in the bitmap */
void
write_bitmap(char *buff, bmp_file_datas_t *datas)
{
size_t ibuff,
iline = 0,
ibitmap = 0;
for (ibuff = 0 ; buff[ibuff] ; ++ibuff)
{
if (isdigit(buff[ibuff]))
{
datas->bitmap[ibitmap] = BMP_COLORMAP_SIZE - 1 - (buff[ibuff] - '0');
++ibitmap;
}
else if (buff[ibuff] == '\n')
{
++iline;
ibitmap = iline * datas->width;
}
}
}
/* Write the datas in the file: the header and the bitmap */
void
write_in_file(bmp_header_t *header,
bmp_file_datas_t *datas,
char const *filename)
{
FILE *file_handler = fopen(filename, "w");
BMP_ASSERT(file_handler != NULL, strerror(errno));
fwrite(header, sizeof(*header), 1, file_handler);
fwrite(datas->bitmap, header->infos.img_size, 1, file_handler);
fclose(file_handler);
}
int main(int argc, char **argv)
{
char *buff;
bmp_header_t *header;
bmp_file_datas_t datas;
if (argc != 3)
{
fprintf(stderr, "Usage: %s <input filename> <output filename>\n",
argv[0]);
return EXIT_FAILURE;
}
buff = get_file_content(argv[1]);
header = bmp_get_header();
get_file_infos(buff, header, &datas);
write_bitmap(buff, &datas);
write_in_file(header, &datas, argv[2]);
free(buff), free(header), free(datas.bitmap);
return EXIT_SUCCESS;
}
A crude method to achieve the required functionality would be: (Not really C, but then it is your homework not mine)
WIDTH = 0, HEIGHT = 0
if(LINE=READ_LINE(fin))
WIDTH = strlen(LINE)
++HEIGHT
else
ERROR!!!
PUSH(LINE)
while(LINE=READ_LINE(fin))
if(WIDTH != strlen(LINE))
ERROR!!!
else
++HEIGHT
PUSH(LINE)
WRITE_BMP_HEADER(WIDTH, HEIGHT)
for(h = 0; h < HEIGHT; ++h)
LINE = POP()
for(w = 0; w < WIDTH; ++w)
COLOR = (LINE[w] - '0') * 255 / 9;
WRITE_BMP_COLOR(COLOR)
WRITE_BMP_PADDING(W)

How can I simply load a greyscale tiff in libtiff and get an array of pixel intensities?

I am trying to understand images some more, and I'm having a great deal of trouble. From using matlab, I have experience in using imread('test.tif'), and getting a beautiful matrix of rows vs. columns, where you have the intensity of each pixel as an integer. So, a 720 x 250 image will give a 720 x 250 matrix, where each cell contains the intensity of the pixel, on a scale from 0-255 (depending on the data type). So, 0 was black, 255 was white.
It was so simple and made so much sense. Now I am attempting to use libtiff, and I am really struggling. I want to do the same thing--access those pixels, and I just can't get it.
I have the following code:
int main(int argc, char *argv[]){
TIFF* tif = TIFFOpen( argv[1], "r");
FILE *fp = fopen("test2.txt", "w+");
if (tif) {
int * buf;
tstrip_t strip;
uint32* bc;
uint32 stripsize;
TIFFGetField( tif, TIFFTAG_STRIPBYTECOUNTS, &bc);
stripsize = bc[0];
buf = _TIFFmalloc(stripsize);
for(strip = 0; strip < TIFFNumberOfStrips(tif); strip++ ) {
if( bc[strip] > stripsize) {
buf = _TIFFrealloc(buf, bc[strip]);
stripsize = bc[strip];
}
TIFFReadRawStrip(tif, strip, buf, bc[strip]);
}
int i;
for (i=0; i<stripsize; i++) {
if ( i % 960 ==0 )
fprintf(fp, "\n");
fprintf(fp,"%d ", buf[i]);
}
_TIFFfree(buf);
TIFFClose(tif);
}
exit(0);
}
But I get completely meaningless results--just completely wacked out numbers. Nothing like the numbers I see when I load the image in matlab.
How can I simply access the pixel values, and look at them?
Thanks so much.
I think you should read Using The TIFF Library article. It contains enough information to get started with libtiff.
Here is some code to read image scanlines and print values of each sample.
main()
{
TIFF* tif = TIFFOpen("myfile.tif", "r");
if (tif) {
uint32 imagelength;
tsize_t scanline;
tdata_t buf;
uint32 row;
uint32 col;
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imagelength);
scanline = TIFFScanlineSize(tif);
buf = _TIFFmalloc(scanline);
for (row = 0; row < imagelength; row++)
{
TIFFReadScanline(tif, buf, row);
for (col = 0; col < scanline; col++)
printf("%d ", buf[col]);
printf("\n");
}
_TIFFfree(buf);
TIFFClose(tif);
}
}
Regarding this article, I think it'll be better to use TIFFRGBAImage approach, because as it turned out TIFF file could be one of different formats: tiled, scanline-based and strip-oriented. Here's an example from the same article.
TIFF* tif = TIFFOpen(argv[1], "r");
if (tif) {
uint32 w, h;
size_t npixels;
uint32* raster;
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
npixels = w * h;
raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
if (raster != NULL) {
if (TIFFReadRGBAImage(tif, w, h, raster, 0)) {
...process raster data...
}
_TIFFfree(raster);
}
TIFFClose(tif);
}
raster is a uint32 array (maximum value= 0xffffffff)
but you are trying to read a 16-bit array (maximum value 0xffff).
you will run into 32bit to 16bit conversion problems.
Reading the scanline method is the better way to do it.
This way you can convert the void* buf to uint16* and access the pixel values.
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <inttypes.h>
#include "tiffio.h"
using namespace std;
void printArray(uint16 * array, uint16 width);
int main()
{
TIFF* tif = TIFFOpen("16bit_grayscale_image.tif", "r");
if (tif) {
uint32 imagelength,height;
tdata_t buf;
uint32 row;
uint32 config;
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imagelength);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &config);
buf = _TIFFmalloc(TIFFScanlineSize(tif));
uint16 s, nsamples;
uint16* data;
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &nsamples);
for (s = 0; s < nsamples; s++)
{
for (row = 0; row < imagelength; row++)
{
TIFFReadScanline(tif, buf, row, s);
data=(uint16*)buf;
printArray(data,imagelength);
}
// printArray(data,imagelength,height);
}
_TIFFfree(buf);
TIFFClose(tif);
}
exit(0);
}
void printArray(uint16 * array, uint16 width)
{
uint32 i;
for (i=0;i<width;i++)
{
printf("%u ", array[i]);
}
printf("\n");
}

Resources