I'm trying to read in pixel data from a 16 bit dpx file that is an extension from a previous git repo (as it only supports 10 bit).
This is the dpx format summary
I'm utilizing this header and cpp to deal with the header info and getting that sort of data.
Note that the variables _pixelOffset, _width, _height, and _channels are based off header information of the dpx. pPixels is a float* array:
#include <iostream>
#include <fstream>
#include <dpxHeader.h>
//First read the file as binary.
std::ifstream _in(_filePath.asChar(), std::ios_base::binary);
// Seek to the pixel offset to start reading where the pixel data starts.
if (!_in.seekg (_pixelOffset, std::ios_base::beg))
{
std::cerr << "Cannot seek to start of pixel data " << _filePath << " in DPX file.";
return MS::kFailure;
}
// Create char to store data of width length of the image
unsigned char *rawLine = new unsigned char[_width * 4]();
// Iterate over height pixels
for (int y = 0; y < _height; ++y)
{
// Read full pixel data for width.
if (!_in.read ((char *)&rawLine[0], _width * 4))
{
std::cerr << "Cannot read scan line " << y << " " << "from DPX file " << std::endl;
return MS::kFailure;
}
// Iterator over width
for (int x = 0; x < _width; ++x)
{
// We do this to flip the image because it's flipped vertically when read in
int index = ((_height - 1 - y) * _width * _channels) + x * _channels;
unsigned int word = getU32(rawLine + 4 * x, _byteOrder);
pPixels[index] = (((word >> 22) & 0x3ff)/1023.0);
pPixels[index+1] = (((word >> 12) & 0x3ff)/1023.0);
pPixels[index+2] = (((word >> 02) & 0x3ff)/1023.0);
}
}
delete [] rawLine;
This currently works for 10 bit files, but as I am new the bitwise operations I'm not completely sure how to extend this to 12 and 16 bit. Anyone have any clues or a proper direction to me in?
This file format is somewhat comprehensive, but if you are only targeting a known subset it shouldn't be too hard to extend.
From your code sample it appears that you are currently working with three components per pixel, and that the components are filled into 32-bit words. In this mode both 12-bit and 16-bit will store two components per word, according to the specification you've provided. For 12-bit, the upper 4 bits of each component is padding data. You will need three 32-bit words to get six color components to decode two pixels:
...
unsigned int word0 = getU32(rawLine + 6 * x + 0, _byteOrder);
unsigned int word1 = getU32(rawLine + 6 * x + 4, _byteOrder);
unsigned int word2 = getU32(rawLine + 6 * x + 8, _byteOrder);
// First pixel
pPixels[index] = (word0 & 0xffff) / (float)0xffff; // (or 0xfff for 12-bit)
pPixels[index+1] = (word0 >> 16) / (float)0xffff;
pPixels[index+2] = (word1 & 0xffff) / (float)0xffff;
x++;
if(x >= _width) break; // In case of an odd amount of pixels
// Second pixel
pPixels[index+3] = (word1 >> 16) / (float)0xffff;
pPixels[index+4] = (word2 & 0xffff) / (float)0xffff;
pPixels[index+5] = (word2 >> 16) / (float)0xffff;
...
Related
I'm currently working on an assignment where I have to produce a Julia set in C++ in sequential, parallel and OpenCL. I have managed to produce an image but the way I have used colours is very ineffective any ideas on how I could improve the colour section of my code at the moment? below is the sequential section of my code any help in improving how I have set the colours would be much appreciated
void sequentialJulia(const complex<float> C, const UINT size = 1000,
const UINT MAX_ITERATIONS = 100, const float limit = 1.7f) {
int start_s = clock();// starts the timer
// Setup output image
fipImage outputImage;
outputImage = fipImage(FIT_BITMAP, size, size, 24);
UINT bytesPerElement = 3;
BYTE* outputBuffer = outputImage.accessPixels();
vector<int> colors{ 100, 140, 180, 220, 225 };// this sets the intsity of the image, if i was to remove 225 the image would be darker
//vector<int> colors{9, 19, 29, 39, 49 }; //THIS DOESNT WORK DO NOT UNCOMMENT
//RGBQUAD color;
complex<float> Z;
std::cout << "Processing...\n";
for (UINT y = 0; y < size; y++) {
//tracking progress;
cout << y * 100 / size << "%\r";
cout.flush();
for (UINT x = 0; x < size; x++) {
Z = complex<float>(-limit + 2.0f * limit / size * x, -limit + 2.0f * limit / size * y);
UINT i;
for (i = 0; i < MAX_ITERATIONS; i++) {
Z = Z * Z + C;
if (abs(Z) > 2.0f) break;
}
if (i < MAX_ITERATIONS ) { //only changing red byte
// bytes per element 9 = blue
// bytes per element 2 = red
// bytes per element 7 = green
outputBuffer[( y * size + x) * bytesPerElement + 9] = colors[i % 5];
}
}
}
cout << "Saving image...\n";
ostringstream name;
name << "..\\Images\\" << C << " size=" << size << " mIterations=" << MAX_ITERATIONS << " sequential19.png" ;
cout << "saving in: " << name.str().c_str() << "\n";
outputImage.save(name.str().c_str());
cout << "...done\n\n";
int stop_s = clock();
cout << "time: " << (stop_s - start_s) / double(CLOCKS_PER_SEC) * 1000 << endl;// stops the timer once code has executed
}
As far as I remember, fractal generators from the early 90's (e.g.: Fractint) used the iteration-bailout index as an index into a table of 256 Red-Green-Blue colours (This was a common limit, as most displays back then were limited to a colour palette of this size anyway.)
So maybe you could define a table of RGB-colours, then lookup on these up exactly how you perform the colors[i % 5]; now, except it would output a RGB-triple of colours[i % TABLE_SIZE].red, .green, .blue. I think it would be best to load your palette in from a separate file.
I've always wondered what a fractal with a 1000-entry colour palette might look like. Quite pretty I think.
EDIT: IIRC Fractint had a palette editing mode, and could save them to files.
In addition to the excellent idea of using a look-up table, you can also interpolate between values in the table instead of just doing a modulus operation to pick one. So you could have a 5-color look-up table, but apply it to hundreds or thousands of iterations by linearly interpolating between the 5 colors. For example, if you have a maximum iteration of 256 and your current calculation takes 168 iterations to escape to infinity, and you have a 5-color look-up table, you could do this to get a color:
float lookupVal = static_cast<float>((colors.size - 1) * i) / MAX_ITERATIONS;
int lookupIndex = static_cast<int>(floor(lookupValue));
float fraction = lookupVal - floor(lookupVal);
float colorF = static_cast<float>(colors [ lookupIndex ]) + fraction * static_cast<float>(colors [ lookupIndex + 1 ] - colors [ lookupIndex ]);
uint8_t color = static_cast<uint8_t>(colorF);
If your look-up table had RGB values instead of just grayscale, you would need to calculate colorF and color for each color channel (red, green, and blue).
I'm using the lodePNG library to encode a png image and change the LSB of the pixels with an imported txt file. I've compiled the program but I'm not sure if the PNG file is actually being encoded according to my bitwise operation.
The lodePNG library decodes/encodes from a PNG image and stores the pixels in the vector "image", 4 bytes per pixel, ordered RGBARGBA...,
void decodeOneStep(const char* filename)
{
unsigned width, height;
//decode
unsigned error = lodepng::decode(image, width, height, filename);
//if there's an error, display it
if (error) std::cout << "decoder error " << error << ": " <<
lodepng_error_text(error) << std::endl;
}
The program takes a command line argument of the text file and the PNG file. I have not included error-checking for arguments yet.
int const MAX_SIZE = 100;
std::vector<unsigned char> image;
int main(int argc, char *argv[])
{
const char* filename;
char* textArray = new char[MAX_SIZE];
std::ifstream textfile;
textfile.open(argv[1]);
int numCount = 0;
while (!textfile.eof() && numCount < MAX_SIZE)
{
textfile.get(textArray[numCount]); //reading single character from file to array
numCount++;
}
textfile.close();
filename = argv[2];
decodeOneStep(filename);
unsigned width = 512, height = 512;
image.resize(width * height * 4);
int pixCount = 0;
for (int i = 0; i < numCount - 1; i++) {
std::cout << textArray[i];
for (int j = 0; j < 8; j++) {
std::cout << ((textArray[i]) & 1); //used to see actual bit value.
image[pixCount++] |= ((textArray[i]) & 1);
(textArray[i]) >>= 1;
}
std::cout << std::endl;
}
encodeOneStep(filename, image, width, height);
In the for-loop, I am going through each pixel in the vector and replacing LSB with a bit from the char. Since a char is 8 bytes, the for-loop loops 8 times. This program should work for most PNG images and texts not exceeding size, but I'm not sure the bitwise operation is actually doing anything. Also, how would I be able to shift the bits so that we store the char bits from MSB to LSB? I feel like I'm understanding something wrong with how the pixel values (bits) are stored in the array.
EDIT: Test i've run on the new bit-operation:
for (int j = 7; j >= 0; j--) {
//These tests were written to see if the 4-bits of the pixels were actually being replaced.
//The LSB of the pixel bits are replaced with the MSB of the text character.
std::cout <<"Initial pixel 4-bits: " << std::bitset <4>(image[pixCount]) << " ";
std::cout << "MSB of char: " << ((textArray[i] >> j) & 0x01) << " ";
std::cout << "Pixel LSB replaced: " << ((image[pixCount] & mask) | ((textArray[i] >> j) & 0x01)) << " ";
image[pixCount] = (image[pixCount] & mask) | ((textArray[i] >> j) & 0x01);
pixCount++;
std::cout << std::endl;
}
Test Result:
For char 'a'
Initial pixel 4-bits : 0000 MSB: 0 Pixel LSB replaced: 0
Initial pixel 4-bits : 0001 MSB: 1 Pixel LSB replaced: 1
Initial pixel 4-bits : 0001 MSB: 1 Pixel LSB replaced: 1
Initial pixel 4-bits : 0000 MSB: 0 Pixel LSB replaced: 0
Initial pixel 4-bits : 0000 MSB: 0 Pixel LSB replaced: 0
Initial pixel 4-bits : 0000 MSB: 0 Pixel LSB replaced: 0
Initial pixel 4-bits : 0000 MSB: 0 Pixel LSB replaced: 0
Initial pixel 4-bits : 0001 MSB: 1 Pixel LSB replaced: 1
You first need to clear out the lsb of the pixel before embedding your secret bit.
unsigned char mask = 0xfe; // in binary 11111110
// and in your loop
image[pixCount] = (image[pixCount] & mask) | (textArray[i] & 1);
pixCount++;
If you want to embed the bits of each character of the secret from the most to the least significant bit, you want to count down the j loop.
for (int j = 7; j >= 0; j--) {
image[pixCount] = (image[pixCount] & mask) | ((textArray[i] >> j) & 0x01);
pixCount++;
}
Edit: To explain the code above, image[pixCount] & mask is an AND operator between your pixel and the chosen mask value (1111110 in binary), so the result is that the lsb is cleared.
(textArray[i] >> j) & 0x01 shifts your char to the left by j and only keeps the lsb. If you map out the maths, this is what you get
// assume our character has the bits `abcdefgh`
j = 7
(character >> j) & 0x01 = 0000000a & 0x01 = a
j = 6
(character >> j) & 0x01 = 000000ab & 0x01 = b
j = 5
(character >> j) & 0x01 = 00000abc & 0x01 = c
// and so on
j = 0
(character >> j) & 0x01 = abcdefgh & 0x01 = h
I have the most strange problem here... I'm using the same code(copy-paste) from Linux in Windows to READ and WRITE and BMP image. And from some reason in Linux every thing works perfectly fine, but when I'm coming to Windows 10 from some I can't open that images and I've receive an error message how said something like this:
"It looks like we don't support this file format."
Do you have any idea what should I do? I will put the code below.
EDIT:
I've solved the padding problem and now it's write the images but they are completely white, any idea why? I've update the code also.
struct BMP {
int width;
int height;
unsigned char header[54];
unsigned char *pixels;
int size;
int row_padded;
};
void writeBMP(string filename, BMP image) {
string fileName = "Output Files\\" + filename;
FILE *out = fopen(fileName.c_str(), "wb");
fwrite(image.header, sizeof(unsigned char), 54, out);
unsigned char tmp;
for (int i = 0; i < image.height; i++) {
for (int j = 0; j < image.width * 3; j += 3) {
// Convert (B, G, R) to (R, G, B)
tmp = image.pixels[j];
image.pixels[j] = image.pixels[j + 2];
image.pixels[j + 2] = tmp;
}
fwrite(image.pixels, sizeof(unsigned char), image.row_padded, out);
}
fclose(out);
}
BMP readBMP(string filename) {
BMP image;
string fileName = "Input Files\\" + filename;
FILE *f = fopen(fileName.c_str(), "rb");
if (f == NULL)
throw "Argument Exception";
fread(image.header, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
image.width = *(int *) &image.header[18];
image.height = *(int *) &image.header[22];
image.row_padded = (image.width * 3 + 3) & (~3);
image.pixels = new unsigned char[image.row_padded];
unsigned char tmp;
for (int i = 0; i < image.height; i++) {
fread(image.pixels, sizeof(unsigned char), image.row_padded, f);
for (int j = 0; j < image.width * 3; j += 3) {
// Convert (B, G, R) to (R, G, B)
tmp = image.pixels[j];
image.pixels[j] = image.pixels[j + 2];
image.pixels[j + 2] = tmp;
}
}
fclose(f);
return image;
}
In my point of view this code should be cross-platform... But it's not... why?
Thanks for help
Check the header
The header must start with the following two signature bytes: 0x42 0x4D. If it's something different a third party application will think that this file doesn't contain a bmp picture despite the .bmp file extension.
The size and the way pixels are stored is also a little bit more complex than what you expect: you assume that the number of bits per pixels is 24 and no no compression is used. This is not guaranteed. If it's not the case, you might read more data than available, and corrupt the file when writing it back.
Furthermore, the size of the header depends also on the BMP version you are using, which you can detect using the 4 byte integer at offset 14.
Improve your code
When you load a file, check the signature, the bmp version, the number of bits per pixel and the compression. For debugging purpose, consider dumping the header to check it manually:
for (int i=0; i<54; i++)
cout << hex << image.header[i] << " ";`
cout <<endl;
Furthermore, when you fread() check that the number of bytes read correspond to the size you wanted to read, so to be sure that you're not working with uninitialized buffer data.
Edit:
Having checked the dump, it appears that the format is as expected. But verifying the padded size in the header with the padded size that you have calculated it appears that the error is here:
image.row_padded = (image.width * 3 + 3) & (~3); // ok size of a single row rounded up to multiple of 4
image.pixels = new unsigned char[image.row_padded]; // oops ! A little short ?
In fact you read row by row, but you only keep the last one in memory ! This is different of your first version, where you did read the full pixels of the picture.
Similarly, you write the last row repeated height time.
Reconsider your padding, working with the total padded size.
image.row_padded = (image.width * 3 + 3) & (~3); // ok size of a single row rounded up to multiple of 4
image.size_padded = image.row_padded * image.height; // padded full size
image.pixels = new unsigned char[image.size_padded]; // yeah !
if (fread(image.pixels, sizeof(unsigned char), image.size_padded, f) != image.size_padded) {
cout << "Error: all bytes couldn't be read"<<endl;
}
else {
... // process the pixels as expected
}
...
I have a binary file which has the following format :
# vtk DataFile Version 4.0
vtk output
BINARY
DATASET POLYDATA
POINTS 10000 double
?�T�����?����h�?�T�����?���� <-- 10000 double values (in binary format) follow separated by space and new line after every 9 values.
I want to read this file byte by byte so that I can store these double values in my array. I have the following code which loads this file into a char *buffer array. Now I want to know how to proceed further?
#include<iostream>
#include<fstream>
#include<sstream>
#include<stdlib.h>
#include<string>
using namespace std;
int main () {
ifstream is ("Data_binary.vtk", ifstream::binary);
if (is) {
// get length of file:
is.seekg (0, is.end);
unsigned long length = is.tellg();
is.seekg (0, is.beg);
char * buffer = new char [length+1];
buffer[length] = '\0';
cout << "Reading " << length << " characters... ";
// read data as a block:
is.seekg(0, is.beg);
is.read (buffer,length);
if (is)
cout << "all characters read successfully." << endl;
else
cout << "error: only " << is.gcount() << " could be read";
is.close();
}
return 0;
}
In ASCII format, an example file would look like the following :
# vtk DataFile Version 4.0
vtk output
ASCII
DATASET POLYDATA
POINTS 18 double
.1 .2 .3 1.4 11.55 1 0 8e-03 5.6
1.02 2.2 3.3 .1 .5 0.001 4e-07 4.2 1.55
For binary file, the double values are present in binary. I want to get double values from binary format.
Use this function.
/*
* read a double from a stream in ieee754 format regardless of host
* encoding.
* fp - the stream
* bigendian - set to if big bytes first, clear for little bytes
* first
*
*/
double freadieee754(FILE *fp, int bigendian)
{
unsigned char buff[8];
int i;
double fnorm = 0.0;
unsigned char temp;
int sign;
int exponent;
double bitval;
int maski, mask;
int expbits = 11;
int significandbits = 52;
int shift;
double answer;
/* read the data */
for (i = 0; i < 8; i++)
buff[i] = fgetc(fp);
/* just reverse if not big-endian*/
if (!bigendian)
{
for (i = 0; i < 4; i++)
{
temp = buff[i];
buff[i] = buff[8 - i - 1];
buff[8 - i - 1] = temp;
}
}
sign = buff[0] & 0x80 ? -1 : 1;
/* exponet in raw format*/
exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4);
/* read inthe mantissa. Top bit is 0.5, the successive bits half*/
bitval = 0.5;
maski = 1;
mask = 0x08;
for (i = 0; i < significandbits; i++)
{
if (buff[maski] & mask)
fnorm += bitval;
bitval /= 2.0;
mask >>= 1;
if (mask == 0)
{
mask = 0x80;
maski++;
}
}
/* handle zero specially */
if (exponent == 0 && fnorm == 0)
return 0.0;
shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */
/* nans have exp 1024 and non-zero mantissa */
if (shift == 1024 && fnorm != 0)
return sqrt(-1.0);
/*infinity*/
if (shift == 1024 && fnorm == 0)
{
#ifdef INFINITY
return sign == 1 ? INFINITY : -INFINITY;
#endif
return (sign * 1.0) / 0.0;
}
if (shift > -1023)
{
answer = ldexp(fnorm + 1.0, shift);
return answer * sign;
}
else
{
/* denormalised numbers */
if (fnorm == 0.0)
return 0.0;
shift = -1022;
while (fnorm < 1.0)
{
fnorm *= 2;
shift--;
}
answer = ldexp(fnorm, shift);
return answer * sign;
}
}
it's a lot, but it's just a snippet to cut and paste, and you never need to worry about binary floating point formats again. It simply reads an IEEE 754 double, regardless of host floating point format.
There's a twin which writes
Instead of reading into a char * buffer, read into a double * buffer. Casting to/from char * is allowed just for this purpose.
vector<double> buffer;
buffer.resize(n);
is.read(reinterpret_cast<char *>(&buffer[0]), n * sizeof(buffer[0]));
You'll need to read the non-binary data first so that the file pointer is located at the start of the binary data. This is defined as coming immediately after the newline character of the last field in the header.
The spec doesn't appear to mandate little-endian or big-endian format, it expects you to know based on the source of the file. If you're lucky the format will match the machine you're using to read the file and no conversion will be necessary. Otherwise you'll need to do a byte swap:
void ByteSwap(double * p)
{
char * pc = reinterpret_cast<char *>(p);
std::swap(pc[0], pc[7]);
std::swap(pc[1], pc[6]);
std::swap(pc[2], pc[5]);
std::swap(pc[3], pc[4]);
}
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;