Search code examples
c++pixel16-bit

Reading 16 bit DPX Pixel Data


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?


Solution

  • 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;
    
        ...