read bytes of data after reading plain text pgm p5 file - c++

I'm trying to read a .pgm version p5 file. The header is in plain text then the actual data is stored in plain bytes. The header can be an arbitrary length. I how can I start reading byte by byte after reading in the plain text line by line?
int main()
{
//Declare
int rows = 0, cols = 0, maxVal = 0;
ifstream infile("image.pgm");
string inputLine = "";
string trash = "";
//First line "P5"
getline(infile,inputLine);
//ignore lines with comments
getline(infile,trash);
while (trash[0] == '#')
{
getline(infile,trash);
}
//get the rows and cols
istringstream iss(trash);
getline(iss, inputLine, ' ');
rows = atoi(inputLine.c_str());
getline(iss, inputLine, ' ');
cols = atoi(inputLine.c_str());
//get the last plain text line maxval
getline(infile,inputLine);
maxVal = atoi(inputLine.c_str());
//Now start reading individual bites
Matrix<int, rows, cols> m;
//now comes the data
for(i = 0; i<rows; i++)
{
for(j = 0; j < cols; j++)
{
//store data into matrix
}
}
system("Pause");
return 0;
}

Use ifstream::read to read a block of binary data and copy it into a buffer. You know the size of data from the image size in the header.
If your matrix object has a method to get an address you can copy it directly, or read it into some temporary buffer and then copy that into the Matrix. Reading a byte a time is likely to be very slow.

Related

Calculating the vertical gradient of 2D image causes strange output

