Code covered by the BSD License  

Highlights from
DPX file reader

DPX file reader

by

 

18 Jan 2006 (Updated )

A DPX file parser .

readdpx(filename)
function [pixels, details] = readdpx(filename)

% Validate input argument.
if (~ischar(filename))
    error('Filename must be a character array');
end

% Read entire file into a UINT8 buffer.
buffer = getBufferFromFile(filename);

[isValid, swapFcn] = isValidDPX(buffer);
if (~isValid)
    error('Bad magic number.  The file may not be valid DPX.');
end

% Extract metadata and pixels from the buffer.
details = getDetailsFromBuffer(buffer, swapFcn);
pixels = readPixels(buffer, details, swapFcn);



%------------------------------------------------------------------------
function buffer = getBufferFromFile(filename)

fid = fopen(filename, 'r');
if (fid == -1)
    error('Could not open file %s for reading.', filename);
end

% Read the whole file into a UINT8 buffer and then tease out the parts
% as needed.
buffer = fread(fid, inf, 'uint8=>uint8');

fclose(fid);



%------------------------------------------------------------------------
function [tf, swapFcn] = isValidDPX(buffer)

% DPX files should begin with a 4-byte magic number (the ASCII bytes for
% SDPX or XPDS).  We can use these values to validate the file and
% determine the endianness.
expectedMagicNumber = uint32(sscanf('53445058', '%x'));
fileMagicNumber = typecast(buffer(1:4), 'uint32');

if isequal(fileMagicNumber, expectedMagicNumber)
   tf = true;
   swapFcn = @(x) x;
   
elseif isequal(swapbytes(fileMagicNumber), expectedMagicNumber)
   tf = true;
   swapFcn = @(x) swapbytes(x);
   
else
   tf = false;
   swapFcn = [];
end



%------------------------------------------------------------------------
function details = getDetailsFromBuffer(allData, swapFcn)

details = struct([]);

details(1).FileDetails = getFileDetails(allData, swapFcn);
details(1).ImageDetails = getImageDetails(allData, swapFcn);
details(1).ImageOrientation = getOrientation(allData, swapFcn);
details(1).IndustryDetails = getIndustryDetails(allData, swapFcn);



%------------------------------------------------------------------------
function fileDetails = getFileDetails(buffer, swapFcn)
        
dataStructure = {...
       0,   1, 'uint32', 'MagicNumber'
       4,   1, 'uint32', 'ImageDataOffset'
       8,   8, 'char',   'HeaderVersion'
      16,   1, 'uint32', 'FileSize'
      20,   1, 'uint32', 'ReadTimeShortCut'
      24,   1, 'uint32', 'GenericHeaderSize'
      28,   1, 'uint32', 'IndustryHeaderSize'
      32,   1, 'uint32', 'UserDefinedDataSize'
      36, 100, 'char',   'ImageFilename'
     136,  24, 'char',   'CreateTime'
     160, 100, 'char',   'Creator'
     260, 200, 'char',   'Project'
     460, 200, 'char',   'Copyright'
     660,   1, 'uint32', 'Key'
     664, 104, 'char',   'Reserved'};
 
fileDetails = parseDataStructure(buffer, dataStructure, swapFcn);



%------------------------------------------------------------------------
function imageDetails = getImageDetails(buffer, swapFcn)

dataStructure = {...
     768,    1, 'uint16', 'Orientation'
     770,    1, 'uint16', 'NumberOfImageElements'
     772,    1, 'uint32', 'Columns'
     776,    1, 'uint32', 'Rows'
     780, 8*72, 'uint8',  'ImageElementDetails'
    1356,   52, 'uint8',  'Reserved'};

elementSubStructure = {...
     0,  1, 'uint32', 'SignedImages'
     4,  1, 'uint32', 'ReferenceLowDataCodeValue'
     8,  1, 'single', 'ReferenceLowQuantity'
    12,  1, 'uint32', 'ReferenceHighDataCodeValue'
    16,  1, 'single', 'ReferenceHighQuantity'
    20,  1, 'uint8',  'Descriptor'
    21,  1, 'uint8',  'TransferCharacteristics'
    22,  1, 'uint8',  'Colorimetry'
    23,  1, 'uint8',  'BitSize'
    24,  1, 'uint16', 'Packing'
    26,  1, 'uint16', 'Encoding'
    28,  1, 'uint32', 'DataOffset'
    32,  1, 'uint32', 'EndOfLinePadding'
    36,  1, 'uint32', 'EndOfImagePadding'
    40, 32, 'char',   'Description'};

% Parse the top-level structure from this part of the buffer.
imageDetails = parseDataStructure(buffer, dataStructure, swapFcn);

% Parse the eight Image Element Details data structures from the
% ImageElement field.
elementBuffer = imageDetails.ImageElementDetails;

