-
Reading a bitmap image
Hello,
I need to load a bitmap image and read its color values and save in an array. If color is white, then I want to store 0 and if color is other than white then I want to store 1. I have searched on the internet and found many code examples but none works for me.
Any ideas ??
Thanks.
-
Sure, understand the bitmap format and write your code accordingly. Check the http://www.wotsit.org website for formats of many files structures. This way you don't have to rely one someone else's code, you'll have the understanding to "go it alone"
-
Read the bitmap header, then read the data which is an array of RGB values (assuming 24bit bitmaps). DON'T read it all in one step since:
a) Structure sizes are usually multiples of 4, which 54 isn't.
b) The way variables are stored may or may not match (little big endian).
Code:
const int BmpHeaderSize = 54;
struct BmpHeader
{
char ID[2];
long FileSize;
long RESERVED;
long BitmapDataOffset;
long BitmapHeaderSize;
long Width;
long Height;
short NrOfPlanes;
short BitsPerPixel;
long Compression;
long BitmapDataSize;
long HorizontalResolution;
long VerticalResolution;
long Colors;
long ImportantColors;
};
-
here is all the stuff
-
here is all the stuff
i just write this code for a reserch work...
this code work (i hope) =) !
Code:
typedef struct
{
unsigned char RGB[3];
}RGB;
typedef struct
{
unsigned int size;
int width,height;
unsigned short int planes;
unsigned short int bpp;
unsigned int compression;
unsigned int imagesize;
int xresolution,yresolution;
unsigned int colours;
unsigned int impcolours;
}INFOHEADER;
// ********** Create Matrix **********
RGB** createMatrix(){
RGB** Matrix;
int i;
Matrix = (RGB **) malloc (sizeof (RGB*) * height);
if (Matrix == NULL){
perror("***** No memory available *****");
exit(0);
}
for (i=0;i<height;i++){
Matrix[i] = (RGB *) malloc (sizeof(RGB) * width);
if (Matrix[i] == NULL){
perror("***** No memory available *****");
exit(0);
}
}
return(Matrix);
}
// ********** Verify if the file is BMP *********
void isBMP(FILE* arq, HEADER head, INFOHEADER info){
char type[3];
unsigned short int bpp;
fseek(arq,0,0);
fread(type,1,2,arq);
type[2] = '\0';
fseek(arq,28,0);
fread(&bpp,1,2,arq);
if (strcmp(type,"BM") || (bpp != 24)){
printf("\nThe file is not BMP format or is not 24 bits\n");
exit(0);
}
}
// ********** Read BMP info from file **********
INFOHEADER readInfo(FILE* arq){
INFOHEADER info;
// Image Width in pixels
fseek(arq,18,0);
fread(&info.width,1,4,arq);
// Image Height in pixels
fseek(arq,22,0);
fread(&info.height,1,4,arq);
// Color depth, BPP (bits per pixel)
fseek(arq,28,0);
fread(&info.bpp,1,2,arq);
// Compression type
// 0 = Normmally
// 1 = 8 bits per pixel
// 2 = 4 bits per pixel
fseek(arq,30,0);
fread(&info.compression,1,4,arq);
// Image size in bytes
fseek(arq,34,0);
fread(&info.imagesize,1,4,arq);
// Number of color used (NCL)
// value = 0 for full color set
fseek(arq,46,0);
fread(&info.colours,1,4,arq);
// Number of important color (NIC)
// value = 0 means all collors important
fseek(arq,50,0);
fread(&info.impcolours,1,4,arq);
return(info);
}
RGB** loadImage(FILE* arq, RGB** Matrix){
int i,j;
RGB tmp;
long pos = 51;
fseek(arq,0,0);
for (i=0; i<height; i++){
for (j=0; j<width; j++){
pos+= 3;
fseek(arq,pos,0);
fread(&tmp,(sizeof(RGB)),1,arq);
Matrix[i][j] = tmp;
}
}
return(Matrix);
}
/* Global */
int height, widht;
/* in your main program you just call */
FILE* arq; /* the bitmap file 24 bits */
RGB** Matrix_aux, Matrix;
INFOHEADER info;
info = readInfo(FILE* arq);
height = info.height;
width = info.width;
Matrix_aux = createMatrix();
Matrix = loadImage(arq,Matrix_aux);
Sorry...
Nonsense is our Motif.
-
Thanks scrapedbr. This program gives 1 error. Please have a look. Also tell me how to load the bmp and how will I get the color values.
-
i think i got it...
Code:
typedef struct
{
char type[2]; // file type
unsigned int size; // file size in bytes
unsigned short int reserved1,reserved2;
unsigned int offset; // offset to image data
}HEADER;
to load the image just call this function:
RGB** loadImage(FILE* arq, RGB** Matrix);
in main;
Code:
HEADER head;
isBMP(arq,head,info);
you acces the color value by this form:
int red, green, blue;
temp = Matrix[i][j].RGB;
red = (int)Matrix[i][j].RGB[0].
green = (int)Matrix[i][j].RGB[1].
blue = (int)Matrix[i][j].RGB[2].
-
i write again the code. is in attach.
gcc -o example example.c
open a bitmap 24 bits file to a matrix, and save again to out.bmp the same file.
-
Thanks scrapedbr for you co-operation. I have taken a very small 24-bit image but getting error message "No Memory available....Not enough Memory". Would you please tell me how to resolve this ??
-
hmmm, just reboot your machine. Or create a swap partition.
-
That seems awful complex for a simple BMP loader.
I've written several of my own. Unless you are coding for a third party library there is no need to do checks for whether or not this is a BMP or not. If you are the only one using it in your game, then you'd better know if it's a BMP or not or you've got issues.
Here is the structure of a BMP:
BitmapFileHeader
----------------------
BitmapInfo
----------------------
1024 byte Palette in format RGB0 (if applicable)
----------------------
ImageData
The following is a DOS 8-bit BMP loader - it does not check for 16,24 bit but could be made to do so quite easily. To convert to Windows simply change the data types, exit(0), and the disk i/o functions to the equivalent Windows ones. I also have a Windows BMP loader but it was taken from Andre Lamothe's book Tricks of the Windows Game Programming Gurus and it does not differ all that much from this one.
BMP.h
Code:
#ifndef _BMP_
#define _BMP_
//Bitmap.cpp
//Implements simple .bmp loading
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <process.h>
typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef unsigned long DWORD;
struct BMPHeader
{
WORD Type;
DWORD Size;
DWORD Reserved;
DWORD Offset;
DWORD HeaderSize;
DWORD Width;
DWORD Height;
WORD Planes;
WORD BitsPerPixel;
DWORD Compression;
DWORD SizeImage;
DWORD XPixelsPerMeter;
DWORD YPixelsPerMeter;
DWORD ColorsUsed;
DWORD ColorsImportant;
};
class BMPImage
{
BMPHeader ImageHeader;
BYTE *ImageData;
BYTE *Palette;
public:
BMPImage(const char *bmpfile);
BYTE GetImageData(WORD offset)
{return ImageData[offset];};
BYTE GetPaletteData(WORD offset) {return Palette[offset];};
DWORD GetHeight(void) {return ImageHeader.Height;};
DWORD GetWidth(void) {return ImageHeader.Width;};
DWORD GetImageSize(void)
{return (ImageHeader.Width*ImageHeader.Height);};
};
#endif
BMP.cpp
Code:
BMPImage::BMPImage(const char *bmpfile)
{
int handle=open(bmpfile,O_RDONLY|O_BINARY);
if (handle==-1)
{
printf("%s file not found\n",bmpfile);
exit(0);
}
read (handle,&ImageHeader,sizeof(BMPHeader));
BYTE *tempPalette=new BYTE[1024];
Palette=new BYTE[768];
ImageData=new
BYTE[ImageHeader.Width*ImageHeader.Height];
read (handle,tempPalette,1024);
int pal=0;
//Transfer the bmp palette into the class palette
//Discard the fourth value
//Reverse the order in prep for VGA palette switch
for (int i=0;i<1020;i+=4)
{
Palette[pal]=tempPalette[i+2]>>2;
Palette[pal+1]=tempPalette[i+1]>>2;
Palette[pal+2]=tempPalette[i]>>2;
pal+=3;
}
read
(handle,ImageData,ImageHeader.Width*ImageHeader.Height);
}
-
This is a fairly robust validation routine. One improvement might be to verify the bit offset by calculating together the height, width, and boundary padding (if any).
Code:
/*
Validate, then read data into the supplied buffers.
If successful, a pointer to the actual bits is returned.
Otherwise, the function returns NULL.
*/
void * GetBitmapInfo(
BITMAPFILEHEADER * bfhBuf,
BITMAPINFOHEADER * bihBuf,
const void * base,
unsigned length)
{
void * bits = 0;
char * ptr = (char*)base;
if(length > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))
{
BITMAPFILEHEADER * bfh = (BITMAPFILEHEADER *)ptr;
BITMAPINFOHEADER * bih = (BITMAPINFOHEADER *)(ptr + sizeof(BITMAPFILEHEADER));
if(((char*)&(bfh->bfType))[0] == 'B'
&&
((char*)&(bfh->bfType))[1] == 'M'
&&
bfh->bfReserved1 == 0
&&
bfh->bfReserved2 == 0
&&
bfh->bfSize == length
&&
bih->biSize == 40
&&
(bih->biBitCount == 1
||
bih->biBitCount == 4
||
bih->biBitCount == 8
||
bih->biBitCount == 24)
&&
/* this doesn't really validate anything,
but it does prevent access violations */
bfh->bfOffBits < length)
{
bfhBuf->bfType = bfh->bfType;
bfhBuf->bfSize = bfh->bfSize;
bfhBuf->bfReserved1 = bfh->bfReserved1;
bfhBuf->bfReserved2 = bfh->bfReserved2;
bfhBuf->bfOffBits = bfh->bfOffBits;
bihBuf->biSize = bih->biSize;
bihBuf->biWidth = bih->biWidth;
bihBuf->biHeight = bih->biHeight;
bihBuf->biPlanes = bih->biPlanes;
bihBuf->biBitCount = bih->biBitCount;
bihBuf->biCompression = bih->biCompression;
bihBuf->biSizeImage = bih->biSizeImage;
bihBuf->biXPelsPerMeter = bih->biXPelsPerMeter;
bihBuf->biYPelsPerMeter = bih->biYPelsPerMeter;
bihBuf->biClrUsed = bih->biClrUsed;
bihBuf->biClrImportant = bih->biClrImportant;
bits = &ptr[bfh->bfOffBits];
}
}
return bits;
}
-
My bmp loader does not account for the padding to the nearest 4 byte boundary. To avoid this problem I simply make my bitmaps dimensions evenly divisible by 4. As well it does not check for 8,16 or 24 bit bmps. But this could be added very easily.
My theory behind not accounting for the padding is this. If the bitmap is evenly divisible by 4, then it is also a power of 2. With that, accessing the image data breaks down to simple bit shifts which are super fast. Although you wont see a major decrease in performance if you don't use dimensions evenly divisible by 4, you will save yourself a lot of headache if you simply follow this rule when creating your bitmaps.
Eventually all of my bitmaps are compiled into RLE chunks of data anyways. The file is preceded with a header that indicates the bitsperpixel for the images, the total number of images in the file, and some basic author information. Then each image is represented as a chunk. Each chunk is preceded by a header indicating the chunk size, and the dimensions of the chunk or dimensions of the image. Palette data, if applicable, is appended to the last 768 bytes of the file in the order RGB. With this format - I've called it CMP or a compiled bmp - I can store all of my bitmaps in one file and they are not easily accessible by other people so the data is safe.
When I load the data, only one palette is loaded since all images will use the same palette (when applicable) and each image is then loaded into an array with this scheme Array[imagenumber][imagedata]. The imagedata is accessed by spritepixely*width+spritepixelx. It is much easier to work with the bitmaps when the raw data is in an array w/o all of the other overhead. Then during the initialization stage the palette is set and the fun begins.
-
That's an interesting approach! Is there any way to add these directories to the executable (in some resource form)?
-
Well you could do it by simply retrieving the size of the EXE, setting the file pointer to one BYTE past that and writing in the binary data from there. Your loader would then have to retrieve the size of the EXE and start loading from there.
Here are my structs:
Code:
typedef struct CMPHeader
{
DWORD NumImages;
WORD BitsPerPixel;
WORD Compressed;
BYTE DateOfFile[8];
BYTE Author[20];
};
typedef struct NCHeader
{
DWORD bmpwidth;
DWORD bmpheight;
DWORD chunksize;
DWORD id;
};
I have a program that will take a text file as input and will output a .CMP file with all the relevant info in it. The text file simply lists the bmps that will be compiled into the cmp.
The CMPHeader is at the start of the file and each NCHeader precedes a chunk of raw bitmap data. My current format allows for different size bitmaps to be compiled in the same file, however, this might cause problems later, but the chunksize field will tell you how much data to read for the next chunk. After that another chunk will appear - there will be CMPHeader.NumImages chunks in the file.
Here is the structure of my cmp file:
---------------------------
CMPHeader
---------------------------
NCHeader -> new chunk header
---------------------------
Raw bitmap data -> RLE compressed if CMPHeader->compressed is 1
---------------------------
NCHeader
---------------------------
Raw bitmap data
---------------------------
...
...
...
...
----------------------------
Palette data in order RGB - 768 bytes - 4th element is stripped from palette data in the bmp files. Palette is taken from first image in the compilation process since all images share the same palette.
------------------EOF
Future revisions will allow a PAL file to be specified as the palette data but will still be placed in the last 768 bytes of the file.
I thought it was easier to place the palette at the end of the file rather than after the CMPHeader. This way the palette is always guaranteed to start at lengthoffile-768.
It's just something I cooked up so that I don't have a million bmps sitting out there on the drive just waiting for someone to alter.