I want to apply a simple derive/gradient filter, [-1, 0, 1], to an image from a .ppm file.
The raw binary data from the .ppm file is read into a one-dimensional array:
uint8_t* raw_image_data;
size_t n_rows, n_cols, depth;
// Open the file as an input binary file
std::ifstream file;
file.open("test_image.ppm", std::ios::in | std::ios::binary);
if (!file.is_open()) { /* error */ }
std::string temp_line;
// Check that it's a valid P6 file
if (!(std::getline(file, temp_line) && temp_line == "P6")) {}
// Then skip all the comments (lines that begin with a #)
while (std::getline(file, temp_line) && temp_line.at(0) == '#');
// Try read in the info about the number of rows and columns
try {
n_rows = std::stoi(temp_line.substr(0, temp_line.find(' ')));
n_cols = std::stoi(temp_line.substr(temp_line.find(' ')+1,temp_line.size()));
std::getline(file, temp_line);
depth = std::stoi(temp_line);
} catch (const std::invalid_argument & e) { /* stoi has failed */}
// Allocate memory and read in all image data from ppm
raw_image_data = new uint8_t[n_rows*n_cols*3];
file.read((char*)raw_image_data, n_rows*n_cols*3);
file.close();
I then read a grayscale image from the data into a two-dimensional array, called image_grayscale:
uint8_t** image_grayscale;
image_grayscale = new uint8_t*[n_rows];
for (size_t i = 0; i < n_rows; ++i) {
image_grayscale[i] = new uint8_t[n_cols];
}
// Convert linear array of raw image data to 2d grayscale image
size_t counter = 0;
for (size_t r = 0; r < n_rows; ++r) {
for (size_t c = 0; c < n_cols; ++c) {
image_grayscale[r][c] = 0.21*raw_image_data[counter]
+ 0.72*raw_image_data[counter+1]
+ 0.07*raw_image_data[counter+2];
counter += 3;
}
}
I want to write the resulting filtered image to another two-dimensional array, gradient_magnitude:
uint32_t** gradient_magnitude;
// Allocate memory
gradient_magnitude = new uint32_t*[n_rows];
for (size_t i = 0; i < n_rows; ++i) {
gradient_magnitude[i] = new uint32_t[n_cols];
}
// Filtering operation
int32_t grad_h, grad_v;
for (int r = 1; r < n_rows-1; ++r) {
for (int c = 1; c < n_cols-1; ++c) {
grad_h = image_grayscale[r][c+1] - image_grayscale[r][c-1];
grad_v = image_grayscale[r+1][c] - image_grayscale[r-1][c];
gradient_magnitude[r][c] = std::sqrt(pow(grad_h, 2) + pow(grad_v, 2));
}
}
Finally, I write the filtered image to a .ppm output.
std::ofstream out;
out.open("output.ppm", std::ios::out | std::ios::binary);
// ppm header
out << "P6\n" << n_rows << " " << n_cols << "\n" << "255\n";
// Write data to file
for (int r = 0; r < n_rows; ++r) {
for (int c = 0; c < n_cols; ++c) {
for (int i = 0; i < 3; ++i) {
out.write((char*) &gradient_magnitude[r][c],1);
}
}
}
out.close();
The output image, however, is a mess.
When I simply set grad_v = 0; in the loop (i.e. solely calculate the horizontal gradient), the output is seemingly correct:
When I instead set grad_h = 0; (i.e. solely calculate the vertical gradient), the output is strange:
It seems like part of the image has been circularly shifted, but I cannot understand why. Moreover, I have tried with many images and the same issue occurs.
Can anyone see any issues? Thanks so much!
Ok, first clue is that the image looks circularly shifted. This hints that strides are wrong. The core of your problem is simple:
n_rows = std::stoi(temp_line.substr(0, temp_line.find(' ')));
n_cols = std::stoi(temp_line.substr(temp_line.find(' ')+1,temp_line.size()));
but in the documentation you can read:
Each PPM image consists of the following:
A "magic number" for identifying the file type. A ppm image's magic number is the two
characters "P6".
Whitespace (blanks, TABs, CRs, LFs).
A width, formatted as ASCII characters in decimal.
Whitespace.
A height, again in ASCII decimal.
[...]
Width is columns, height is rows. So that's the classical error that you get when implementing image processing stuff: swapping rows and columns.
From a didactic point of view, why are you doing this mistake? My guess: poor debugging tools. After making a working example from your question (effort that I would have saved if you had provided a MCVE), I run to the end of image loading and used Image Watch to see the content of your image with #mem(raw_image_data, UINT8, 3, n_cols, n_rows, n_cols*3). Result:
Ok, let's try to swap them: #mem(raw_image_data, UINT8, 3, n_rows, n_cols, n_rows*3). Result:
Much better. Unfortunately I don't know how to specify RGB instead of BGR in Image Watch #mem pseudo command, so the wrong colors.
Then we come back to your code: please compile with all warnings on. Then I'd use more of the std::stream features for parsing your input and less std::stoi() or find(). Avoid memory allocation by using std::vector and make a (possibly template) class for images. Even if you stick to your pointer to pointer, don't make multiple new for each row: make a single new for the pointer at row 0, and have the other pointers point to it:
uint8_t** image_grayscale = new uint8_t*[n_rows];
image_grayscale[0] = new uint8_t[n_rows*n_cols];
for (size_t i = 1; i < n_rows; ++i) {
image_grayscale[i] = image_grayscale[i - 1] + n_cols;
}
Same effect, but easier to deallocate and to manage as a single piece of memory. For example, saving as a PGM becomes:
{
std::ofstream out("output.pgm", std::ios::binary);
out << "P5\n" << n_rows << " " << n_cols << "\n" << "255\n";
out.write(reinterpret_cast<char*>(image_grayscale[0]), n_rows*n_cols);
}
Fill your borders! Using the single allocation style I showed you you can do it as:
uint32_t** gradient_magnitude = new uint32_t*[n_rows];
gradient_magnitude[0] = new uint32_t[n_rows*n_cols];
for (size_t i = 1; i < n_rows; ++i) {
gradient_magnitude[i] = gradient_magnitude[i - 1] + n_cols;
}
std::fill_n(gradient_magnitude[0], n_rows*n_cols, 0);
Finally the gradient magnitude is an integer value between 0 and 360 (you used a uint32_t). Then you save only the least significant byte of it! Of course it's wrong. You need to map from [0,360] to [0,255]. How? You can saturate (if greater than 255 set to 255) or apply a linear scaling (*255/360). Of course you can do also other things, but it's not important.
Here you can see the result on a zoomed version of the three cases: saturate, scale, only LSB (wrong):
With the wrong version you see dark pixels where the value should be higer than 255.

C++ Matrix to dynamic 2D arrray

