I'm having some troubles with pnm files (which is kinda obvious or else I wouldn't be posting here XD). Thing is, my teacher asked us to develop a simple pnm reader in binary mode then print it to the screen. I'm using libEGL (a framework avaliable here). My problem is that it works only with these two images and fails with any other one.
With birch.pnm and checkers.pnm it works, but cathedral.pnm, cotton.pnm and fish_tile.pnm it just simple enters an infinite loop or throws and error.
The images are avaliable here
My code is as follows:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include "engcomp_glib.h"
using namespace std;
struct RGB{
char red, green, blue;
};
int main(int argc, char* argv[]){
RGB **image;
RGB pixel;
//ifstream _file("..\\bin\\birch.pnm");
ifstream _file("..\\bin\\checkers.pnm");
//ifstream _file("..\\bin\\cotton.pnm");
//ifstream _file("..\\bin\\cathedral.pnm");
//ifstream _file("..\\bin\\fish_tile.pnm");
string type, maxColor;
int width, height;
if(_file){
_file >> type;
if(type != "P6")
cout << "Error! File type is not allowed." << endl;
_file >> width >> height >> maxColor;
_file.close();
egl_inicializar(width, height, true);
image = new RGB *[height];
for(int row = 0; row < height; row++)
image[row] = new RGB[width];
//Working 8D
//_file.open("..\\bin\\birch.pnm", ios::binary);
_file.open("..\\bin\\checkers.pnm", ios::binary);
//Not working D:<
//_file.open("..\\bin\\cathedral.pnm", ios::binary);
//_file.open("..\\bin\\fish_tile.pnm", ios::binary);
//_file.open("..\\bin\\cotton.pnm", ios::binary);
//imagem img; img.carregar("..\\bin\\birch.pnm");
_file.seekg(0, _file.end);
int size = _file.tellg();
int currentSize = 0, counter = 0;
char byte;
_file.seekg(0, _file.beg);
do{
_file.read(reinterpret_cast<char *> (&byte), sizeof(char));
if(byte == 10 || byte == 13)
counter++;
}while(counter < 3);
int rows = 0, columns = 0;
while(size != currentSize){
_file.read(reinterpret_cast<char *> (&pixel), sizeof(RGB));
if(rows < height && columns < width){
image[rows][columns] = pixel;
rows++;
}
else if(rows == height){
rows = 0;
columns++;
image[rows][columns] = pixel;
rows++;
}
//else if(columns >= width)
//currentSize = size;
currentSize = _file.tellg();
}
_file.close();
while(!key[SDLK_ESCAPE]){
for(int row = 0; row < height; row++)
for(int column = 0; column < width; column++)
//egl_pixel(row, column, image[row][column].red, image[row][column].green, image[row][column].blue);
egl_pixel(column, row, image[column][row].red, image[column][row].green, image[column][row].blue);
//img.desenha(0, 0);
egl_desenha_frame(false);
}
}
egl_finalizar();
return 0;
}
It doesn't make sense, as it works for two of them, should work form them all
I opened them all in a text editor and they have the header, so the problem is not there. What am I doing wrong? My colleague wrote a code that stores the pixels into an array with size [height * width] and can read almost all of the images but cathedral.pnm.
Thanks for the patience and help :)
The specs for the pnm state that the values in the header are separated by whitespace, usually newlines, but they could also be spaces or tabs (or something else I can't think of at the moment ;). The cathedral file for instance has a space as separator.
And you're reading the files top to bottom, left to right, in stead of left to right, top to bottom, as per the specs.
And if you want to be really correct, if maxColor is not less than 256, you should read shorts in stead of chars.
You can find the specs here by the way:
http://netpbm.sourceforge.net/doc/ppm.html
Good luck!
Related
The assignment is to manipulate a PPM file and to store the RGB values to an array. I am able to grab the header of the file and get P6, the width, the height, and the maximum RGB value but I cannot get the RGB values themselves. I know that pixel data is separated by a white space but stream input still cannot find it. We have not learned classes or pointers yet so can it be done without using either?
#include <fstream>
#include <iostream>
#include <string>
const int WID = 648;
const int HEIGHT = 486;
int main() {
int arr[HEIGHT * WID * 3];
unsigned char r, g, b;
std::string header;
int wid, hei, max;
std::ifstream fin;
fin.open("file.ppm");
if (fin.fail()) {
std::cout << "file did not open: << std::endl;
}
fin >> header;
fin >> wid >> hei >> max;
for (int i; i < HEI * WID * 3; ++i) {
r = arr[i];
g = arr[i + 1];
b = arr[i + 2];
}
std::cout << header << wid << hei << max << r << g << b;
fin.close();
return 0;
}
This code will display the P6, wid, hei, and max but not RGB pixels. If I change the types around on unsigned r g b than I can get a 0 or a symbol. If use a string to capture everything after max I get a ton of random symbols. Help would be greatly appreciated I've been stuck for a while. Any help will be appreciated.
This is my first question at Stack overflow. I'm new to Image processing and to C++, I'm working with bitmap files now. While creating a Bitmap file using C++, the file can not be opened using any viewers. I used a hex editor to view the file and there were random data in Image size field in the info header. After editing it in the hex editor, the bitmap is view-able. I don't know what is wrong with the code.
The header (bitmap.h) I created is as follows
#include<iostream>
#include<fstream>
using namespace std;
struct BmpSignature
{
unsigned char data[2];
BmpSignature(){ data[0] = data[1] = 0; }
};
struct BmpHeader
{
unsigned int fileSize; // this field gives out the size of the full Image includong the headers. it is of 4 byte in width
unsigned short reserved1; // this field is reserved. it is 2 byte in width
unsigned short reserved2; //This field is also reserved. it is 2 byte in width
unsigned int dataOffset; // this gives the starting location of the starting of the image data array
};
struct BmpInfoHeader
{
unsigned int size; // this field gives the size of the Bitmap info Header. This is 4 byte in width
unsigned int width; // this gives the width of the image
unsigned int height; // this gives the height of the image
unsigned short planes; //this gives the number of planes in the image
unsigned short bitCount; // this gives the number of bits per pixels in the image. for ex. like 24 bits, 8 bits
unsigned short compression; // gives info whether the image is compressed or not
unsigned int ImageSize; // gives the actual size of the image
unsigned int XPixelsPerM; // give the number of pixels in the X direction. It is usually 2834
unsigned int YPixelsPerM;// give the number of pixels in the Y direction. It is usually 2834
unsigned int ColoursUsed; // this field gives the number of Colours used in the Image
unsigned int ColoursImp; // gives the number of Important colours in the image. if all colours are important it is usually 0
};
the cpp file I created is as follows (Create_Bitmap.cpp)
#include"bitmap.h"
#include<cmath>
#include<fstream>
using namespace std;
int main()
{
ofstream fout;
fout.open("D:/My Library/test1.bmp", ios::out |ios::binary);
BmpHeader header;
BmpInfoHeader infoheader;
BmpSignature sign;
infoheader.size = 40;
infoheader.height = 15;
infoheader.width = 15;
infoheader.planes = 1;
infoheader.bitCount = 8;
infoheader.compression = 0;
infoheader.ImageSize = 0;
infoheader.XPixelsPerM = 0;
infoheader.YPixelsPerM = 0;
infoheader.ColoursUsed = 0;
infoheader.ColoursImp = 0;
unsigned char* pixelData;
int pad=0;
for (int i = 0; i < infoheader.height * infoheader.width; i++)
{
if ((i) % 16 == 0) pad++;
}
int arrsz = infoheader.height * infoheader.width + pad;
pixelData = new unsigned char[arrsz];
unsigned char* offsetData;
offsetData = new unsigned char[4 * 256];
int xn = 0;
int yn = 4 * 256;
for (int i = 0; i < yn; i+=4)
{
offsetData[i] = xn;
offsetData[i+1] = xn;
offsetData[i+2] = xn;
offsetData[i+3] = 0;
xn++;
}
int num = 0;
for (int i = 0; i < arrsz; i++)
{
pixelData[i] = i;
}
sign.data[0] = 'B'; sign.data[1] = 'M';
header.fileSize = 0;
header.reserved1 = header.reserved2 = 0;
header.dataOffset = 0;
fout.seekp(0, ios::beg);
fout.write((char*)&sign, sizeof(sign));
fout.seekp(2, ios::beg);
fout.write((char*)&header, sizeof(header));
fout.seekp(14, ios::beg);
fout.write((char*)&infoheader, sizeof(infoheader));
fout.seekp(54, ios::beg);
fout.write((char*)offsetData, yn);
fout.write((char*)pixelData, arrsz);
fout.close();
delete[] pixelData;
delete[] offsetData;
return 0;
}
I have attached the screenshot of the created bmp file in a hex editor with the image size field selected
Bitmap Image opened in Hex Editor
Upon replacing the contents in the field using hex editor the Bitmap file can be viewed with an Image Viewer. I don't know what is wrong in this code
So you want to write in BMP format? Remember that compiler may insert padding in C++ POD structs. You may need use some compiler pragma to pack the struct. Also make sure you use little-endian for all integers, but that should be OK since you are on Windows, assuming an x86.
Is there a way to read .pfm files in OpenCV?
Thank you very much for any suggestions!
PFM is an uncommon image format and I don't know why the Middlebury dataset chose to use that, probably because it uses floating point values.
Anyway I was able to read the images with OpenCV:
import numpy as np
import cv2
groundtruth = cv2.imread('disp0.pfm', cv2.IMREAD_UNCHANGED)
Note the IMREAD_UNCHANGED flag. Somehow it is able to read all the correct values even if OpenCV does not support it.
But wait a minute: inf values are commonly used to set INVALID pixel disparity, so to properly display the image you should do:
# Remove infinite value to display
groundtruth[groundtruth==np.inf] = 0
# Normalize and convert to uint8
groundtruth = cv2.normalize(groundtruth, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
# Show
cv2.imshow("groundtruth", groundtruth)
cv2.waitKey(0)
cv2.destroyAllWindows()
Based on the description of the ".pfm" file formate (see http://netpbm.sourceforge.net/doc/pfm.html), I wrote the following read/write functions, which only depend standard C/C++ library. It is proved to work well on reading/writing the pfm file, like, the ground truth disparity ".pfm" files from MiddleBury Computer Vision (see http://vision.middlebury.edu/stereo/submit3/).
#ifndef _PGM_H_
#define _PGM_H_
#include <fstream>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <bitset> /*std::bitset<32>*/
#include <cstdio>
enum PFM_endianness { BIG, LITTLE, ERROR};
class PFM {
public:
PFM();
inline bool is_little_big_endianness_swap(){
if (this->endianess == 0.f) {
std::cerr << "this-> endianness is not assigned yet!\n";
exit(0);
}
else {
uint32_t endianness = 0xdeadbeef;
//std::cout << "\n" << std::bitset<32>(endianness) << std::endl;
unsigned char * temp = (unsigned char *)&endianness;
//std::cout << std::bitset<8>(*temp) << std::endl;
PFM_endianness endianType_ = ((*temp) ^ 0xef == 0 ?
LITTLE : (*temp) ^ (0xde) == 0 ? BIG : ERROR);
// ".pfm" format file specifies that:
// positive scale means big endianess;
// negative scale means little endianess.
return ((BIG == endianType_) && (this->endianess < 0.f))
|| ((LITTLE == endianType_) && (this->endianess > 0.f));
}
}
template<typename T>
T * read_pfm(const std::string & filename) {
FILE * pFile;
pFile = fopen(filename.c_str(), "rb");
char c[100];
if (pFile != NULL) {
fscanf(pFile, "%s", c);
// strcmp() returns 0 if they are equal.
if (!strcmp(c, "Pf")) {
fscanf(pFile, "%s", c);
// atoi: ASCII to integer.
// itoa: integer to ASCII.
this->width = atoi(c);
fscanf(pFile, "%s", c);
this->height = atoi(c);
int length_ = this->width * this->height;
fscanf(pFile, "%s", c);
this->endianess = atof(c);
fseek(pFile, 0, SEEK_END);
long lSize = ftell(pFile);
long pos = lSize - this->width*this->height * sizeof(T);
fseek(pFile, pos, SEEK_SET);
T* img = new T[length_];
//cout << "sizeof(T) = " << sizeof(T);
fread(img, sizeof(T), length_, pFile);
fclose(pFile);
/* The raster is a sequence of pixels, packed one after another,
* with no delimiters of any kind. They are grouped by row,
* with the pixels in each row ordered left to right and
* the rows ordered bottom to top.
*/
T* tbimg = (T *)malloc(length_ * sizeof(T));// top-to-bottom.
//PFM SPEC image stored bottom -> top reversing image
for (int i = 0; i < this->height; i++) {
memcpy(&tbimg[(this->height - i - 1)*(this->width)],
&img[(i*(this->width))],
(this->width) * sizeof(T));
}
if (this->is_little_big_endianness_swap()){
std::cout << "little-big endianness transformation is needed.\n";
// little-big endianness transformation is needed.
union {
T f;
unsigned char u8[sizeof(T)];
} source, dest;
for (int i = 0; i < length_; ++i) {
source.f = tbimg[i];
for (unsigned int k = 0, s_T = sizeof(T); k < s_T; k++)
dest.u8[k] = source.u8[s_T - k - 1];
tbimg[i] = dest.f;
//cout << dest.f << ", ";
}
}
delete[] img;
return tbimg;
}
else {
std::cout << "Invalid magic number!"
<< " No Pf (meaning grayscale pfm) is missing!!\n";
fclose(pFile);
exit(0);
}
}
else {
std::cout << "Cannot open file " << filename
<< ", or it does not exist!\n";
fclose(pFile);
exit(0);
}
}
template<typename T>
void write_pfm(const std::string & filename, const T* imgbuffer,
const float & endianess_) {
std::ofstream ofs(filename.c_str(), std::ifstream::binary);
// ** 1) Identifier Line: The identifier line contains the characters
// "PF" or "Pf". PF means it's a color PFM.
// Pf means it's a grayscale PFM.
// ** 2) Dimensions Line:
// The dimensions line contains two positive decimal integers,
// separated by a blank. The first is the width of the image;
// the second is the height. Both are in pixels.
// ** 3) Scale Factor / Endianness:
// The Scale Factor / Endianness line is a queer line that jams
// endianness information into an otherwise sane description
// of a scale. The line consists of a nonzero decimal number,
// not necessarily an integer. If the number is negative, that
// means the PFM raster is little endian. Otherwise, it is big
// endian. The absolute value of the number is the scale
// factor for the image.
// The scale factor tells the units of the samples in the raster.
// You use somehow it along with some separately understood unit
// information to turn a sample value into something meaningful,
// such as watts per square meter.
ofs << "Pf\n"
<< this->width << " " << this->height << "\n"
<< endianess_ << "\n";
/* PFM raster:
* The raster is a sequence of pixels, packed one after another,
* with no delimiters of any kind. They are grouped by row,
* with the pixels in each row ordered left to right and
* the rows ordered bottom to top.
* Each pixel consists of 1 or 3 samples, packed one after another,
* with no delimiters of any kind. 1 sample for a grayscale PFM
* and 3 for a color PFM (see the Identifier Line of the PFM header).
* Each sample consists of 4 consecutive bytes. The bytes represent
* a 32 bit string, in either big endian or little endian format,
* as determined by the Scale Factor / Endianness line of the PFM
* header. That string is an IEEE 32 bit floating point number code.
* Since that's the same format that most CPUs and compiler use,
* you can usually just make a program use the bytes directly
* as a floating point number, after taking care of the
* endianness variation.
*/
int length_ = this->width*this->height;
this->endianess = endianess_;
T* tbimg = (T *)malloc(length_ * sizeof(T));
// PFM SPEC image stored bottom -> top reversing image
for (int i = 0; i < this->height; i++) {
memcpy(&tbimg[(this->height - i - 1)*this->width],
&imgbuffer[(i*this->width)],
this->width * sizeof(T));
}
if (this->is_little_big_endianness_swap()) {
std::cout << "little-big endianness transformation is needed.\n";
// little-big endianness transformation is needed.
union {
T f;
unsigned char u8[sizeof(T)];
} source, dest;
for (int i = 0; i < length_; ++i) {
source.f = tbimg[i];
for (size_t k = 0, s_T = sizeof(T); k < s_T; k++)
dest.u8[k] = source.u8[s_T - k - 1];
tbimg[i] = dest.f;
//cout << dest.f << ", ";
}
}
ofs.write((char *)tbimg, this->width*this->height * sizeof(T));
ofs.close();
free(tbimg);
}
inline float getEndianess(){return endianess;}
inline int getHeight(void){return height;}
inline int getWidth(void){return width;}
inline void setHeight(const int & h){height = h;}
inline void setWidth(const int & w){width = w;}
private:
int height;
int width;
float endianess;
};
#endif /* PGM_H_ */
Forgive me to leave lots of useless comments in the code.
A simple example shows the write/read:
int main(){
PFM pfm_rw;
string temp = "img/Motorcycle/disp0GT.pfm";
float * p_disp_gt = pfm_rw.read_pfm<float>(temp);
//int imgH = pfm_rw.getHeight();
//int imgW = pfm_rw.getWidth();
//float scale = pfm_rw.getEndianess();
string temp2 = "result/Motorcycle/disp0GT_n1.pfm";
pfm_rw.write_pfm<float>(temp2, p_disp_gt, -1.0f);
return 1;
}
As far as I know, OpenCV doesn't support to read PFM files directly.
You can refer to the code snippet here for a simple PFM reader, which will enable you to read PFM files into COLOR *data with COLOR defined as follows:
typedef struct {
float r;
float g;
float b;
} COLOR;
I'm trying to get a Kinect to find a red shape and seem to be having difficulty comparing the actual RGBA values from each pixel. The code should be pretty self-explanatory. The text output file is just for me to get a quick representation of what's getting assigned to the array for future calculations.
void CColorBasics::getRGB (BYTE* bitPointer, LONG width, LONG height)
{
int red, green, blue, alpha;
int arr[640][480];
ofstream myfile;
myfile.open ("asdf.txt");
for (int i=0; i<height; i ++)
{
for (int ii=0; ii<width; ii = ii = ii+4)
{
blue = (int)bitPointer[ii];
green = (int)bitPointer[ii+1];
red = (int)bitPointer[ii+2];
alpha = (int)bitPointer[ii+3];
//calculate differences between BG and R and store result in an array
if (red > (green+blue) && red >= 150)
{
arr[i][ii] = 1;
myfile << "1";
}
else
{
arr[i][ii] = 0;
myfile << "0";
}
}
myfile << "\n";
}
myfile.close();
}
Instead of getting a pattern that resembles anything close to what the RGBA sees, I get some semi-random stuff that seems to have to do with how much red is in the picture, but certainly isn't formatted correctly. Essentially, if a pixel is "significantly red", I would like a '1' to be stored in that pixel's location in the array[][] - otherwise, store a '0' in that spot. Tips would definitely be appreciated!
friends!
Programming language is C++.
I have a byte matrix
unsigned char Map[Height][Width];
I initialize it with zeros.
Then I read byte matrix from text file.
Of course, text file can be larger, than my matrix is. In this case I must read information from file in such way:
Text file has extra information. I don't need it.
In another case Matrix can be larger then information within text file:
In this case program must read all the information from file. The part of matrix that didn't get information from file is already initialized. It's OK.
What is the best way to read information from file to the matrix in my case?
I tried to use fgets, but it reads all information from file in consecutive way - byte after byte. But I don't need to read extra bytes. Of course, I can read file byte after byte and check counter. But I am sure that this is'nt best solution. Does a better solution exist?
The formatting of my text file is'nt relevant here. I read information from file like bytes field.
Assuming your text files look something like this:
3254352
6536543
8875687
4315254
5345435
1212122
the solution with standard library streams would be something like this:
#include <fstream>
#include <string>
int main()
{
std::ifstream matrix_file("matrix.txt");
const size_t Width = 5;
const size_t Height = 5;
unsigned char Map[Width][Height];
size_t line_count = 0;
std::string line;
while (std::getline(matrix_file, line) && line_count < Height) {
line.resize(Width);
for (size_t i = 0; i < line.length(); ++i)
Map[i][line_count] = line[i];
++line_count;
}
if (line_count < Height) {
// the file didn't have enough lines to fill our matrix,
// so we'll need to fill the rest with zeroes here
}
}
The line.resize(Width); line above will automatically put null characters in the string when Width is greater than the length of the line in the file. Also, don't forget to check if stream was opened succesfully before you try to read from it (fstream::is_open(), for example). I skipped that check for brevity.
If you can read all the lines with fgets(), you can copy the i-th line character by character to the i-th row of your table. Stop when you encounter the end of the line or when you have copied Width characters. Be careful that fgets() might leave a NL character at the end of the line that you might want to remove (NL + CR on Windows).
Repeat the above procedure until you have read Height lines or reached the end of the file.
My solution is basically C (because you say you are not familiar with the C++ functions). You should use standard C++ libraries (fstream, string) if you want a real C++ implementation.
#include <cstdio>
#include <cstring>
#define Height (...)
#define Width (...)
// I assume that each input line in the file can contain at most width * 3 characters.
// Three extra characters for NL, CR, 0.
// Change this if you expect the file to contain longer lines.
#define BUFFER_WIDTH (Width * 3 + 3)
unsigned char Map[Height][Width];
unsigned char line[BUFFER_WIDTH];
// Remove CR, NL at the end of the line.
void clean_line(char *line)
{
int len = strlen(line);
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
{
line[len - 1] = '\0';
len--;
}
}
void read_table(const char *filename)
{
FILE *fp = fopen(filename, "r");
int row = 0;
while (!feof(fp) && row < Height)
{
fgets(line, BUFFER_WIDTH, fp);
clean_line(line);
int len = strlen(line);
int rowLen = len > Width ? Width : len;
for (int col = 0; col < rowLen; col++)
{
Map[row][col] = line[col];
}
row++;
}
fclose(fp);
}
Read the entire file into a buffer:
FILE *fp;
long len;
char *buf;
fp=fopen("thefileyouwanttoread.txt","rb");
fseek(fp,0,SEEK_END); //go to end
len=ftell(fp); //get position at end (length)
fseek(fp,0,SEEK_SET); //go to beg.
buf=(char *)malloc(len); //malloc buffer
fread(buf,len,1,fp); //read into buffer
fclose(fp);
Copy the file into your byte array, check to see which is bigger to determine how to copy:
char *startByteArr;
unsigned char Map[Height][Width];
startByteArr = &Map[0][0];
if (len > Height*Width){
memcpy(startByteArr,buf,Height*Width);
else {
memcpy(startByteArr,buf,len);
}
This assumes that the first dimension is the same though. To account for varying width in the file, you could change the memcpy like:
char *startByteArr;
char *EndLineFile;
int lineLengthFile;
EndLineFile = strstr (buf,"\r\n");
lineLenghtFile = (int)(EndLineFile-buf);
unsigned char Map[Height][Width];
startByteArr = &Map[0][0];
int i;
if (lineLengthFile > Width){
for(i = 0; i < Height;i++){
memcpy(startByteArr+i*Width,buf+i*lineLengthFile,Width);
}
else {
for(i = 0; i < Height;i++){
memcpy(startByteArr+i*Width,buf+i*lineLengthFile,lineLengthFile);
}
}
I believe that would be the fastest way, just grab the whole file into memory in one read, then memcpy the segments you need to a byte array.