Reading RGB pixels from bmp file - c++

I have a small bmp file and I want to get the RGB values of each pixel and output those values into a txt file if R, G, and B aren't all zero. I wrote the following program; it reads the header data correctly, but the RGB values aren't coming up. I assume I did something wrong in the for loop.
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
int main()
{
ifstream ifs;
ofstream ofs;
char input[80];
char output[80];
cout<<"Input file name"<<endl;
cin>>input;
ifs.open(input, ios::binary);
if(!ifs)
{
cout<<"Error in opening file"<<endl;
system("pause");
return 0;
}
cout<<"Output file name"<<endl;
cin>>output;
ofs.open(output, ios::binary);
ifs.seekg(2);
int file_size;
ifs.read((char*)&file_size, sizeof(int));
ofs<<"Bitmap size: "<<file_size<<"\r\n";
ifs.seekg(10);
int beg;
ifs.read((char*)&beg, sizeof(int));
ofs<<"Beggining of image: "<<beg<<"\r\n";
ifs.seekg(18);
int columns;
ifs.read((char*)&columns, sizeof(int));
ofs<<"Column number: "<<columns<<"\r\n";
ifs.seekg(22);
int rows;
ifs.read((char*)&rows, sizeof(int));
ofs<<"Row number: "<<rows<<"\r\n";
int image_size=0;
columns+=(3*columns)%4;
image_size=3*columns*rows;
ofs<<"Size of image"<<image_size<<"\r\n";
ifs.seekg(beg);
unsigned char R,G,B;
for(int i=0; i<image_size; i+=3)
{
ifs.read((char*)&B, sizeof(unsigned char));
ifs.read((char*)&G, sizeof(unsigned char));
ifs.read((char*)&R, sizeof(unsigned char));
if(R!=0 || G!=0 || B!=0)
ofs<<"R: "<<R<<" G: "<<G<<" B: "<<B<<" position in file: "<<ifs.tellg()<<"\r\n";
}
system("pause");
return 0;
}

I ran the code and it works fine, I presume you mean by 'RGB values aren't coming up' you are not seeing the integer values, in which case this will fix it:
ofs<<"R: "<<int(R)<<" G: "<<int(G)<<" B: "<<int(B)<<" position in file: "<<ifs.tellg()<<"\r\n";
Update: I posted earlier that you could replace ifs.read() with ifs >> R >> G >> B; As #Benjamin Lindley points out, this is incorrect as the >> operator is for formatted text, not binary. This means if the file contains eg a space/newline/etc character, the operator will skip it and take the next char. Better to use ifs.get(char) in this simple case.

You make several assumptions on the encoding of the image that you need to check.
If you look at the BMP header, you'll see:
at offset 28 that the file doesn't necessarily have 3*8 bits per pixel as you assume. It can have 1, 4, 8, or 24 bits per pixel;
at offset 30, the compression type is specified. It can be 0 for none
(your assumption) but also be Running Length Encoding: 1=RLE-8 or 2=RLE-4.
at offset 34 you can directly read the size of image data in bytes so that you don't need to calculate it on your own.
Attention also that sizeof(int) could be in theory different from 4. It's not the problem here, but this explains the practice of using microsoft's DWORD (for int) and WORD (for short) as documented here.
I suspect that RLE is used in you file: In this case, due to the compression, you can no longer look at the pixel bytes at a fixed position: you'd need to uncompress the data first.

Related

C++ storing 0 and 1 more efficiently, like in a binary file?

I want to store multiple arrays which all entries consist of either 0 or 1.
This file would be quite large if i do it the way i do it.
I made a minimalist version of what i currently do.
#include <iostream>
#include <fstream>
using namespace std;
int main(){
ofstream File;
File.open("test.csv");
int array[4]={1,0,0,1};
for(int i = 0; i < 4; ++i){
File << array[i] << endl;
}
File.close();
return 0;
}
So basically is there a way of storing this in a binary file or something, since my data is 0 or 1 in the first place anyways?
If yes, how to do this? Can i also still have line-breaks and maybe even commas in that file? If either of the latter does not work, that's also fine. Just more importantly, how to store this as a binary file which has only 0 and 1 so my file is smaller.
Thank you very much!
So basically is there a way of storing this in a binary file or something, since my data is 0 or 1 in the first place anyways? If yes, how to do this? Can i also still have line-breaks and maybe even commas in that file? If either of the latter does not work, that's also fine. Just more importantly, how to store this as a binary file which has only 0 and 1 so my file is smaller.
The obvious solution is to take 64 characters, say A-Z, a-z, 0-9, and + and /, and have each character code for six entries in your table. There is, in fact, a standard for this called Base64. In Base64, A encodes 0,0,0,0,0,0 while / encodes 1,1,1,1,1,1. Each combination of six zeroes or ones has a corresponding character.
This still leaves commas, spaces, and newlines free for your use as separators.
If you want to store the data as compactly as possible, I'd recommend storing it as binary data, where each bit in the binary file represents one boolean value. This will allow you to store 8 boolean values for each byte of disk space you use up.
If you want to store arrays whose lengths are not multiples of 8, it gets a little bit more complicated since you can't store a partial byte, but you can solve that problem by storing an extra byte of meta-data at the end of the file that specifies how many bits of the final data-byte are valid and how many are just padding.
Something like this:
#include <iostream>
#include <fstream>
#include <cstdint>
#include <vector>
using namespace std;
// Given an array of ints that are either 1 or 0, returns a packed-array
// of uint8_t's containing those bits as compactly as possible.
vector<uint8_t> packBits(const int * array, size_t arraySize)
{
const size_t vectorSize = ((arraySize+7)/8)+1; // round up, then +1 for the metadata byte
vector<uint8_t> packedBits;
packedBits.resize(vectorSize, 0);
// Store 8 boolean-bits into each byte of (packedBits)
for (size_t i=0; i<arraySize; i++)
{
if (array[i] != 0) packedBits[i/8] |= (1<<(i%8));
}
// The last byte in the array is special; it holds the number of
// valid bits that we stored to the byte just before it.
// That way if the number of bits we saved isn't an even multiple of 8,
// we can use this value later on to calculate exactly how many bits we should restore
packedBits[vectorSize-1] = arraySize%8;
return packedBits;
}
// Given a packed-bits vector (i.e. as previously returned by packBits()),
// returns the vector-of-integers that was passed to the packBits() call.
vector<int> unpackBits(const vector<uint8_t> & packedBits)
{
vector<int> ret;
if (packedBits.size() < 2) return ret;
const size_t validBitsInLastByte = packedBits[packedBits.size()-1]%8;
const size_t numValidBits = 8*(packedBits.size()-((validBitsInLastByte>0)?2:1)) + validBitsInLastByte;
ret.resize(numValidBits);
for (size_t i=0; i<numValidBits; i++)
{
ret[i] = (packedBits[i/8] & (1<<(i%8))) ? 1 : 0;
}
return ret;
}
// Returns the size of the specified file in bytes, or -1 on failure
static ssize_t getFileSize(ifstream & inFile)
{
if (inFile.is_open() == false) return -1;
const streampos origPos = inFile.tellg(); // record current seek-position
inFile.seekg(0, ios::end); // seek to the end of the file
const ssize_t fileSize = inFile.tellg(); // record current seek-position
inFile.seekg(origPos); // so we won't change the file's read-position as a side effect
return fileSize;
}
int main(){
// Example of packing an array-of-ints into packed-bits form and saving it
// to a binary file
{
const int array[]={0,0,1,1,1,1,1,0,1,0};
// Pack the int-array into packed-bits format
const vector<uint8_t> packedBits = packBits(array, sizeof(array)/sizeof(array[0]));
// Write the packed-bits to a binary file
ofstream outFile;
outFile.open("test.bin", ios::binary);
outFile.write(reinterpret_cast<const char *>(&packedBits[0]), packedBits.size());
outFile.close();
}
// Now we'll read the binary file back in, unpack the bits to a vector<int>,
// and print out the contents of the vector.
{
// open the file for reading
ifstream inFile;
inFile.open("test.bin", ios::binary);
const ssize_t fileSizeBytes = getFileSize(inFile);
if (fileSizeBytes < 0)
{
cerr << "Couldn't read test.bin, aborting" << endl;
return 10;
}
// Read in the packed-binary data
vector<uint8_t> packedBits;
packedBits.resize(fileSizeBytes);
inFile.read(reinterpret_cast<char *>(&packedBits[0]), fileSizeBytes);
// Expand the packed-binary data back out to one-int-per-boolean
vector<int> unpackedInts = unpackBits(packedBits);
// Print out the int-array's contents
cout << "Loaded-from-disk unpackedInts vector is " << unpackedInts.size() << " items long:" << endl;
for (size_t i=0; i<unpackedInts.size(); i++) cout << unpackedInts[i] << " ";
cout << endl;
}
return 0;
}
(You could probably make the file even more compact than that by running zip or gzip on the file after you write it out :) )
You can indeed write and read binary data. However having line breaks and commas would be difficult. Imagine you save your data as boolean data, so only ones and zeros. Then having a comma would mean you need an special character, but you have only ones and zeros!. The next best thing would be to make an object of two booleans, one meaning the usual data you need (c++ would then read the data in pairs of bits), and the other meaning whether you have a comma or not, but I doubt this is what you need. If you want to do something like a csv, then it would be easy to just fix the size of each column (int would be 4 bytes, a string of no more than 32 char for example), and then just read and write accordingly. Suppose you have your binary
To initially save your array of the an object say pets, then you would use
FILE *apFile;
apFile = fopen(FILENAME,"w+");
fwrite(ARRAY_OF_PETS, sizeof(Pet),SIZE_OF_ARRAY, apFile);
fclose(apFile);
To access your idx pet, you would use
Pet m;
ifstream input_file (FILENAME, ios::in|ios::binary|ios::ate);
input_file.seekg (sizeof(Pet) * idx, ios::beg);
input_file.read((char*) &m,sizeof(Pet));
input_file.close();
You can also add data add the end, change data in the middle and so on.

