1

I have to take a .raw12 file, separate the 4 channels (R, G, G, B) and save them as valid images (8-bits in memory) without using any external library (.ppm) in C++.

You can read about raw12 here and ppm file format here.

I have written the code but it is giving me this output.

Click Here

I have tried a lot of things but it is always giving me output similar to the one above. I think there is a problem in datatype conversion but I am not sure.

I am trying to debug it from 2 days, still no luck.

Here is this code.

#include <fstream>
#include <iostream>
using namespace std;


const int BUFFERSIZE = 4096;


int main () 
{

    ifstream infile;

    infile.open("portrait.raw12", ios::binary | ios::in);

    ofstream outfile;

    outfile.open("Redtwo.ppm", ios::binary);

    //outfile.write("P6 ", 3);
    //outfile.write("1536 2048 ", 8);
    //outfile.write("2048 ", 4);
    //outfile.write("255 ", 4);


    //outfile << "P6\n" << 1536 << "\n" << 2048 << "\n255\n";
    outfile << "P6"     << "\n"
        << 1536  << " "
        << 2048  << "\n"
        << 255   << "\n"
       ;
    uint8_t * bufferRow = new uint8_t[BUFFERSIZE];

    if(!infile)
    {
        cout<<"Failed to open"<<endl;
    }
    int size=1536*2048*3;
    char * RedChannel=new char[size];
    int GreenChannel_1,GreenChannel_2,BlueChannel;
    int rowNum=0;
    int i=0;
    int j=0;
    int pixel=1;
    while(rowNum<3072)
    {
        infile.read(reinterpret_cast<char*>(bufferRow), BUFFERSIZE);
        if(rowNum%2==0)
        {
            while(i<BUFFERSIZE)
            {
                RedChannel[j]=(uint8_t)bufferRow[i];
                GreenChannel_1=((uint8_t)(bufferRow[i+1] & 0x0F) << 4) | ((uint8_t)(bufferRow[i+2] >> 4) & 0x0F);
                i+=3;
                //Collect s;
                //s.r=(char)RedChannel[j];
                //s.g=(char)0;
                //s.b=(char)0;
                //unsigned char c = (unsigned char)(255.0f * (float)RedChannel[j] + 0.5f); 
                //outfile.write((char*) &c, 3);
                //outfile.write((char*) 255, sizeof(c));
                //outfile.write(reinterpret_cast<char*>(&RedChannel), 4);
                if(pixel<=3 && rowNum<5)
                {
                    cout<<"RedChannel: "<<RedChannel[j]<<endl;
                    if(pixel!=3)
                        cout<<"GreenChannel 1: "<<GreenChannel_1<<endl;
                }
                pixel++;
                j++;

            }
            RedChannel[j]='\n';
            j++;

        }
        else
        {
            while(i<BUFFERSIZE)
            {
                GreenChannel_2=(uint8_t)bufferRow[i];
                BlueChannel=((uint8_t)(bufferRow[i+1] & 0x0F) << 4) | ((uint8_t)(bufferRow[i+2] >> 4) & 0x0F);
                i+=3;
                if(pixel<=3 && rowNum<5)
                {
                    cout<<"GreenChannel 2: "<<GreenChannel_2<<endl;
                    if(pixel!=3)
                        cout<<"BlueChannel: "<<BlueChannel<<endl;
                }
                pixel++;
            }
        }
        rowNum++;
        i=0;
        pixel=1;
        if(rowNum<5)
            cout<<" "<<endl;
    }
    infile.close();
    outfile.write(RedChannel, size);
    outfile.close();
}

Github Link To The Code

