Reputation: 621
I'm writing code in C++ (on Windows) and I'm trying to extract the pixel values of a grayscale bmp. I don't care about keeping any of the metadata, and just want to store the pixel values in a char array. I haven't been able to find a standard or "typical" way of doing this manually, so I'm wondering if there's perhaps a simple library that people use to load bitmaps into memory.
Thanks in advance!
Upvotes: 13
Views: 55427
Reputation: 784
Expanding on what Yola wrote, this should be able to read in and output a file. It is not well tested, but seems to work. It uses format of the file it reads in when it outputs.
#include <iostream>
#include <unistd.h>
#include <fstream>
using std::cout;
using std::endl;
using std::ofstream;
using std::ifstream;
#pragma pack(1)
#pragma once
typedef int LONG;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
unsigned char** reds;
unsigned char** greens;
unsigned char** blues;
int rows;
int cols;
void ColorTest() {
// Makes Red Rectangle in top left corner. Rectangle stretches to right alot
for (int i = rows / 10; i < 3 * rows / 10; i++)
for (int j = cols / 10; j < 7 * cols / 10; j++)
reds[i][j] = 0xff;
// Makes small green box in bottom right
for (int i = 8 * rows / 10; i < rows; i++)
for (int j = 8 * cols / 10; j < cols; j++)
greens[i][j] = 0xff;
// Makes White box in the middle of the screeene
for (int i = rows * 4 / 10; i < rows * 6 / 10; i++)
for (int j = cols * 4 / 10; j < cols * 6 / 10; j++) {
greens[i][j] = 0xff;
reds[i][j] = 0xff;
blues[i][j] = 0xff;
}
// Blue verticle rectangle bottom left
for (int i = rows * 6 / 10; i < rows; i++)
for (int j = cols * 0; j < cols * 1 / 10; j++)
blues[i][j] = 0xff;
}
void RGB_Allocate(unsigned char**& dude) {
dude = new unsigned char*[rows];
for (int i = 0; i < rows; i++)
dude[i] = new unsigned char[cols];
}
bool FillAndAllocate(char*& buffer, const char* Picture, int& rows, int& cols, int& BufferSize) { //Returns 1 if executed sucessfully, 0 if not sucessfull
std::ifstream file(Picture);
if (file) {
file.seekg(0, std::ios::end);
std::streampos length = file.tellg();
file.seekg(0, std::ios::beg);
buffer = new char[length];
file.read(&buffer[0], length);
PBITMAPFILEHEADER file_header;
PBITMAPINFOHEADER info_header;
file_header = (PBITMAPFILEHEADER) (&buffer[0]);
info_header = (PBITMAPINFOHEADER) (&buffer[0] + sizeof(BITMAPFILEHEADER));
rows = info_header->biHeight;
cols = info_header->biWidth;
BufferSize = file_header->bfSize;
return 1;
}
else {
cout << "File" << Picture << " don't Exist!" << endl;
return 0;
}
}
void GetPixlesFromBMP24(unsigned char** reds, unsigned char** greens, unsigned char** blues, int end, int rows, int cols, char* FileReadBuffer) { // end is BufferSize (total size of file)
int count = 1;
int extra = cols % 4; // The nubmer of bytes in a row (cols) will be a multiple of 4.
for (int i = 0; i < rows; i++){
count += extra;
for (int j = cols - 1; j >= 0; j--)
for (int k = 0; k < 3; k++) {
switch (k) {
case 0:
reds[i][j] = FileReadBuffer[end - count++];
break;
case 1:
greens[i][j] = FileReadBuffer[end - count++];
break;
case 2:
blues[i][j] = FileReadBuffer[end - count++];
break;
}
}
}
}
void WriteOutBmp24(char* FileBuffer, const char* NameOfFileToCreate, int BufferSize) {
std::ofstream write(NameOfFileToCreate);
if (!write) {
cout << "Failed to write " << NameOfFileToCreate << endl;
return;
}
int count = 1;
int extra = cols % 4; // The nubmer of bytes in a row (cols) will be a multiple of 4.
for (int i = 0; i < rows; i++){
count += extra;
for (int j = cols - 1; j >= 0; j--)
for (int k = 0; k < 3; k++) {
switch (k) {
case 0: //reds
FileBuffer[BufferSize - count] = reds[i][j];
break;
case 1: //green
FileBuffer[BufferSize - count] = greens[i][j];
break;
case 2: //blue
FileBuffer[BufferSize - count] = blues[i][j];
break;
}
count++;
}
}
write.write(FileBuffer, BufferSize);
}
int main(int args, char** cat) {
char* FileBuffer; int BufferSize;
#define Picture "ReadInPicture.bmp"
if (!FillAndAllocate(FileBuffer, Picture, rows, cols, BufferSize)){cout << "File read error" << endl; return 0;}
cout << "Rows: " << rows << " Cols: " << cols << endl;
RGB_Allocate(reds);
RGB_Allocate(greens);
RGB_Allocate(blues);
GetPixlesFromBMP24( reds, greens, blues,BufferSize, rows, cols, FileBuffer);
ColorTest();
#define WriteOutFile "OutputPicture.bmp"
WriteOutBmp24(FileBuffer, WriteOutFile,BufferSize);
return 1;
}
Upvotes: 3
Reputation: 65
if coding in Visual Studios, before declaring your tagBITMAPFILEHEADER and tagBITMAPINFOHEADER structs (shown in Yola's response) be sure to include "#pragma pack(2)". Ohterwise the struct will be padded to the next 4 byte boundary, instead of the next 2 byte boundary, and the data will be garbage.
reference http://tipsandtricks.runicsoft.com/Cpp/BitmapTutorial.html
Upvotes: 4
Reputation: 19033
and ready to go code, tested with g++ (not Windows, but may help someone):
#pragma pack(1)
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
#include "bmp.h"
vector<char> buffer;
PBITMAPFILEHEADER file_header;
PBITMAPINFOHEADER info_header;
void fill() {
std::ifstream file("data.bmp");
if (file) {
file.seekg(0,std::ios::end);
streampos length = file.tellg();
file.seekg(0,std::ios::beg);
buffer.resize(length);
file.read(&buffer[0],length);
file_header = (PBITMAPFILEHEADER)(&buffer[0]);
info_header = (PBITMAPINFOHEADER)(&buffer[0] + sizeof(BITMAPFILEHEADER));
}
}
int main() {
fill();
cout << buffer[0] << buffer[1] << endl;
cout << file_header->bfSize << endl;
cout << info_header->biWidth << " " << info_header->biHeight << endl;
return 0;
}
In bmp.h i have defined structures:
#pragma once
typedef int LONG;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
Upvotes: 15
Reputation: 119
You have 2 good options:
Load and parse the BMP file yourself. BMP files start with a BITMAPFILEHADER, followed by a BITMAPINFOHEADER, followed by 0 or more RGBQUAD's (palette entry). Offset to pixel data is in BITMAPFILEHADER, but you should check BITMAPINFOHEADER to make sure the image format is what you expect/support.
Call LoadImage() API with LR_CREATEDIBSECTION flag, it will return a handle to a DIB section. Next you call GetObject() passing in the returned handle and a pointer to a DIBSECTION structure. Then you read DIBSECTION structure for bitmap size, format, pointer to pixel data, etc.
Option 2 is better if you're on Windows, because presumably LoadImage() checks for invalid file formats for you, and can load more than just BMP files.
When accessing Windows BMP pixels, remember lines are always DWORD-aligned.
Upvotes: 2
Reputation: 308158
Read the entire file into memory. There will be a small header at the front, and the rest of it will be the pixel values.
The first part will be a BITMAPFILEHEADER
structure. The only part you care about is the bfOffBits, which gives the number of bytes from the start of the file to the pixel values.
The next part after the BITMAPFILEHEADER
will be a BITMAPINFOHEADER
. This will be useful to determine the format of the pixels.
This will be followed by a palette, if the bit depth requires one.
There are a couple of gotchas with the pixel values. First is that the order is (blue,green,red), just opposite of the way everybody else does it. Second is that the rows go from bottom to top of the image, again backwards from everybody else. Finally, the number of bytes in a row will always be padded up to the next multiple of 4.
I almost forgot to mention, it is possible for a JPEG or PNG file to be encoded as a BMP, but this is not common. Have a look at the biCompression field of the BITMAPINFOHEADER
, if it's anything but BI_RGB you'll need a little more help.
Upvotes: 4
Reputation: 71008
There are definitely libraries out there (see other answers), but in a jiffy, it's frankly a brain-dead simple file format that you can parse yourself quite easily. Details are here:
http://www.fileformat.info/format/bmp/egff.htm
(I've been out of Win32 for a few years, but the LoadImage
function can get you an HBITMAP from a BMP file. I'm not certain how to turn that into a pixel array directly, but I'd imagine there's some contortion with a DC that would let you grab the values. http://support.microsoft.com/kb/158898
More hints: http://alexkr.com/source-code/26/accessing-bitmap-pixels-in-gdi/)
Upvotes: 2