Work on DPX import
This commit is contained in:
parent
d906731fa7
commit
cc6639606a
|
@ -4,3 +4,6 @@
|
|||
[submodule "include/args"]
|
||||
path = include/args
|
||||
url = https://github.com/Taywee/args.git
|
||||
[submodule "include/dpx"]
|
||||
path = include/dpx
|
||||
url = https://github.com/malcolmhumphreys/dpx.git
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3ba0f19e8fbf4becf165d7deda2c8cfb334e6c8c
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
#ifndef DPX_HPP
|
||||
#define DPX_HPP
|
||||
|
||||
|
@ -12,55 +13,61 @@ using namespace cv;
|
|||
|
||||
class DPX {
|
||||
public:
|
||||
DPX();
|
||||
DPX(const std::string& filepath);
|
||||
~DPX() = default;
|
||||
|
||||
bool read(const string& filename);
|
||||
Mat getMat() const;
|
||||
Mat getMatBitDepth(int targetBitDepth) const;
|
||||
// Get the loaded raw image data
|
||||
cv::Mat getRawImage() const { return raw; }
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
int getBitDepth() const;
|
||||
// Get image properties
|
||||
int getBitDepth() const { return bitDepth; }
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
|
||||
// Convert to 8-bit or 10-bit representation
|
||||
cv::Mat convertTo8Bit() const;
|
||||
cv::Mat convertTo10Bit() const;
|
||||
|
||||
private:
|
||||
struct DPXHeader {
|
||||
uint32_t magic; // Magic number (0x53445058 "SDPX" or 0x58504453 "XPDS")
|
||||
uint32_t magic; // Magic number (0x53445058 or 'SDPX')
|
||||
uint32_t imageOffset; // Offset to image data
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t descriptor; // Image descriptor
|
||||
uint8_t bitDepth; // Bits per component
|
||||
uint8_t imageElements; // Number of image elements
|
||||
bool isBigEndian; // File endianness
|
||||
uint32_t fileVersion; // Version number of header format
|
||||
uint32_t fileSize; // Total image file size in bytes
|
||||
uint16_t dittoKey; // Image content flag
|
||||
uint16_t headerSize; // Generic header length in bytes
|
||||
uint32_t industrySize; // Industry specific header length
|
||||
uint32_t userSize; // User defined data length
|
||||
uint32_t encryptKey; // Encryption key
|
||||
};
|
||||
|
||||
DPXHeader header;
|
||||
Mat image;
|
||||
struct DPXImageElement {
|
||||
uint32_t dataSign; // Data sign (0 = unsigned, 1 = signed)
|
||||
uint32_t lowData; // Reference low data code value
|
||||
uint32_t lowQuantity; // Reference low quantity
|
||||
uint32_t highData; // Reference high data code value
|
||||
uint32_t highQuantity; // Reference high quantity
|
||||
uint8_t descriptor; // Descriptor for image element
|
||||
uint8_t transfer; // Transfer characteristics
|
||||
uint8_t colorimetric; // Colorimetric specification
|
||||
uint8_t bitDepth; // Bit depth
|
||||
uint16_t packing; // Packing method
|
||||
uint16_t encoding; // Encoding method
|
||||
};
|
||||
|
||||
bool readHeader(ifstream& file);
|
||||
void readImageData(ifstream& file);
|
||||
uint32_t swapEndian(uint32_t value);
|
||||
uint16_t swapEndian(uint16_t value);
|
||||
cv::Mat raw; // Raw image data
|
||||
int bitDepth; // Image bit depth
|
||||
int width; // Image width
|
||||
int height; // Image height
|
||||
|
||||
// Private helper methods
|
||||
void readHeader(std::ifstream& file);
|
||||
void readImageData(std::ifstream& file, uint32_t imageOffset);
|
||||
void processImageData(std::vector<uint8_t>& buffer);
|
||||
void unpack10BitData(const std::vector<uint8_t>& buffer, cv::Mat& output);
|
||||
void swapEndianness(uint8_t* data, size_t size);
|
||||
bool isLittleEndian() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/* Example usage
|
||||
int main() {
|
||||
try {
|
||||
DPX dpx;
|
||||
dpx.read("image.dpx");
|
||||
|
||||
// Get the original 16-bit Mat
|
||||
Mat mat16 = dpx.getMat();
|
||||
|
||||
// Get 8-bit version
|
||||
Mat mat8 = dpx.getMatBitDepth(8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Error: " << e.what() << endl;
|
||||
return 1;
|
||||
}
|
||||
}*/
|
||||
*/
|
|
@ -17,7 +17,7 @@
|
|||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#include "dpx.hpp"
|
||||
#include "dpx/include/dpx.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
|
353
src/dpx.cpp
353
src/dpx.cpp
|
@ -1,262 +1,133 @@
|
|||
#include "dpx.hpp"
|
||||
|
||||
DPX::DPX() {}
|
||||
|
||||
uint32_t DPX::swapEndian(uint32_t value) {
|
||||
return ((value & 0xFF000000) >> 24) |
|
||||
((value & 0x00FF0000) >> 8) |
|
||||
((value & 0x0000FF00) << 8) |
|
||||
((value & 0x000000FF) << 24);
|
||||
}
|
||||
|
||||
uint16_t DPX::swapEndian(uint16_t value) {
|
||||
return ((value & 0xFF00) >> 8) |
|
||||
((value & 0x00FF) << 8);
|
||||
}
|
||||
|
||||
bool DPX::readHeader(ifstream& file) {
|
||||
// DPX Header Map for reference:
|
||||
// 0x0000-0x0003: Magic number
|
||||
// 0x0004-0x0007: Image data offset
|
||||
// 0x0008-0x000B: Version number of header format
|
||||
// 0x000C-0x000F: Total image file size
|
||||
// 0x0010-0x0013: Ditto Key
|
||||
// 0x0014-0x0017: Generic section header length
|
||||
// 0x0018-0x001B: Industry specific header length
|
||||
// 0x001C-0x001F: User defined header length
|
||||
|
||||
// Read and check magic number
|
||||
file.read(reinterpret_cast<char*>(&header.magic), sizeof(header.magic));
|
||||
|
||||
const uint32_t MAGIC_BE = 0x53445058; // "SDPX"
|
||||
const uint32_t MAGIC_LE = 0x58504453; // "XPDS"
|
||||
|
||||
cout << "{ \"dpx_magic_number\" : \"0x" << hex << header.magic << dec << "\" }" << endl;
|
||||
|
||||
if (header.magic == MAGIC_BE) {
|
||||
header.isBigEndian = true;
|
||||
} else if (header.magic == MAGIC_LE) {
|
||||
header.isBigEndian = false;
|
||||
} else {
|
||||
throw runtime_error("Not a valid DPX file - Invalid magic number");
|
||||
}
|
||||
|
||||
// Read Core Header
|
||||
uint32_t genericHeaderLength;
|
||||
file.seekg(0x14); // Go to generic header length
|
||||
file.read(reinterpret_cast<char*>(&genericHeaderLength), sizeof(genericHeaderLength));
|
||||
if (!header.isBigEndian) {
|
||||
genericHeaderLength = swapEndian(genericHeaderLength);
|
||||
}
|
||||
cout << "{ \"dpx_header_length\" : " << genericHeaderLength << " }" << endl;
|
||||
|
||||
// Image Information Header starts at 0x0200 (512)
|
||||
file.seekg(0x0200);
|
||||
|
||||
// Read orientation
|
||||
uint32_t orientation;
|
||||
file.read(reinterpret_cast<char*>(&orientation), sizeof(orientation));
|
||||
if (!header.isBigEndian) {
|
||||
orientation = swapEndian(orientation);
|
||||
}
|
||||
|
||||
// Read number of image elements
|
||||
uint16_t numberOfElements;
|
||||
file.read(reinterpret_cast<char*>(&numberOfElements), sizeof(numberOfElements));
|
||||
if (!header.isBigEndian) {
|
||||
numberOfElements = swapEndian(numberOfElements);
|
||||
}
|
||||
|
||||
// Read image dimensions
|
||||
file.read(reinterpret_cast<char*>(&header.width), sizeof(header.width));
|
||||
file.read(reinterpret_cast<char*>(&header.height), sizeof(header.height));
|
||||
if (!header.isBigEndian) {
|
||||
header.width = swapEndian(header.width);
|
||||
header.height = swapEndian(header.height);
|
||||
}
|
||||
|
||||
cout << "{ \"dpx_width\" : " << header.width << ", \"dpx_height\" :" << header.height << " }" << endl;
|
||||
|
||||
// Image Element Information starts at 0x0604 (1540)
|
||||
file.seekg(0x0604);
|
||||
|
||||
// Read data sign (0 = unsigned)
|
||||
uint32_t dataSign;
|
||||
file.read(reinterpret_cast<char*>(&dataSign), sizeof(dataSign));
|
||||
if (!header.isBigEndian) {
|
||||
dataSign = swapEndian(dataSign);
|
||||
}
|
||||
|
||||
// Read bit depth
|
||||
file.read(reinterpret_cast<char*>(&header.bitDepth), sizeof(header.bitDepth));
|
||||
cout << "{ \"dpx_bit_depth\" : " << static_cast<int>(header.bitDepth) << " }"<< endl;
|
||||
|
||||
// Read packing method
|
||||
uint16_t packing;
|
||||
file.read(reinterpret_cast<char*>(&packing), sizeof(packing));
|
||||
if (!header.isBigEndian) {
|
||||
packing = swapEndian(packing);
|
||||
}
|
||||
cout << "{ \"dpx_packing\" : " << packing << " }" << endl;
|
||||
|
||||
// Read image offset (from start of file)
|
||||
file.seekg(0x04);
|
||||
file.read(reinterpret_cast<char*>(&header.imageOffset), sizeof(header.imageOffset));
|
||||
if (!header.isBigEndian) {
|
||||
header.imageOffset = swapEndian(header.imageOffset);
|
||||
}
|
||||
cout << "{ \"dpx_offset\" : " << header.imageOffset << " }" << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DPX::readImageData(ifstream& file) {
|
||||
file.seekg(header.imageOffset);
|
||||
|
||||
image = Mat(header.height, header.width, CV_16UC3);
|
||||
|
||||
if (header.bitDepth == 10) {
|
||||
size_t packedSize = ((header.width * header.height * 3 * 10 + 31) / 32) * 4;
|
||||
vector<uint8_t> buffer(packedSize);
|
||||
|
||||
file.read(reinterpret_cast<char*>(buffer.data()), packedSize);
|
||||
|
||||
uint16_t* imgPtr = reinterpret_cast<uint16_t*>(image.data);
|
||||
size_t pixelCount = 0;
|
||||
|
||||
for (size_t i = 0; i < packedSize - 3; i += 4) {
|
||||
// Extract four bytes
|
||||
uint32_t b0 = buffer[i];
|
||||
uint32_t b1 = buffer[i + 1];
|
||||
uint32_t b2 = buffer[i + 2];
|
||||
uint32_t b3 = buffer[i + 3];
|
||||
|
||||
// Combine bytes according to endianness
|
||||
uint32_t packed;
|
||||
if (header.isBigEndian) {
|
||||
packed = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
||||
} else {
|
||||
packed = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
|
||||
}
|
||||
|
||||
// Extract three 10-bit values
|
||||
uint16_t pix1 = (packed >> 22) & 0x3FF;
|
||||
uint16_t pix2 = (packed >> 12) & 0x3FF;
|
||||
uint16_t pix3 = (packed >> 2) & 0x3FF;
|
||||
|
||||
// Scale to 16-bit
|
||||
pix1 <<= 6;
|
||||
pix2 <<= 6;
|
||||
pix3 <<= 6;
|
||||
|
||||
if (pixelCount < header.width * header.height * 3) {
|
||||
size_t row = (pixelCount / 3) / header.width;
|
||||
size_t col = (pixelCount / 3) % header.width;
|
||||
size_t channel = pixelCount % 3;
|
||||
|
||||
size_t idx = row * header.width * 3 + col * 3 + (2 - channel);
|
||||
imgPtr[idx] = pix1;
|
||||
|
||||
if (pixelCount + 1 < header.width * header.height * 3) {
|
||||
channel = (pixelCount + 1) % 3;
|
||||
idx = ((pixelCount + 1) / 3) / header.width * header.width * 3 +
|
||||
((pixelCount + 1) / 3) % header.width * 3 + (2 - channel);
|
||||
imgPtr[idx] = pix2;
|
||||
}
|
||||
|
||||
if (pixelCount + 2 < header.width * header.height * 3) {
|
||||
channel = (pixelCount + 2) % 3;
|
||||
idx = ((pixelCount + 2) / 3) / header.width * header.width * 3 +
|
||||
((pixelCount + 2) / 3) % header.width * 3 + (2 - channel);
|
||||
imgPtr[idx] = pix3;
|
||||
}
|
||||
}
|
||||
|
||||
pixelCount += 3;
|
||||
}
|
||||
} else {
|
||||
// Handle other bit depths (8, 12, 16)
|
||||
size_t bytesPerPixel = (header.bitDepth + 7) / 8;
|
||||
vector<uint8_t> buffer(header.width * header.height * 3 * bytesPerPixel);
|
||||
|
||||
file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
|
||||
|
||||
uint16_t* imgPtr = reinterpret_cast<uint16_t*>(image.data);
|
||||
|
||||
for (int y = 0; y < header.height; y++) {
|
||||
for (int x = 0; x < header.width; x++) {
|
||||
for (int c = 0; c < 3; c++) {
|
||||
size_t idx = (y * header.width * 3 + x * 3 + c) * bytesPerPixel;
|
||||
uint16_t pixel = 0;
|
||||
|
||||
switch (header.bitDepth) {
|
||||
case 8:
|
||||
pixel = buffer[idx] << 8;
|
||||
break;
|
||||
case 12:
|
||||
if (header.isBigEndian) {
|
||||
pixel = ((uint16_t)buffer[idx] << 8) | buffer[idx + 1];
|
||||
} else {
|
||||
pixel = ((uint16_t)buffer[idx + 1] << 8) | buffer[idx];
|
||||
}
|
||||
pixel = pixel << 4;
|
||||
break;
|
||||
case 16:
|
||||
if (header.isBigEndian) {
|
||||
pixel = ((uint16_t)buffer[idx] << 8) | buffer[idx + 1];
|
||||
} else {
|
||||
pixel = ((uint16_t)buffer[idx + 1] << 8) | buffer[idx];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
imgPtr[y * header.width * 3 + x * 3 + (2 - c)] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DPX::read(const string& filename) {
|
||||
ifstream file(filename, ios::binary);
|
||||
/*
|
||||
DPX::DPX(const std::string& filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
throw runtime_error("Could not open file: " + filename);
|
||||
throw std::runtime_error("Failed to open DPX file: " + filepath);
|
||||
}
|
||||
|
||||
if (!readHeader(file)) {
|
||||
return false;
|
||||
readHeader(file);
|
||||
readImageData(file, header.imageOffset);
|
||||
}
|
||||
|
||||
void DPX::readHeader(std::ifstream& file) {
|
||||
DPXHeader header;
|
||||
file.read(reinterpret_cast<char*>(&header), sizeof(DPXHeader));
|
||||
|
||||
// Check magic number (SDPX in ASCII)
|
||||
if (header.magic != 0x53445058) {
|
||||
if (header.magic == 0x58504453) { // XPDS - wrong endianness
|
||||
swapEndianness(reinterpret_cast<uint8_t*>(&header), sizeof(DPXHeader));
|
||||
} else {
|
||||
throw std::runtime_error("Invalid DPX file format");
|
||||
}
|
||||
}
|
||||
|
||||
readImageData(file);
|
||||
return true;
|
||||
// Read image information header (located after main header)
|
||||
uint32_t orientation;
|
||||
file.read(reinterpret_cast<char*>(&orientation), sizeof(uint32_t));
|
||||
file.read(reinterpret_cast<char*>(&width), sizeof(uint32_t));
|
||||
file.read(reinterpret_cast<char*>(&height), sizeof(uint32_t));
|
||||
|
||||
// Read image element information
|
||||
DPXImageElement element;
|
||||
file.read(reinterpret_cast<char*>(&element), sizeof(DPXImageElement));
|
||||
|
||||
bitDepth = element.bitDepth;
|
||||
}
|
||||
|
||||
Mat DPX::getMat() const {
|
||||
return image;
|
||||
void DPX::readImageData(std::ifstream& file, uint32_t imageOffset) {
|
||||
// Seek to image data
|
||||
file.seekg(imageOffset, std::ios::beg);
|
||||
|
||||
// Calculate buffer size based on bit depth and dimensions
|
||||
size_t bytesPerPixel = (bitDepth + 7) / 8;
|
||||
size_t rowPadding = (width * 3 * bytesPerPixel) % 4 ? 4 - ((width * 3 * bytesPerPixel) % 4) : 0;
|
||||
size_t bufferSize = (width * 3 * bytesPerPixel + rowPadding) * height;
|
||||
|
||||
// Read image data into buffer
|
||||
std::vector<uint8_t> buffer(bufferSize);
|
||||
file.read(reinterpret_cast<char*>(buffer.data()), bufferSize);
|
||||
|
||||
processImageData(buffer);
|
||||
}
|
||||
|
||||
Mat DPX::getMatBitDepth(int targetBitDepth) const {
|
||||
if (targetBitDepth != 8 && targetBitDepth != 10) {
|
||||
throw invalid_argument("Target bit depth must be 8 or 10");
|
||||
void DPX::processImageData(std::vector<uint8_t>& buffer) {
|
||||
switch (bitDepth) {
|
||||
case 10:
|
||||
raw = cv::Mat(height, width, CV_16UC3);
|
||||
unpack10BitData(buffer, raw);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
raw = cv::Mat(height, width, CV_16UC3);
|
||||
// Handle 16-bit data
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int c = 0; c < 3; c++) {
|
||||
size_t idx = (y * width + x) * 6 + c * 2;
|
||||
uint16_t value = (buffer[idx] << 8) | buffer[idx + 1];
|
||||
raw.at<cv::Vec3w>(y, x)[c] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unsupported bit depth: " + std::to_string(bitDepth));
|
||||
}
|
||||
}
|
||||
|
||||
Mat result;
|
||||
if (targetBitDepth == 8) {
|
||||
image.convertTo(result, CV_8UC3, 1.0/256.0);
|
||||
} else if (targetBitDepth == 10) {
|
||||
image.convertTo(result, CV_16UC3, 1.0/64.0);
|
||||
void DPX::unpack10BitData(const std::vector<uint8_t>& buffer, cv::Mat& output) {
|
||||
// Handle 10-bit packed data (DaVinci Resolve format)
|
||||
// 3 pixels (9 bytes) -> 3 * 10-bit values
|
||||
size_t pixelIdx = 0;
|
||||
for (size_t i = 0; i < buffer.size() - 8; i += 9) {
|
||||
int y = pixelIdx / width;
|
||||
int x = pixelIdx % width;
|
||||
|
||||
if (y >= height) break;
|
||||
|
||||
// Unpack 3 10-bit values from 9 bytes
|
||||
uint32_t val1 = ((buffer[i] << 2) | (buffer[i + 1] >> 6)) & 0x3FF;
|
||||
uint32_t val2 = ((buffer[i + 1] << 4) | (buffer[i + 2] >> 4)) & 0x3FF;
|
||||
uint32_t val3 = ((buffer[i + 2] << 6) | (buffer[i + 3] >> 2)) & 0x3FF;
|
||||
|
||||
// Scale to 16-bit
|
||||
output.at<cv::Vec3w>(y, x)[0] = static_cast<uint16_t>(val1 << 6);
|
||||
output.at<cv::Vec3w>(y, x)[1] = static_cast<uint16_t>(val2 << 6);
|
||||
output.at<cv::Vec3w>(y, x)[2] = static_cast<uint16_t>(val3 << 6);
|
||||
|
||||
pixelIdx++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int DPX::getWidth() const {
|
||||
return header.width;
|
||||
cv::Mat DPX::convertTo8Bit() const {
|
||||
cv::Mat output;
|
||||
raw.convertTo(output, CV_8UC3, 1.0 / 256.0);
|
||||
return output;
|
||||
}
|
||||
|
||||
int DPX::getHeight() const {
|
||||
return header.height;
|
||||
cv::Mat DPX::convertTo10Bit() const {
|
||||
cv::Mat output;
|
||||
if (bitDepth == 16) {
|
||||
raw.convertTo(output, CV_16UC3, 1.0 / 64.0);
|
||||
} else {
|
||||
output = raw.clone();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
int DPX::getBitDepth() const {
|
||||
return header.bitDepth;
|
||||
void DPX::swapEndianness(uint8_t* data, size_t size) {
|
||||
for (size_t i = 0; i < size/2; i++) {
|
||||
std::swap(data[i], data[size - 1 - i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool DPX::isLittleEndian() const {
|
||||
uint16_t number = 0x1;
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(&number);
|
||||
return (*ptr == 1);
|
||||
}
|
||||
*/
|
|
@ -16,9 +16,10 @@ Mat Image::loadImage (string& image_path, uint64_t& x, uint64_t& y, uint64_t& w,
|
|||
string ext = getExtLower(located_path);
|
||||
//cout << "{ \"image_extension\" : \"" << ext << "\" }" << endl;
|
||||
if (ext == "dpx") {
|
||||
DPX dpx;
|
||||
dpx.read(located_path);
|
||||
loaded = dpx.getMatBitDepth(8);
|
||||
DpxReader in;
|
||||
//in.open(image_path);
|
||||
//DPX dpx(located_path);
|
||||
//loaded = dpx.convertTo8Bit();
|
||||
cout << "{ \"dpx\" : \"" << image_path << "\" }" << endl;
|
||||
} else {
|
||||
loaded = imread(located_path, IMREAD_COLOR);
|
||||
|
|
Loading…
Reference in New Issue