• Advertisement
Sign in to follow this  

reading data from an image format

This topic is 4139 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

How is it possible to read data from an image file such gif, bmp, png with C and C++? I don’t have the slightest idea how it’s work. Anyone could give me some direction?

Share this post


Link to post
Share on other sites
Advertisement
All the formats are different so if you want to read them directly with something like a binary istream, you need to know a bit about the specific format of the type you are reading. Here might be a good place to start, but if you Google "bitmap format" or "png format" or whatever, you should find some information.

If you are looking for API calls to load various image formats, again it depends on the format you are looking at. For example, the Win32 API has a LoadImage call that can read BMP or ICO files, or Direct3D has D3DXCreateTextureFromFile that can recognise bmp, dds, dib, hdr, jpg, pfm, png, ppm and tga formats.

Share this post


Link to post
Share on other sites
a question. Can fstream be used to read data from a BMP image? Let's say I want to read each pixel from a 10pixel*10pixel BMP?

Thank you for your support.

Share this post


Link to post
Share on other sites
Yes. You would need to open an fstream in ios::binary mode, then parse the information in the file according to the BMP structure.

If you have access to the Win32 API, you can then ios::read into a BITMAPINFO structure that prefixes the bitmap file and contains information about the structure of the image.

You then need to use this information to decide how to process the pixel data, which will be stored differently depending on the colour depth of the particular image file.

To be honest, it is easier to read the bitmap in with LoadImage, then use GetDIBits to acquire a buffer of RGBQUAD structures for each pixel and work on that. That will take care of translating different colour formats for you as well as freeing you from having to worry about the difference between the width and the stride of the image.

I have some example code at home that uses this approach that I can post later tonight if you want, or PM me.

Share this post


Link to post
Share on other sites
If possible, I'm trying to learn in console mode to learn the basic. I don't know if you could make a very limited code so I could learn the process.

I would like to learn reading from a BMP file (4bpp), and also write on a new BMP file, assigning a color to each pixel.


Share this post


Link to post
Share on other sites
Can't really help with writing the image, but the following will open a bitmap file and dump the pixel data into an array of RGBQUAD's:


#include <windows.h>

bool Open(const char *path,Pixels *Px)
{
HBITMAP H;
BITMAP Bm;
BITMAPINFO Info;
HDC Dc;

H=(HBITMAP)LoadImage(GetModuleHandle(NULL),path,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);
if(H==NULL) return false;

GetObject(H,sizeof(Bm),&Bm);

int w=Bm.bmWidth;
int h=Bm.bmHeight;

ZeroMemory(&Info,sizeof(BITMAPINFO));
Info.bmiHeader.biSize=sizeof(Info.bmiHeader);
Info.bmiHeader.biPlanes=1;
Info.bmiHeader.biBitCount=32;
Info.bmiHeader.biCompression=BI_RGB;
Info.bmiHeader.biWidth=w;
Info.bmiHeader.biHeight=-h;

Dc=CreateCompatibleDC(NULL);
SelectObject(Dc,H);

RGBQUAD *Ptr=new RGBQUAD[w*h];
GetDIBits(Dc,H,0,h,Ptr,&Info,DIB_RGB_COLORS);

// use Ptr to do whatever with pixels

delete [] Ptr;
DeleteObject(H);
return true;
}






At the point marked with a comment, Ptr points to an array of RGBQUAD structures containing per-pixel colour information. RGBQUAD is defined by Win32 API as:


typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;


and this approach will take care of converting different colour depths and so on. It also enforces that the width and stride of the RGBQUAD data are the same, which is not always the case with the raw pixel data in a bitmap file.

Given Ptr, w and h, you can access a given pixel by the formula:

RGBQUAD SpecificPixel=Ptr[(Y*w)+X];

then examine its red, green and blue components via the members of the RGBQUAD structure.

I find this easier than having to worry about the differences in format of bitmaps of different colour depths and so on.

In terms of writing a bitmap, there is an equivalent SetDIBits that could be of use, but I am not aware of a Win32 API call to save a bitmap even from a BITMAP or HBITMAP and believe you would need to manually fill a BITMAPINFO structure and write that, the palette (if any), and the pixel data directly to a file.

HTH Paul

Share this post


Link to post
Share on other sites
This is a very old (~3yrs ago when i was more full of nubness) and ugly class that can read and write bitmaps ( i believe it can only write 24bit bitmaps, but IIRC it can read in any format ). It's got some dependencies on other classes in a very old hobby engine i developed in that era, but might be a decent reference.

