C++: .bmp to byte array in a file - c++

Yes i have been through the other questions that are related to this, but i found them not much help. They were some help but i am still a bit confused. So here what what i need to do:
We have a 132x65 screen. I have a 132x65 .bmp. I want to go through the .bmp and separate it into little 1x8 columns to get the binary of that 32-bit column. Then do that 132 times across, and do that 9 times down. Anything that is not white should be counted as a bit. example:
If the top left pixel of the picture is any color that is not white and the 7 pixels below that are white then that would be the first element of the array, the hex of that number, so the array would look like this:
array [] = { 0x01 } and then it would continue to fill through those 132 columns and then do it again for 9 "sections" of rows. And the file result would be ONLY that array in a separate file.
I understand the header format for this, i have read the wiki article on .bmp file formats, my main problem is i don't really know how to interact with the .bmp when i actually want it to go inside and interact with each pixel from the image. I really dont need the whole thing, but maybe just an example of grabbing each pixel from the .bmp and outputting the color of the pixel into a file or something. My c++ is a little rusty (been doing java and javscript lately).

If you want to read a known format BMP and don't care about how it's done (ie, internal-only thing) you can just take the BMP, ignore the header and use it as a pixel array. It is stored line by line starting at the bottom left. There are some detail snags for how it's packed but in my experience if you take a 32bpp image it can be completely ignored.
As a really simple example:
unsigned int *buffer;
void readfile() {
FILE *f = fopen("file.bmp", "rb");
buffer = new unsigned int[132*65];
fseek(f, 54);
fread(buffer, 132*65*4, 1, f);
fclose(f);
}
unsigned int getpixel(int x, int y) {
//assuming your x/y starts from top left, like I usually do
return buffer[(64 - y) * 132 + x];
}

I had the same problem, but by reading BMP file format description I wrote a function that reads a .BMP file and stores it into a array.
Maybe this function can help you:
unsigned int PIC::BinToNum(char *b,int bytes)
{
unsigned int tmpx = 0;
unsigned int pw = 1;
for(int i=0;i<bytes;i++)
{
tmpx += ((unsigned char)b[i]* pw);
pw = pw * 256;
}
return tmpx;
}
int PIC::Open(const char *path)
{
int pad = 0;
unsigned int sof = 0;
unsigned int tx = 0;
char tmp[4] = {0,0,0,0};
fstream file;
file.open(path,ios::in);
if(file.fail())
{
width=height=ColorBits=size=0;
return -1;
}
else
{
file.seekg(0,ios::beg);
file.read(tmp,2);
if(!(tmp[0] == 66 && tmp[1] == 77))
{
width=height=ColorBits=size=0;
return 0;
}
else
{
file.seekg(2,ios::beg); // 0x2 size
file.read(tmp,4);
size = BinToNum(tmp,4);
file.seekg(18,ios::beg); // 0x12 width
file.read(tmp,4);
width = BinToNum(tmp,4);
file.seekg(22,ios::beg); // 0x16 height
file.read(tmp,4);
height = BinToNum(tmp,4);
file.seekg(28,ios::beg); // 0x1C Bits per Pixel
file.read(tmp,2);
ColorBits = BinToNum(tmp,2);
file.seekg(10,ios::beg); // 0x0A start offset
file.read(tmp,4);
sof=BinToNum(tmp,4);
file.seekg(34,ios::beg); // 0x22 Padding
file.read(tmp,4);
pad = BinToNum(tmp,4);
pad = (int)(pad / height); // Compute Spacing in each row
pad = pad - (width*ColorBits/8);
// Initialize Matrix//
matrix = new(unsigned int[height*width]);
for(int h=height-1;h>=0;h--)
{
for(int w=0;w<=width-1;w++)
{
file.seekg(sof,ios::beg);
file.read(tmp,(int)(ColorBits/8));
tx = BinToNum(tmp,(int)(ColorBits/8));
matrix[(h*width)+w] = tx;
sof+=(int)(ColorBits/8);
}
sof +=pad;
}
}
}
file.close();
return 1;
}
Note:This functions is member of a class that i named it "PIC"...

Related

Reading BMP file into an array

I am writing a longer program and I found myself needing to read a .bmp file into an array in a specific way so that the rest of the program can use it without extensive rewrites. I failed to find older answers that would resolve my problem, and I am pretty much at the beginner stages.
The image I am trying to read is used to create a text font, so I want to read it character by character into an array, where the pixels belonging to one character are added in order to a 2d bool (true if pixel is not black) array [character_id] [pixel_n]. The dimensions of characters are predetermined and known, and the file is cropped so that they all appear in a single row with no unaccounted margins.
This is the specific file I am trying to read, though here it might not show up as .bmp
As an example, shown here, I want to read the pixels in the order of the yellow line, then jump to another character. For clarity each character is 5px wide and 11px high, with 1px of margin on both sides horizontally.
Based on what I was able to find, I have written a function to do it, but I fail to make it work as intended, as far as I can tell even the pixel values are not being read correctly:
void readBMP(char* filename)
{
int i;
FILE* f = fopen(filename, "rb");
unsigned char info[54];
// read the 54-byte header
fread(info, sizeof(unsigned char), 54, f);
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
// number of pixels in total
int size = 3 * width * height;
unsigned char* data = new unsigned char[size];
// number of characters to read
int counter1 = size / ((font_width + 2) * font_height) / 3 ;
// read the rest of the data at once
fread(data, sizeof(unsigned char), size, f);
fclose(f);
//loop that goes from character to character
for(int i = 0; i < counter1; i++)
{
int tmp = 0;
//loop that reads one character into font_ref array
for(int j = 0; j < font_height; j++)
{
//loop for each row of a character
for(int k = 0; k < font_width; k++)
{
int w = static_cast<int>(data[3*(j*(font_width+2)*(counter1) + i*(font_width + 2) + 1 + k + j*font_width + j)-1]);
if( w != 0 )
font_ref [i][(tmp)] = 1;
else
font_ref [i][(tmp)] = 0;
tmp++;
}
}
}
}
(bool font_ref [150][font_width*font_height]; is the array where the font is being loaded and stored)
this code reads something, but the result is a seemingly random mess and I am unable to resolve that. Here is an example of lowercase alphabet printed using another function in the program, where white pixels represent true bools. I am aware that some libraries exist to work with graphical files, however in this program I wanted to possibly avoid that to learn more lower-level things, and the goal is rather limited and specific.
Thank you in advance for any help with the issue.
The main errors are in the offset computation for a pixel in the bitmap data:
int w = static_cast<int>(data[3*(j*(font_width+2)*(counter1) + i*(font_width + 2) + 1 + k + j*font_width + j)-1]);
j*(font_width+2)*(counter1) - This doesn't take into account that
although you say the file is cropped, there is extra black space to the right of the last character cell, so the true width must be used;
(as drescherjm and user3386109 mentioned) padding bytes are appended to the rows so that their length is a multiple of four bytes.
+ j*font_width + j)-1 - This part makes no sense - perhaps you tried to compensate the above errors.
This would be correct:
int w = data[j*(3*width+3&~3)+3*(i*(font_width+2)+1+k)];

Reading .raw file containing Heightmap

I am using the libnoise library to generate a random terrain and saving it in a .raw file that has its elevation points measured in meters. This terrain file contains 16-bit signed big-endian values, in row-major order, ordered south to north. This is the code I am using for reading the file.
struct HeightMapType
{
float x, y, z;
float nx, ny, nz;
float r, g, b;
};
bool Terrain::LoadRawFile()
{
int error, i, j, index;
FILE* filePtr;
unsigned long long imageSize, count;
unsigned short* rawImage;
// Create the float array to hold the height map data.
m_heightMap = new HeightMapType[m_terrainWidth * m_terrainHeight];
if(!m_heightMap)
{
return false;
}
// Open the 16 bit raw height map file for reading in binary.
error = fopen_s(&filePtr, m_terrainFilename, "rb");
if(error != 0)
{
return false;
}
// Calculate the size of the raw image data.
imageSize = m_terrainHeight * m_terrainWidth;
// Allocate memory for the raw image data.
rawImage = new unsigned short[imageSize];
if(!rawImage)
{
return false;
}
// Read in the raw image data.
count = fread(rawImage, sizeof(unsigned short), imageSize, filePtr);
if(count != imageSize)
{
return false;
}
// Close the file.
error = fclose(filePtr);
if(error != 0)
{
return false;
}
// Copy the image data into the height map array.
for(j=0; j<m_terrainHeight; j++)
{
for(i=0; i<m_terrainWidth; i++)
{
index = (m_terrainWidth * j) + i;
// Store the height at this point in the height map array.
m_heightMap[index].y = (float)rawImage[index];
}
}
// Release the bitmap image data.
delete [] rawImage;
rawImage = 0;
// Release the terrain filename now that it has been read in.
delete [] m_terrainFilename;
m_terrainFilename = 0;
return true;
}
The code does not return any error but this is the result rendered: rawFileRendering.
I tested the code with another heightmap saved in a raw file (given by rastertek) and it works.
Do you know why the rendered scene is like this?
Thank you for your help.
Two problems:
You use unsigned short, but you said in the description that the numbers are signed. So you should use signed short instead
You don't do anything with endianness. If you are on a little endian machine, you should convert your values from big endian to little endian.
You can convert endianness with this:
short endianConvert(short x) {
unsigned short v = (unsigned short)x;
return (short)(v>>8|v<<8);
}