Tukai
  • 13
  • 1
  • 8
  • Check your logic: RGGB are aligned every 6 bytes, RedChannel should have 12 bits, ou are just getting 8 bits. The example on bottom of the page you linked gives you a good example. The format do not seems so good. I really miss the interpretation of values (raw should be linear). – Giacomo Catenazzi Feb 19 '19 at 14:33
  • Please provide the raw12 data file. – Mark Setchell Feb 19 '19 at 19:06
  • The documentation you link to says the image is 3072 rows tall by 4096 columns wide, yet you have written a PPM format that claims to have 1536 columns wide by 2048 rows tall, so you have halved the size and rotated by 90 degrees. Why? – Mark Setchell Feb 19 '19 at 19:12
  • Your data are 12-bit per sample, but you write a `MAX_VALUE` of 255 in the PPM header implying your PPM file has 8-bits per sample. I would have expected you to write a `MAX_VALUE` of 4096 in the header and then write 2 bytes per sample. – Mark Setchell Feb 19 '19 at 19:15
  • @GiacomoCatenazzi I am really sorry. In the problem statement it is given, I have to take 8 bits and write that 8 bits into the .ppm file. I have to convert that 12-bits to 8-bits and to do that I have to chop-off the 4 least significant bits. In the problem statement it was also mentioned that I have to print the intensity value of first 5x5 pixels. I should have mentioned that to avoid confusion. – Tukai Feb 20 '19 at 16:14
  • @MarkSetchell I am sorry. The 1st row of the .raw12 file is RGRGRGRG... which implies half of them are red and half of them are green. The 2nd row of .raw12 file is GBGBGBGB... which implies every even row don't have Red channel. That's why I halved it. I also tried 3072x2048. The problem this persist. I certainly didn't rotate the image by 90 degree. What are you talking about. I will add the github link from where you can download the code and the .raw12 file. – Tukai Feb 20 '19 at 16:19
  • By *"rotated"*, I meant you have the width and height back-to-front in your PPM file, i.e. switched. – Mark Setchell Feb 20 '19 at 17:22

1 Answers1

2

I have simplified your code quite a lot and made it output just one single channel, since your question says you should generate one image per channel.

Now you have something working, you can add back in the other parts - it's easier to start with something that works! I won't do all your challenge for you... that would be no fun and leave you without any sense of achievement. You can do the rest - good luck!

#include <fstream>
#include <iostream>
using namespace std;

// Enough for one line of the input image
const int BUFFERSIZE = 4096 * 3;

int main(){

    ifstream infile;
    ofstream outfile;

    infile.open("portrait.raw12", ios::binary | ios::in);
    outfile.open("result.pgm", ios::binary);

    // Write single channel PGM file
    outfile << "P5\n2048 1536\n255\n";

    unsigned char * bufferRow = new unsigned char[BUFFERSIZE];

    if(!infile)
    {
        cout<<"Failed to open"<<endl;
    }
    int size=2048*1536;
    unsigned char * RedChannel=new unsigned char[size];
    unsigned char * Redp = RedChannel;

    for(int rowNum=0;rowNum<1536;rowNum++){
        // Read an entire row
        infile.read(reinterpret_cast<char*>(bufferRow), BUFFERSIZE);
        if(rowNum%2==0){
            for(int i=0;i<BUFFERSIZE;i+=3){
                *Redp++=bufferRow[i];
            }
        }
    }
    infile.close();
    outfile.write(reinterpret_cast<char*>(RedChannel), size);
    outfile.close();
}

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Thanks a lot sir. You are so cool. *o*. I gave just a few trivial questions. Q1. Why the of for loop in the line 29 is "rowNum<1536" and not "rowNum<3072"? Q2. When to use a pointer and when not to use it? you used *Redp. Q3. How you become a better coder like you? I am noob but I am willing to learn. Again, Thanks a lot, Sir. – Tukai Feb 21 '19 at 16:06
  • I'm glad it helps. Q1. I find the description of the RAW12 format really poor and spent ages trying to understand what it means and make the filesizes match the rows, columns and sample sizes and 1536 seems correct. Q2 I think `*Redp++ = xyz` is easier to get right since the data are written and the pointer is updated all in one go, whereas if you do `Red[j] = xyz; ...; ...; j +=1` you can easily get out of sync and not update the pointer. – Mark Setchell Feb 21 '19 at 16:48
  • As for being good, there's still plenty that could be improved in the program but we are all learning! – Mark Setchell Feb 21 '19 at 16:51
  • Thanks a lot, Sir. When I posted the question in stackoverflow, I had little hope that somebody will solve it for me. Really sir, Thanks a lot for your valuable time and effort. – Tukai Feb 22 '19 at 14:16