It's likely still got a few bugs, i only really ever worked with a specific range of bitmaps (24 & 32 bit).

header:

#ifndef ENGINE_BITMAP_READER
#define ENGINE_BITMAP_READER

#include <windows.h>
#include <math.h>
#include <string>
#include <vector>


#include "../openGL.h"

#define ONE_HALF_BYTE 0
#define BYTES_PER_INT 4

class E_DataManager;

struct Transparency {
//rgb triplet to which to add transparency
unsigned char r,g,b;

//amount of opacity 0 -> 255 (transparent opaque)
unsigned char t;

Transparency();
Transparency(const char * tstring);
Transparency(unsigned char r, unsigned char g, unsigned char b, unsigned char t);

void modify(unsigned char * data, int numTriplets);
};

class BitmapFile {
protected:

int width, height;

unsigned int numColors;

char * fileName;

BitmapFile();

void clearMemory();

void loadFile(const char * fileName, unsigned char alpha);

void extractBitsNoColorIndex(unsigned char * rawData, int numBytes, int bytesPerPixel, unsigned char alpha);

public:
/**
* alpha is the alpha channel value of this bitmap
* 0xFF = opaque 0x00 = transparent
* optional params
* cycles through the data list to add the defined
* transparencies to the array
*/

BitmapFile(const char * fileName, unsigned char alpha, int numT = 0, Transparency * t = NULL);

/**
* create a BitmapFile object
* with the passed data array
*/

BitmapFile(const unsigned char *data, int width, int height);

virtual ~BitmapFile();

int getWidth();
int getHeight();
unsigned int getNumColors();
const char * getFileName();

void writeFile(const std::string &fileName);

/**
* returns the pixel colors at width = w, height = h (w and h start numbering from top left of image
* color should be an int[4]
*/

void getPixelAt(int w, int h, unsigned char color[]);

void setPixelAt(int w, int h, unsigned char color[]);

void drawAtCurrentRaster();

BitmapFile & operator=(const BitmapFile & bm2);

friend E_DataManager;

public:
/**
* representation of color data in big array
* each float in the array is a single r,g or b color value
* triplits are laid out as neighbors. thus the top left pixel's
* colors are: data[0], data[1], data[2]
*/

unsigned char * data;

//length of data array
int dataSize;

};

inline unsigned int BitmapFile::getNumColors()
{
return numColors;
}

#endif



cpp

#include "BitmapFile.h"

#include <fstream>

BitmapFile::BitmapFile()
: width(0),
height(0),
numColors(0),
data(NULL),
dataSize(0),
fileName(NULL)
{
}

BitmapFile::BitmapFile(const unsigned char *data, int width, int height)
: width(width),
height(height),
numColors(24), //cause we're 3bytes per pixel (plus an alpha)
data(NULL),
dataSize(width*height*4),
fileName(NULL)
{
if ( width > 0 && height > 0 )
{
this->data = new unsigned char[dataSize];
if ( data )
{
memcpy(this->data, data, sizeof(unsigned char) * dataSize);
}
else
{
memset(this->data, 0, sizeof(unsigned char) * dataSize);
}
}
}

BitmapFile::BitmapFile(const char * fileName, unsigned char alpha, int numT, Transparency * t)
: width(0),
height(0),
numColors(0),
data(NULL),
dataSize(0),
fileName(NULL)
{
loadFile(fileName, alpha);

if (numT != 0 && t != NULL)
{
for (int i = 0; i < numT; ++i)
{
t.modify(data, height*width);
}
}
}

BitmapFile::~BitmapFile() {
clearMemory();
}

void BitmapFile::clearMemory() {
//WindowManager::msgbox("delete data");
if (data) {
delete[] data;
data = NULL;
}

//WindowManager::msgbox("delete fileName");
if (fileName) {
delete[] fileName;
fileName = NULL;
}

//WindowManager::msgbox("done");
}

BitmapFile & BitmapFile::operator=(const BitmapFile & bm2) {
clearMemory();

width = bm2.width;
height = bm2.height;
numColors = bm2.numColors;
dataSize = bm2.dataSize;
if (dataSize) {
data = new unsigned char[dataSize];
memcpy(data, bm2.data, sizeof(unsigned char) * dataSize);
}
if (bm2.fileName) {
fileName = new char[strlen(bm2.fileName) + 1];
strcpy(fileName, bm2.fileName);
}

return *this;
}

