-
Notifications
You must be signed in to change notification settings - Fork 0
Getting Started
This guide will help you install DTUTMO and process your first HDR image.
DTUTMO requires Python 3.10 or higher. Install the core dependencies:
pip install numpy scipyClone and install DTUTMO:
git clone https://github.com/TSOR666/DTUCAM25.git
cd dtutmo
pip install -e .For significantly faster processing (10-50× speedup on large images), install PyTorch:
pip install "torch>=2.5.1" "torchvision>=0.20.1"Or install with GPU support directly:
pip install -e .[torch]import dtutmo
print(dtutmo.__version__) # Should print: 2.1Process an HDR image with default settings:
import numpy as np
from dtutmo import CompleteDTUTMO
# Load your HDR image (linear RGB, cd/m²)
hdr_image = load_hdr_image("scene.exr") # Replace with your loader
# Create tone mapper with defaults
tmo = CompleteDTUTMO()
# Process the image
ldr_image = tmo.process(hdr_image)
# Save result (values in [0, 1])
save_image("output.png", ldr_image)That's it! DTUTMO will automatically:
- Apply optical transfer function and glare
- Extract cone and rod signals
- Compute local adaptation
- Apply photoreceptor responses
- Map to display values
- Encode for sRGB display
Configure specific behaviors:
from dtutmo import CompleteDTUTMO, DTUTMOConfig, CAMType, DisplayStandard
config = DTUTMOConfig(
# Observer properties
observer_age=35,
field_diameter=60, # degrees
# Enable/disable stages
use_otf=True, # Optical transfer function
use_glare=True, # CIE disability glare
use_bilateral=True, # Base-detail separation
use_local_adapt=True, # Local adaptation
# Color appearance model
use_cam=CAMType.DTUCAM, # Recommended
# Display target
target_display=DisplayStandard.REC_709,
target_luminance=100.0, # cd/m²
)
tmo = CompleteDTUTMO(config)
ldr_image = tmo.process(hdr_image)Target HDR10 displays:
config = DTUTMOConfig(
target_display=DisplayStandard.REC_2100_PQ,
target_luminance=1000.0, # cd/m² (HDR display)
)
tmo = CompleteDTUTMO(config)
hdr10_output = tmo.process(hdr_image)Use PyTorch for GPU processing:
import torch
from dtutmo import TorchDTUTMO
# Move image to GPU (BCHW format)
hdr_tensor = torch.from_numpy(hdr_image).permute(2, 0, 1).unsqueeze(0).cuda()
# Create GPU-accelerated tone mapper
tmo = TorchDTUTMO()
# Process on GPU
ldr_tensor = tmo.process(hdr_tensor)
# Convert back to numpy
ldr_image = ldr_tensor.squeeze(0).permute(1, 2, 0).cpu().numpy()from dtutmo import CompleteDTUTMO, DTUTMOConfig, DisplayMapping
config = DTUTMOConfig(
observer_age=30,
use_cam=CAMType.DTUCAM,
display_mapping=DisplayMapping.PRODUCTION_HYBRID, # Best quality/speed
target_display=DisplayStandard.REC_709,
)
tmo = CompleteDTUTMO(config)
# Process multiple images
for hdr_path in hdr_image_paths:
hdr = load_hdr(hdr_path)
ldr = tmo.process(hdr)
save_image(hdr_path.replace('.exr', '.png'), ldr)tmo = CompleteDTUTMO()
result = tmo.process(hdr_image, return_intermediate=True)
# Access intermediate products
ldr_output = result['output']
adaptation_map = result['local_adaptation']
cone_responses = result['photoreceptor_responses']['cones']
rod_response = result['photoreceptor_responses']['rod']
csf_output = result['csf_output']
# Visualize adaptation
import matplotlib.pyplot as plt
plt.imshow(adaptation_map, cmap='viridis')
plt.colorbar(label='Adaptation Luminance (cd/m²)')
plt.show()from dtutmo import CompleteDTUTMO, DTUTMOConfig, ViewingCondition
config = DTUTMOConfig(
viewing_condition=ViewingCondition.DIM, # Typical reading room
observer_age=45, # Radiologist age
use_bilateral=True, # Preserve fine details
use_cam=CAMType.DTUCAM, # Physiological accuracy
target_luminance=150.0, # Medical display
)
tmo = CompleteDTUTMO(config)
diagnostic_image = tmo.process(hdr_scan)from pathlib import Path
from dtutmo import CompleteDTUTMO
import OpenEXR
import numpy as np
def load_exr(path):
"""Load OpenEXR HDR image"""
exr = OpenEXR.InputFile(str(path))
# ... implementation ...
return rgb_array
def process_directory(input_dir, output_dir):
"""Process all HDR images in a directory"""
tmo = CompleteDTUTMO()
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
for hdr_file in input_path.glob("*.exr"):
print(f"Processing {hdr_file.name}...")
hdr = load_exr(hdr_file)
ldr = tmo.process(hdr)
output_file = output_path / f"{hdr_file.stem}.png"
save_image(output_file, ldr)
print("Done!")
# Usage
process_directory("hdr_images/", "output/")For quick previews during HDR capture or editing:
from dtutmo import DisplayMapping
config = DTUTMOConfig(
display_mapping=DisplayMapping.WHITEBOARD, # Fastest
use_bilateral=False, # Skip expensive stages
use_cam=CAMType.NONE, # Direct mapping
)
tmo = CompleteDTUTMO(config)
preview = tmo.process(hdr_image) # ~0.4s for 1920×1080For final output with best quality/performance balance:
from dtutmo import DisplayMapping
config = DTUTMOConfig(
display_mapping=DisplayMapping.PRODUCTION_HYBRID, # Recommended
use_cam=CAMType.DTUCAM, # Best color appearance
)
tmo = CompleteDTUTMO(config)
final_output = tmo.process(hdr_image) # ~0.7s for 1920×1080For research requiring maximum accuracy:
from dtutmo import DisplayMapping
config = DTUTMOConfig(
display_mapping=DisplayMapping.FULL_INVERSE, # Most accurate
use_cam=CAMType.DTUCAM,
)
tmo = CompleteDTUTMO(config)
reference = tmo.process(hdr_image) # ~2.1s for 1920×1080DTUTMO expects:
-
Format: NumPy array, shape
(H, W, 3) - Color space: Linear RGB (sRGB primaries, Rec. 709)
- Units: cd/m² (candelas per square meter)
- Range: Arbitrary (handles 0.001 to 100,000+ cd/m²)
import OpenEXR
import Imath
import numpy as np
def load_exr(filename):
exr = OpenEXR.InputFile(filename)
header = exr.header()
dw = header['dataWindow']
size = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1)
channels = ['R', 'G', 'B']
channel_data = [exr.channel(c, Imath.PixelType(Imath.PixelType.FLOAT))
for c in channels]
rgb = [np.frombuffer(data, dtype=np.float32).reshape(size[::-1])
for data in channel_data]
return np.stack(rgb, axis=-1)import cv2
def load_hdr(filename):
# OpenCV loads as float32 in BGR
bgr = cv2.imread(filename, cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
return rgbfrom PIL import Image
import numpy as np
def load_tiff(filename, max_luminance=1000.0):
img = Image.open(filename)
arr = np.array(img, dtype=np.float32)
# Normalize to cd/m²
if arr.max() > 1.0:
arr = arr / 65535.0 # 16-bit
arr = arr * max_luminance # Scale to physical units
return arrDTUTMO outputs display-encoded images:
-
Format: NumPy array, shape
(H, W, 3) - Range: [0, 1] (display code values)
-
Encoding: Depends on
target_display:-
REC_709: sRGB gamma (2.4) -
REC_2100_PQ: PQ (SMPTE ST 2084) -
REC_2100_HLG: HLG (Hybrid Log-Gamma)
-
from PIL import Image
import numpy as np
def save_image(filename, img_array):
"""Save tone-mapped image as PNG"""
# Convert [0,1] to [0,255]
img_8bit = (np.clip(img_array, 0, 1) * 255).astype(np.uint8)
Image.fromarray(img_8bit).save(filename)Solution: Check input units. DTUTMO expects physical luminance (cd/m²). If your HDR uses relative values, scale appropriately:
# If HDR is in relative units [0, 1]
hdr_scaled = hdr_relative * peak_luminance # e.g., * 1000.0
# Process
ldr = tmo.process(hdr_scaled)Solution: Verify input is linear RGB. If gamma-encoded:
def linearize_srgb(img_gamma):
"""Convert sRGB gamma to linear"""
linear = img_gamma.copy()
low = img_gamma <= 0.04045
linear[low] = img_gamma[low] / 12.92
linear[~low] = ((img_gamma[~low] + 0.055) / 1.055) ** 2.4
return linear
hdr_linear = linearize_srgb(hdr_gamma)
ldr = tmo.process(hdr_linear)Solutions:
- Use GPU acceleration (10-50× faster)
- Reduce resolution for preview
- Use
DisplayMapping.WHITEBOARD(3× faster) - Disable optional stages:
config = DTUTMOConfig(
use_otf=False, # Skip optical blur
use_glare=False, # Skip glare
use_bilateral=False # Skip detail preservation
)Solutions:
- Process tiles:
def process_tiled(tmo, hdr, tile_size=512):
h, w = hdr.shape[:2]
output = np.zeros((h, w, 3))
for y in range(0, h, tile_size):
for x in range(0, w, tile_size):
tile = hdr[y:y+tile_size, x:x+tile_size]
output[y:y+tile_size, x:x+tile_size] = tmo.process(tile)
return output- Reduce image resolution
- Use GPU (handles larger images)
Now that you can process HDR images, explore:
- Configuration Guide - Detailed parameter tuning
- Pipeline Stages - Understand each processing stage
- Display Mapping - Choose the right mapping strategy
- Color Appearance Models - DTUCAM vs XLR-CAM vs CIECAM16
- Performance Optimization - Speed up processing