for elementNumber = 1:8
    
    elementBufferStart = (elementNumber - 1) * 72 + 1;
    elementBufferEnd = elementNumber * 72;
    
    tmpBuffer = elementBuffer(elementBufferStart:elementBufferEnd);
    imageDetails.ImageElementDetailsParsed(elementNumber) = ...
        parseDataStructure(tmpBuffer, elementSubStructure, swapFcn);
    
end



%------------------------------------------------------------------------
function orientationDetails = getOrientation(buffer, swapFcn)
        
dataStructure = {...
    1408,   1, 'uint32', 'XOffset'
    1412,   1, 'uint32', 'YOffset'
    1416,   1, 'single', 'XCenter'
    1420,   1, 'single', 'YCenter'
    1424,   1, 'uint32', 'XOriginalSize'
    1428,   1, 'uint32', 'YOriginalSize'
    1432, 100, 'char',   'SourceImageFilename'
    1532,  24, 'char',   'SourceImageCreationTime'
    1556,  32, 'char',   'InputDeviceName'
    1588,  32, 'char',   'InputDeviceSerialNumber'
    1620,   4, 'uint16', 'BorderValidity'
    1628,   2, 'uint32', 'PixelAspectRatio'
    1636,  28, 'uint8',  'Reserved'};

orientationDetails = parseDataStructure(buffer, dataStructure, swapFcn);



%------------------------------------------------------------------------
function industryDetails = getIndustryDetails(buffer, swapFcn)

% There is more information in the DPX standard for this, but it doesn't
% affect how to read the file.
industryDetails = [];
    
    
    
%------------------------------------------------------------------------
function details = parseDataStructure(buffer, dataStructure, swapFcn)

details = struct([]);

% For each part of the data structure, extract data from the buffer,
% transform it, and store it in the output structure.
for fieldNumber = 1:size(dataStructure, 1)
    
    thisField = dataStructure(fieldNumber, :);
    name = thisField{4};
    
    details(1).(name) = getData(buffer, thisField, swapFcn);
    
end



%------------------------------------------------------------------------
function data = getData(buffer, fieldDetails, swapFcn)

% Find the offset and extent to this data element in the buffer.
% The data structure offsets are 0-based, and MATLAB is 1-based.
dataStart = fieldDetails{1} + 1;
datatype = fieldDetails{3};
dataEnd = fieldDetails{1} + fieldDetails{2} * sizeof(datatype);

rawData = buffer(dataStart:dataEnd);

% ASCII character data in the file is 8-bit and is not converted.
if (strcmp(datatype, 'char'))
    data = char(rawData)';
else
    data = swapFcn(typecast(rawData, datatype));
end



%------------------------------------------------------------------------
function numBytes = sizeof(datatype)

switch (datatype)
    case {'char', 'uint8', 'int8'}
        numBytes = 1;
        
    case {'uint16', 'int16'}
        numBytes = 2;
        
    case {'uint32', 'int32', 'single'}
        numBytes = 4;
        
    case {'uint64', 'int64', 'double'}
        numBytes = 8;
        
    otherwise
        error('Unknown datatype %s.', datatype)
        
end



%------------------------------------------------------------------------
function pixels = readPixels(buffer, details, swapFcn)

% Do we support this colorspace or data arrangement?
%
% There are many ways of organizing samples inside of DPX files.  This
% example code handles RGB and grayscale data (each with a linear
% response).  Other varieties include YCbCr colorspaces and RGB images
% stored one color sample per image element.  Nonlinear response curves can
% also be used.
descriptor = details.ImageDetails.ImageElementDetailsParsed(1).Descriptor;
switch (descriptor)
    case 6
        numChannels = 1;
        
    case 50
        numChannels = 3;
        
    otherwise
        error('Unsupported DPX format: %d', descriptor)
        
end

% Determine sizes and sample locations.
rows = details.ImageDetails.Rows;
columns = details.ImageDetails.Columns;
bitDepth = details.ImageDetails.ImageElementDetailsParsed(1).BitSize;

startOfPixels = details.FileDetails.ImageDataOffset;
endOfPixels = startOfPixels + ...
    double(rows * columns) * numChannels * double(bitDepth)/8;

% Convert the buffer to an array of output pixels.
%
% DPX files can also contain bit-depths that cause pixel samples not to end
% on byte boundaries (e.g., 10-bit and 12-bit images).  We won't handle
% them in this example, since the padding details could be confusing.
switch bitDepth
    case 8
        pixels = buffer((startOfPixels + 1):endOfPixels);

    case 16
        pixels = typecast(buffer((startOfPixels + 1):endOfPixels), 'uint16');
        pixels = swapFcn(pixels);

    otherwise
        error('Unsupported bit-depth.');
        
end

% Rearrange the data to follow MATLAB's conventions.
pixels = reshape(pixels, [numChannels, columns, rows]);
pixels = permute(pixels, [3 2 1]);

Contact us