How to Read in a Bmp File
The BMP format, ofttimes referred to as the Windows Bitmap Format is in my stance, ane of the simplest ways to shop paradigm information in a reckoner. Fifty-fifty though it was originally defined past Microsoft for its operating systems, nowadays its usage is widespread and can exist recognized by practically every major imaging software. For the same reason, there are lots of libraries and APIs to work with images stored in this format.
The purpose of this mail is to provide some guidance for those programmers who can't or don't want to use a third party library to work with images in the BMP format (e.grand. targeting embedded platforms, developing sensitive proprietary software or for learning purposes). And even though our working example will be presented in the c linguistic communication, it can be easily ported to other languages if necessary.
If you're hither simply for the lawmaking, here'south a somewhat stripped-downwards implementation of the functions to open and save BMP files. Exist enlightened that for legibility no safety validations are being performed (eastward.k. bank check for NULL pointers, validate file operations, etc.) equally they are left to the reader. You can download the source code "bmp.c" and the sample image "img.bmp" from this link
#include #include #include #ascertain DATA_OFFSET_OFFSET 0x000A #define WIDTH_OFFSET 0x0012 #define HEIGHT_OFFSET 0x0016 #define BITS_PER_PIXEL_OFFSET 0x001C #define HEADER_SIZE 14 #define INFO_HEADER_SIZE twoscore #define NO_COMPRESION 0 #define MAX_NUMBER_OF_COLORS 0 #define ALL_COLORS_REQUIRED 0 typedef unsigned int int32; typedef short int16; typedef unsigned char byte; void ReadImage(const char *fileName,byte **pixels, int32 *width, int32 *height, int32 *bytesPerPixel) { FILE *imageFile = fopen(fileName, "rb"); int32 dataOffset; fseek(imageFile, DATA_OFFSET_OFFSET, SEEK_SET); fread(&dataOffset, four, one, imageFile); fseek(imageFile, WIDTH_OFFSET, SEEK_SET); fread(width, 4, i, imageFile); fseek(imageFile, HEIGHT_OFFSET, SEEK_SET); fread(height, 4, 1, imageFile); int16 bitsPerPixel; fseek(imageFile, BITS_PER_PIXEL_OFFSET, SEEK_SET); fread(&bitsPerPixel, 2, 1, imageFile); *bytesPerPixel = ((int32)bitsPerPixel) / eight; int paddedRowSize = (int)(four * ceil((float)(*width) / iv.0f))*(*bytesPerPixel); int unpaddedRowSize = (*width)*(*bytesPerPixel); int totalSize = unpaddedRowSize*(*height); *pixels = (byte*)malloc(totalSize); int i = 0; byte *currentRowPointer = *pixels+((*top-i)*unpaddedRowSize); for (i = 0; i < *height; i++) { fseek(imageFile, dataOffset+(i*paddedRowSize), SEEK_SET); fread(currentRowPointer, one, unpaddedRowSize, imageFile); currentRowPointer -= unpaddedRowSize; } fclose(imageFile); } void WriteImage(const char *fileName, byte *pixels, int32 width, int32 pinnacle,int32 bytesPerPixel) { FILE *outputFile = fopen(fileName, "wb"); //*****HEADER************// const char *BM = "BM"; fwrite(&BM[0], 1, one, outputFile); fwrite(&BM[1], 1, one, outputFile); int paddedRowSize = (int)(four * ceil((float)width/4.0f))*bytesPerPixel; int32 fileSize = paddedRowSize*acme + HEADER_SIZE + INFO_HEADER_SIZE; fwrite(&fileSize, 4, 1, outputFile); int32 reserved = 0x0000; fwrite(&reserved, 4, ane, outputFile); int32 dataOffset = HEADER_SIZE+INFO_HEADER_SIZE; fwrite(&dataOffset, iv, 1, outputFile); //*******INFO*HEADER******// int32 infoHeaderSize = INFO_HEADER_SIZE; fwrite(&infoHeaderSize, iv, 1, outputFile); fwrite(&width, 4, one, outputFile); fwrite(&elevation, four, 1, outputFile); int16 planes = 1; //ever 1 fwrite(&planes, 2, 1, outputFile); int16 bitsPerPixel = bytesPerPixel * 8; fwrite(&bitsPerPixel, 2, i, outputFile); //write pinch int32 compression = NO_COMPRESION; fwrite(&compression, 4, 1, outputFile); write image size (in bytes) int32 imageSize = width*height*bytesPerPixel; fwrite(&imageSize, 4, ane, outputFile); int32 resolutionX = 11811; //300 dpi int32 resolutionY = 11811; //300 dpi fwrite(&resolutionX, four, 1, outputFile); fwrite(&resolutionY, 4, 1, outputFile); int32 colorsUsed = MAX_NUMBER_OF_COLORS; fwrite(&colorsUsed, iv, 1, outputFile); int32 importantColors = ALL_COLORS_REQUIRED; fwrite(&importantColors, 4, one, outputFile); int i = 0; int unpaddedRowSize = width*bytesPerPixel; for ( i = 0; i < height; i++) { int pixelOffset = ((top - i) - i)*unpaddedRowSize; fwrite(&pixels[pixelOffset], i, paddedRowSize, outputFile); } fclose(outputFile); } int chief() { byte *pixels; int32 width; int32 elevation; int32 bytesPerPixel; ReadImage("img.bmp", &pixels, &width, &peak,&bytesPerPixel); WriteImage("img2.bmp", pixels, width, height, bytesPerPixel); free(pixels); return 0; }
The BMP file structure
The structure of a minimal BMP file looks like this:
Name | Size | Offset (Hex) | Clarification/Notes |
Bitmap File Header | |||
BMP signature | 2 bytes | 0x00 | The characters 'B' and 'One thousand' |
File size | 4 bytes | 0x02 | Size in bytes of the file including headers and pixel data |
Reserved | 4 bytes | 0x06 | Unused |
Data offset | 4 bytes | 0x0A | Start in the file where the pixel data is stored |
Bitmap Information Header | |||
Size of the header | 4 bytes | 0x0E | The header is fixed size: twoscore bytes |
Width | iv bytes | 0x12 | Width of the prototype in pixels |
Elevation | iv bytes | 0x16 | Meridian of the image in pixels |
Planes | 2 bytes | 0x1A | Number of color planes (must be one) |
Bits per pixel | 2 bytes | 0x1C | Number of bits per pixel |
Compression | 4 bytes | 0x1E | Compression method |
Image size | four bytes | 0x22 | Can be 0 if image is not compressed, otherwise is the size of the compressed paradigm |
Pixels per meter in X centrality | 4 bytes | 0x26 | Horizontal resolution in pixels per meter |
Pixels per meter in Y axis | four bytes | 0x2A | Vertical resolution in pixels per meter |
Colors used | four bytes | 0x2E | Number used colors |
Important colors | 4 bytes | 0x32 | Number of important color. Tin can be 0 If all colors are important |
Color tabular array | Only if Bits per pixel < 8 | ||
Data | |||
Pixel data | Variable | Variable | Stored bottom-upward |
As the table shows, in that location are mainly three parts:
- Bitmap file header: provides information near the file itself.
- Bitmap information header: contains the properties of the pixel data.
- Data: the actual pixel data.
About of the time you volition be working with 24-bit RGB color or viii-bit grayscale images but it's of import to consider that in the case where the pixels are represented with less than 8 bits, a Color Table must be included in the bitmap data header. Other optional fields might exist nowadays in the file but for simplicity they are not covered in this post. For more than information most this you can consult this Wikipedia commodity or this mail service by Nathan Liesch.
Reading a BMP file
To excerpt the pixel information you could decide to read every field from the header based on the table above. Just in reality in that location are simply 4 parts we're interested in:
- The information first (so you know where the pixel data is)
- Image peak
- Paradigm width
- Bits per pixel
This can be achieved with the regular fopen role by moving to the correct offset within the file (using fseek):
#define DATA_OFFSET_OFFSET 0x000A #ascertain WIDTH_OFFSET 0x0012 #define HEIGHT_OFFSET 0x0016 #ascertain BITS_PER_PIXEL_OFFSET 0x001C FILE *imageFile = fopen(fileName, "rb"); int32 dataOffset; fseek(imageFile, DATA_OFFSET_OFFSET, SEEK_SET); fread(&dataOffset, iv, ane, imageFile); fseek(imageFile, WIDTH_OFFSET, SEEK_SET); fread(width, four, 1, imageFile); fseek(imageFile, HEIGHT_OFFSET, SEEK_SET); fread(height, 4, 1, imageFile); int16 bitsPerPixel; fseek(imageFile, BITS_PER_PIXEL_OFFSET, SEEK_SET); fread(&bitsPerPixel, ii, one, imageFile);
Once we take this information we can allocate enough memory to contain the pixel data, that is height x width x bytes per pixel (bits per pixel divided past viii).
At this point you lot might exist tempted to just read the rest of the file starting from the information outset. If you practice, you might find that your paradigm is upside-down and information technology has some extra pixels. Our example image would wait like this:
This is because the epitome is stored bottom-up and the rows contain padding bytes to make their size a multiple of 4. In that location are many ways of treatment this situation. I option is to have a pointer to the last row of the (past at present empty) pixel assortment, read a row from the file into that address (ignoring the padding bytes) and moving the pointer to the next row. The following code snippet illustrates this process:
//point to the final row of our pixel array (unpadded) byte *currentRowPointer = *pixels+((*superlative-ane)*unpaddedRowSize); for (i = 0; i < *height; i++) { fseek(imageFile, dataOffset+(i*paddedRowSize), SEEK_SET); fread(currentRowPointer, 1, unpaddedRowSize, imageFile); currentRowPointer -= unpaddedRowSize; }
At present you lot take a pixel assortment to work with!
Writing BMP files
If you lot want other programs to exist able to work with the a file you created, you need to adhere to the file'southward structure specification. This is quite straightforward if y'all follow the table above. Look at the code sample for more details.
At this stage the key is to remember that rows must exist padded to a multiple of 4 and stored lesser-up. To practise this you can only contrary the process we used for reading the file:
for ( i = 0; i < summit; i++) { //get-go writing from the kickoff of last row in the pixel array int pixelOffset = ((top - i) - 1)*unpaddedRowSize; fwrite(&pixels[pixelOffset], 1, paddedRowSize, outputFile); }
And with that piece of lawmaking we covered the well-nigh basic operations. The provided source code was tested on Windows, Ubuntu and RHEL, but you might need to make some changes for other targets.
Notes for Windows users:
Make sure to open the output file in binary fashion. Remember that in Windows line endings are represented with 2 characters (\n and \r). For this reason, if your file is open in text style, every time you lot write the value x (0x0A, the \northward character) an actress 13 volition be added (0x0D, the \r character). These kind of errors are very difficult to debug without a Hex editor and lot of patience.
If you're using Visual Studio you lot might experience problems with the fopen part. You can alter it openf_s or add the _CRT_SECURE_NO_WARNINGS pre-processor definition equally explained hither.
Y'all can use BITMAPFILEHEADER and BITMAPINFOHEADER to read/write the file header.
Notes for Linux users:
Since we're using math.h remember to link your program with the -lm pick (gcc and other compilers).
Source: https://elcharolin.wordpress.com/2018/11/28/read-and-write-bmp-files-in-c-c/
0 Response to "How to Read in a Bmp File"
Postar um comentário