How to load an JPEG image into a char array C++?

i want to store an JPEG image into a normal unsigned char array, i'd used ifstream to store it; however, when i checked if the array i'd stored is correct or not ( by rewrite it again to an JPEG image), the image that i rewrote by using the stored array couldn't show correctly, so i think the problem must come from the technique that i use to store the image into an array is not correct. I want an array which can be stored perfectly so that i can use it to rewrite back into a JPEG image again.I'd really appreciate if anyone can help me solve this problem!
int size = 921600;
unsigned char output[size];
int i = 0;
ifstream DataFile;
DataFile.open("abc.jpeg");
while(!DataFile.eof()){
DataFile >> output[i];
i++;
}
/* i try to rewrite the above array into a new image here */
FILE * image2;
image2 = fopen("def.jpeg", "w");
fwrite(output,1,921600, image2);
fclose(image2);
There are multiple problems in the shown code.
while(!DataFile.eof()){
This is always a bug. See the linked question for a detailed explanation.
DataFile >> output[i];
The formatted extraction operator, >>, by definition, skips over all white space characters and ignores them. Your jpg file surely has bytes 0x09, 0x20, and a few others, somewhere in it, and this automatically skips over and does not read them.
In order to do this correctly, you need to use read() and gcount() to read your binary file. Using gcount() correctly should also result in your code detecting the end-of-file condition properly.
Make sure to add error check when opening files. Find the file size and read in to the buffer according to the filesize.
You might also look in to using std::vector<unsigned char> for character storage.
int main()
{
std::ifstream DataFile("abc.jpeg", std::ios::binary);
if(!DataFile.good())
return 0;
DataFile.seekg(0, std::ios::end);
size_t filesize = (int)DataFile.tellg();
DataFile.seekg(0);
unsigned char output[filesize];
//or std::vector
//or unsigned char *output = new unsigned char[filesize];
if(DataFile.read((char*)output, filesize))
{
std::ofstream fout("def.jpeg", std::ios::binary);
if(!fout.good())
return 0;
fout.write((char*)output, filesize);
}
return 0;
}

C++ Segmentation Fault when loading Image Pixels from Binary File

Doing some C++ for fun and have a problem where when I load an Image after doing some modifications to the image, it gives me segmentation fault. I feel like I'm missing something but I don't know where.
EDIT Here's the code for both the save and load function, (assume that all necessary header files are included):
int Image::save(const char* filename)
{
if(filename == NULL)
{
return 1;
}
///*
ofstream outFile(filename, ios::out | ios::binary);
if (!outFile)
{
return 1;
}
outFile.write(reinterpret_cast<char*>(&cols), sizeof(unsigned int));
outFile.write(reinterpret_cast<char*>(&rows), sizeof(unsigned int));
outFile.write(reinterpret_cast<char*>(pixels), sizeof(uint8_t) * cols * rows);
outFile.close();
return 0;
}
int Image::load(const char* filename)
{
if(filename == NULL)
{
return 1;
}
///*
ifstream inFile(filename, ios::in | ios::binary);
if (!inFile)
{
return 1;
}
**//feels like the segmentation fault is happening here**
inFile.read(reinterpret_cast<char*>(&cols), sizeof(unsigned int));
inFile.read(reinterpret_cast<char*>(&rows), sizeof(unsigned int));
inFile.read(reinterpret_cast<char*>(pixels), sizeof(uint8_t) * cols * rows);
inFile.close();
return 0;
}
EDIT
Here's the header file that I am working with:
class Image {
public:
unsigned int cols;
unsigned int rows;
uint8_t* pixels;
...
/* Saves the image in the file filename. In a format that can be
loaded by load(). Returns 0 on success, else a non-zero error
code. */
int save( const char* filename );
/* Load an image from the file filename, replacing the current
image size and data. The file is in a format that was saved by
save(). Returns 0 success, else a non-zero error code . */
int load( const char* filename );
};
You're moving the file pointer to the end of the file before trying to read it when you open it with ios::ate. You want to read from the beginning of the file, so ios::ate should be removed.
Also you are reading in a loop, and not writing in a loop. Your while should be an if, or just removed.
Also read does not adjust your pointer (or shouldn't...see my next point), but merely reads data into where you are pointing to. So the NULL check (if pixels==NULL) is nonsensical.
Also, you shouldn't be using the address-of operator (&) for pixels. pixels is already a pointer and both your read and write of this variable should have the & removed, like so:
inFile.read(reinterpret_cast<char*>(pixels), sizeof(uint8_t) * cols * rows);
You may find this helpful:
http://boredzo.org/pointers/
edit:
inFile.read(reinterpret_cast<char*>(&cols), sizeof(unsigned int));
inFile.read(reinterpret_cast<char*>(&rows), sizeof(unsigned int));
resize(cols, rows, 0);
inFile.read(reinterpret_cast<char*>(pixels), sizeof(uint8_t) * cols * rows);
Your resize() needs to make sure the pointer isn't NULL before trying to delete it, and you probably should make fill() a separate function.
But at least do
int Image::resize(unsigned int width, unsigned int height, uint8_t fillcolor)
{
if (pixels != NULL)
delete[] pixels;
...
In addition to #zzxyz's answer, you may be running into a problem with byte order. When you read cols and rows, c++ might order the bytes in integers from least significant to most significant (little endian), while the file could order the bytes from most significant to least significant (big endian), for example (see more here). This could give you values of cols and rows wildly different from what you expect, and reading cols * rows bytes could make inFile try to read far beyond the length of the file. I recommend checking or printing the values of cols and rows and making sure they line up with what you expect; if not, you'll have to reverse the order of the bytes in the integers.

get Horizontal Resolution from .tif in C++

When a person right-clicks a .tif file, then clicks Properties, then clicks the Details tab, they can see Horizontal Resolution. My question is: How can I read this number in from a .tif file using C++?
deAngel, I tried your code on a specific .tif image I have and ithe code froze. Could you send me an e-mail to steve61706#yahoo.com and then I can send you the .tif file that caused the code to freeze? I really need this to work. Thank you so much.
Use libTIFF
Read the fields (sample code here)
Profit!
If you just want to get one or two fields from the header then adding libtiff may be overkill. In this case you can parse the file yourself and locate the tags that you want, the format is quite simple to parse. See the file format details and tag reference.
Below is an example which reads the horizontal and vertical resolution from a TIFF file.
As reference I used the TIFF Revision 6.0 sections 2 and 8 (TIFF structure, IFD structure and data types).
Please note: the example only treats the LSB byte order case and only the first image in the TIFF file. A good implementation must also take into account the MSB order and multiple images per file (which could have different resolutions). I appologize for bugs that may have slipped in (unintentionally). Source was compiled with Borland C++ 3.1. I tried to keep it as simple as I could...
#include <iostream.h>
#include <fstream.h>
//converts a 4-byte LSB order array to ULong
unsigned long lsbToUL(unsigned char *buff)
{
unsigned long val=0;
int i=0;
for(i=3;i>0;i--)
val=(val+buff[i])<<8;
val=val+buff[0];
return val;
}
//converts a 2-byte LSB order array to UInt
unsigned int lsbToUI(unsigned char *buff)
{
unsigned int val=0;
val=val+buff[1];
val=val<<8;
val=val+buff[0];
return val;
}
//reads the resolution values of the first image of a TIFF file
void tiffRes(char *path, double *xRes, double *yRes, int *unit)
{
unsigned int XRES=0x011A; //TIFF XResolution tag
unsigned int YRES=0x011B; //TIFF YResolution tag
unsigned int UNIT=0x0128; //TIFF Resolution Unit tag
unsigned char buff[5];
unsigned long seekAddr=0;
unsigned int tag=0;
unsigned long xAddr=0,yAddr=0;
unsigned int resUnit=0;
ifstream f;
f.open(path,ios::binary);
if(f.fail())
{
cout<<"File error"<<endl;
return;
}
f.get(buff,3); //reads the byte order
if(buff[0]==0x49 && buff[1]==0x49)
{
//this example treats the LSB case
f.seekg(4); //seeks the offset of first IFD from header
f.get(buff,5); //reads the address of the first IFD
seekAddr=lsbToUL(buff);
seekAddr=seekAddr+2;
f.seekg(seekAddr); //seeks the addr of the first TIFF tag
//skipping the no. of directory entries as it is not used
f.get(buff,3); //reads the first TIFF tag
tag=lsbToUI(buff);
//reading the file for XRES,YRES and UNIT TIFF tags
//should be a better way of doing this
while(xAddr==0 || yAddr==0 || resUnit==0)
{
if(tag==UNIT || tag==XRES || tag==YRES)
{
if(tag==UNIT)
{
f.seekg(seekAddr+8);
f.get(buff,3); //read the Resolution Unit
resUnit=lsbToUI(buff);
*unit=resUnit;
}
else
{
f.seekg(seekAddr+8);
f.get(buff,5); //reads the address of the XRes or YRes
if(tag==XRES)
xAddr=lsbToUL(buff); //XRes address
else
yAddr=lsbToUL(buff); //YRes address
}
}
seekAddr=seekAddr+12; //computes the address of next IFD
f.seekg(seekAddr); //seeks the next IFD
f.get(buff,3); //reads the next tag
tag=lsbToUI(buff);
}
//actual reading of the resolution values
//X resolution
f.seekg(xAddr);
f.get(buff,5);
*xRes=lsbToUL(buff);
f.seekg(xAddr+4);
f.get(buff,5);
*xRes=*xRes/(double)lsbToUL(buff);
//Y resolution
f.seekg(yAddr);
f.get(buff,5);
*yRes=lsbToUL(buff);
f.seekg(yAddr+4);
f.get(buff,5);
*yRes=*yRes/(double)lsbToUL(buff);
f.close();
}
else
{
//Not covered by the example
f.close();
if(buff[0]==0x4D && buff[1]==0x4D)
cout<<"MSB mode used. Not covered by the example..."<<endl;
else
cout<<"Unkown byte order..."<<endl;
}
}
void main()
{
double *xRes=new double;
double *yRes=new double;
int *unit=new int;
typedef char string[8];
string resUnit[4]={"","NO_UNIT","INCH","CM"};
char *path="test.tif";
*xRes=0;
*yRes=0;
*unit=0;
tiffRes(path,xRes,yRes,unit);
cout<<"UNIT: "<<resUnit[(*unit)]<<endl;
cout<<"X res: "<<(*xRes)<<endl;
cout<<"Y res: "<<(*yRes)<<endl;
}

C++ Binary file read issue

I was making a function to read a file containing some dumped data (sequence of 1 byte values). As the dumped values were 1 byte each, I read them as chars. I opened the file in binary mode, read the data as chars and did a casting into int (so I get the ascii codes). But the data read isn't correct (compared in a hex-editor). Here's my code:
int** read_data(char* filename, int** data, int& height, int& width)
{
data=new int*[height];
int row,col;
ifstream infile;
infile.open(filename,ios::binary|ios::in);
if(!infile.good())
{
return 0;
}
char* ch= new char[width];
for(row=0; row<height; row++)
{
data[row]=new int[width];
infile.read(ch,width);
for(col=0; col<width; col++)
{
data[row][col]=int(ch[col]);
cout<<data[row][col]<<" ";
}
cout<<endl;
}
infile.close();
return data;
}
Any ideas what might be wrong with this code?
My machine is windows, I'm using Visual Studio 2005 and the (exact) filename that i passed is:
"D:\\files\\output.dat"
EDIT: If I don't use unsigned char, the first 8 values, which are all 245, are read as -11.
I think, you might have to use unsigned char and unsigned int to get correct results. In your code, the bytes you read are interpreted as signed values. I assume you did not intend that.
Your error seems to cover in using of char* for ch. When you try to output it, all chars are printed until the first zero value.
A plain char can be either signed or unsigned, depending on the compiler. To get a consistent (and correct) result, you can cast the value to unsigned char before assigning to the int.
data[row][col]=static_cast<unsigned char>(ch[col]);