What is .raw file format in cuda sample code? - c++

I am working on rendering some volume data via cuda v6.5 sample code - volumeRender. I have some voxel data with different RGB color in another program. I want to render them by outputing them to .raw file format. However, how to output the exact same .raw format as input .raw file in volumeRender is confusing me a lot.
In my knowledgement, a .raw file can contain an array of volume data.
I tried to open .raw file, named "Bucky.raw" , in volumrRender, with text editor, but it didn't work. The text was garbled text. So I have no clue of the format of the .raw file.
Next, I tried to read sample code and figure out how the .cpp file read this format. The only information I get was the volume size. It is
unsigned char * width * height * depth.
Dose that mean each voxel data is represented by only one unsigned char? What does this unsigned char mean? Is it the gray scale of voxel?
I tried to output gray scale of my voxel RGB data, but the result is quite wired like below.
At the top is my voxel data and visualize by PCL. The other is the render result.
Obviously, it's the wrong format for the volumeRender program.
I know there are lots of different formats of .raw files, but I think maybe there is only one kind of .raw format just for cuda sample code.
Does anyone know how to store .raw format just like "Bucky.raw" format?

Does that mean each voxel data is represented by only one unsigned char? What does this unsigned char mean? Is it the gray scale of voxel?
Yes, each voxel is represented by one unsigned char (i.e. VolumeType), the value (0-255) represents the "transmissivity" of the voxel (i.e. the inverse of the density), with a voxel value of 0 being the lowest transmissivity/highest "density" (creating "darker" areas) and voxel value of 255 being low density (creating "brighter" areas).
The storage order has a rapidly varying dimension in x, then y, then z. The actual dimensions of the volume can be easily discovered in the program as volumeSize.width (x), volumeSize.height (y), and volumeSize.depth (z). x represents the horizontal direction, y the vertical, and z represents the direction into or out of the screen.
Does anyone know how to store .raw format just like "Bucky.raw" format?
Here is a program you can use to experiment with writing different kinds of "Bucky.raw" files. The dimensions will be 32,32,32 which is the default values in the program.
If you compile this program, then run it with a command line parameter of 0-4, you can see different patterns when you run the volumeRender sample code.
cat buckywriter.cpp
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
const int sz = 32;
const int method_max = 4;
int myfunc(int x, int y, int z, int method){
switch (method){
case 0:
return x;
break;
case 1:
return y;
break;
case 2:
return z;
break;
case 3:
return x+y;
break;
case 4:
return (x+y)*2*((z+1)/float(sz));
break;
default:
return x;
break;
}
return 0;
}
int main(int argc, char *argv[]){
int method = 0;
if (argc > 1) method = atoi(argv[1]);
ofstream fout;
string foname("Bucky.raw");
fout.open(foname.c_str());
if (!fout) {std::cout << "file open failed" << std::endl; return 1;}
for (int z = 0; z < sz; z++)
for (int y = 0; y < sz; y++)
for (int x = 0; x < sz; x++)
fout << (unsigned char)(myfunc(x, y, z, method));
if (!fout) {std::cout << "file write failed" << std::endl; return 1;}
fout.close();
return 0;
}
$ g++ buckywriter.cpp -o buckywriter
$ ./buckywriter 3
$ ./volumeRender
This is the graphical output for the above buckywriter 3 case, which creates an increasing transmissivity gradient in x and y:

I created a simple example program in Swift using Xcode that will convert a Goxel model into the .raw format used by the NVIDIA CUDA example programs VolumeRender and VolumeFilter.
Build a model in Goxel, export it as .txt
Then run it through the Goxel2Raw program.
Goxel2Raw on Github

Related

How can one write a multi-dimensional vector of image data to an output file?

