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.
Related
I have been having some issues with trying to read data out of a binary file that is structured so that the header is the first 1024, or 4069 bytes, and the payloads are all regular blocks after that. Below is a model of the code that I have been using:
Header:
#pragma once
#include <stdio.h>
#include <iostream>
#include <fstream>
class BinaryFile
{
public:
long MagicNumber;
long HeaderSize;
long PayloadSize;
//... about 20 other header details
char* Padding; // unused area of the header that is reserved for future use
std::ifstream BinaryFileStream;
BinaryFile();
int Open_Binary_File(const char* path);
int Load_Payload_Into_Buffer(int payload_index, void* buffer);
};
And the C++ code that I have tried to use:
#include "BinaryFile.h"
BinaryFile:BinaryFile()
{
MagicNumber = 0;
HeaderSize = 1024;
PayloadSize = 62830080;
// ... all the other header items, initalized with default values
std:ifstream BinaryFileStream;
Padding[360];
}
int BinaryFile::Open_Binary_File(const char* path) // This is the one that I would like to be using
{
BinaryFileStream.open(path, std::ifstream::binary);
BinaryFileStream.read((char*)&MagicNumber, (size_t) 4);
BinaryFileStream.read((char*)&HeaderSize, (size_t) 4);
// ... The rest of the header is placed into the object
BinaryFileStream.read((char*)&Padding, (size_t) 360);
// The file pointer should now be 1024 bytes into the file, at the end of the header, and at the start of the first payload
return 0;
}
int Load_Payload_Into_Buffer(int payload_index, void* buffer)
{
buffer = malloc(PayloadSize);
size_t offset = HeaderSize + static_cast<long long>(frame_index) * PayloadSize;
BinaryFileStream.seekg(offset, BinaryFileStream.beg);
BinaryFileStream.read((char*)buffer, PayloadSize);
return 0;
Error:
return 1;
}
Below are some variations that I have tried:
int BinaryFile::Open_Binary_File(const char* path)
{
BinaryFileStream.open(path, std::ifstream::binary);
BinaryFileStream.read((char*)&MagicNumber, (size_t) 4);
BinaryFileStream.read((char*)&HeaderSize, (size_t) 4);
// ... The rest of the header is placed into the object
BinaryFileStream.read((char*)&Padding, (size_t) 360);
BinaryFileStream.clear();
BinaryFileStream._Seekbeg.seekg((size_t)0, BinaryFileStream._Seekbeg);
BinaryFileStream.sync();
// The file pointer should now be 0 bytes into the file, at the start of the header
return 0;
}
int BinaryFile::Open_Binary_File(const char* path)
{
BinaryFileStream.open(path, std::ifstream::binary);
BinaryFileStream.read((char*)&MagicNumber, (size_t) 4);
BinaryFileStream.read((char*)&HeaderSize, (size_t) 4);
// ... The rest of the header is placed into the object
BinaryFileStream.read((char*)&Padding, (size_t) 360);
BinaryFileStream.clear();
BinaryFileStream.seekg((size_t)-1024);
BinaryFileStream.sync();
// The file pointer should now be 0 bytes into the file, at the start of the header
return 0;
}
The issue that I am running into to is, the payload that is being returned to the buffer contains some of the next payload. The setting of the file pointer doesn't seem to be working the way that I expect it to. i.e. if I say
BinaryFileStream._Seekbeg.seekg(position, BinaryFileStream._Seekbeg)
I expect the pointer to return to the start of the file, and then seek along the number of bytes that I have said in position. Is there a different way to do this? or is there something that I am missing?
Turns out, what I needed to do was multiply the HeaderSize by CHAR_BIT so it would look something like this:
int Load_Payload_Into_Buffer(int payload_index, void* buffer)
{
buffer = malloc(PayloadSize);
size_t offset = HeaderSize*CHAR_BIT + static_cast<long long>(frame_index) * PayloadSize;
BinaryFileStream.seekg(offset, BinaryFileStream.beg);
BinaryFileStream.read((char*)buffer, PayloadSize);
return 0;
Error:
return 1;
}
And what I thought was a portion of the next payload , was in fact the same payload, but split at some arbitrary point, and stacked next to it. So the payload was split approx. 3/4 of the way through, and it was then re-arranged, so that the original last quarter was now at in the first position, and the original first 3/4 was now in the 2nd position.
Hopefully that makes some sense, and will help someone in the future!
I am trying to open a bitmap file, edit it, and then save the edited version as a new file. This is eventually to mess with using steganography. I am trying to save the bitmap information now but the saved file will not open. No errors in compilation or run time. It opens fine and the rest of the functions work.
void cBitmap::SaveBitmap(char * filename)
{
// attempt to open the file specified
ofstream fout;
// attempt to open the file using binary access
fout.open(filename, ios::binary);
unsigned int number_of_bytes(m_info.biWidth * m_info.biHeight * 4);
BYTE red(0), green(0), blue(0);
if (fout.is_open())
{
// same as before, only outputting now
fout.write((char *)(&m_header), sizeof(BITMAPFILEHEADER));
fout.write((char *)(&m_info), sizeof(BITMAPINFOHEADER));
// read off the color data in the bass ackwards MS way
for (unsigned int index(0); index < number_of_bytes; index += 4)
{
red = m_rgba_data[index];
green = m_rgba_data[index + 1];
blue = m_rgba_data[index + 2];
fout.write((const char *)(&blue), sizeof(blue));
fout.write((const char *)(&green), sizeof(green));
fout.write((const char *)(&red), sizeof(red));
}
}
else
{
// post file not found message
cout <<filename << " not found";
}
// close the file
fout.close();
}
You're missing the padding bytes after each RGB row. The rows have to be a multiple of 4 bytes each.
Also, are you supposed to be writing a 24 or 32-bit bmp file? If you're writing 24-bit, you're just missing padding. If you're writing 32-bit, then you're missing each extra byte (alpha). Not enough information to fix your code sample short of writing a complete bmp writer that would support all possible options.
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.
This is my first post,so I am sorry if I write something wrong.I cannot understand how height and width are being extracted from the header.Here's the code till the part I am interested.
GLuint load_bmp(const char* imagepath)
{
unsigned char header[54];
unsigned int imageSize;
unsigned int dataPos;
unsigned int width;
unsigned int height;
unsigned char *data;
FILE *file=fopen(imagepath,"rb");
if(!file)
{
return false;
}
else
{
if(fread(header,1,54,file)!=54)
{
return false;
}
if((header[0]!='B')||(header[1]!='M'))
{
return false;
}
dataPos=*(int*)&header[0x0A];//This line
imageSize=*(int*)&header[0x22];//This line
height=*(int*)&header[0x12];//This line
width=*(int*)&header[0x16];//This line
}
}
How do you get the right values using these 4 lines of code?
The header is read into a buffer. The lines in question then cast addresses into that buffer as if they were pointing to binary integers, and read them.
So for example the height is a four-byte integer that is represented by the bytes from header[0x12] to header[0x15]. The code casts the address of the first byte as if it's pointing to an integer, then reads the contents of that integer pointer. I don't know if C++ has more guarantees about this sort of thing than C, but if not, then the code is making some assumptions about the size and byte representation of an int that won't work in some environments.
I am trying to read data from binary file, and having issues. I have reduced it down to the most simple case here, and it still won't work. I am new to c++ so I may be doing something silly but, if anyone could advise I would be very grateful.
Code:
int main(int argc,char *argv[]) {
ifstream myfile;
vector<bool> encoded2;
cout << encoded2 << "\n"<< "\n" ;
myfile.open(argv[2], ios::in | ios::binary |ios::ate );
myfile.seekg(0,ios::beg);
myfile.read((char*)&encoded2, 1 );
myfile.close();
cout << encoded2 << "\n"<< "\n" ;
}
Output
00000000
000000000000000000000000000011110000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Compression_Program(58221) malloc: * error for object 0x10012d: Non-aligned pointer being freed
* set a breakpoint in malloc_error_break to debug
Thanks in advance.
Do not cast a vector<bool>* to a char*. It is does not do anything predictable.
You are reading on encoded2: myfile.read((char*)&encoded2, 1 );. this is wrong. you can to read a bool and then put it in encoded2
bool x;
myfile.read( &x, 1 );
encoded2[0] = x;
Two mistakes here:
you assume the address of a vector is the address of the first element
you rely on vector<bool>
Casting a vector into a char * is not really a good thing, because a vector is an object and stores some state along with its elements.
Here you are probably overwriting the state of the vector, thus the destructor of fails.
Maybe you would like to cast the elements of the vector (which are guaranteed to be stored contiguously in memory). But another trap is that vector<bool> may be implementation-optimized.
Therefore you should do a encoded2.reserve(8) and use myfile.read(reinterpret_cast<char *>(&encoded2[0])).
But probably you want to do something else and we need to know what the purpose is here.
You're overwriting a std::vector, which you shouldn't do. A std::vector is actually a pointer to a data array and an integer (probably a size_t) holding its size; if you overwrite these with practically random bits, data corruption will occur.
Since you're only reading a single byte, this will suffice:
char c;
myfile.read(&c, 1);
The C++ language does not provide an efficient I/O method for reading bits as bits. You have to read bits in groups. Also, you have to worry about Endianess when reading int the bits.
I suggest the old fashioned method of allocating a buffer, reading into the buffer then operating on the buffer.
Allocating a buffer
const unsigned int BUFFER_SIZE = 1024 * 1024; // Let the compiler calculate it.
//...
unsigned char * const buffer = new unsigned char [BUFFER_SIZE]; // The pointer is constant.
Reading in the data
unsigned int bytes_read = 0;
ifstream data_file("myfile.bin", ios::binary); // Open file for input without translations.
data_file.read(buffer, BUFFER_SIZE); // Read data into the buffer.
bytes_read = data_file.gcount(); // Get actual count of bytes read.
Reminders:
delete the buffer when you are
finished with it.
Close the file when you are finished
with it.
myfile.read((char*) &encoded2[0], sizeof(int)* COUNT);
or you can use push_back();
int tmp;
for(int i = 0; i < COUNT; i++) {
myfile.read((char*) &tmp, 4);
encoded2.push_back(tmp);
}