From 095ed397b2aecf88d0f25f02858065956e70beca Mon Sep 17 00:00:00 2001 From: mattmcw Date: Thu, 7 Nov 2024 18:07:28 -0500 Subject: [PATCH] Work on DPX file import. Currently breaking --- include/dpx.hpp | 66 ++++++++++++++ include/dxf.hpp | 80 ---------------- include/image.hpp | 2 +- src/dpx.cpp | 226 ++++++++++++++++++++++++++++++++++++++++++++++ src/dxf.cpp | 186 -------------------------------------- src/image.cpp | 10 +- 6 files changed, 300 insertions(+), 270 deletions(-) create mode 100644 include/dpx.hpp delete mode 100644 include/dxf.hpp create mode 100644 src/dpx.cpp delete mode 100644 src/dxf.cpp diff --git a/include/dpx.hpp b/include/dpx.hpp new file mode 100644 index 0000000..169d6fb --- /dev/null +++ b/include/dpx.hpp @@ -0,0 +1,66 @@ +#ifndef DPX_HPP +#define DPX_HPP + +#include +#include +#include +#include +#include + +using namespace std; +using namespace cv; + +class DPX { +public: + DPX(); + + bool read(const string& filename); + Mat getMat() const; + Mat getMatBitDepth(int targetBitDepth) const; + + int getWidth() const; + int getHeight() const; + int getBitDepth() const; + +private: + struct DPXHeader { + uint32_t magic; // Magic number (0x53445058 "SDPX" or 0x58504453 "XPDS") + 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 + }; + + DPXHeader header; + Mat image; + + bool readHeader(ifstream& file); + void readImageData(ifstream& file); + uint32_t swapEndian(uint32_t value); + uint16_t swapEndian(uint16_t value); +}; + +#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; + } +}*/ \ No newline at end of file diff --git a/include/dxf.hpp b/include/dxf.hpp deleted file mode 100644 index c2a54ba..0000000 --- a/include/dxf.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include -#include -#include - -using namespace cv; -using namespace std; - -struct DXFHeader { - int32_t width; - int32_t height; - uint8_t bitDepth; - uint8_t version; - uint8_t flags; - uint8_t reserved; -}; - -class DXF { - public: - explicit DXF(const string& filepath) : - _filepath(filepath), - _width(0), - _height(0), - _bitDepth(0) { - analyzeFile(); - loadImageData(); - } - - int getWidth() const { return _width; } - int getHeight() const { return _height; } - int getBitDepth() const { return _bitDepth; } - bool hasDXFHeader() const { return true; } - - Mat to8bitBGR() const; - Mat to10bitBGR() const; - - private: - string _filepath; - int _width; - int _height; - int _bitDepth; - Mat _imageData; - - void analyzeFile(); - bool readDXFHeader(ifstream& file); - void analyzeDXFContent(ifstream& file); - int detectBitDepth(const vector& sample); - void loadImageData(); - void unpackBits(const std::vector& rawData); - void calculateDimensions(streamsize fileSize); - void validateImageProperties(); - - static uint16_t extractBits(const std::vector& buffer, size_t bitPos, int bitDepth, uint16_t maxValue); -}; - -/* Example usage -int main() { - try { - DXF dxf("sample.dxf"); - - cout << "Image dimensions: " << dxf.getWidth() << "x" << - dxf.getHeight() << endl; - cout << "Original bit depth: " << dxf.getBitDepth() << endl; - - Mat bgr8bit = dxf.to8BitBGR(); - Mat bgr10bit = dxf.to10BitBGR(); - - - imwrite("output_8bit_bgr.png", bgr8bit); - imwrite("output_10bit_bgr.png", bgr10bit); - - return 0; - } - catch (const exception& e) { - cerr << "Error: " << e.what() << endl; - return 1; - } -}*/ \ No newline at end of file diff --git a/include/image.hpp b/include/image.hpp index 793f857..206df6c 100644 --- a/include/image.hpp +++ b/include/image.hpp @@ -17,7 +17,7 @@ #include #include -#include "dxf.hpp" +#include "dpx.hpp" using namespace std; using namespace cv; diff --git a/src/dpx.cpp b/src/dpx.cpp new file mode 100644 index 0000000..58ba324 --- /dev/null +++ b/src/dpx.cpp @@ -0,0 +1,226 @@ +#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) { + // Read magic number + file.read(reinterpret_cast(&header.magic), sizeof(header.magic)); + + // Check for both possible magic numbers (big and little endian) + const uint32_t MAGIC_BE = 0x53445058; // "SDPX" in big endian + const uint32_t MAGIC_LE = 0x58504453; // "XPDS" in little endian + + cout << "{ \"dpx_magic_number\" : \"" << hex << header.magic << "\" }" << endl; + + if (header.magic == MAGIC_BE) { + header.isBigEndian = true; + } else if (header.magic == MAGIC_LE) { + header.isBigEndian = false; + } else { + uint32_t swapped_magic = swapEndian(header.magic); + cout << "{ \"dpx_swapped_magic_number\" : \"0x" << hex << swapped_magic << "\" }" << endl; + + if (swapped_magic == MAGIC_BE) { + header.isBigEndian = true; + header.magic = swapped_magic; + } else if (swapped_magic == MAGIC_LE) { + header.isBigEndian = false; + header.magic = swapped_magic; + } else { + throw runtime_error("Not a valid DPX file - Invalid magic number"); + } + } + + // Read image data offset + file.seekg(4); + file.read(reinterpret_cast(&header.imageOffset), sizeof(header.imageOffset)); + if (header.isBigEndian) { + header.imageOffset = swapEndian(header.imageOffset); + } + + // Seek to image information offset (768 bytes from start) + file.seekg(768); + file.read(reinterpret_cast(&header.width), sizeof(header.width)); + file.read(reinterpret_cast(&header.height), sizeof(header.height)); + + if (header.isBigEndian) { + header.width = swapEndian(header.width); + header.height = swapEndian(header.height); + } + + cout << "{ \"dimensions\" : { \"width\" : " << header.width << ", \"height\" :" << header.height << " }, "; + + // Read image element information (1556 bytes from start) + file.seekg(1556); + file.read(reinterpret_cast(&header.descriptor), sizeof(header.descriptor)); + file.read(reinterpret_cast(&header.bitDepth), sizeof(header.bitDepth)); + file.read(reinterpret_cast(&header.imageElements), sizeof(header.imageElements)); + + cout << " \"bit_depth\" : " << static_cast(header.bitDepth) << ", "; + cout << " \"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 buffer(packedSize); + + file.read(reinterpret_cast(buffer.data()), packedSize); + + uint16_t* imgPtr = reinterpret_cast(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 buffer(header.width * header.height * 3 * bytesPerPixel); + + file.read(reinterpret_cast(buffer.data()), buffer.size()); + + uint16_t* imgPtr = reinterpret_cast(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); + if (!file.is_open()) { + throw runtime_error("Could not open file: " + filename); + } + + if (!readHeader(file)) { + return false; + } + + readImageData(file); + return true; +} + +Mat DPX::getMat() const { + return image; +} + +Mat DPX::getMatBitDepth(int targetBitDepth) const { + if (targetBitDepth != 8 && targetBitDepth != 10) { + throw invalid_argument("Target bit depth must be 8 or 10"); + } + + 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); + } + return result; +} + +int DPX::getWidth() const { + return header.width; +} + +int DPX::getHeight() const { + return header.height; +} + +int DPX::getBitDepth() const { + return header.bitDepth; +} \ No newline at end of file diff --git a/src/dxf.cpp b/src/dxf.cpp deleted file mode 100644 index 109feb8..0000000 --- a/src/dxf.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "dxf.hpp" - - -Mat DXF::to8bitBGR() const { - if (_imageData.empty()) { - throw runtime_error("No image data loaded"); - } - - Mat output(_height, _width, CV_8UC3); - - double scale = 255.0 / ((1 << _bitDepth) - 1); - - for (int y = 0; y < _height; ++y) { - const uint16_t* srcRow = _imageData.ptr(y); - uint8_t* dstRow = output.ptr(y); - - for (int x = 0; x < _width; ++x) { - uint8_t value = static_cast(srcRow[x] * scale + 0.5); - dstRow[x * 3] = value; // B - dstRow[x * 3 + 1] = value; // G - dstRow[x * 3 + 2] = value; // R - } - } - - return output; -} - -Mat DXF::to10bitBGR() const { - if (_imageData.empty()) { - throw runtime_error("No image data loaded"); - } - - Mat output(_height, _width, CV_16UC3); - - double scale = 1023.0 / ((1 << _bitDepth) - 1); - - for (int y = 0; y < _height; ++y) { - const uint16_t* srcRow = _imageData.ptr(y); - uint16_t* dstRow = output.ptr(y); - - for (int x = 0; x < _width; ++x) { - uint16_t value = static_cast(srcRow[x] * scale + 0.5); - value = min(value, static_cast(1023)); - - uint16_t shiftedValue = value << 6; - - dstRow[x * 3] = shiftedValue; // B - dstRow[x * 3 + 1] = shiftedValue; // G - dstRow[x * 3 + 2] = shiftedValue; // R - } - } - - return output; -} - -void DXF::analyzeFile() { - ifstream file(_filepath, ios::binary); - if (!file.is_open()) { - throw runtime_error("Could not open file: " + _filepath); - } - - if (!readDXFHeader(file)) { - analyzeDXFContent(file); - } - - validateImageProperties(); -} - -bool DXF::readDXFHeader(ifstream& file) { - struct DXFHeader { - uint32_t magic; - uint32_t width; - uint32_t height; - uint8_t bitDepth; - uint8_t version; - uint16_t flags; - }; - - DXFHeader header; - file.read(reinterpret_cast(&header), sizeof(header)); - - if (file.gcount() != sizeof(header)) { - return false; - } - - if (header.magic != 0x44584631) { - return false; - } - - _width = header.width; - _height = header.height; - _bitDepth = header.bitDepth; - - return true; -} - -void DXF::analyzeDXFContent(ifstream& file) { - file.seekg(0, ios::end); - streamsize fileSize = file.tellg(); - file.seekg(0, ios::beg); - - vector sample(min(static_cast(1024), fileSize)); - file.read(reinterpret_cast(sample.data()), sample.size()); - - _bitDepth = detectBitDepth(sample); - - calculateDimensions(fileSize); -} - -int DXF::detectBitDepth(const vector& sample) { - uint16_t maxValue = 0; - - for (size_t i = 0; i < sample.size() - 1; i += 2) { - uint16_t value = (static_cast(sample[i + 1]) << 8) | sample[i]; - maxValue = max(maxValue, value); - } - - int bitDepth = 16; - while (bitDepth > 8 && maxValue < (1 << (bitDepth - 1))) { - bitDepth--; - } - - return bitDepth; -} - -void DXF::loadImageData() { - ifstream file(_filepath, ios::binary); - if (!file.is_open()) { - throw runtime_error("Could not open file: " + _filepath); - } - if (hasDXFHeader()) { - file.seekg(sizeof(struct DXFHeader)); - } - - size_t dataSize = static_cast(ceil(_width * _height * _bitDepth / 8.0)); - vector rawData(dataSize); - - file.read(reinterpret_cast(rawData.data()), dataSize); - - _imageData = Mat(_height, _width, CV_16UC1); - - unpackBits(rawData); -} - -void DXF::unpackBits(const vector& rawData) { - size_t bitPos = 0; - const uint16_t maxValue = (1 << _bitDepth) - 1; - - for (int y = 0; y < _height; ++y) { - uint16_t* row = _imageData.ptr(y); - for (int x = 0; x < _width; ++x) { - row[x] = extractBits(rawData, bitPos, _bitDepth, maxValue); - bitPos += _bitDepth; - } - } -} - -uint16_t DXF::extractBits(const vector& buffer, size_t bitPos, int bitDepth, uint16_t maxValue) { - size_t bytePos = bitPos / 8; - size_t bitOffset = bitPos % 8; - - uint32_t temp = 0; - for (int i = 0; i < 3 && bytePos + i < buffer.size(); ++i) { - temp |= static_cast(buffer[bytePos + i]) << (16 - (i * 8)); - } - - temp >>= (24 - (bitOffset + bitDepth)); - return static_cast(temp & maxValue); -} - -void DXF::calculateDimensions(streamsize fileSize) { - int pixelCount = (fileSize * 8) / _bitDepth; - _width = static_cast(sqrt(pixelCount)); - _height = _width; -} - -void DXF::validateImageProperties() { - if (_width <= 0 || _height <= 0 || - _width > 65536 || _height > 65536) { - throw runtime_error("Invalid image dimensions"); - } - - if (_bitDepth < 8 || _bitDepth > 16) { - throw runtime_error("Unsupported bit depth: " + to_string(_bitDepth)); - } -} \ No newline at end of file diff --git a/src/image.cpp b/src/image.cpp index 2d17725..f24386b 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -13,9 +13,13 @@ Mat Image::getBlank () { Mat Image::loadImage (string& image_path, uint64_t& x, uint64_t& y, uint64_t& w, uint64_t& h) { string located_path = samples::findFile(image_path); - if (getExtLower(located_path) == ".dxf") { - DXF dxf(located_path); - loaded = dxf.to8bitBGR(); + string ext = getExtLower(located_path); + //cout << "{ \"image_extension\" : \"" << ext << "\" }" << endl; + if (ext == "dpx") { + DPX dpx; + dpx.read(located_path); + loaded = dpx.getMatBitDepth(8); + cout << "{ \"dpx\" : \"" << image_path << "\" }" << endl; } else { loaded = imread(located_path, IMREAD_COLOR); }