Question:
Is there a good way to write a 3D float vector of size (9000,9000,4) to an output file in C++?
My C++ program generates a 9000x9000 image matrix with 4 color values (R, G, B, A) for each pixel. I need to save this data as an output file to be read into a numpy.array() (or similar) using python at a later time. Each color value is saved as a float (can be larger than 1.0) which will be normalized in the python portion of the code.
Currently, I am writing the (9000,9000,4) sized vector into a CSV file with 81 million lines and 4 columns. This is slow for reading and writing and it creates large files (~650MB).
NOTE: I run the program multiple times (up to 20) for each trial, so read/write times and file sizes add up.
Current C++ Code:
This is the snippet that initializes and writes the 3D vector.
// initializes the vector with data from 'makematrix' class instance
vector<vector<vector<float>>> colorMat = makematrix->getMatrix();
outfile.open("../output/11_14MidRed9k8.csv",std::ios::out);
if (outfile.is_open()) {
outfile << "r,g,b,a\n"; // writes column labels
for (unsigned int l=0; l<colorMat.size(); l++) { // 0 to 8999
for (unsigned int m=0; m<colorMat[0].size(); m++) { // 0 to 8999
outfile << colorMat[l][m][0] << ',' << colorMat[l][m][1] << ','
<< colorMat[l][m][2] << ',' << colorMat[l][m][3] << '\n';
}
}
}
outfile.close();
Summary:
I am willing to change the output file type, the data structures I used, or anything else that would make this more efficient. Any and all suggestions are welcome!
Use the old C file functions and binary format
auto startT = chrono::high_resolution_clock::now();
ofstream outfile;
FILE* f = fopen("example.bin", "wb");
if (f) {
const int imgWidth = 9000;
const int imgHeight = 9000;
fwrite(&imgWidth, sizeof(imgWidth), 1, f);
fwrite(&imgHeight, sizeof(imgHeight), 1, f);
for (unsigned int i=0; i<colorMat.size(); ++i)
{
fwrite(&colorMat[i], sizeof(struct Pixel), 1, f);
}
}
auto endT = chrono::high_resolution_clock::now();
cout << "Time taken : " << chrono::duration_cast<chrono::seconds>(endT-startT).count() << endl;
fclose(f);
The format is the following :
[ImageWidth][ImageHeight][RGBA][RGBA[RGBA]... for all ImageWidth * ImageHeight pixels.
Your sample ran in 119s in my machine. This code ran in 2s.
But please note that the file will be huge anyway : you are writing the equivalent of two 8K files without any kind of compression.
Besides that, some tips on your code :
Don't use a vector of floats to represent your pixels. They won't have more components than RGBA. Instead create a simple struct with four floats.
You don't need to look through width and height separately. Internally all lines are put sequentially one after the other. It is easier to create a one dimension array of width * height size.

Write 2D array to PGM grayscale image

I'm creating various 2D arrays of sizes from 100x100 to 2000x2000 elements. The values within the arrays can be clamped down to 0 - 255 gray scale and then need to be written to a PGM image in order to visually represent the data.
For example, I'm declaring the arrays globally as:
element case1[100][100];
element is a structure of double pixelValue and a Boolean value (that won't be used when actually writing to the file but is necessary in the program).
In writing to the PGM image, I am having errors considering the FILE *fp in this area of the code when writing after the header:
int *p
for (int x = 0; x < dimension; x++)
{
for (int y = 0; y < dimension; y++)
{ //also doesn't work as: fp << (unsigned char)case1[x][y].pix;
int pix = case1[x][y].pixelValue;
*p = pix;
fp << (unsigned char)*p;
}
}
fclose(fp);
I'm unsure of how to work with the pointer in order to get the pixelValue from each location within the 2D array. I need to be able to iterate through each pixelValue to get the visual representation of the data.
Thank you for your help!
Used fputc() instead so that I could directly insert values instead of using pointers.

C++ create png/bitmap from array of numbers

So i found this link regarding my question, but it is for c#
Create a PNG from an array of bytes
I have a variable int array of numbers.
i will call it "pix[ ]"
for now it can be any size from 3 to 256, later possibly bigger.
What i want to do now, is to convert it into a pixel image.
I am still a noobin c++ so pleas excuse me.
I tried to download some libaries that make working with libpng easier, but they do not seem to be working (ubuntu, code::blocks)
So i have questions in the following:
1) how do you create a new bitmap (which libaries, which command)?
2) how do i fill it with information from "pix[ ]" ?
3) how do i save it?
if it is a repost of a question i am happy about a link also ;)
Here is what i worked out so far, thanks for your help.
int main(){
FILE *imageFile;
int x,y,pixel,height=2,width=3;
imageFile=fopen("image.pgm","wb");
if(imageFile==NULL){
perror("ERROR: Cannot open output file");
exit(EXIT_FAILURE);
}
fprintf(imageFile,"P3\n"); // P3 filetype
fprintf(imageFile,"%d %d\n",width,height); // dimensions
fprintf(imageFile,"255\n"); // Max pixel
int pix[100] {200,200,200, 100,100,100, 0,0,0, 255,0,0, 0,255,0, 0,0,255};
fwrite(pix,1,18,imageFile);
fclose(imageFile);
}
i have not fully understood what it does. i can open the output image, but it is not a correct representation of the Array.
If i change things around, for example making a 2 dimensional array, then the image viewer tells me "expected an integer" and doesn't show me an image.
So far so good.
As i have the array before the image i created a function aufrunden to round up to the next int number because i want to create a square image.
int aufrunden (double h)
{
int i =h;
if (h-i == 0)
{
return i;
}
else
{
i = h+1;
return i;
}
}
This function is used in the creation of the image.
If the image is bigger than the information the array provides like this (a is the length of th array)
double h;
h= sqrt(a/3.0);
int i = aufrunden(h);
FILE *imageFile;
int height=i,width=i;
It might happen now, that the array is a=24 long. aufrunden makes the image 3x3 so it has 27 values...meaning it is missing the values for 1 pixel.
Or worse it is only a=23 long. also creating a 3x3 image.
What will fwrite(pix,1,18,imageFile); write in those pixels for information? It would be best if the remaing values are just 0.
*edit never mind, i will just add 0 to the end of the array until it is filling up the whole square...sorry
Consider using a Netpbm format (pbm, pgm, or ppm).
These images are extremely simple text files that you can write without any special libraries. Then use some third-party software such as ImageMagick, GraphicsMagick, or pnmtopng to convert your image to PNG format. Here is a wiki article describing the Netpbm format.
Here's a simple PPM image:
P3 2 3 255
0 0 0 255 255 255
255 0 0 0 255 255
100 100 100 200 200 200
The first line contains "P3" (the "magic number identifying it as a text-PPM), 2 (width), 3 (height), 255 (maximum intensity).
The second line contains the two RGB pixels for the top row.
The third and fourth lines each contain the two RGB pixels for rows 2 and 3.
Use a larger number for maximum intensity (e.g. 1024) if you need a larger range of intensities, up to 65535.
Edited by Mark Setchell beyond this point - so I am the guilty party!
The image looks like this (when the six pixels are enlarged):
The ImageMagick command to convert, and enlarge, is like this:
convert image.ppm -scale 400x result.png
If ImageMagick is a bit heavyweight, or difficult to install you can more simply use the NetPBM tools (from here) like this (it's a single precompiled binary)
pnmtopng image.ppm > result.png
If, as it seems, you have got Magick++ and are happy to use that, you can write your code in C/C++ like this:
////////////////////////////////////////////////////////////////////////////////
// sample.cpp
// Mark Setchell
//
// ImageMagick Magick++ sample code
//
// Compile with:
// g++ sample.cpp -o sample $(Magick++-config --cppflags --cxxflags --ldflags --libs)
////////////////////////////////////////////////////////////////////////////////
#include <Magick++.h>
#include <iostream>
using namespace std;
using namespace Magick;
int main(int argc,char **argv)
{
unsigned char pix[]={200,200,200, 100,100,100, 0,0,0, 255,0,0, 0,255,0, 0,0,255};
// Initialise ImageMagick library
InitializeMagick(*argv);
// Create Image object and read in from pixel data above
Image image;
image.read(2,3,"RGB",CharPixel,pix);
// Write the image to a file - change extension if you want a GIF or JPEG
image.write("result.png");
}
You are not far off - well done for trying! As far as I can see, you only had a couple of mistakes:
You had P3 where you would actually need P6 if writing in binary.
You were using int type for your data, whereas you need to be using unsigned char for 8-bit data.
You had the width and height interchanged.
You were using the PGM extension which is for Portable Grey Maps, whereas your data is colour, so you need to use the PPM extension which is for Portable Pix Map.
So, the working code looks like this:
#include <stdio.h>
#include <stdlib.h>
int main(){
FILE *imageFile;
int x,y,pixel,height=3,width=2;
imageFile=fopen("image.ppm","wb");
if(imageFile==NULL){
perror("ERROR: Cannot open output file");
exit(EXIT_FAILURE);
}
fprintf(imageFile,"P6\n"); // P6 filetype
fprintf(imageFile,"%d %d\n",width,height); // dimensions
fprintf(imageFile,"255\n"); // Max pixel
unsigned char pix[]={200,200,200, 100,100,100, 0,0,0, 255,0,0, 0,255,0, 0,0,255};
fwrite(pix,1,18,imageFile);
fclose(imageFile);
}
If you then run that, you can convert the resulting image to a nice big PNG with
convert image.ppm -scale 400x result.png
If you subsequently need 16-bit data, you would change the 255 to 65535, and store in an unsigned short array rather than unsigned char and when you come to the fwrite(), you would need to write double the number of bytes.
The code below will take an integer array of pixel colors as input and write it to a .bmp bitmap file or, in reverse, read a .bmp bitmap file and store its image contents as an int array. It only requires the <fstream> library. The input parameter path can be for example C:/path/to/your/image.bmp and data is formatted as data[x+y*width]=(red<<16)|(green<<8)|blue;, whereby red, green and blue are integers in the range 0-255 and the pixel position is (x,y).
#include <string>
#include <fstream>
using namespace std;
typedef unsigned int uint;
int* read_bmp(const string path, uint& width, uint& height) {
ifstream file(path, ios::in|ios::binary);
if(file.fail()) println("\rError: File \""+filename+"\" does not exist!");
uint w=0, h=0;
char header[54];
file.read(header, 54);
for(uint i=0; i<4; i++) {
w |= (header[18+i]&255)<<(8*i);
h |= (header[22+i]&255)<<(8*i);
}
const int pad=(4-(3*w)%4)%4, imgsize=(3*w+pad)*h;
char* img = new char[imgsize];
file.read(img, imgsize);
file.close();
int* data = new int[w*h];
for(uint y=0; y<h; y++) {
for(uint x=0; x<w; x++) {
const int i = 3*x+y*(3*w+pad);
data[x+(h-1-y)*w] = (img[i]&255)|(img[i+1]&255)<<8|(img[i+2]&255)<<16;
}
}
delete[] img;
width = w;
height = h;
return data;
}
void write_bmp(const string path, const uint width, const uint height, const int* const data) {
const int pad=(4-(3*width)%4)%4, filesize=54+(3*width+pad)*height; // horizontal line must be a multiple of 4 bytes long, header is 54 bytes
char header[54] = { 'B','M', 0,0,0,0, 0,0,0,0, 54,0,0,0, 40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,24,0 };
for(uint i=0; i<4; i++) {
header[ 2+i] = (char)((filesize>>(8*i))&255);
header[18+i] = (char)((width >>(8*i))&255);
header[22+i] = (char)((height >>(8*i))&255);
}
char* img = new char[filesize];
for(uint i=0; i<54; i++) img[i] = header[i];
for(uint y=0; y<height; y++) {
for(uint x=0; x<width; x++) {
const int color = data[x+(height-1-y)*width];
const int i = 54+3*x+y*(3*width+pad);
img[i ] = (char)( color &255);
img[i+1] = (char)((color>> 8)&255);
img[i+2] = (char)((color>>16)&255);
}
for(uint p=0; p<pad; p++) img[54+(3*width+p)+y*(3*width+pad)] = 0;
}
ofstream file(path, ios::out|ios::binary);
file.write(img, filesize);
file.close();
delete[] img;
}
The code snippet was inspired by https://stackoverflow.com/a/47785639/9178992
For .png images, use lodepng.cpp and lodepng.h:
#include <string>
#include <vector>
#include <fstream>
#include "lodepng.h"
using namespace std;
typedef unsigned int uint;
int* read_png(const string path, uint& width, uint& height) {
vector<uchar> img;
lodepng::decode(img, width, height, path, LCT_RGB);
int* data = new int[width*height];
for(uint i=0; i<width*height; i++) {
data[i] = img[3*i]<<16|img[3*i+1]<<8|img[3*i+2];
}
return data;
}
void write_png(const string path, const uint width, const uint height, const int* const data) {
uchar* img = new uchar[3*width*height];
for(uint i=0; i<width*height; i++) {
const int color = data[i];
img[3*i ] = (color>>16)&255;
img[3*i+1] = (color>> 8)&255;
img[3*i+2] = color &255;
}
lodepng::encode(path, img, width, height, LCT_RGB);
delete[] img;
}

Writing to .BMP - distorted image

I'd like to write a normal map to a .bmp file, so I've implemented a simple .bmp writer first:
void BITMAPLOADER::writeHeader(std::ofstream& out, int width, int height)
{
BITMAPFILEHEADER tWBFH;
tWBFH.bfType = 0x4d42;
tWBFH.bfSize = 14 + 40 + (width*height*3);
tWBFH.bfReserved1 = 0;
tWBFH.bfReserved2 = 0;
tWBFH.bfOffBits = 14 + 40;
BITMAPINFOHEADER tW2BH;
memset(&tW2BH,0,40);
tW2BH.biSize = 40;
tW2BH.biWidth = width;
tW2BH.biHeight = height;
tW2BH.biPlanes = 1;
tW2BH.biBitCount = 24;
tW2BH.biCompression = 0;
out.write((char*)(&tWBFH),14);
out.write((char*)(&tW2BH),40);
}
bool TERRAINLOADER::makeNormalmap(unsigned int width, unsigned int height)
{
std::ofstream file;
file.open("terrainnormal.bmp");
if(!file)
{
file.close();
return false;
}
bitmaploader.writeHeader(file,width,height);
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
file << static_cast<unsigned char>(255*x/height); //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
file << static_cast<unsigned char>(0); //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
file << static_cast<unsigned char>(0); //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
};
};
file.close();
return true;
};
The writeHeader(...) function is from SO, from a solved,working post. (I've forgot the name of it)
The getHeight(...) is using bicubic interpolation, so I can write it to big resolution images, and it stays smooth. It will be also used for collision detection and now is used as a LOD factor for my clipmaps.
Now the problem is that this outputs a distorted image. The pictures will tell everything I think:
The expected/distorted result(s):
for the heightmap: I have the function that describes a mesh: getHeight(x,z). It gives back the correct results because I've tested it with shaders (by sending heights as vertex attribs) too. The image downloaded from internet:
And with the y(x,z) function values written to a .BMP: (the commented out part of the code):
With a simple function: file << static_cast<unsigned char>(255*(float)x/height)
which should be a simple blend from black to white to the right.
I used an image size of 256 x 256, because I've read it should be multiple of 4. I CAN use libraries, but I'd like to solve this problem without one. So, what caused this distortion?
EDIT:
On the last image some lines are also colored, but they shouldn't be. This post is similar, but my heightmap is not distorted linearly as in this post: Image Distortion with Lock Bits
EDIT:
Another strange issue is when I don't make all colors the same, it get's distorted in colors too. For example set only the RED to the heights, and leave G and B 0, it became not only RED, but a noisy colored heightmap.
EDIT /comments/
If I understood them right, there's the size of the header, then comes my pixel data. Now before the pixel data there must be 4 * n bytes. So that padding mean after the header I put some more data that fills the place.
For example assuming (I will look up hot to get it exactly) my header is 55 bytes, then I should add 1 more byte to it because 55+1 = 56 and 4|56.
So
file << static_cast<unsigned char>('a');
for(int y = 1; y <= width; y++)
{
for(int x = 1; x <= height; x++)
{
file << static_cast<unsigned char>(x);
file << static_cast<unsigned char>(x);
file << static_cast<unsigned char>(x);
};
};
should be correct.
But I realized the real issue (as Jigsore commented). When I cast from int to char, it seems like a 1 digit number becomes 1 byte, 2 digits number 2, and 3 digits 3 bytes. Clamping the height to 3 digits works well, but the image is a bit whitey, because 'darkest' color becomes (100,100,100) instead of (0,0,0). Also, this is the cause of the non-regular distortion, because it depends on how many 'hills' or 'mountains' are there in one row. How can I solve this, and I hope the last problem? I don't want to compress the image to 100-256 range.;)
Open your file in binary mode.
Under Windows, if you open a file in the default text mode, it will write an extra 0x0d (Return) character after every 0x0a (Linefeed) that gets written out. The first time this happens it will change the colors of the following pixels, as the RGB order gets out of alignment. After it happens 3 times you'll be off by a full pixel.

Unpacking RGB values in a Point Cloud Data Set

This is regarding unpacking the encoded rgb values in a pcl file. I did this with the procedure described in the pcl documentation, but the unpacked rgb values I get are not quite correct. When I plot them with R the representation given does not correspond to the colors in the real setting (I am to a certain degree sure the problem is not with the way it was plotted with R).
For example in the image attached the area demarcated should have colors gray and blue (two chairs and a table).
Source pcl file could be found at: https://docs.google.com/open?id=0Bz5-HVcDiF6SanBZU0JWVmJwWHM and the
file with the unpacked color values at: https://docs.google.com/open?id=0Bz5-HVcDiF6SV2pYQ0xUbTAwVmM. Also following is the code used for unpacking the color values in a c plus plus setting:
uint32_t rgbD = *reinterpret_cast<int*>(&kinectValue);
uint16_t rD = (rgbD >> 16) & 0x0000ff;
uint16_t gD = (rgbD >> 8) & 0x0000ff;
uint16_t bD = (rgbD) & 0x0000ff;
I would really appreciate if you could let me know where I have gone wrong.
Update:
Following is the R code snippet I used in plotting the values in 3D:
library(rgl)
pcd <- read.table(file.choose(),sep="")
names(pcd) <- c("x","y","z","r","g","b")
plot3d(pcd$x,pcd$y,pcd$z,col=rgb(pcd$r,pcd$g,pcd$b,maxColorValue=255))
Update:
Following is the code I used to read data, in C++:
/*
Reads in a file from Kinect with unpacked color values, filter the color value component and
sends it to be unpacked
*/
int fileRead(){
string line;
int lineNum = 0;
ifstream myfile ("res/OnePerson4.pcd");
if (myfile.is_open())
{
while ( myfile.good() )
{
lineNum++;
getline (myfile,line);
// Exclude the header information in the kinect file from the unpacking process
//if(lineNum > 10 && lineNum <20){//This for loop is activated when testing
if(lineNum > 10){
//Test code to extract the x,y,z values
string xyzvalFromKinectStr = line.substr(0,line.find_last_of(' '));
//cout<<xyzvalFromKinectStr<<"\n";
//Extract the packed rgb value
string valFromKinectStr = line.substr(line.find_last_of(' '));
double kinectVal = ::atof(valFromKinectStr.c_str());
kinectToRgb(kinectVal, xyzvalFromKinectStr);
}
}
myfile.close();
}
else
{
cout << "Unable to open file";
}
return 0;
}
Here's my working solution. First I ran your input through grep to filter out NANs in coordinates:
$ grep -v nan OnePerson4.pcd > OnePerson4.pcd.filtered
Then I extracted the data via C++ code like this:
#include <stdio.h>
int main()
{
if (FILE *f = fopen("OnePerson4.pcd.filtered", "rt"))
{
for (;;)
{
float x = 0;
float y = 0;
float z = 0;
float color_float = 0;
if (fscanf(f, "%f %f %f %f", &x, &y, &z, &color_float) != 4)
{
break;
}
unsigned color = *(unsigned const *)&color_float;
unsigned r = color & 0xff;
unsigned g = (color >> 8) & 0xff;
unsigned b = (color >> 16) & 0xff;
printf("%f,%f,%f,%d,%d,%d\n", x, y, z, r, g, b);
}
fclose(f);
}
return 0;
}
I didn't know in which byte order RGB is stored, so you might have to swap R and B. It's usually either RGB or BGR.
Then I used your code to plot the points (I changed read.table to read.csv):
library(rgl)
pcd <- read.csv(file.choose())
names(pcd) <- c("x","y","z","r","g","b")
plot3d(pcd$x,pcd$y,pcd$z,col=rgb(pcd$r,pcd$g,pcd$b,maxColorValue=255))
And this is what I get:
So I'm assuming the problem is with the code where you read your color from the pcd file. The rest looks fine to me.
Update: Your problem is the double type. Change it to float and it should work. Though storing unsigned int as a float is, at the very least, questionable. This is fragile and doesn't not guarantee the colors would be correct after you read them. Some bits might be off.
Another note: you could use >> stream operator to extract numbers from the file. It's much easier than manually parsing it with string methods. You can read about it, for example, here.