Apple ProRes Log captures 10-bit HDR video with minimal processing, making it ideal for photogrammetry, radiance fields, and VFX. This package uses ffmpeg with Apple's AppleLogToLin-v1.0.cube LUT to decode log-encoded video to linear images. Apple log video is currently supported on iPhones 15, 16. This is the best way I know to get as close to unprocessed RAW video as possible on iPhone without making your own capture app like MotionCam on Android. The Apple ProRes Log white paper is attached here. For reference, video editing software like DaVinci Resolve provides similar log color grading functionality in their GUI.
The script uses an ffmpeg filter graph that:
- Converts color matrix:
zscaletransforms from BT.2020 limited range to full range - Applies the LUT:
lut1duses Apple's official LUT to decode from log to linear - Converts color space:
zscaletransforms from linear BT.2020 to sRGB (BT.709) for display-referred outputs
All three output formats (32-bit EXR, 16-bit PNG, 8-bit PNG) are generated in a single ffmpeg pass.
- ffmpeg (better to install system level)
conda install -c conda-forge ffmpeg -y - Python >= 3.7
From the repository:
git clone https://github.com/btilmon/apple-log2linear.git
cd apple-log2linear
pip install .Or install directly from GitHub:
pip install git+https://github.com/btilmon/apple-log2linear.gitFor development:
pip install -e .apple-log2linear --video_path /path/to/video.MOVWith frame skipping (keep every 10th frame):
apple-log2linear --video_path /path/to/video.MOV --step 10from appleLog2Linear import process_apple_log_video
# Process video - outputs saved directly to output_dir (or video's directory if not specified)
result = process_apple_log_video(
video_path="/path/to/video.MOV",
step=10, # keep every 10th frame
output_32bit=True, # 32-bit EXR linear output
output_16bit=False, # disable 16-bit PNG
output_8bit=True, # 8-bit sRGB PNG for COLMAP
output_dir="/path/to/output", # optional: where to save outputs
)
# Access results
print(result['images_32bit_dir']) # /path/to/output/images-32bit-linear
print(result['images_8bit_dir']) # /path/to/output/images
print(result['exr_count']) # Number of EXR frames
print(result['png8_count']) # Number of 8-bit PNG framesUse return_imgs=True to get images loaded as numpy arrays:
from appleLog2Linear import process_apple_log_video
# Process and return images as numpy arrays
images = process_apple_log_video(
video_path="/path/to/video.MOV",
step=10,
output_32bit=True,
output_16bit=False,
output_8bit=True,
output_dir="/path/to/output",
return_imgs=True, # Return images as dict
)
# Access images by output directory name
# Keys are: 'images-32bit-linear', 'images-16bit-srgb', 'images'
linear_images = images['images-32bit-linear'] # List of 32-bit numpy arrays
srgb_images = images['images'] # List of 8-bit numpy arrays
# Iterate over frames
for i, img in enumerate(srgb_images):
print(f"Frame {i}: shape={img.shape}, dtype={img.dtype}")| Argument | Description | Default |
|---|---|---|
--video_path |
Full path to the .MOV file | Required |
--step |
Keep every Nth frame | 1 (all frames) |
--lut_path |
Path to LUT file | Bundled LUT |
--output_dir |
Directory for output folders | Video's directory |
--no-32bit |
Disable 32-bit EXR output | False |
--no-16bit |
Disable 16-bit PNG output | False |
--no-8bit |
Disable 8-bit PNG output | False |
| Argument | Description | Default |
|---|---|---|
return_imgs |
Return images as dict instead of file paths | False |
Frames are saved to output directory (defaults to video's directory):
| Folder | Format | Description |
|---|---|---|
images-32bit-linear/ |
32-bit EXR | Linear, unclipped (maximum fidelity) |
images-16bit-srgb/ |
16-bit PNG | sRGB, display-referred |
images/ |
8-bit PNG | sRGB, display-referred (for COLMAP, etc.) |
- Go to
Settings → Camera → Formats - Enable Apple ProRes
- Select "Log" from ProRes Encoding
- Verify "ProRes LOG" appears in the camera app
- I recommend enabling the setting to lock white balance on record, or using BlackMagic camera app for finer grained control of camera settings.
Note: At 4K 30FPS, expect ~6GB per minute of video.