void BitmapFile::loadFile(const char * fileName, unsigned char alpha)
{
if (fileName == NULL) {
MessageBox(NULL, "BitmapFile::loadFile(...) - attempt to load file with NULL fileName", "Error", MB_OK);
return;
}

size_t strLength = strlen(fileName);
this->fileName = new char[strLength + 1];
strcpy(this->fileName, fileName);

std::ifstream in(fileName, std::ios::in | std::ios::binary);

if (!in) {
char strOut[255];
sprintf(strOut, "ERROR: opening bitmap file - ");
strcat(strOut, fileName);
MessageBox(NULL, strOut, "Error", MB_OK);
clearMemory();
return;
}

BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bmiHeader;

in.read((char *) &(bmfHeader), sizeof(BITMAPFILEHEADER));
in.read((char *) &(bmiHeader), sizeof(BITMAPINFOHEADER));

if (bmiHeader.biCompression != BI_RGB) {
MessageBox(NULL, "cannot load file -\n compression of bitmap file is not BI_RGB", "error", MB_OK);
clearMemory();
return;
}

width = bmiHeader.biWidth;
height = bmiHeader.biHeight;

int bytesPerPixel = 0;

switch (bmiHeader.biBitCount) {
case 4: bytesPerPixel = ONE_HALF_BYTE; break;
case 8: bytesPerPixel = 1; break;
case 16: bytesPerPixel = 2; break;
case 24: bytesPerPixel = 3; break;
case 32: bytesPerPixel = 4; break;
}

//figure out the number of colors
//if biClrUsed is == 0, then the number of colors is the max num of colors
//possible for the number of bytesPerPixel (as per BMP file format spec
if (bmiHeader.biClrUsed != 0)
numColors = bmiHeader.biClrUsed;
else if (bmfHeader.bfOffBits != (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)))
numColors = 1 << bmiHeader.biBitCount;

//get the color indices
RGBQUAD * bmiColors = NULL;

if (numColors != 0)
{
bmiColors = new RGBQUAD[numColors];
in.read((char *) bmiColors, sizeof(RGBQUAD) * numColors);
}

//each row of the bitmap data is padded to be a multiple of 4
//extraBytesPerLine is the number of extra bytes padded on each row of the
//bitmap data
int extraBytesPerLine = 0;

//256 color bitmaps pad each row of the data array to be a multiple of 4 for data access speed reasons
if (bytesPerPixel == 1 && width%4 != 0) {
extraBytesPerLine = 4 - width % 4;
}

//number of bytes of data contained in the actual bitmap data array
int numBytes = 0;

if (bytesPerPixel != ONE_HALF_BYTE)
numBytes = width * height * bytesPerPixel + extraBytesPerLine * height;

//WindowManager::msgbox("bmfHeader.bfOffBits: %d", bmfHeader.bfOffBits);

//tag on array padding so we have enough room at end of array to
//do sucessful conversions in saveByteListAsColorData()
unsigned char * rawData = new unsigned char[numBytes + BYTES_PER_INT];

//read in the bitmap data from the bitmap offset onwards
in.seekg(bmfHeader.bfOffBits); //bmfHeader.bfSize - numBytes - 1);
in.read((char *) rawData, sizeof(unsigned char) * numBytes);

//close the file
in.close();


//allocate space for unpacked pixel data
//4 bytes per color -> RGBA
dataSize = width * height * 4;
data = new unsigned char[dataSize];

//16bit uncompressed bitmaps aren't necessarily indexed. when numColors == 0
//the actual data _IS_ the color value
if (numColors == 0) {
//this will put the correct color data into the data array
extractBitsNoColorIndex(rawData, numBytes, bytesPerPixel, alpha);
delete[] rawData;
return;
}

//readPos is the read position in the rawData array
//we use a seperate variable from i (in the loop below) because in the case of
//256 color bitmaps we want to be skipping over any padding that there is in the
//bitmap aray
int readPos = 0;

