Created
February 11, 2024 20:32
-
-
Save promto-c/a268eebbb989d1b7aed01f17292c5a2c to your computer and use it in GitHub Desktop.
Compare the execution times of reading and processing the image data from DPX file 12bit packed in 16-bit vs. 32-bit modes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import struct, math | |
| import time | |
| import numpy as np | |
| def read_dpx_header(file): | |
| headers = {} | |
| # Generic File Header | |
| generic_file_header_format = ">I I 8s I I I I I 100s 24s 100s 200s 200s I 104s" | |
| headers['GenericFileHeader'] = struct.unpack( | |
| generic_file_header_format, file.read(768) | |
| ) | |
| # Determine endianness based on magic number | |
| magic_number = headers['GenericFileHeader'][0] | |
| if magic_number == 0x53445058: | |
| headers['endianness'] = 'be' # big-endian | |
| elif magic_number == 0x58504453: | |
| headers['endianness'] = 'le' # little-endian | |
| # Generic Image Header | |
| generic_image_header_format = ">H H I I" | |
| headers['GenericImageHeader'] = struct.unpack( | |
| generic_image_header_format, file.read(12) | |
| ) | |
| return headers | |
| def read_dpx_12bit_packed(image_path: str, read_as_32bit: bool) -> np.ndarray: | |
| with open(image_path, "rb") as file: | |
| meta = read_dpx_header(file) | |
| width = meta['GenericImageHeader'][2] | |
| height = meta['GenericImageHeader'][3] | |
| offset = meta['GenericFileHeader'][1] | |
| file.seek(offset) | |
| if read_as_32bit: | |
| words_per_line = math.ceil(width * 9/8) | |
| raw = np.fromfile(file, dtype=np.uint32, count=words_per_line*height) | |
| else: | |
| words_per_line = math.ceil(width * 9/4) | |
| raw = np.fromfile(file, dtype=np.uint16, count=words_per_line*height) | |
| word_lines = raw.reshape(height, words_per_line) | |
| if meta['endianness'] == 'be': | |
| raw.byteswap(True) | |
| # Constants for the process | |
| components_per_pixel = 3 # RGB components | |
| # Extract 8 components from 6 halfwords (read as 16bit) | |
| # Extract 8 components from 3 words (read as 32bit) | |
| # word1: B5 B6 B7 B8 B9 B10 B11 B12 G1 G2 G3 G4 G5 G6 G7 G8 G9 G10 G11 G12 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 | |
| # word2: BBBBGGGGGGGGGGGG RRRRRRRRRRRR B1 B2 B3 B4 | |
| # word3: GGGGGGGGGGGGRRRR RRRRRRRRBBBBBBBB | |
| if read_as_32bit: | |
| image_data = np.array([ | |
| word_lines[:, 0::3] & 0xFFF, | |
| (word_lines[:, 0::3] >> 12) & 0xFFF, | |
| ((word_lines[:, 0::3] >> 24) & 0xFF) | ((word_lines[:, 1::3] & 0xF) << 8), | |
| (word_lines[:, 1::3] >> 4) & 0xFFF, | |
| (word_lines[:, 1::3] >> 16) & 0xFFF, | |
| ((word_lines[:, 1::3] >> 28) & 0xF) | ((word_lines[:, 2::3] & 0xFF) << 4), | |
| (word_lines[:, 2::3] >> 8)& 0xFFF, | |
| (word_lines[:, 2::3] >> 20 & 0xFFF) | |
| ], dtype=np.uint16).transpose(1, 2, 0).reshape(height, width, components_per_pixel) # Reshape to (height, width, components_per_pixel) | |
| else: | |
| image_data = np.array([ | |
| (word_lines[:, 1::6] & 0xFFF), | |
| ((word_lines[:, 0::6] & 0xFF) << 4) | (word_lines[:, 1::6] >> 12), | |
| ((word_lines[:, 3::6] & 0xF) << 8) | (word_lines[:, 0::6] >> 8), | |
| (word_lines[:, 3::6] >> 4), | |
| (word_lines[:, 2::6] & 0xFFF), | |
| ((word_lines[:, 5::6] & 0xFF) << 4) | (word_lines[:, 2::6] >> 12), | |
| ((word_lines[:, 4::6] & 0xF) << 8) | (word_lines[:, 5::6] >> 8), | |
| (word_lines[:, 4::6] >> 4 & 0xFFF) | |
| ], dtype=np.uint16).transpose(1, 2, 0).reshape(height, width, components_per_pixel) # Reshape to (height, width, components_per_pixel) | |
| # Convert to float32 and normalize | |
| image_data = image_data.astype(np.float32) | |
| image_data /= 0x0FFF | |
| return image_data | |
| # Test Execution Time | |
| # ------------------- | |
| def measure_execution_time(func, *args, **kwargs): | |
| start_time = time.time() | |
| func(*args, **kwargs) | |
| return time.time() - start_time | |
| def test_multiple_times(image_path, num_tests=10): | |
| times_16bit = [] | |
| times_32bit = [] | |
| for _ in range(num_tests): | |
| times_16bit.append(measure_execution_time(read_dpx_12bit_packed, image_path, read_as_32bit=False)) | |
| times_32bit.append(measure_execution_time(read_dpx_12bit_packed, image_path, read_as_32bit=True)) | |
| avg_time_16bit = sum(times_16bit) / num_tests | |
| avg_time_32bit = sum(times_32bit) / num_tests | |
| print(f"Average 16-bit reading time over {num_tests} tests: {avg_time_16bit} seconds") | |
| print(f"Average 32-bit reading time over {num_tests} tests: {avg_time_32bit} seconds") | |
| if __name__ == "__main__": | |
| image_path = 'dpx12.0000.dpx' | |
| test_multiple_times(image_path, num_tests=100) | |
| # Average 16-bit reading time over 100 tests: 0.047649037837982175 seconds | |
| # Average 32-bit reading time over 100 tests: 0.05539111137390137 seconds |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment