I am applying a basic tutorial on image processing in C and I am having problems with this program to convert RGB into grayscale but the output pic is somehow corrupted and although the code runs with no errors, and I cant put my hands on the problem. The code is below.
FILE *fIn = fopen("tiger.bmp","rb"); //Input File name
FILE *fOut = fopen("tiger_gray.bmp","wb"); //Output File name
int i,j,y;
unsigned char byte[54];
if(fIn==NULL)
{
printf("File does not exist.\n");
}
for(i=0;i<54;i++) //read the 54 byte header from fIn
{
byte[i] = getc(fIn);
}
fwrite(byte,sizeof(unsigned char),54,fOut); //write the header back
// extract image height, width and bit Depth from image Header
int height = *(int*)&byte[18];
int width = *(int*)&byte[22];
int bitDepth = *(int*)&byte[28];
printf("width: %d\n",width);
printf("height: %d\n",height );
int size = height*width;
unsigned char buffer[size][3]; //to store the image data
for(i=0;i<size;i++) //RGB to gray
{
y=0;
buffer[i][2]=getc(fIn); //blue
buffer[i][1]=getc(fIn); //green
buffer[i][0]=getc(fIn); //red
y=(buffer[i][0]*0.3) + (buffer[i][1]*0.59) + (buffer[i][2]*0.11); //conversion formula of rgb to gray
putc(y,fOut);
putc(y,fOut);
putc(y,fOut);
}
fclose(fOut);
fclose(fIn);
There were two major problems in your code.
You had the width and height reversed.
You were not accounting for
the required padding at the end of every row to make it a multiple
of 4 bytes.
You were also allocating a large buffer that you did not need based on how the rest of the code was written. Generally I'd prefer to read/process either one full row at a time or even the full image at once, but to do that you want to use malloc or calloc because the data may be larger than the available stack. In this case, to keep things simple, I just process one pixel at a time.
I also got rid of getc/putc because I prefer fread/fwrite and you're never really dealing with 1 byte at a time.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *fIn = fopen("tiger.bmp", "rb");
FILE *fOut = fopen("tiger_gray.bmp", "wb");
if (!fIn || !fOut)
{
printf("File error.\n");
return 0;
}
unsigned char header[54];
fread(header, sizeof(unsigned char), 54, fIn);
fwrite(header, sizeof(unsigned char), 54, fOut);
int width = *(int*)&header[18];
int height = abs(*(int*)&header[22]);
int stride = (width * 3 + 3) & ~3;
int padding = stride - width * 3;
printf("width: %d (%d)\n", width, width * 3);
printf("height: %d\n", height);
printf("stride: %d\n", stride);
printf("padding: %d\n", padding);
unsigned char pixel[3];
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
fread(pixel, 3, 1, fIn);
unsigned char gray = pixel[0] * 0.3 + pixel[1] * 0.58 + pixel[2] * 0.11;
memset(pixel, gray, sizeof(pixel));
fwrite(&pixel, 3, 1, fOut);
}
fread(pixel, padding, 1, fIn);
fwrite(pixel, padding, 1, fOut);
}
fclose(fOut);
fclose(fIn);
return 0;
}
Related
I am generating 500 random points (each point is of dimension=4) and writing them into a binary file as the following:
FILE *fp = fopen("file.bin", "wb+");
float *buf = (float *) calloc(500 * 4, sizeof(float));
srand(1);
for (i = 0; i < (500 * 4); ++i) {
buf[i] = (float) rand() / (float) RAND_MAX; // Later I shall treat every 4 values as one point.
}
fwrite(buf, sizeof(float), 500 * 4 * sizeof(float), fp);
fclose(fp);
Later in my code, I want to read only the first 100 points (i.e., 100*4 as each point is of dim=4). I use the following code to do so but I am having a problem and it does not really print anything:
FILE *fp = fopen("file.bin", "rb+");
fseek(fp, 0, SEEK_SET);
int c, cnt=0;
while(1)
{
c = fgetc(fp);
if(feof(fp) || (cnt == 100)) { break; }
printf("%c", c);
cnt++;
}
printf("\n");
fclose(fp);
If you want to read back just 100 elements (as in 4 floats), that's the number you put into fread like so...
fread(buf, sizeof(float), 100 * 4, fp);
as per the various comments above your call to fwrite should look similar as you don't need the extra sizeof(float)
fwrite(buf, sizeof(float), 500 * 4, fp);
basically my problem is that when I use the following program:
#include <stdlib.h>
#include <stdio.h>
#define SIZE 1000
int main() {
FILE *fp;
int r, i;
char fp_string[600] = "/Users/mac/Library/Mobile Documents/com~apple~CloudDocs/College/Program With Persistent Data/Lab 3/num1000.bin";
fp = fopen(fp_string, "rb+");
r = 11;
fseek(fp, 3 * sizeof(int), SEEK_SET);
fwrite(&r, sizeof(int), 1, fp);
fseek(fp, 10 * sizeof(int), SEEK_SET);
fwrite(&r, sizeof(int), 1, fp);
fclose(fp);
return 0;
}
It updates the binary file (which is 1000 integers) with the 3rd and 10th digits to be 11.
But when I do the following:
#include <stdlib.h>
#include <stdio.h>
#define SIZE 1000
int main() {
FILE *fp;
int r, i;
char fp_string[600] = "/Users/mac/Library/Mobile Documents/com~apple~CloudDocs/College/Program With Persistent Data/Lab 3/num1000.bin";
fp = fopen(fp_string, "rb+");
r = 11;
printf("\n\n Before making any changes:\n");
for (i = 0; i < SIZE; i++) {
fseek(fp, i * sizeof(int), SEEK_SET);
fread(&r, sizeof(int), 1, fp);
printf("%d ", r);
}
fseek(fp, 3 * sizeof(int), SEEK_SET);
fwrite(&r, sizeof(int), 1, fp);
fseek(fp, 10 * sizeof(int), SEEK_SET);
fwrite(&r, sizeof(int), 1, fp);
printf("\n\n After making changes:\n");
fseek(fp, 0, SEEK_SET);
for (i = 0; i < SIZE; i++) {
fread(&r, sizeof(int), 1, fp);
printf("%d ", r);
}
fclose(fp);
return 0;
}
It doesn't change anything at all. Just in case you where wondering, to check if the first program worked what I did is:
I would run the program you have underneath this text to check the integers stored in the binary file.
I would run the program you have on top of this text (the second one I posted) to change the 3rd and 10th integer to 11.
I would run the program you have underneath to check that those integers were changed to 11.
That way it worked, but the first program doesn't seem to change anything, it shows the exact same numbers again.
#include <stdlib.h>
#include <stdio.h>
#define SIZE 1000
int main() {
FILE *fp;
int r, i;
char fp_string[600] = "/Users/mac/Library/Mobile Documents/com~apple~CloudDocs/College/Program With Persistent Data/Lab 3/num1000.bin";
fp = fopen(fp_string, "rb");
for (i=0;i<SIZE;i++) {
fread(&r, sizeof(int), 1, fp);
printf("%d ", r);
}
fclose(fp);
return 0;
}
The first for loop is reading every number in the file into rbefore printing them. At the end of the loop, r contains the last number in the file, and that gets written into the places where you seek.
Either use a different variable in the loop, or put the r = 11; assignment after the loop.
Task: read bmp file (byte by byte) and write it to another bmp file.
How to port this short app from Windows to Linux in C ?
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
BITMAPFILEHEADER bfh_l;
BITMAPINFOHEADER bih_l;
RGBTRIPLE rgb_l;
FILE * f1, * f2;
f1 = fopen("1.bmp", "r+b");
f2 = fopen("3.bmp", "w+b");
int i, j;
size_t t;
fread(&bfh_l,sizeof(bfh_l),1,f1);
fwrite(&bfh_l, sizeof(bfh_l), 1, f2);
fread(&bih_l,sizeof(bih_l),1,f1);
fwrite(&bih_l, sizeof(bih_l), 1, f2);
size_t padding = 0;
if ((bih_l.biWidth * 3) % 4)
padding = 4 - (bih_l.biWidth * 3) % 4;
for( i=0;i< bih_l.biHeight;i++)
{
for ( j = 0; j < bih_l.biWidth; j++)
{
fread(&rgb_l, sizeof(rgb_l),1, f1);
fwrite(&rgb_l, sizeof(rgb_l), 1, f2);
}
for ( t = 0; t < padding; ++t)
{
fread(&rgb_l, sizeof(rgb_l),1, f1);
fwrite(&rgb_l, sizeof(rgb_l), 1, f2);
}
}
fclose(f1);
fclose(f2);
return 0;
}
I'm looking for a simple and if possible cross-platform solution.
The only Windows specific thing in your code is that you use 3 structures defined in Windows.h. You could simply define them by yourself as there is no magic behind it. The definitions can be found here:
BITMAPFILEHEADER
BITMAPINFOHEADER
RGBTRIPLE
To convert the wrapper types (e.g. DWORD) to native C types you can use this list.
Ok, so I have code that will read in the header from a ppm image, allocate memory for the size of the image, and will successfully print the blank spots (a random number repeated in terminal) for each pixel. All I need to know is how I am supposed to read in the red green and blue values (the 3 separate values ranging from 0-255) for each pixel. I don't know how to access this data within each pixel. Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
int subscript(int row,int column,int numberColumns);
int sub(int rd, int gr, int bl);
//table of contents for the subscript
int main(void){
char header[5];
scanf("%s",header);
printf("%s",header);
printf("\n");
int width, height, depth;
scanf("%d %d %d\n", &width, &height, &depth);
printf("%d %d %d\n", width, height, depth);
int red = 0;
int green = 0;
int blue = 0;
int sm;
int r = 0;
int c = 0;
//allocate memory for bit to be used throughout main
unsigned char *bit = malloc(width*height);
int *rgb = malloc(3*depth);
//loops to read in table and print it
while(r < height){
while(c < width)
{
int col;
scanf("%d",&col);
//brings in allocated memory and values
bit[subscript(r,c,width)] = col;
int clr;
rgb[sub(red,green,blue)] = clr;
int color = clr + col;
printf(" %d",clr);
c=c+1;
}
printf("\n");
r = r + 1;
c = 0;
}
free(bit);
}
int subscript(int row,int column, int numberColumns)
{
return row * numberColumns + column;
//retuns items for subscript
}
int sub(int rd, int gr, int bl)
{
return rd+gr+bl;
}
PPM files store colours as triples of bytes (one byte each for r, g, and b in that order) if the maximum colour value field in the header is less than 256 and triples of two bytes if it is 256 or greater (so two bytes for each of r, g, and b, again in that order).
In the two bytes per channel case, the bytes are MSB first (big-endian).
scanf("%c", &byte_var); will read you a single character (byte) into byte_var, which should be an appropriate type. You can then process it accordingly. scanf("%hhu", &byte_var); will read an unsigned character if you are using C99 - if you are using C89 you should look up getchar.
Now, scanf returns the number of successfully converted arguments or EOF, so you should be checking the result for both incorrect input and the end of input.
For example:
int n;
char c;
while ((n = scanf("%c", &c)) != EOF && 1 == n) {
// do something
}
An example use of this would be:
// Read width, height, work out how many bytes you need per pixel.
...
// Now read some pixels
// Here I assume one byte per pixel; this would be a good situation
// to use a function for reading different channel widths
w = 0, h = 0;
while ((n = scanf("%hhu%hhu%hhu", &r, &g, &b)) != EOF && 3 == n
&& w < width && h < height) {
// now do something with r, g, b
// e.g. store them in an appropriate pixels array at pixels[w][h]
...
++w;
if (w == width) {
w = 0, ++h;
}
}
I also see you are reading from stdin, which I find a bit unusual since the title indicates you are working with a file. You can open a file with fopen (don't forget to check the result), use fscanf to read from it, and use fclose to close it afterwards.
However, I suggest reading the whole file into memory and processing it there, because reading a file a few bytes at a time is slow.
Last, I note that you are only allocating width * height bytes for bit when you need bytes_per_channel * num_channels * width * height, not checking whether your malloc failed, not freeing rgb. You should fix these things.
I am trying to copy data from a buffer that contains characters into a struct. The buffer only contains data for the blue, green, and red members of the struct - alpha will be set to zero. The code is as follows, but I can't seem to get the loops figured out.
typedef struct __attribute__((packed)){
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t alpha;
} rgb_pixel_t;
int i, j;
char *buffer;
buffer = malloc(sizeof(rgb_pixel_t) * bmp->dib.width);
bmp_malloc_pixels(bmp);
rgb_pixel_t pixel = {42, 42, 42, 0};
fseek(fp, bmp->header.offset, SEEK_SET);
fread(buffer, sizeof(char), ((bmp->dib.depth / 8) * bmp->dib.width), fp);
for(i = 0; i < 600; ++i) {
for(j = 0; j < 400; ++j) {
bmp_set_pixel(bmp, i, j, pixel); // This isn't right.
}
}
Don't do it that way. Instead of having a char buffer, have a rgb_pixel_t buffer, and read into that:
rgb_pixel_t *pixels = malloc(sizeof(rgb_pixel_t) * bmp->dib.width);
/* ... */
size_t pixels_to_read = bmp->dib.depth / 8 * bmp->dib.width / sizeof(rgb_pixel_t);
fread(pixels, sizeof(rgb_pixel_t), pixels_to_read, fp);
/* ... */
As noted by Drew McGowen in a comment, this will of course not work if the bitmap data doesn't have an alpha channel. Also, there are images that have less bits per pixel, or that doesn't have RGB data at all (instead have index into color table, typically images with 256 colors or less).
You might want to find a library which handle all that for you, and present format-independent API for you to use, instead of having to muck around with the raw image data yourself.