Task 152: .DPX File Format
Task 152: .DPX File Format
1. List of Properties of the .DPX File Format Intrinsic to Its File System
The .DPX (Digital Picture Exchange) file format is defined in the SMPTE ST 268M specification, which describes a header structure for storing image metadata, followed by image data. The format is big-endian by default (determined by the magic number). The header is divided into sections: File Information (0-767 bytes), Image Information (768-1599 bytes), Image Source Information (1600-1919 bytes), Motion Picture Film Information (1920-2175 bytes), and Television Information (2176-2303 bytes), with an optional user-defined section after. The properties (fields) intrinsic to the file format are the metadata fields in these headers, which describe the file's structure, image characteristics, source, and timing. Below is a comprehensive list based on the specification, including field name, offset (in bytes), size (in bytes), data type, and brief description. Reserved or TBD fields are included as they are part of the format.
File Information Header (Offsets 0-767)
- Magic number: Offset 0, Size 4, Type U32, Description: Identifies format and endianness ('SDPX' for big-endian, 'XPDS' for little-endian).
- Offset to image data: Offset 4, Size 4, Type U32, Description: Byte offset from file start to image data.
- Version: Offset 8, Size 8, Type ASCII, Description: Header format version (e.g., "V2.0").
- File size: Offset 16, Size 4, Type U32, Description: Total file size in bytes.
- Ditto key: Offset 20, Size 4, Type U32, Description: 0 if same as previous frame, 1 if new.
- Generic header length: Offset 24, Size 4, Type U32, Description: Length of generic header sections (typically 2304 bytes if no user data).
- Industry header length: Offset 28, Size 4, Type U32, Description: Length of industry-specific headers (film + TV, typically 384 bytes).
- User header length: Offset 32, Size 4, Type U32, Description: Length of user-defined data (0 if none).
- File name: Offset 36, Size 100, Type ASCII, Description: Image filename (NULL-terminated).
- Creation date/time: Offset 136, Size 24, Type ASCII, Description: Timestamp in ISO 8601 format (yyyy:mm:dd:hh:mm:ssLTZ).
- Creator: Offset 160, Size 100, Type ASCII, Description: Software/hardware that created the file.
- Project: Offset 260, Size 200, Type ASCII, Description: Project name or title.
- Copyright: Offset 460, Size 200, Type ASCII, Description: Copyright or right-to-use statement.
- Encryption key: Offset 660, Size 4, Type U32, Description: Encryption key (0xFFFFFFFF if unencrypted).
- Reserved: Offset 664, Size 104, Type TBD, Description: Reserved for future use (fill with undefined values).
Image Information Header (Offsets 768-1599)
- Image orientation: Offset 768, Size 2, Type U16, Description: Image rotation/flip code (0-7).
- Number of image elements: Offset 770, Size 2, Type U16, Description: Number of components (1-8, e.g., 1 for grayscale, 3 for RGB).
- Pixels per line: Offset 772, Size 4, Type U32, Description: Width in pixels (even number).
- Lines per image element: Offset 776, Size 4, Type U32, Description: Height in lines.
- Image Element 1-8 (each 72 bytes, starting at 780, 852, 924, 996, 1068, 1140, 1212, 1284):
- Data sign: Relative offset 0, Size 4, Type U32, Description: 0 unsigned, 1 signed.
- Reference low data code: Relative offset 4, Size 4, Type U32, Description: Minimum expected code value.
- Reference low quantity: Relative offset 8, Size 4, Type R32, Description: Quantity represented by low data code.
- Reference high data code: Relative offset 12, Size 4, Type U32, Description: Maximum expected code value.
- Reference high quantity: Relative offset 16, Size 4, Type R32, Description: Quantity represented by high data code.
- Descriptor: Relative offset 20, Size 1, Type U8, Description: Component type (e.g., 50 for RGB).
- Transfer characteristic: Relative offset 21, Size 1, Type U8, Description: Transfer function (e.g., 1 for printing density).
- Colorimetric specification: Relative offset 22, Size 1, Type U8, Description: Color space (e.g., 1 for printing density).
- Bit depth: Relative offset 23, Size 1, Type U8, Description: Bits per sample (e.g., 10, 12, 16).
- Packing: Relative offset 24, Size 2, Type U16, Description: Packing method (0 packed, 1 filled).
- Encoding: Relative offset 26, Size 2, Type U16, Description: Compression (0 none, 1 RLE).
- Data offset: Relative offset 28, Size 4, Type U32, Description: Offset to this element's data.
- End-of-line padding: Relative offset 32, Size 4, Type U32, Description: Bytes of padding at end of line.
- End-of-image padding: Relative offset 36, Size 4, Type U32, Description: Bytes of padding at end of image.
- Description: Relative offset 40, Size 32, Type ASCII, Description: Element description.
- Reserved: Offset 1356, Size 244, Type TBD, Description: Reserved for future use.
Image Source Information Header (Offsets 1600-1919)
- X offset: Offset 1600, Size 4, Type U32, Description: X offset from original image.
- Y offset: Offset 1604, Size 4, Type U32, Description: Y offset from original image.
- X center: Offset 1608, Size 4, Type R32, Description: X center in pixel units.
- Y center: Offset 1612, Size 4, Type R32, Description: Y center in line units.
- X original size: Offset 1616, Size 4, Type U32, Description: Original width in pixels.
- Y original size: Offset 1620, Size 4, Type U32, Description: Original height in lines.
- Source image filename: Offset 1624, Size 100, Type ASCII, Description: Original source filename.
- Source image date/time: Offset 1724, Size 24, Type ASCII, Description: Original source timestamp.
- Input device name: Offset 1748, Size 32, Type ASCII, Description: Input device name.
- Input device serial number: Offset 1780, Size 32, Type ASCII, Description: Input device serial.
- Border validity (XL, XR, YT, YB): Offset 1812, Size 8, Type U16[4], Description: Border validity coordinates.
- Aspect ratio (horizontal, vertical): Offset 1820, Size 8, Type U32[2], Description: Pixel aspect ratio.
- X scanned size: Offset 1828, Size 4, Type R32, Description: Horizontal scanned size (inches/mm).
- Y scanned size: Offset 1832, Size 4, Type R32, Description: Vertical scanned size (inches/mm).
- Reserved: Offset 1836, Size 84, Type TBD, Description: Reserved for future use (adjusted to pad to 320 bytes).
Motion Picture Film Information Header (Offsets 1920-2175)
- Film manufacturer ID: Offset 1920, Size 2, Type U16, Description: Film manufacturer code.
- Film type: Offset 1922, Size 2, Type U16, Description: Film stock type.
- Perfs offset: Offset 1924, Size 2, Type U16, Description: Perforation offset.
- Prefix: Offset 1926, Size 6, Type U32, Description: Edge code prefix (packed).
- Count: Offset 1932, Size 4, Type U32, Description: Edge code count.
- Format: Offset 1936, Size 32, Type ASCII, Description: Film format (e.g., "35 mm").
- Frame position: Offset 1968, Size 4, Type U32, Description: Frame position in sequence.
- Sequence length: Offset 1972, Size 4, Type U32, Description: Sequence length in frames.
- Held count: Offset 1976, Size 4, Type U32, Description: Held frames.
- Frame rate: Offset 1980, Size 4, Type R32, Description: Frame rate (fps).
- Shutter angle: Offset 1984, Size 4, Type R32, Description: Shutter angle in degrees.
- Frame identification: Offset 1988, Size 32, Type ASCII, Description: Frame ID.
- Slate info: Offset 2020, Size 100, Type ASCII, Description: Slate information.
- Reserved: Offset 2120, Size 56, Type TBD, Description: Reserved for future use.
Television Information Header (Offsets 2176-2303)
- Time code: Offset 2176, Size 4, Type U32, Description: SMPTE time code (packed BCD).
- User bits: Offset 2180, Size 4, Type U32, Description: User bits (packed BCD).
- Interlace: Offset 2184, Size 1, Type U8, Description: 0 progressive, 1 interlaced.
- Field number: Offset 2185, Size 1, Type U8, Description: Field number for interlaced video.
- Video signal: Offset 2186, Size 1, Type U8, Description: Video signal standard.
- Zero: Offset 2187, Size 1, Type U8, Description: Padding (0).
- Horizontal sample rate: Offset 2188, Size 4, Type R32, Description: Horizontal sampling rate (Hz).
- Vertical sample rate: Offset 2192, Size 4, Type R32, Description: Vertical sampling rate (Hz).
- Temporal frame rate: Offset 2196, Size 4, Type R32, Description: Frame rate (fps).
- Time offset: Offset 2200, Size 4, Type R32, Description: Time offset from sync to first pixel.
- Gamma: Offset 2204, Size 4, Type R32, Description: Gamma value.
- Black level: Offset 2208, Size 4, Type R32, Description: Black level code value.
- Black gain: Offset 2212, Size 4, Type R32, Description: Black gain.
- Breakpoint: Offset 2216, Size 4, Type R32, Description: Breakpoint level.
- White level: Offset 2220, Size 4, Type R32, Description: Reference white level code value.
- Integration times: Offset 2224, Size 4, Type R32, Description: Integration time (sec).
- Reserved: Offset 2228, Size 76, Type TBD, Description: Reserved for future use.
2. Two Direct Download Links for .DPX Files
- ftp://ftp.nfb.ca/pub/0000047.dpx (A sample 10-bit DPX file from NFB).
- https://www.dropbox.com/s/2j2gfozldyj1zj0/Shot1_Super2k_FLAT_0006248.dpx?dl=1 (A Super2K DPX sample file; dl=1 forces direct download).
3. Ghost Blog Embedded HTML JavaScript for Drag and Drop .DPX File Dump
This is an embeddable HTML snippet with JavaScript for a Ghost blog post. It creates a drop zone where users can drag and drop a .DPX file. The script reads the file as an ArrayBuffer, uses DataView to parse the header (assuming big-endian for simplicity; checks magic), and dumps all properties to the screen in a pre element.
4. Python Class for .DPX File
This Python class opens a .DPX file, decodes the header, reads all properties, prints them to console, and can write the header back to a new file (for simplicity, writes the original header without image data modifications).
import struct
import sys
class DPXFile:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {}
self.endian = '>'
self.parse_header()
def parse_header(self):
with open(self.filepath, 'rb') as f:
header = f.read(2304) # Generic header size
if len(header) < 2304:
raise ValueError("Invalid DPX file")
magic = struct.unpack('>I', header[0:4])[0]
if magic == 0x58504453:
self.endian = '<'
elif magic != 0x53445058:
raise ValueError("Invalid magic number")
self.properties['Magic number'] = header[0:4].decode('ascii', errors='ignore')
self.properties['Offset to image data'] = self._unpack('I', 4)
self.properties['Version'] = self._str(8, 8)
self.properties['File size'] = self._unpack('I', 16)
self.properties['Ditto key'] = self._unpack('I', 20)
self.properties['Generic header length'] = self._unpack('I', 24)
self.properties['Industry header length'] = self._unpack('I', 28)
self.properties['User header length'] = self._unpack('I', 32)
self.properties['File name'] = self._str(36, 100)
self.properties['Creation date/time'] = self._str(136, 24)
self.properties['Creator'] = self._str(160, 100)
self.properties['Project'] = self._str(260, 200)
self.properties['Copyright'] = self._str(460, 200)
self.properties['Encryption key'] = self._unpack('I', 660)
self.properties['Image orientation'] = self._unpack('H', 768)
num_elem = self._unpack('H', 770)
self.properties['Number of image elements'] = num_elem
self.properties['Pixels per line'] = self._unpack('I', 772)
self.properties['Lines per image element'] = self._unpack('I', 776)
for i in range(num_elem):
off = 780 + i * 72
prefix = f'Image Element {i+1} '
self.properties[prefix + 'Data sign'] = self._unpack('I', off)
self.properties[prefix + 'Reference low data code'] = self._unpack('I', off + 4)
self.properties[prefix + 'Reference low quantity'] = self._unpack('f', off + 8)
self.properties[prefix + 'Reference high data code'] = self._unpack('I', off + 12)
self.properties[prefix + 'Reference high quantity'] = self._unpack('f', off + 16)
self.properties[prefix + 'Descriptor'] = self._unpack('B', off + 20)
self.properties[prefix + 'Transfer characteristic'] = self._unpack('B', off + 21)
self.properties[prefix + 'Colorimetric specification'] = self._unpack('B', off + 22)
self.properties[prefix + 'Bit depth'] = self._unpack('B', off + 23)
self.properties[prefix + 'Packing'] = self._unpack('H', off + 24)
self.properties[prefix + 'Encoding'] = self._unpack('H', off + 26)
self.properties[prefix + 'Data offset'] = self._unpack('I', off + 28)
self.properties[prefix + 'End-of-line padding'] = self._unpack('I', off + 32)
self.properties[prefix + 'End-of-image padding'] = self._unpack('I', off + 36)
self.properties[prefix + 'Description'] = self._str(off + 40, 32)
self.properties['X offset'] = self._unpack('I', 1600)
self.properties['Y offset'] = self._unpack('I', 1604)
self.properties['X center'] = self._unpack('f', 1608)
self.properties['Y center'] = self._unpack('f', 1612)
self.properties['X original size'] = self._unpack('I', 1616)
self.properties['Y original size'] = self._unpack('I', 1620)
self.properties['Source image filename'] = self._str(1624, 100)
self.properties['Source image date/time'] = self._str(1724, 24)
self.properties['Input device name'] = self._str(1748, 32)
self.properties['Input device serial number'] = self._str(1780, 32)
self.properties['Border validity XL'] = self._unpack('H', 1812)
self.properties['Border validity XR'] = self._unpack('H', 1814)
self.properties['Border validity YT'] = self._unpack('H', 1816)
self.properties['Border validity YB'] = self._unpack('H', 1818)
self.properties['Aspect ratio horizontal'] = self._unpack('I', 1820)
self.properties['Aspect ratio vertical'] = self._unpack('I', 1824)
self.properties['X scanned size'] = self._unpack('f', 1828)
self.properties['Y scanned size'] = self._unpack('f', 1832)
self.properties['Film manufacturer ID'] = self._unpack('H', 1920)
self.properties['Film type'] = self._unpack('H', 1922)
self.properties['Perfs offset'] = self._unpack('H', 1924)
self.properties['Prefix'] = self._unpack('I', 1926)
self.properties['Count'] = self._unpack('I', 1932)
self.properties['Format'] = self._str(1936, 32)
self.properties['Frame position'] = self._unpack('I', 1968)
self.properties['Sequence length'] = self._unpack('I', 1972)
self.properties['Held count'] = self._unpack('I', 1976)
self.properties['Frame rate'] = self._unpack('f', 1980)
self.properties['Shutter angle'] = self._unpack('f', 1984)
self.properties['Frame identification'] = self._str(1988, 32)
self.properties['Slate info'] = self._str(2020, 100)
self.properties['Time code'] = self._unpack('I', 2176)
self.properties['User bits'] = self._unpack('I', 2180)
self.properties['Interlace'] = self._unpack('B', 2184)
self.properties['Field number'] = self._unpack('B', 2185)
self.properties['Video signal'] = self._unpack('B', 2186)
self.properties['Zero'] = self._unpack('B', 2187)
self.properties['Horizontal sample rate'] = self._unpack('f', 2188)
self.properties['Vertical sample rate'] = self._unpack('f', 2192)
self.properties['Temporal frame rate'] = self._unpack('f', 2196)
self.properties['Time offset'] = self._unpack('f', 2200)
self.properties['Gamma'] = self._unpack('f', 2204)
self.properties['Black level'] = self._unpack('f', 2208)
self.properties['Black gain'] = self._unpack('f', 2212)
self.properties['Breakpoint'] = self._unpack('f', 2216)
self.properties['White level'] = self._unpack('f', 2220)
self.properties['Integration times'] = self._unpack('f', 2224)
self.header_data = header # For writing
def _unpack(self, fmt, off):
fmt = self.endian + fmt
with open(self.filepath, 'rb') as f:
f.seek(off)
return struct.unpack(fmt, f.read(struct.calcsize(fmt)))[0]
def _str(self, off, len):
with open(self.filepath, 'rb') as f:
f.seek(off)
return f.read(len).decode('ascii', errors='ignore').rstrip('\x00')
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, output_path):
with open(output_path, 'wb') as f:
f.write(self.header_data)
# To write full file, append image data from original, but for demo, just header
with open(self.filepath, 'rb') as orig:
orig.seek(self.properties['Offset to image data'])
f.write(orig.read())
# Usage: dpx = DPXFile('example.dpx'); dpx.print_properties(); dpx.write('output.dpx')
5. Java Class for .DPX File
This Java class opens a .DPX file, decodes the header, reads all properties, prints them to console, and can write the header back to a new file.
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class DPXFile {
private String filepath;
private Map<String, Object> properties = new HashMap<>();
private ByteOrder endian = ByteOrder.BIG_ENDIAN;
private byte[] headerData;
public DPXFile(String filepath) throws IOException {
this.filepath = filepath;
parseHeader();
}
private void parseHeader() throws IOException {
headerData = Files.readAllBytes(Paths.get(filepath));
if (headerData.length < 2304) throw new IOException("Invalid DPX file");
ByteBuffer bb = ByteBuffer.wrap(headerData).order(ByteOrder.BIG_ENDIAN);
int magic = bb.getInt(0);
if (magic == 0x58504453) {
endian = ByteOrder.LITTLE_ENDIAN;
bb.order(endian);
} else if (magic != 0x53445058) {
throw new IOException("Invalid magic number");
}
properties.put("Magic number", new String(headerData, 0, 4));
properties.put("Offset to image data", getU32(bb, 4));
properties.put("Version", getStr(bb, 8, 8));
properties.put("File size", getU32(bb, 16));
properties.put("Ditto key", getU32(bb, 20));
properties.put("Generic header length", getU32(bb, 24));
properties.put("Industry header length", getU32(bb, 28));
properties.put("User header length", getU32(bb, 32));
properties.put("File name", getStr(bb, 36, 100));
properties.put("Creation date/time", getStr(bb, 136, 24));
properties.put("Creator", getStr(bb, 160, 100));
properties.put("Project", getStr(bb, 260, 200));
properties.put("Copyright", getStr(bb, 460, 200));
properties.put("Encryption key", getU32(bb, 660));
properties.put("Image orientation", getU16(bb, 768));
int numElem = getU16(bb, 770);
properties.put("Number of image elements", numElem);
properties.put("Pixels per line", getU32(bb, 772));
properties.put("Lines per image element", getU32(bb, 776));
for (int i = 0; i < numElem; i++) {
int off = 780 + i * 72;
String prefix = "Image Element " + (i + 1) + " ";
properties.put(prefix + "Data sign", getU32(bb, off));
properties.put(prefix + "Reference low data code", getU32(bb, off + 4));
properties.put(prefix + "Reference low quantity", getR32(bb, off + 8));
properties.put(prefix + "Reference high data code", getU32(bb, off + 12));
properties.put(prefix + "Reference high quantity", getR32(bb, off + 16));
properties.put(prefix + "Descriptor", (int) getU8(bb, off + 20));
properties.put(prefix + "Transfer characteristic", (int) getU8(bb, off + 21));
properties.put(prefix + "Colorimetric specification", (int) getU8(bb, off + 22));
properties.put(prefix + "Bit depth", (int) getU8(bb, off + 23));
properties.put(prefix + "Packing", getU16(bb, off + 24));
properties.put(prefix + "Encoding", getU16(bb, off + 26));
properties.put(prefix + "Data offset", getU32(bb, off + 28));
properties.put(prefix + "End-of-line padding", getU32(bb, off + 32));
properties.put(prefix + "End-of-image padding", getU32(bb, off + 36));
properties.put(prefix + "Description", getStr(bb, off + 40, 32));
}
properties.put("X offset", getU32(bb, 1600));
properties.put("Y offset", getU32(bb, 1604));
properties.put("X center", getR32(bb, 1608));
properties.put("Y center", getR32(bb, 1612));
properties.put("X original size", getU32(bb, 1616));
properties.put("Y original size", getU32(bb, 1620));
properties.put("Source image filename", getStr(bb, 1624, 100));
properties.put("Source image date/time", getStr(bb, 1724, 24));
properties.put("Input device name", getStr(bb, 1748, 32));
properties.put("Input device serial number", getStr(bb, 1780, 32));
properties.put("Border validity XL", getU16(bb, 1812));
properties.put("Border validity XR", getU16(bb, 1814));
properties.put("Border validity YT", getU16(bb, 1816));
properties.put("Border validity YB", getU16(bb, 1818));
properties.put("Aspect ratio horizontal", getU32(bb, 1820));
properties.put("Aspect ratio vertical", getU32(bb, 1824));
properties.put("X scanned size", getR32(bb, 1828));
properties.put("Y scanned size", getR32(bb, 1832));
properties.put("Film manufacturer ID", getU16(bb, 1920));
properties.put("Film type", getU16(bb, 1922));
properties.put("Perfs offset", getU16(bb, 1924));
properties.put("Prefix", getU32(bb, 1926));
properties.put("Count", getU32(bb, 1932));
properties.put("Format", getStr(bb, 1936, 32));
properties.put("Frame position", getU32(bb, 1968));
properties.put("Sequence length", getU32(bb, 1972));
properties.put("Held count", getU32(bb, 1976));
properties.put("Frame rate", getR32(bb, 1980));
properties.put("Shutter angle", getR32(bb, 1984));
properties.put("Frame identification", getStr(bb, 1988, 32));
properties.put("Slate info", getStr(bb, 2020, 100));
properties.put("Time code", getU32(bb, 2176));
properties.put("User bits", getU32(bb, 2180));
properties.put("Interlace", (int) getU8(bb, 2184));
properties.put("Field number", (int) getU8(bb, 2185));
properties.put("Video signal", (int) getU8(bb, 2186));
properties.put("Zero", (int) getU8(bb, 2187));
properties.put("Horizontal sample rate", getR32(bb, 2188));
properties.put("Vertical sample rate", getR32(bb, 2192));
properties.put("Temporal frame rate", getR32(bb, 2196));
properties.put("Time offset", getR32(bb, 2200));
properties.put("Gamma", getR32(bb, 2204));
properties.put("Black level", getR32(bb, 2208));
properties.put("Black gain", getR32(bb, 2212));
properties.put("Breakpoint", getR32(bb, 2216));
properties.put("White level", getR32(bb, 2220));
properties.put("Integration times", getR32(bb, 2224));
}
private int getU32(ByteBuffer bb, int off) {
return bb.getInt(off);
}
private int getU16(ByteBuffer bb, int off) {
return bb.getShort(off) & 0xFFFF;
}
private byte getU8(ByteBuffer bb, int off) {
return bb.get(off);
}
private float getR32(ByteBuffer bb, int off) {
return bb.getFloat(off);
}
private String getStr(ByteBuffer bb, int off, int len) {
byte[] bytes = new byte[len];
bb.position(off);
bb.get(bytes);
return new String(bytes).replaceAll("\0.*", "");
}
public void printProperties() {
properties.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String outputPath) throws IOException {
Files.write(Paths.get(outputPath), headerData);
// To write full, append image data
byte[] fullData = Files.readAllBytes(Paths.get(filepath));
Files.write(Paths.get(outputPath), fullData);
}
// Usage: DPXFile dpx = new DPXFile("example.dpx"); dpx.printProperties(); dpx.write("output.dpx");
}
6. JavaScript Class for .DPX File
This JavaScript class (for Node.js) opens a .DPX file, decodes the header, reads all properties, prints them to console, and can write the header back to a new file.
const fs = require('fs');
class DPXFile {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
this.endian = true; // big-endian
this.headerData = fs.readFileSync(filepath);
this.view = new DataView(this.headerData.buffer);
this.parseHeader();
}
parseHeader() {
const magic = this.view.getUint32(0, false);
if (magic === 0x58504453) this.endian = false;
else if (magic !== 0x53445058) throw new Error('Invalid DPX file');
const getU32 = (off) => this.view.getUint32(off, this.endian);
const getU16 = (off) => this.view.getUint16(off, this.endian);
const getU8 = (off) => this.view.getUint8(off);
const getR32 = (off) => this.view.getFloat32(off, this.endian);
const getStr = (off, len) => {
let str = '';
for (let i = 0; i < len; i++) {
const char = this.view.getUint8(off + i);
if (char === 0) break;
str += String.fromCharCode(char);
}
return str;
};
this.properties['Magic number'] = getStr(0, 4);
this.properties['Offset to image data'] = getU32(4);
this.properties['Version'] = getStr(8, 8);
this.properties['File size'] = getU32(16);
this.properties['Ditto key'] = getU32(20);
this.properties['Generic header length'] = getU32(24);
this.properties['Industry header length'] = getU32(28);
this.properties['User header length'] = getU32(32);
this.properties['File name'] = getStr(36, 100);
this.properties['Creation date/time'] = getStr(136, 24);
this.properties['Creator'] = getStr(160, 100);
this.properties['Project'] = getStr(260, 200);
this.properties['Copyright'] = getStr(460, 200);
this.properties['Encryption key'] = getU32(660);
this.properties['Image orientation'] = getU16(768);
const numElem = getU16(770);
this.properties['Number of image elements'] = numElem;
this.properties['Pixels per line'] = getU32(772);
this.properties['Lines per image element'] = getU32(776);
for (let i = 0; i < numElem; i++) {
const off = 780 + i * 72;
const prefix = `Image Element ${i+1} `;
this.properties[prefix + 'Data sign'] = getU32(off);
this.properties[prefix + 'Reference low data code'] = getU32(off + 4);
this.properties[prefix + 'Reference low quantity'] = getR32(off + 8);
this.properties[prefix + 'Reference high data code'] = getU32(off + 12);
this.properties[prefix + 'Reference high quantity'] = getR32(off + 16);
this.properties[prefix + 'Descriptor'] = getU8(off + 20);
this.properties[prefix + 'Transfer characteristic'] = getU8(off + 21);
this.properties[prefix + 'Colorimetric specification'] = getU8(off + 22);
this.properties[prefix + 'Bit depth'] = getU8(off + 23);
this.properties[prefix + 'Packing'] = getU16(off + 24);
this.properties[prefix + 'Encoding'] = getU16(off + 26);
this.properties[prefix + 'Data offset'] = getU32(off + 28);
this.properties[prefix + 'End-of-line padding'] = getU32(off + 32);
this.properties[prefix + 'End-of-image padding'] = getU32(off + 36);
this.properties[prefix + 'Description'] = getStr(off + 40, 32);
}
this.properties['X offset'] = getU32(1600);
this.properties['Y offset'] = getU32(1604);
this.properties['X center'] = getR32(1608);
this.properties['Y center'] = getR32(1612);
this.properties['X original size'] = getU32(1616);
this.properties['Y original size'] = getU32(1620);
this.properties['Source image filename'] = getStr(1624, 100);
this.properties['Source image date/time'] = getStr(1724, 24);
this.properties['Input device name'] = getStr(1748, 32);
this.properties['Input device serial number'] = getStr(1780, 32);
this.properties['Border validity XL'] = getU16(1812);
this.properties['Border validity XR'] = getU16(1814);
this.properties['Border validity YT'] = getU16(1816);
this.properties['Border validity YB'] = getU16(1818);
this.properties['Aspect ratio horizontal'] = getU32(1820);
this.properties['Aspect ratio vertical'] = getU32(1824);
this.properties['X scanned size'] = getR32(1828);
this.properties['Y scanned size'] = getR32(1832);
this.properties['Film manufacturer ID'] = getU16(1920);
this.properties['Film type'] = getU16(1922);
this.properties['Perfs offset'] = getU16(1924);
this.properties['Prefix'] = getU32(1926);
this.properties['Count'] = getU32(1932);
this.properties['Format'] = getStr(1936, 32);
this.properties['Frame position'] = getU32(1968);
this.properties['Sequence length'] = getU32(1972);
this.properties['Held count'] = getU32(1976);
this.properties['Frame rate'] = getR32(1980);
this.properties['Shutter angle'] = getR32(1984);
this.properties['Frame identification'] = getStr(1988, 32);
this.properties['Slate info'] = getStr(2020, 100);
this.properties['Time code'] = getU32(2176);
this.properties['User bits'] = getU32(2180);
this.properties['Interlace'] = getU8(2184);
this.properties['Field number'] = getU8(2185);
this.properties['Video signal'] = getU8(2186);
this.properties['Zero'] = getU8(2187);
this.properties['Horizontal sample rate'] = getR32(2188);
this.properties['Vertical sample rate'] = getR32(2192);
this.properties['Temporal frame rate'] = getR32(2196);
this.properties['Time offset'] = getR32(2200);
this.properties['Gamma'] = getR32(2204);
this.properties['Black level'] = getR32(2208);
this.properties['Black gain'] = getR32(2212);
this.properties['Breakpoint'] = getR32(2216);
this.properties['White level'] = getR32(2220);
this.properties['Integration times'] = getR32(2224);
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(outputPath) {
fs.writeFileSync(outputPath, this.headerData);
// To write full file
const fullData = fs.readFileSync(this.filepath);
fs.writeFileSync(outputPath, fullData);
}
}
// Usage: const dpx = new DPXFile('example.dpx'); dpx.printProperties(); dpx.write('output.dpx');
7. C Class for .DPX File
This C++ class opens a .DPX file, decodes the header, reads all properties, prints them to console, and can write the header back to a new file.
#include <iostream>
#include <fstream>
#include <cstring>
#include <vector>
#include <map>
#include <endian.h> // For endian conversion if needed
class DPXFile {
private:
std::string filepath;
std::map<std::string, std::string> properties;
bool bigEndian = true;
std::vector<char> headerData;
std::ifstream file;
public:
DPXFile(const std::string& fp) : filepath(fp) {
file.open(filepath, std::ios::binary);
if (!file) throw std::runtime_error("Cannot open file");
headerData.resize(2304);
file.read(headerData.data(), 2304);
if (file.gcount() < 2304) throw std::runtime_error("Invalid DPX file");
uint32_t magic;
memcpy(&magic, headerData.data(), 4);
if (magic == 0x53445058) bigEndian = true;
else if (magic == 0x58504453) bigEndian = false;
else throw std::runtime_error("Invalid magic number");
parseHeader();
file.close();
}
void parseHeader() {
// Helper functions
auto getU32 = [&](size_t off) -> uint32_t {
uint32_t val;
memcpy(&val, headerData.data() + off, 4);
return bigEndian ? be32toh(val) : le32toh(val);
};
auto getU16 = [&](size_t off) -> uint16_t {
uint16_t val;
memcpy(&val, headerData.data() + off, 2);
return bigEndian ? be16toh(val) : le16toh(val);
};
auto getU8 = [&](size_t off) -> uint8_t {
return static_cast<uint8_t>(headerData[off]);
};
auto getR32 = [&](size_t off) -> float {
uint32_t val = getU32(off);
float f;
memcpy(&f, &val, 4);
return f;
};
auto getStr = [&](size_t off, size_t len) -> std::string {
std::string str(headerData.begin() + off, headerData.begin() + off + len);
str = str.substr(0, str.find('\0'));
return str;
};
properties["Magic number"] = getStr(0, 4);
properties["Offset to image data"] = std::to_string(getU32(4));
properties["Version"] = getStr(8, 8);
properties["File size"] = std::to_string(getU32(16));
properties["Ditto key"] = std::to_string(getU32(20));
properties["Generic header length"] = std::to_string(getU32(24));
properties["Industry header length"] = std::to_string(getU32(28));
properties["User header length"] = std::to_string(getU32(32));
properties["File name"] = getStr(36, 100);
properties["Creation date/time"] = getStr(136, 24);
properties["Creator"] = getStr(160, 100);
properties["Project"] = getStr(260, 200);
properties["Copyright"] = getStr(460, 200);
properties["Encryption key"] = std::to_string(getU32(660));
properties["Image orientation"] = std::to_string(getU16(768));
uint16_t numElem = getU16(770);
properties["Number of image elements"] = std::to_string(numElem);
properties["Pixels per line"] = std::to_string(getU32(772));
properties["Lines per image element"] = std::to_string(getU32(776));
for (uint16_t i = 0; i < numElem; ++i) {
size_t off = 780 + i * 72;
std::string prefix = "Image Element " + std::to_string(i + 1) + " ";
properties[prefix + "Data sign"] = std::to_string(getU32(off));
properties[prefix + "Reference low data code"] = std::to_string(getU32(off + 4));
properties[prefix + "Reference low quantity"] = std::to_string(getR32(off + 8));
properties[prefix + "Reference high data code"] = std::to_string(getU32(off + 12));
properties[prefix + "Reference high quantity"] = std::to_string(getR32(off + 16));
properties[prefix + "Descriptor"] = std::to_string(getU8(off + 20));
properties[prefix + "Transfer characteristic"] = std::to_string(getU8(off + 21));
properties[prefix + "Colorimetric specification"] = std::to_string(getU8(off + 22));
properties[prefix + "Bit depth"] = std::to_string(getU8(off + 23));
properties[prefix + "Packing"] = std::to_string(getU16(off + 24));
properties[prefix + "Encoding"] = std::to_string(getU16(off + 26));
properties[prefix + "Data offset"] = std::to_string(getU32(off + 28));
properties[prefix + "End-of-line padding"] = std::to_string(getU32(off + 32));
properties[prefix + "End-of-image padding"] = std::to_string(getU32(off + 36));
properties[prefix + "Description"] = getStr(off + 40, 32);
}
properties["X offset"] = std::to_string(getU32(1600));
properties["Y offset"] = std::to_string(getU32(1604));
properties["X center"] = std::to_string(getR32(1608));
properties["Y center"] = std::to_string(getR32(1612));
properties["X original size"] = std::to_string(getU32(1616));
properties["Y original size"] = std::to_string(getU32(1620));
properties["Source image filename"] = getStr(1624, 100);
properties["Source image date/time"] = getStr(1724, 24);
properties["Input device name"] = getStr(1748, 32);
properties["Input device serial number"] = getStr(1780, 32);
properties["Border validity XL"] = std::to_string(getU16(1812));
properties["Border validity XR"] = std::to_string(getU16(1814));
properties["Border validity YT"] = std::to_string(getU16(1816));
properties["Border validity YB"] = std::to_string(getU16(1818));
properties["Aspect ratio horizontal"] = std::to_string(getU32(1820));
properties["Aspect ratio vertical"] = std::to_string(getU32(1824));
properties["X scanned size"] = std::to_string(getR32(1828));
properties["Y scanned size"] = std::to_string(getR32(1832));
properties["Film manufacturer ID"] = std::to_string(getU16(1920));
properties["Film type"] = std::to_string(getU16(1922));
properties["Perfs offset"] = std::to_string(getU16(1924));
properties["Prefix"] = std::to_string(getU32(1926));
properties["Count"] = std::to_string(getU32(1932));
properties["Format"] = getStr(1936, 32);
properties["Frame position"] = std::to_string(getU32(1968));
properties["Sequence length"] = std::to_string(getU32(1972));
properties["Held count"] = std::to_string(getU32(1976));
properties["Frame rate"] = std::to_string(getR32(1980));
properties["Shutter angle"] = std::to_string(getR32(1984));
properties["Frame identification"] = getStr(1988, 32);
properties["Slate info"] = getStr(2020, 100);
properties["Time code"] = std::to_string(getU32(2176));
properties["User bits"] = std::to_string(getU32(2180));
properties["Interlace"] = std::to_string(getU8(2184));
properties["Field number"] = std::to_string(getU8(2185));
properties["Video signal"] = std::to_string(getU8(2186));
properties["Zero"] = std::to_string(getU8(2187));
properties["Horizontal sample rate"] = std::to_string(getR32(2188));
properties["Vertical sample rate"] = std::to_string(getR32(2192));
properties["Temporal frame rate"] = std::to_string(getR32(2196));
properties["Time offset"] = std::to_string(getR32(2200));
properties["Gamma"] = std::to_string(getR32(2204));
properties["Black level"] = std::to_string(getR32(2208));
properties["Black gain"] = std::to_string(getR32(2212));
properties["Breakpoint"] = std::to_string(getR32(2216));
properties["White level"] = std::to_string(getR32(2220));
properties["Integration times"] = std::to_string(getR32(2224));
}
void printProperties() {
for (const auto& prop : properties) {
std::cout << prop.first << ": " << prop.second << std::endl;
}
}
void write(const std::string& outputPath) {
std::ofstream out(outputPath, std::ios::binary);
out.write(headerData.data(), headerData.size());
// To write full file
std::ifstream orig(filepath, std::ios::binary);
orig.seekg(getU32(4), std::ios::beg);
char buf[1024];
while (orig.read(buf, 1024)) {
out.write(buf, orig.gcount());
}
out.close();
}
private:
uint32_t getU32(size_t off) {
uint32_t val;
memcpy(&val, headerData.data() + off, 4);
return bigEndian ? be32toh(val) : le32toh(val);
}
};
// Usage: DPXFile dpx("example.dpx"); dpx.printProperties(); dpx.write("output.dpx");