Writing to .BMP - distorted image

I'd like to write a normal map to a .bmp file, so I've implemented a simple .bmp writer first:
void BITMAPLOADER::writeHeader(std::ofstream& out, int width, int height)
{
BITMAPFILEHEADER tWBFH;
tWBFH.bfType = 0x4d42;
tWBFH.bfSize = 14 + 40 + (width*height*3);
tWBFH.bfReserved1 = 0;
tWBFH.bfReserved2 = 0;
tWBFH.bfOffBits = 14 + 40;
BITMAPINFOHEADER tW2BH;
memset(&tW2BH,0,40);
tW2BH.biSize = 40;
tW2BH.biWidth = width;
tW2BH.biHeight = height;
tW2BH.biPlanes = 1;
tW2BH.biBitCount = 24;
tW2BH.biCompression = 0;
out.write((char*)(&tWBFH),14);
out.write((char*)(&tW2BH),40);
}
bool TERRAINLOADER::makeNormalmap(unsigned int width, unsigned int height)
{
std::ofstream file;
file.open("terrainnormal.bmp");
if(!file)
{
file.close();
return false;
}
bitmaploader.writeHeader(file,width,height);
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
file << static_cast<unsigned char>(255*x/height); //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
file << static_cast<unsigned char>(0); //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
file << static_cast<unsigned char>(0); //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
};
};
file.close();
return true;
};
The writeHeader(...) function is from SO, from a solved,working post. (I've forgot the name of it)
The getHeight(...) is using bicubic interpolation, so I can write it to big resolution images, and it stays smooth. It will be also used for collision detection and now is used as a LOD factor for my clipmaps.
Now the problem is that this outputs a distorted image. The pictures will tell everything I think:
The expected/distorted result(s):
for the heightmap: I have the function that describes a mesh: getHeight(x,z). It gives back the correct results because I've tested it with shaders (by sending heights as vertex attribs) too. The image downloaded from internet:
And with the y(x,z) function values written to a .BMP: (the commented out part of the code):
With a simple function: file << static_cast<unsigned char>(255*(float)x/height)
which should be a simple blend from black to white to the right.
I used an image size of 256 x 256, because I've read it should be multiple of 4. I CAN use libraries, but I'd like to solve this problem without one. So, what caused this distortion?
EDIT:
On the last image some lines are also colored, but they shouldn't be. This post is similar, but my heightmap is not distorted linearly as in this post: Image Distortion with Lock Bits
EDIT:
Another strange issue is when I don't make all colors the same, it get's distorted in colors too. For example set only the RED to the heights, and leave G and B 0, it became not only RED, but a noisy colored heightmap.
EDIT /comments/
If I understood them right, there's the size of the header, then comes my pixel data. Now before the pixel data there must be 4 * n bytes. So that padding mean after the header I put some more data that fills the place.
For example assuming (I will look up hot to get it exactly) my header is 55 bytes, then I should add 1 more byte to it because 55+1 = 56 and 4|56.
So
file << static_cast<unsigned char>('a');
for(int y = 1; y <= width; y++)
{
for(int x = 1; x <= height; x++)
{
file << static_cast<unsigned char>(x);
file << static_cast<unsigned char>(x);
file << static_cast<unsigned char>(x);
};
};
should be correct.
But I realized the real issue (as Jigsore commented). When I cast from int to char, it seems like a 1 digit number becomes 1 byte, 2 digits number 2, and 3 digits 3 bytes. Clamping the height to 3 digits works well, but the image is a bit whitey, because 'darkest' color becomes (100,100,100) instead of (0,0,0). Also, this is the cause of the non-regular distortion, because it depends on how many 'hills' or 'mountains' are there in one row. How can I solve this, and I hope the last problem? I don't want to compress the image to 100-256 range.;)
Open your file in binary mode.
Under Windows, if you open a file in the default text mode, it will write an extra 0x0d (Return) character after every 0x0a (Linefeed) that gets written out. The first time this happens it will change the colors of the following pixels, as the RGB order gets out of alignment. After it happens 3 times you'll be off by a full pixel.

bitmap display not correct

tagBITMAPFILEHEADER bh;
BITMAPINFOHEADER bih;
char buf[3];
unsigned char bmp[200][600];
int width ,height;
ifstream fin(L"D:\\xx\\3.bmp",ios::_Nocreate|ios::binary);
fin.read((char*)&bh,sizeof(bh));
fin.read((char*)&bih,sizeof(bih));
width=bih.biWidth;
height=bih.biHeight;
HWND myconsole=GetConsoleWindow();
HDC mydc=GetDC(myconsole);
for(int i=height;i>=1;i--)
for(int j=1;j<=width;j++)
{
fin.read(buf,sizeof(buf));
bmp[i][j*3]=buf[0];
bmp[i][j*3+1]=buf[1];
bmp[i][j*3+2]=buf[2];
}
for(int i=1;i<=height;i++)
{
for(int j=1;j<=width;j++)
{
COLORREF color;
color=RGB(bmp[i][j*3],bmp[i][j*3+1],bmp[i][j*3+2]);
SetPixel(mydc,j,i,color);
}
//cout<<width<<endl;
}
ReleaseDC(myconsole,mydc);
fin.close();
return 0;
source bmp
display like this
i read pixel to an array, this bmp's bitcount is 24, so i use a char[3] to store the data,
but finaly display on the console like this,i can't figure it out.
Bitmap image data rows must be aligned on a 32-bit boundary. You therefore have to calculate the amount of padding that you need and skip that number of bytes at the end of every row.
In general the formula to calculate the number of bytes per row and the number you must skip is:
int rowBytes = ( (width * bytes_per_pixel) + 3 ) & ~3;
int skipBytes = rowBytes - (width * bytes_per_pixel);
Then skip skipBytes at the end of every row when reading the file.

Read Bmp Greyscales into C

I looked for how to read a Bmp file into a 2 or 1 dimensional Array under C , there are many solutions but not the one i need.
I need to read the Black and white bmp into (to beginn) 2 dimensional array which have to contain values from 0 to 255 (greyscale)
and then transform it to 1 dimensional array(but that's not a problem).
Matlab does this automticly but i want to be more autonomous working under C/C++
at the end the bmp shall be saved into a Postgre Database int array.
Thanks
There's a bmp loader which I made for another SO question:
http://nishi.dreamhosters.com/u/so_bmp_v0.zip
The example bmp there is RGB, but it seems to work with grayscale as well.
FILE* f = fopen( "winnt.bmp", "rb" ); if( f==0 ) return 1;
fread( buf, 1,sizeof(buf), f );
fclose(f);
BITMAPFILEHEADER& bfh = (BITMAPFILEHEADER&)buf[0];
BITMAPINFO& bi = (BITMAPINFO&)buf[sizeof(BITMAPFILEHEADER)];
BITMAPINFOHEADER& bih = bi.bmiHeader;
char* bitmap = &buf[bfh.bfOffBits];
int SX=bih.biWidth, SY=bih.biHeight;
bitmap here is the pointer to the pixel table (should be made unsigned
for proper access though). Note that pixel rows in bmp can be stored in
reverse order.
Sorry, misread question :/
If you don't mind "twisting" the rules a tiny little bit
#include <stdio.h>
int main(void) {
int data[100][30] = {{0}}; /* initialize 2D array to all zeroes */
int *p1d;
size_t index;
data[42][20] = 42; /* set 1 element ot 42 */
p1d = &data[0][0];
index = 42*30 + 20;
printf("%d (should be 42)\n", p1d[index]); /* pretend it's a 1D array */
return 0;
}