//convert the data into a pure RGB format (1 byte per color value / 3 bytes per pixel)
for (int i = 0; i < width * height; ++i) {

//in 256 color bitmaps, skip over the padding
if (bytesPerPixel == 1 && extraBytesPerLine != 0 && readPos != 0
&& (readPos+extraBytesPerLine)%(width+extraBytesPerLine) == 0) {

readPos += extraBytesPerLine;
i--;
continue;
}

//we don't have to worry about reading beyond the end of the array here
//because we put in BYTES_PER_INT padding into byteData up above in the
//constructor
unsigned int * pInt = (unsigned int*)(rawData + readPos*bytesPerPixel);

//since we're subsequently shifting bits we want a copy of the value, not
//the value in the array itself so we don't corrupt any data that we are shifting
//out of the currently pointed at int
unsigned int colorIndex = *pInt;

//shift out the data we don't want
//NOTE: this is little endian specific shifting
// for big endian just do the >> bitsToShift shift
int bitsToShift = ((BYTES_PER_INT - bytesPerPixel) * 8);
colorIndex = (colorIndex << bitsToShift) >> bitsToShift;

//if a color index is out of range, set the color to the nearest neighbor
if (colorIndex >= numColors) {
//
// TODO -
// convert to non WindowManager specific code
//
//WindowManager::msgbox("color index (%d) out of range (numColors = %d)", colorIndex, numColors);
if (i != 0) {
data[4*i] = data[4*i - 4];
data[4*i + 1] = data[4*i - 3];
data[4*i + 2] = data[4*i - 2];
data[4*i + 3] = data[4*i - 1];
++readPos;
continue;
} else {
colorIndex = 0;
}
}

//set the colors in the data array from the color index table
data[4*i] = bmiColors[colorIndex].rgbRed;
data[4*i + 1] = bmiColors[colorIndex].rgbGreen;
data[4*i + 2] = bmiColors[colorIndex].rgbBlue;

data[4*i + 3] = alpha;

++readPos;

}

delete[] bmiColors;
delete[] rawData;
}

void BitmapFile::writeFile(const std::string &fileName)
{
std::ofstream out(fileName.c_str(), std::ios::out | std::ios::binary);

if (!out)
{
char strOut[255];
sprintf(strOut, "ERROR: opening bitmap file - ");
strcat(strOut, fileName.c_str());
MessageBox(NULL, strOut, "Error", MB_OK);
clearMemory();
return;
}

//each row of the bitmap data is padded to be a multiple of 4
//extraBytesPerLine is the number of extra bytes padded on each row of the
//bitmap data
int extraBytesPerLine = (width%4 != 0)? 4 - width%4: 0;
unsigned char *padding = NULL;
if ( extraBytesPerLine > 0 )
{
padding = new unsigned char[extraBytesPerLine];
memset(padding, 0, sizeof(unsigned char) *extraBytesPerLine);
}

BITMAPFILEHEADER bmfHeader;
((char*)&bmfHeader.bfType)[0] = 0x42; // 'B'
((char*)&bmfHeader.bfType)[1] = 0x4d; // 'M'
bmfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + ( sizeof(unsigned char) * (width*3+extraBytesPerLine) * height );
bmfHeader.bfReserved1 = 0;
bmfHeader.bfReserved2 = 0;
bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

BITMAPINFOHEADER bmiHeader;
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = width;
bmiHeader.biHeight = height;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = numColors;
bmiHeader.biCompression = BI_RGB;
bmiHeader.biSizeImage = 0; //Specifies the size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.
bmiHeader.biXPelsPerMeter = width/1000;
bmiHeader.biYPelsPerMeter = height/1000;
bmiHeader.biClrUsed = 0; //if biClrUsed is == 0, then the number of colors is the max num of colors
//possible for the number of bytesPerPixel (as per BMP file format spec
bmiHeader.biClrImportant = 0; //Specifies the number of color indexes that are required for displaying the bitmap. If this value is zero, all colors are required.

out.write( (char*) &bmfHeader, sizeof(BITMAPFILEHEADER) );
out.write( (char*) &bmiHeader, sizeof(BITMAPINFOHEADER) );

unsigned char *strippedData = new unsigned char[width*height*3];
for ( int h = 0; h < height; ++h )
{
for ( int w = 0; w < width; ++w )
{
memcpy( &strippedData[(w + width*h)*3], &data[(w + width*h)*4], sizeof(unsigned char)*3 );

//don't remember why..
//but our internal representation flips R and B in ordering
unsigned char temp = strippedData[(w + width*h)*3];
strippedData[(w + width*h)*3] = strippedData[(w + width*h)*3 + 2];
strippedData[(w + width*h)*3 + 2] = temp;
}
}

for ( int i = 0; i < height; ++i )
{
out.write( (char*) &strippedData[width*3*i], sizeof(unsigned char) * width*3 );
if ( padding )
{
out.write( (char*) padding, sizeof(unsigned char) *extraBytesPerLine);
}
}

delete padding;

out.close();
}

