I have a problem with reading pixels from bmp file. It might be something with padding at the end of row or the base64 padding. I have no clue. I've been struggling with this for some days and can't move on because the next task requires this one to be solved.
I only share important parts of the code, since reading bmp header worked fine (tests had 0 failures).
bmp.c
struct pixel* read_data(FILE* stream, const struct bmp_header* header){
if(stream == NULL || header == NULL){
return 0;
}
// w == 1 && p == 1; w == 2 && p == 2; w == 3 && p == 3; w == 4 && p == 0
int padding = header->width % 4;
int num_of_pixels = header->width * header->height;
struct pixel* Pixel[num_of_pixels];
fseek(stream, 54, SEEK_SET); //move 54B (header size)
int index_p = 0;
for(int i = 0; i < header->height; i++){
for(int j = 0; j < header->width; j++){
Pixel[index_p] = malloc(sizeof(struct pixel));
fread(&(Pixel[index_p]->blue), 1, 1, stream);
fread(&(Pixel[index_p]->green), 1, 1, stream);
fread(&(Pixel[index_p]->red), 1, 1, stream);
index_p++;
}
fseek(stream, padding, SEEK_CUR); //padding at the end of row
}
return *Pixel;
}
bmp.h
struct pixel {
uint8_t blue;
uint8_t green;
uint8_t red;
//uint8_t alpha;
} __attribute__((__packed__));
/**
* Read the pixels
*
* Reads the data (pixels) from stream representing the image. If the stream
* is not open or header is not provided, returns `NULL`.
*
* #param stream opened stream, where the image data are located
* #param header the BMP header structure
* #return the pixels of the image or `NULL` if stream or header are broken
*/
struct pixel* read_data(FILE* stream, const struct bmp_header* header);
header if needed (basically we use only 24bit color)
struct bmp_header{
uint16_t type; // "BM" (0x42, 0x4D)
uint32_t size; // file size
uint16_t reserved1; // not used (0)
uint16_t reserved2; // not used (0)
uint32_t offset; // offset to image data (54B)
uint32_t dib_size; // DIB header size (40B)
uint32_t width; // width in pixels
uint32_t height; // height in pixels
uint16_t planes; // 1
uint16_t bpp; // bits per pixel (24)
uint32_t compression; // compression type (0/1/2) 0
uint32_t image_size; // size of picture in bytes, 0
uint32_t x_ppm; // X Pixels per meter (0)
uint32_t y_ppm; // X Pixels per meter (0)
uint32_t num_colors; // number of colors (0)
uint32_t important_colors; // important colors (0)
} __attribute__((__packed__));
main.c I do not need to assign any variables to called functions because we have a program for testing this, I just have to call them in main
int main(){
struct bmp_header* header;
FILE *stream = fopen("./assets/square.2x3.bmp", "rb");
header = read_bmp_header(stream);
read_data(stream, header);
read_bmp(stream);
struct bmp_image* image;
image = malloc(sizeof(struct bmp_image));
free_bmp_image(image);
fclose(stream);
return 0;
}
testing (there are more tests, but this should be enough)
1:
FILE* stream = "Qk0+AAAAAAAAADYAAAAoAAAAAgAAAAEAAAABABgAAAAAAAgAAAAjLgAAIy4AAAAAAAAAAAAA/wAAAP8AAAA="; // base64 encoded stream
struct bmp_header* header = read_bmp_header(stream);
fseek(stream, offset, SEEK_SET);
Assertion 'read_data(stream, header) == "/wAAAP8A"' failed. [got "/wAAFctV"]
2:
FILE* stream = "Qk1GAAAAAAAAADYAAAAoAAAAAgAAAAIAAAABABgAAAAAABAAAAAjLgAAIy4AAAAAAAAAAAAA/wAAAAAAAAAAAP8A/wAAAA=="; // base64 encoded stream
struct bmp_header* header = read_bmp_header(stream);
fseek(stream, offset, SEEK_SET);
Assertion 'read_data(stream, header) == "/wAAAAAAAAD/AP8A"' failed. [got "/wAAAAAAAAAAAAAA"]
So after the "==" is expected result and in the brackets is my the result from my code. As I mentioned, it might be something with padding, since it starts well but doesn't end well.
Thanks for help.
Short Answer: Set padding to (4-((3*width)%4))%4
Long answer:
Your code included:
int padding = header->width % 4;
//Some lines of code
fseek(stream, padding, SEEK_CUR);
In a bitmap, padding is added until each row is a multiple of 4 bytes. You took padding as width % 4.
First off, each pixel takes 3 bytes(for rgb). So it should be (3*width)%4. Next, we need to subtract it from 4 bytes (Since padding is 4-pixels occupied). So padding would be 4-((3*width)%4). Another small modification, if (3*width)%4==0 then padding would come to be 4 (whereas, we expect it to be 0). So we take another mod4 just to be sure
So padding would come out to be (4-((3*width)%4))%4
EDIT:
As pointed out by user Craig Estey in the comments, its better to use sizeof(struct pixel) instead of 3
Related
I am currently working on loading a bitmap file in C. I am kind of new to C, and I ran into a problem: I have a file pointer that reads unsigned chars to a struct of rgb pixels, (it's called rgb but it reads in the order of b,g,r and padding - this is the default of bitmap format file). My file is 12x12 pixels and when it reaches to row 9 it putting only the value '204' in each component, whike the image is white (i.e. all components = 255). All the components before this equal 255.
EDIT: I changed the enum to a three defined values returned by the image state (didn't load, blue, not blue).
EDIT2: I edited the code, but now im equals 0xffffffff and cannot be accessed to.
here's the code:
int CalaculateBlueness()
{
bih bih;
bfh bfh;
int counterblue = 0;
hsv hsv;
FILE *filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
//if the file doesn't exist in memory
if (filePtr == NULL)
{
return IMAGE_NOT_LOADED;
}
//read the bitmap file header
fread(&bfh, sizeof(bfh), 1, filePtr);
//verify that this is a bmp file by check bitmap id
if (bfh.bitmap_type[0] != 'B' || bfh.bitmap_type[1] != 'M')
{
fclose(filePtr);
return IMAGE_NOT_LOADED;
}
fclose(filePtr);
//ensure that the filePtr will point at the start of the image info header
filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
fseek(filePtr, 14, SEEK_CUR);
//read the bitmap info header
fread(&bih, sizeof(bih), 1, filePtr);
if (bih.bit_count!=24)
{
return ERROR_BPP;
}
//point the pointer file to the start of the raw data
//fseek(filePtr, bfh.file_size, SEEK_SET);
int size = bih.height * WIDTHBYTES(bih.width * 32);
unsigned char *im = calloc(1, size);
//put the raw bitmap pixel data in strcut rgb array
fread(&im, sizeof(size), 1, filePtr);
//convert each pixel to it's hue value and check if in the range of blue
for (size_t i = 0; i < bih.height; i++)
{
for (size_t j = 0; j < bih.width; j++)
{
hsv =rgbpixel_hue(im);
if (hsv.h>190 && hsv.h<250)
{
counterblue++;
}
fseek(im, 3, SEEK_CUR);
}
}
//check if more than 80% of the image is blue and return the defined state according to the result
if (counterblue > BLUE_THRESHOLD*(bih.height*bih.width))
{
return BLUE;
}
return NOT_BLUE;
}
Reading in bitmaps is always a difficult thing.
There are quite some points to consider.
fread(&im[i][j].res, sizeof(unsigned char), 1, filePtr);
With this line your read the reserved byte of an RGBQUAD from the file. However, this member is not in the file. The image data in the file contains scanlines. See below.
After reading the Bitmap File Header, you open the file again. However, you didn't close it. This might be succesful or it fails because the file was already open. But you don't chek the return value of fopen. Anyway, there is no need to do this because after having read the BFH the filepointer is positioned at the BITMAPINFOHEADER; you can just read it in. And you need to read it in, otherwise you won't know the dimensions of the bitmap.
From MSDN documentation:
The established bitmap file format consists of a BITMAPFILEHEADER structure followed by a BITMAPINFOHEADER [...] structure. An array of RGBQUAD structures (also called a color table) follows the bitmap information header structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).
For 24 bits-per-pixel bitmaps, there is no color table.
so the sequence is now:
//read the bitmap file header
fread(&bfh, sizeof(bfh), 1, filePtr);
//read the bitmap info header
fread(&bih, sizeof(bih), 1, filePtr);
int bpp= bih.biBitCount;
if (bpp != 24) return 0; // error: must be 24 bpp/ 3 bytes per pixel
Now we must calculate the amount of memory needed to store the image. An image consists of heighth rows of width pixels. The rows are called scanlines.
For some reason, a scanline is alligned on a 4 byte boundary. This means that the last bytes of a scanline may not be in use. So the amount of memory to read the bitmap data from the file is the heigth of the image, times the number of scanlines of the image:
// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
int size= bih.biHeight * WIDTHBYTES(bih.biWidth*32));
unsigned char *im= malloc(size);
Finally you can read the data. If there was a color table, you should have skipped that with a seek but since there is none, you can now read the data:
fread(im, size, 1, filePtr);
Now, to address the pixels you cannot use a simple two-dimensional notation governed by width and heigth of the image...due to the scanlines. You can use the following:
int scanlineSize= WIDTHBYTES(bih.biWidth*bih.biBitCount);
unsigned char *p, *scanline= im;
for (int i=0; i<bih.biHeight; i++)
{
p= scanline;
for (int j=0; j<bih.biWidth; j++)
{
g= *p++;
b= *p++;
r= *p++;
}
scanline += scanlineSize;
}
So to address a 3-byte pixel (x,y) use: im[y*scanlineSize + x*3]
.. except that I believe that scanlines are reversed, so the pixel would be at im[(bih.biHeight-y)*scanlinesize + x*3].
UPDATE: complete function
#include <winGDI.h>
// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
unsigned char *readBitmap(char *szFilename)
{
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
int i, j, size, scanlineSize;
unsigned char r, g, b, *p, *img, *scanline;
FILE *filePtr;
if ((filePtr=fopen(szFilename, "rb"))==0) return 0;
//read the bitmap file header
if (fread(&bfh, sizeof(bfh), 1, filePtr)!=1
|| bfh.bfType != 'MB') {fclose(filePtr); return 0;}
//read the bitmap info header
if (fread(&bih, sizeof(bih), 1, filePtr)!=1
|| bih.biSize!=sizeof(bih)) {fclose(filePtr); return 0;}
if (bih.biBitCount != 24) {fclose(filePtr); return 0;} // error: must be 24 bpp/ 3 bytes per pixel
// allocate memory and read the image
scanlineSize= WIDTHBYTES(bih.biWidth * bih.biBitCount);
size= bih.biHeight * scanlineSize;
if ((img= malloc(size))==0) {fclose(filePtr); return 0;}
if (fread(img, size, 1, filePtr)!=1) {free (img); fclose(filePtr); return 0;}
fclose(filePtr);
scanline= img;
for (i=0; i<bih.biHeight; i++)
{
p= scanline;
for (j=0; j<bih.biWidth; j++)
{
g= *p++;
b= *p++;
r= *p++;
}
scanline += scanlineSize;
}
return img;
}
close filePtr before opening file again
fclose(filePtr);
filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
and offset to raw data is bfh.offset
//point the pointer file to the start of the raw data
fseek(filePtr, bfh.offset, SEEK_SET);
I'm trying to read in a given BMP file and store it in an image. I'm confused about the syntax since I'm struggling to understand the .h files given to me. Here's how I'm going about reading in the image:
BMPImage * readImage(FILE * fp) {
// FILL IN
BMPHeader * hp = malloc(sizeof(BMPHeader);
Pixel * p = malloc(sizeof(Pixel));
p -> pixels = malloc(p -> height_px * sizeof(Pixel *));
for(int i = 0; i < p -> height_px; i++){
p -> pixels[i] = malloc(p -> width_px * sizeof(Pixel));
}
for (i = 0; i < p -> height_px; i++){
for(int j = 0; j < p -> width_px; j++){
Pixel px = fread(hp, sizeof(Pixel), 1, fp);
p -> pixels[i][j] = px;
}
}
return p;
}
Here's the .h file:
typedef struct __attribute__((packed)) BMPHeader { // Total: 54 bytes
uint16_t type; // Magic identifier: 0x4d42
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset; // Offset to image data in bytes from beginning of file (54 bytes)
uint32_t dib_header_size; // DIB Header size in bytes (40 bytes)
int32_t width_px; // Width of the image
int32_t height_px; // Height of image
uint16_t num_planes; // Number of color planes
uint16_t bits_per_pixel; // Bits per pixel
uint32_t compression; // Compression type
uint32_t image_size_bytes; // Image size in bytes
int32_t x_resolution_ppm; // Pixels per meter
int32_t y_resolution_ppm; // Pixels per meter
uint32_t num_colors; // Number of colors
uint32_t important_colors; // Important colors
} BMPHeader;
typedef struct __attribute__((packed)) Pixel {
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t alpha;
} Pixel;
typedef struct BMPImage {
BMPHeader header;
int norm_height; //normalized height
Pixel * * pixels;
} BMPImage;
How should I correct my reading method?
Given how you're struggling with structs, lists, and basic I/O, reading in a BMP is probably over your head right now. I'd suggest trying to populate a simpler struct. If this is for a production code, please use an existing library.
I'm going to do some basic fixups so your code at least compiles, hopefully you can take it from there.
BMPImage *readImage(FILE * fp) {
// Allocate space for the image.
// This also covers BMPHeader, since it's not a pointer.
BMPImage *bmp = malloc(sizeof(BMPImage));
BMPHeader *bmph = &(bmp->header);
The basic unit is the BMPImage struct. That contains the BMPHeader as well as a pointer to list of Pixels. The BMPHeader is not a pointer, the actual memory is contained in the BMPImage struct, so malloc(sizeof(BMPImage)) also allocates memory for the BMPHeader. I've taken a pointer to the BMPHeader to make it easier to work with, otherwise the code is sprinkled with bmp.header->height_x.
I'll leave populating the BMPHeader to you. Once you've done that...
// Allocate space for the pixels.
bmp->pixels = malloc( bmph->height_px * sizeof(Pixel *) );
for(int i = 0; i < bmph->height_px; i++){
bmp->pixels[i] = malloc(bmph->width_px * sizeof(Pixel));
}
I think you've basically got the memory allocation right. Your problem was trying to use the Pixel struct when you should have been using BMPImage. While the height and width comes from the BMPHeader.
That isn't really how you should be reading in a BMP since the size of the pixels are variable according to the bits_per_pixel header. Your structs only support the 8bpp format. It also might be compressed. I'm going to assume this is an exercise and not production code, so I'll avoid going further into the details of BMP.
// Read in each pixel
for (int i = 0; i < bmph->height_px; i++){
for(int j = 0; j < bmph->width_px; j++){
Pixel px;
if( fread(&px, sizeof(Pixel), 1, fp) < 1 ) {
fprintf(stderr, "Error while reading bmp: %s", strerror(errno));
return NULL;
}
bmp->pixels[i][j] = px;
}
}
Again, you had the BMPImage and BMPHeader mixed up with Pixel. Furthermore, fopen does not return the structure to be read. Instead, you're responsible for allocating the necessary memory (a theme in C). fopen returns the number of items read. It's very important to do error checking on all file operations, else you'll be left with difficult to understand garbage.
I suspect you can do this much simpler by reading in all the pixels in one big chunk, but I don't know the details of the BMP format.
This is by no means 100% correct, I'm not even sure it's 80% correct, but it'll at least clean up the worst of the confusion.
Here is untested code.
void readImage(FILE * fp, BMPImage *bpp) {
Pixel pixel;
for (i = 0; i < p -> height_px; i++){
for(int j = 0; j < p -> width_px; j++){
fread(&pixel, sizeof(Pixel), 1, fp);
bpp->pixels[i][j] = pixel;
}
}
int
main()
{
BMPHeader *hp = malloc(sizeof(* hp));
FILE *fp;
int i;
... read header info and stored into hp->header
... open fp file
hp->pixels = malloc(bpp->header.width_px * sizeof(Pixel *));
for( i=0; i<hp->header.width_px; i++ )
hp->pixels[i] = malloc(bpp->header.height_px * sizeof(Pixel));
//Send in pointers.
readImage(fp, hp);
... need to free up memory.
}
.
First, need to follow composite data structure.
Second, need to free up memory since it looks like it will need a lot of memory.
It is a good practice to allocate/free up memory in one area.
Well basically I wrote this program for my computer course (shoutout CS50) that recovers images from a .raw file. I have managed to have the program recover 48 of the 50 files in that file.
The issue im having right now with the program is that the program cannot recover both the first and the second file located on .raw. It either reads and writes the very first file (this girl in a snowy background) or the second file on the .raw (guy behind books).
For some reason if I change fopen from write to append I can switch between the photo of the girl and the guy, but I cant seem to be able to open both.
https://github.com/CoreData/cs50/blob/master/pset4/jpg/card.raw
This is the link to card.raw, unfortunately its not the same one that Im using but even using this one you get two different images for image1.jpg depending on whether you have fopen with an "a" or "w".
Any ideas???
if you guys want any additional info just let me know
#include <stdio.h>
#include <stdlib.h>
#include "bmp2.h"
int main(void)
{
/*OPEN CARD FILE*/
char* infile = "card.raw";;
FILE* card = fopen(infile, "r");
if (card == NULL)
{
printf("Could not open %s.\n", "card.raw");
return 2;
}
int f = 0, c = 0, l = 0, x = 128, imageno = 1;
// c signals that a jpg is being written
// l size control, 0 means 0 jpgs
FILE* images;
char* title = (char*)malloc(15);
/*repeat until end of card*/
do
{
//read one block into buffer
INTROJPG *buffer = (INTROJPG*)malloc(sizeof(INTROJPG)*x);
for (int i = 0; i < 128; i++)
{
fread(&buffer[i], sizeof(INTROJPG), 1, card);
}
if (buffer[0].first == 0xff && buffer[0].second == 0xd8 && buffer[0].third == 0xff)
{
sprintf(title, "image%d.jpg", imageno); //change jpg title
if (f == 1) //close previous jpg
{
fclose(images);
imageno++;
}
images = fopen(title, "w");
f = 1; //very first jpg has been opened
c = 1; //jpg open
l++; //jpg count + 1
}
//jpg already open?
if (c == 1)
{
for (int i = 0; i < 128; i++)
{
fwrite(&buffer[i], sizeof(INTROJPG), 1, images);
}
}
free(buffer);
}
while (l < 50);
free(title);
return 5;
//close any remaining files
}
and this is my bmp2.h file
#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;
typedef struct
{
BYTE first;
BYTE second;
BYTE third;
BYTE fourth;
} __attribute__((__packed__))
INTROJPG;
typedef struct
{
BYTE image;
}
BYTEIMAGE;
First things first, I'll try to improve a few things in your code. I've also done this pset and it is nice to help others.
INTROJPG *buffer = (INTROJPG*)malloc(sizeof(INTROJPG)*x);
At this part, you know that the size of both INTROJPG and x are constant, so there is no need to constantly allocate and free memory at every iteration, that takes much more time than simply creating a normal array. Also, why is the buffer a pointer to INTROJPG? If it is only to test for a header at each iteration, I don't think it is worth it, you could simply access the first 4 bytes of a normal BYTE array.
I'd create a static array of 512 BYTEs (the struct on the library), because this is the size you are constantly allocating and freeing and also you are using BYTEs, not INTROJPGs.
Second, at this section and another similar one:
for (int i = 0; i < 128; i++)
{
fread(&buffer[i], sizeof(INTROJPG), 1, card);
}
There is absolutely no need for this loop or, again, even using INTROJPG. You are always reading and writing 512 bytes, you could use:
fread(buffer, 4, 128, card);
// or even better
fread(buffer, 512, 1, card);
Now about your problem, I've tested your code (without any modifications) multiple times and found nothing wrong with image1.jpg and image2.jpg. Yes, I changed "w" mode to "a" and vice-versa.
However, your code is faulty in regard to the last image, your last image is image49.jpg, when it should be image50.jpg, and your image49.jpg does not even open, and that's because the loop is finished before the rest of image49.jpg is stored, i.e., you are storing only the first 512 bytes of image49.jpg.
To fix that, I've changed the condition of the do-while loop to keep going until the end of the card file, IIRC the problem guarantees the last block being part of the last image or something like that, if not, it's up to you to fix this little problem!
#include <stdio.h>
#include <stdlib.h>
#include "bmp2.h"
int main(void)
{
/*OPEN CARD FILE*/
char* infile = "card.raw";;
FILE* card = fopen(infile, "r");
if (card == NULL)
{
printf("Could not open %s.\n", "card.raw");
return 2;
}
int f = 0, c = 0, imageno = 1;
// c signals that a jpg is being written
// l size control, 0 means 0 jpgs
FILE* images;
char title[25];
BYTE buffer[512];
/*repeat until end of card*/
do
{
fread(buffer, 512, 1, card);
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff)
{
sprintf(title, "image%d.jpg", imageno); //change jpg title
if (f == 1) //close previous jpg
{
fclose(images);
imageno++;
}
images = fopen(title, "w");
f = 1; //very first jpg has been opened
c = 1; //jpg open
}
//jpg already open?
if (c == 1) fwrite(buffer, 512, 1, images);
}
while (!feof(card));
return 5;
//close any remaining files
}
One last thing, why are you returning 5 at the end of the program? Just curious.
This is my function, I am using the headers BMP according to wikipedia BITMAPINFOHEADER. But, I am getting a file without any image...when putting padding, the process stops.
// Structures for header info
#pragma pack(push,1)
/* Windows 3.x bitmap file header */
typedef struct {
char filetype[2]; /* magic - always 'B' 'M' */
unsigned int filesize;
short reserved1;
short reserved2;
unsigned int dataoffset; /* offset in bytes to actual bitmap data */
} file_header;
/* Windows 3.x bitmap full header, including file header */
typedef struct {
file_header fileheader;
unsigned int headersize;
int width;
int height;
short planes;
short bitsperpixel; /* we only support the value 24 here */
unsigned int compression; /* we do not support compression */
unsigned int bitmapsize;
int horizontalres;
int verticalres;
unsigned int numcolors;
unsigned int importantcolors;
} bitmap_header;
#pragma pack(pop)
int RGB2GREY(char* input, char *greyImage) {
//variable declaration:
FILE *fp, *grey;
bitmap_header* hp;
int n;
char *data;
int oldBitsperpixel;
//Open input file:
fp = fopen(input, "rb");
if(fp==NULL){
//cleanup
}
//Read the input file headers:
hp=(bitmap_header*)malloc(sizeof(bitmap_header));
if(hp==NULL)
return 3;
n=fread(hp, sizeof(bitmap_header), 1, fp);
if(n<1){
//cleanup
}
//Read the data of the image:
data = (char*)malloc(sizeof(char)*hp->bitmapsize);
if(data==NULL){
//cleanup
}
//Put me in the position after header...
fseek(fp,sizeof(char)*hp->fileheader.dataoffset,SEEK_SET);
printf("Width %d and Height %d\n",hp->width,hp->height);
int i, j;
unsigned char BGR[3];
unsigned colorIntensity[3];
/*unsigned char bmppad[hp->width] = {0};*/
printf("New bitmapSize %d\n\n",hp->bitsperpixel);
//Open greayImage file:
grey = fopen(greyImage, "wb");
if(grey==NULL){
//cleanup
}
//Writes the header
n=fwrite(hp,sizeof(char),sizeof(bitmap_header),grey);
if(n<1){
//cleanup
}
//Again going to position after header
fseek(out,sizeof(char)*hp->fileheader.dataoffset,SEEK_SET);
for (i=0; i<hp->height; i++){
for (j=0; j<hp->width; j++){
//Reading pixel by pixel
fread(BGR, 3, 1, fp); //1 unsigned char of 3 positions
unsigned char colorGrey;
colorGrey = (unsigned char) 0.3*BGR[2] + 0.6*BGR[1] + 0.1*BGR[0];
colorIntensity[2] = colorGrey;
colorIntensity[1] = colorGrey;
colorIntensity[0] = colorGrey;
/*printf("B %d G %d R %d ",BGR[0],BGR[1],BGR[2]);
printf("Gray %d ",colorIntensity);*/
fwrite(colorIntensity, 3, 1, grey);
}
/*
// Adding pad option1
//fwrite(bmppad, sizeof(bmppad), 1, grey);
//Adding pad option2
for (j=0; j>hp->width; j++){
fwrite(0, 1, 1, grey);
}*/
}
fclose(fp);
fclose(grey);
free(hp);
free(data);
return 0;
}
In the grey output file, I get nothing...moreover, I wonder if there is a way to reduce from 24 to 8 bits.
ps. My code came from reading/writing bmp files in c
The formula came from Create greyscale BMP from RGB BMP
Thanks,
You are essentially turning a 32-bit color bitmap into a 32-bit grey bitmap by changing the color values in a way that they appear grey (you are not saving any space in this way; the bitmap stays as large as it was). Anayway, it explains why you do not need to adapt the bitmap header.
But when you read every three bytes and change every three bytes, you do not take scanlines into account.
An image consists of scanlines and a scanline consits of pixels. Scanlines are alligned on even word boundaries so the last few bytes of a scanline are unused (and the scanline is thus a bit longer than all the pixels on it).
To properly process the input and create the output, your loop must be:
(EDIT: updated to use 1 byte per pixel output):
#pragma pack(push,1)
typedef struct {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} pal_entry;
#pragma pack(pop)
int ToGreyScale(FILE *fp, FILE *grey, bitmap_header *hp)
{
int i, j;
int iScanlineSizeIn = ((hp->width * hp->bitsperpixel) + 31) / 32 * 4;
int iScanlineSizeOut= ((hp->width * 8 ) + 31) / 32 * 4;
unsigned char *scanlineIn = malloc(iScanlineSizeIn), *pIn;
unsigned char *scanlineOut= malloc(iScanlineSizeOut), *pOut;
pal_entry pal[256];
for (i=0; i<256; i++) // create a gray scale palette
{pal[i].rgbBlue= i; pal[i].rgbGreen= i; pal[i].rgbRed= i;}
hp->bitsperpixel= 8; // set output bits-per-pixel
hp->fileheader.filesize= sizeof(bitmap_header) + sizeof(pal) + hp->width*iScanlineSizeOut;
fwrite(hp, sizeof(bitmap_header), 1, grey); // write the header...
fwrite(pal, 256*sizeof(pal_entry), 1, grey); //..followed by palette
for (i=0; i<hp->height; i++)
{
if (fread(scanlineIn, iScanlineSizeIn, 1, fp) != 1) return(0);
pIn = scanlineIn;
pOut= scanlineOut;
for (j=0; j<hp->width; j++)
{
*pOut++ = (unsigned char) ((0.1 * *pIn++) + (0.6 * *pIn++) + (0.3 * *pIn++));
}
fwrite(scanlineOut, iScanlineSizeOut, 1, grey);
}
free(scanlineIn);
free(scanlineOut);
return(1);
}
I am currently facing issues when trying to apply the run-length algorithm to a .bmp picture. When I am doing the encoding, all is working well, but at decoding there is a problem and I cannot figure it out, namely I cannot read the number of repetitive pixels and thus my output is a blank image. When I use the debugger, the number of repetitive pixels is not readed correctly from the binary file (the result of applying RLE on the bitmap file). Here is my code
#include<stdlib.h>
#include <stdint.h>
//structure defiens bitmap header
struct BITMAPFILEHEADER{
uint8_t type[2];//type of file (bit map)
uint32_t size;//size of file
uint16_t reserved1;//
uint16_t reserved2;//
uint32_t offsetbits;//off set bits
} __attribute__ ((packed));
struct BITMAPINFOHEADER{
uint32_t size;//bitmap size
// uint16_t w2;
uint32_t width;//width of bitmap
//uint16_t h2;
uint32_t height;//hight of bitmap
uint16_t planes;
uint16_t bitcount;
uint32_t compression;// compression ratio (zero for no compression)
uint32_t sizeimage;//size of image
long xpelspermeter;
long ypelspermeter;
uint32_t colorsused;
uint32_t colorsimportant;
} __attribute__ ((packed));
//const char* INPUT_FILE = "/home/bogdan/bee.bmp";
const char* INPUT_FILE = "/home/bogdan/Linux.bmp";
const char* ENCODED_FILE = "/home/bogdan/encoded.bin";
const char* DECODED_FILE = "/home/bogdan/decoded.bmp";
typedef struct SINGLE_PIXEL{
uint8_t green;//Green level 0-255
uint8_t red; //Red level 0-255
} PIXEL;
int comparePixels(PIXEL, PIXEL);
void encode();
void decode(char*);
int main()
{
encode();
decode(ENCODED_FILE);
return 0;
}
void encode() {
uint32_t i=0;//to count pixels read
uint32_t pixno=0;//number of pixels to read
struct BITMAPFILEHEADER source_head;//to store file header
struct BITMAPINFOHEADER source_info;//to store bitmap info header
PIXEL pixel;// the current pixel
FILE *in;// bitmap imput pointer file
FILE *out;//output file pointer
if(!(in=fopen(INPUT_FILE,"rb")))//open in binary read mode
{
printf("\ncan not open file");//error at opening file
exit(-1);
}
out=fopen(ENCODED_FILE,"wb");//opne in binary write mode
//read the headers to source file
fread(&source_head,sizeof(struct BITMAPFILEHEADER),1,in);
fread(&source_info,sizeof(struct BITMAPINFOHEADER),1,in);
//write the headers to the output file
fwrite(&source_head,sizeof(struct BITMAPFILEHEADER),1,out);
fwrite(&source_info,sizeof(struct BITMAPINFOHEADER),1,out);
//cumpute the number of pixels to read
pixno=source_info.width*source_info.height;
// init list of pixels
PIXEL pixArr[pixno];
printf("total pixels: %d", pixno);
//printf("w:%f h:%u pn:%lu", (source_head.size/1024.0/1024), source_info.height, pixno);
uint32_t sum = 0;
//read, modify and write pixels
for(i=0;i<pixno;++i)
{
//read pixel form source file
fread(&pixel,sizeof(PIXEL),1,in);
pixArr[i] = pixel;
}
for (i = 0; i < pixno; i++) {
// printf ("i = %d\tred = %d green = %d blue = %d\n",i, pixArr[i].red, pixArr[i].green, pixArr[i].blue);
int runlength = 1;
while ((i + 1) < pixno) {
if (comparePixels(pixArr[i], pixArr[i+1]) == 0){
// printf ("i = %d\t red = %d green = %d blue = %d\n",i, pixArr[i].red, pixArr[i].green, pixArr[i].blue);
runlength++;
i++;
} else {
break;
}
}
//fprintf(out, "%d", runlength);
fwrite(&runlength, sizeof(runlength), 1, out);
fwrite(&pixel,sizeof(PIXEL),1,out);
sum += runlength;
runlength = 0;
}
//write the modification to the output file
//close all fiels
fclose(in);
fclose(out);
printf("sum = %d",sum);
}
void decode(char * filePath) {
uint32_t i=0;//to count pixels read
uint32_t j=0;
uint32_t totalPixels=0;//number of pixels to read
uint32_t pixelRepetition = 1;
struct BITMAPFILEHEADER source_head;//to store file header
struct BITMAPINFOHEADER source_info;//to store bitmap info header
PIXEL pixel;// the current pixel
FILE *in;// bitmap encoded pointer file
FILE *out;//decoded bitmap file pointer
if (!(in = fopen(filePath, "rb"))) {
printf("\ncan not open file");
exit(-1);
}
out = fopen(DECODED_FILE, "wb");
//read the headers to source file
fread(&source_head,sizeof(struct BITMAPFILEHEADER),1,in);
fread(&source_info,sizeof(struct BITMAPINFOHEADER),1,in);
//write the headers to the output file
fwrite(&source_head,sizeof(struct BITMAPFILEHEADER),1,out);
fwrite(&source_info,sizeof(struct BITMAPINFOHEADER),1,out);
totalPixels=source_info.width*source_info.height;
while(i < totalPixels) {
fread(&pixelRepetition, sizeof(pixelRepetition), 1, out);
fread(&pixel,sizeof(PIXEL),1,in);
for (j = 0; j < pixelRepetition; j++) {
fwrite(&pixel,sizeof(PIXEL),1,out);
}
i += pixelRepetition;
}
fclose(in);
fclose(out);
}
int comparePixels(PIXEL px1, PIXEL px2) {
if (px1.red == px2.red && px1.green == px2.green && px1.blue == px2.blue) {
return 0;
} else {
return -1;
}
}
My implementation is working as follow: I read the header of a bmp file and put it directly to another file. After that I compare pixels to see if they have the same RGB values. If this is true (according to comparePixels function) I put the number of consecutive identical pixels and one pixel (the one which is repeated) into the file. This is the encode phase. At decoding, I am reading the header of the image and then I am trying to read the number of repetitions (1 default, means no repetitive pixels) and the pixel which is repeated. I will really appreciate any type of help. Thank you.
You are repeatedly writing out only the very last pixel you read in:
fwrite(&runlength, sizeof(runlength), 1, out);
fwrite(&pixel,sizeof(PIXEL),1,out);
You need to write out your current pixel instead:
fwrite(pixArr[i], sizeof(PIXEL),1,out);