let's say we have a matrix in a matrix.txt file, stored like this:
and we want to transform it into:
Number 8 (first number) means how big will 2D array be. After that it means:
1 is connected to 2 (value of connection is 1, it will always be 1)
1 is connected to 8
3 is connected to 4
And when transformed into 2D dynamical array, we want the value 1 in ARRRAY 0,1...0,7...2,3 and soo on (i didnt use square brackets because stackoverflow read them as links).
int number;
int **a = new int*[number];
for (int i = 0; i<number; i++) {
a[i] = new int[number];
}
for (int i = 0; i<number; i++) {
delete[]a[i];
}
delete[]a;
string line;
ifstream myfile("matrix.txt");
if (myfile.is_open())
{
getline(myfile, line);
istringstream(line)>> number;
while (getline(myfile, line)){
cout << line << '\n';
//HERE I SHOULD TURN THOSE NUMBERS INTO VALUES IN 2D ARRAY
}
myfile.close();
}
So my question is: How do i turn this numbers into matrix in 2d array?
Thank you
The easy way, but possibly not the fastest way, is to write the line into a std::stringstream, then read back out of the stringstream into row, column, and value variables. If you are reading from a file the cost of reading the file in the first place usually dwarfs the cost of parsing the file the slow way. If it matters in your case (and profile the code first to make sure it does) look into manually parsing the file. That said, this basic logic will hold.
while (getline(myfile, line)){
cout << line << '\n';
std::stringstream linestream(line);
int row;
int column;
int value;
if (linestream >> row >> column >> value)
{
a[row-1][column-1] = value;
a[column-1][row-1] = value;// mirror
}
else
{
// handle file formatting error
}
}
Off topic, consider using a matrix class to manage a for you rather than a raw 2D Array. The matrix class here at isocppp.org is good and fast, as well as wrapped in some very good general-purpose advice.
The above code with the isocpp matrix looks like:
while (getline(myfile, line)){
cout << line << '\n';
std::stringstream linestream(line);
int row;
int column;
int value;
if (linestream >> row >> column >> value)
{
a(row-1,column-1) = value;
a(column-1,row-1) = value;// mirror
}
else
{
// handle file formatting error
}
}
Nearly identical and a lot easier to use because you don't have to worry about managing the memory yourself, passing the array dimensions around, or a bit of bad code (such as a[4] = 0;) nuking a row of your array.
Addendum
This code
int number;
int **a = new int*[number];
for (int i = 0; i<number; i++) {
a[i] = new int[number];
}
for (int i = 0; i<number; i++) {
delete[]a[i];
}
delete[]a;
has two serious problems:
a is sized with number, and number hasn't been assigned yet. number could be anything from an instantly fatal negative number (can't have an array with a negative size) to a potentially fatal huge number (Your computer have 9,223,372,036,854,775,807 squared bytes of RAM? Didn't think so.)
It deletes the storage immediately after allocating it. Freeing the memory is a great habit to get into, but it's best to free the memory after using it, not before.
So:
// define `a` here
string line;
ifstream myfile("matrix.txt");
if (myfile.is_open())
{
getline(myfile, line);
istringstream(line)>> number;
// allocate storage for `a` here
while (getline(myfile, line)){
cout << line << '\n';
//line reading code goes here
}
myfile.close();
}
// delete `a` somewhere down here after it's been used.
I suggest you use C++ vector from the STL instead of 2d C array which are unsafe.
You could do something like :
ifstream myfile("matrix.txt");
// Read matrix size
size_t matrix_size = 0; myfile >> matrix_size;
vector<vector<int> > matrix(matrix_size);
for(size_t i=0; i < matrix.size(); ++i) matrix[i].resize(matrix_size);
while( myfile.good() )
{
// Read row,col,val and set matrix value
int row=0, col=0, val=0; myfile >> row >> col >> val;
--row; --col; // Since your indices go from 1 to N
if(row < matrix_size && row >= 0 && col < matrix_size && col >= 0) {
matrix[row][col] = val; matrix[col][row] = val;
}
}

How to read a labyrinth from a txt file and put it into 2D array

I just started a small project which reads a txt file like this:
4
XSXX
X X
XX X
XXFX
So my question is how to read this and put the labyrinth to 2D char array in C++. I tried to use 'getline' but I just make my code more complex. Do you know if there is an easy way to solve this problem ?
char temp;
string line;
int counter = 0;
bool isOpened=false;
int size=0;
ifstream input(inputFile);//can read any file any name
// i will get it from user
if(input.is_open()){
if(!isOpened){
getline(input, line);//iterater over every line
size= atoi(line.c_str());//atoi: char to integer method.this is to generate the size of the matrix from the first line
}
isOpened = true;
char arr2[size][size];
while (getline(input, line))//while there are lines
{
for (int i = 0; i < size; i++)
{
arr2[counter][i]=line[i];//decides which character is declared
}
counter++;
}
Your error is due to the fact that you are trying to declare an array with a size that is a non-constant expression.
In your case size representing the number of elements in the array, must be a constant expression, since arrays are blocks of static memory whose size must be determined at compile time, before the program runs.
To solve it you could either leave the array with empty brackets and the size will be automatically deduced by the number of elements you place in it or
you could use std::string and std::vector and then to read the .txt file you could write something like:
// open the input file
ifstream input(inputFile);
// check if stream successfully attached
if (!input) cerr << "Can't open input file\n";
string line;
int size = 0;
// read first line
getline(input, line);
stringstream ss(line);
ss >> size;
vector<string> labyrinth;
// reserve capacity
labyrinth.reserve(size);
// read file line by line
for (size_t i = 0; i < size; ++i) {
// read a line
getline(input, line);
// store in the vector
labyrinth.push_back(line);
}
// check if every character is S or F
// traverse all the lines
for (size_t i = 0; i < labyrinth.size(); ++i) {
// traverse each character of every line
for (size_t j = 0; j < labyrinth[i].size(); ++j) {
// check if F or S
if (labyrinth[i][j] == 'F' || labyrinth[i][j] == 'S') {
// labyrinth[i][j] is F or S
}
if (labyrinth[i][j] != 'F' || labyrinth[i][j] != 'S') {
// at least one char is not F or S
}
}
}
As you can see this vector is already "a kind of" 2D char array only with a lot of additionally provided facilities that allow a lot of operations on its content.

PGM File Reader Doesn't Read Asymmetric Files

I'm writing a simple PGM file reader for a basic CV idea, and I'm having a weird issue. My method seems to work alright for symmetric files (255 x 255, for example), but when I try to read an asymmetric file (300 x 246), I get some weird input. One file reads to a certain point and then dumps ESCAPE characters (ASCII 27) into the remainder of the image (see below), and others just won't read. I think this might be some flawed logic or a memory issue. Any help would be appreciated.
// Process files of binary type (P5)
else if(holdString[1] == '5') {
// Assign fileType value
fileType = 5;
// Read in comments and discard
getline(fileIN, holdString);
// Read in image Width value
fileIN >> width;
// Read in image Height value
fileIN >> height;
// Read in Maximum Grayscale Value
fileIN >> max;
// Determine byte size if Maximum value is over 256 (1 byte)
if(max < 256) {
// Collection variable for bytes
char readChar;
// Assign image dynamic memory
*image = new int*[height];
for(int index = 0; index < height; index++) {
(*image)[index] = new int[width];
}
// Read in 1 byte at a time
for(int row = 0; row < height; row++) {
for(int column = 0; column < width; column++) {
fileIN.get(readChar);
(*image)[row][column] = (int) readChar;
}
}
// Close the file
fileIN.close();
} else {
// Assign image dynamic memory
// Read in 2 bytes at a time
// Close the file
}
}
Tinkered with it a bit, and came up with at least most of a solution. Using the .read() function, I was able to draw the whole file in and then read it piece by piece into the int array. I kept the dynamic memory because I wanted to draw in files of different sizes, but I did pay more attention to how it was read into the array, so thank you for the suggestion, Mark. The edits seem to work well on files up to 1000 pixels wide or tall, which is fine for what I'm using it for. After, it distorts, but I'll still take that over not reading the file.
if(max < 256) {
// Collection variable for bytes
int size = height * width;
unsigned char* data = new unsigned char[size];
// Assign image dynamic memory
*image = new int*[height];
for(int index = 0; index < height; index++) {
(*image)[index] = new int[width];
}
// Read in 1 byte at a time
fileIN.read(reinterpret_cast<char*>(data), size * sizeof(unsigned char));
// Close the file
fileIN.close();
// Set data to the image
for(int row = 0; row < height; row++) {
for(int column = 0; column < width; column++) {
(*image)[row][column] = (int) data[row*width+column];
}
}
// Delete temporary memory
delete[] data;
}

C++ create a variable number of arrays

I have read columns from a .txt file (the file has at the beginning the number of columns (nCol)) and put the values into an array (float values[nCol][nLin]).
Now, I want to copy the values (ex.: values[0][nLin], values[1][nLin]...) into different float arrays depending on the number of columns.
How can I crate float arrays for each column if the number of columns my change depending on the file I am reading?
//------ Declares Array for values ------//
const int nCol = countCols;
float values[nCol][nLin];
// Fill Array with '-1'
for(int c=0; c<nCol; c++){
for(int l=0; l<nLin; l++) {
values[c][l] = -1;
}
}
//------ Skips the reading of line of values file ------//
getline(inFile, dummyLine);
// reads file to end of *file*, not line
while(!inFile.eof()) {
for(int y=0; y<nLin; y++){
for (int i=0; i<nCol; i++) {
inFile >> values[i][y];
}
i=0;
}
}
const int nValues = countLines;
float Col1[nValues]=-1,
Col2[nValues]=-1,
Col3[nValues]=-1,
Col4[nValues]=-1,
Col5[nValues]=-1;
//------ Put values in specific Arrays ------//
for(int v=0; v<nValues; v++) {
Col1[v] = values[0][v];
Col2[v] = values[1][v];
Col3[v] = values[2][v];
Col4[v] = values[3][v];
Col5[v] = values[4][v];
}
cout << endl;
I want that float Col1[] to be from 1 to nCol, the last one to be float ColnCol[]
The best way IMO would be to use std::vector< std::vector<float> >
You do not need to make different 1D columns as you can manipulate this vector of vector as you want.
Instead you should use std::vector. It is a better choice for dynamic size allocation of a data type.