int BitmapFile::getWidth() {
return width;
}

int BitmapFile::getHeight()
{
return height;
}

const char * BitmapFile::getFileName()
{
return fileName;
}

void BitmapFile::getPixelAt(int w, int h, unsigned char color[])
{
if (w < 0 || w >= width || h < 0 || h >= height)
{
memset(color, 0, sizeof(unsigned char) * 4);
return;
}
else
{
int offset = (w + h*width)*4;
color[0] = data[offset];
color[1] = data[offset+1];
color[2] = data[offset+2];
color[3] = data[offset+3];
}
}

void BitmapFile::setPixelAt(int w, int h, unsigned char color[])
{
if (w < 0 || w >= width || h < 0 || h >= height)
{
return;
}
else
{
int offset = (w + h*width)*4;
data[offset] = color[0];
data[offset+1] = color[1];
data[offset+2] = color[2];
data[offset+3] = color[3];
}
}

void BitmapFile::drawAtCurrentRaster() {
if (data) {
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
glFlush();
}
}

void BitmapFile::extractBitsNoColorIndex(unsigned char * rawData, int numBytes, int bytesPerPixel, unsigned char alpha) {

int bitsPerColor = 0;
int extraBits = 0;

switch(bytesPerPixel) {
case 2: bitsPerColor = 5; extraBits = 1; break;
case 3: bitsPerColor = 8; break;
case 4: bitsPerColor = 8; break;
}

int extraBitsPerRead = (BYTES_PER_INT-bytesPerPixel) * 8 + extraBits;

//scale the colors from 1->32 to 8->256 to get the appropriate color values
int colorScale = 256/(1 << bitsPerColor);

for (int i = 0; i < width * height; ++i) {

//we don't have to worry about reading beyond the end of the array here
//because we put in BYTES_PER_INT padding into byteData up above in the
//constructor
unsigned int * pInt = (unsigned int*)(rawData + i*bytesPerPixel);

//since we're subsequently shifting bits we want a copy of the value, not
//the value in the array itself so we don't corrupt any data that we are shifting
//out of the currently pointed at int
unsigned int colorIndex = *pInt;

//get rid of the most significant bit, which isn't used for 16-bit bitmaps
//and loose the trailing word of info on the unsigned int b/c 16-bit is only
//one word per pixel
colorIndex = (colorIndex << extraBitsPerRead) >> extraBitsPerRead;

unsigned int red = (colorIndex >> (bitsPerColor*2)) * colorScale;
unsigned int green = ((colorIndex << (extraBitsPerRead + bitsPerColor)) >> (extraBitsPerRead + bitsPerColor*2)) * colorScale;
unsigned int blue = ((colorIndex << (extraBitsPerRead + bitsPerColor*2)) >> (extraBitsPerRead + bitsPerColor*2)) * colorScale;

//set the colors in the data array from the color index table
data[4*i] = red;
data[4*i + 1] = green;
data[4*i + 2] = blue;
data[4*i + 3] = alpha;
}

}



/**
* METHODS FOR TRANSPARENCY
*/

Transparency::Transparency() {
r = g = b = t = 0;
}

Transparency::Transparency(const char * string) {
if (!string) {
r=g=b=0;
return;
}

size_t strSize = strlen(string);
char * ts = new char[strSize + 1];
strcpy(ts, string);
char *begin = ts, *end = ts;

unsigned char a[4] = {0,0,0,0};
for (int i = 0; i < 4 && end <= ts+strSize-1; ++i) {
end = strchr(begin, ',');
if (!end)
end = ts + strSize;
char tmp = *end;
*end = '\0';
a = (unsigned char) atoi(begin);
*end = tmp;
begin = end;
while ((*begin == ',' || *begin == ' ') && begin < ts+strSize)
begin++;
}

r=a[0];
g=a[1];
b=a[2];
t=a[3];
delete[] ts;
}

Transparency::Transparency(unsigned char r, unsigned char g, unsigned char b, unsigned char t) {
this->r = r;
this->g = g;
this->b = b;
this->t = t;
}

void Transparency::modify(unsigned char * data, int numTriplets) {
for (int i = 0; i < numTriplets; ++i) {
if (data[4*i] == r && data[4*i+1] == g && data[4*i+2] == b)
data[4*i+3] = t;
}
}



-me

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement