ralpha-assets/RalphaData/downloads/luts/generate_luts.py

486 lines
17 KiB
Python

#!/usr/bin/env python3
"""
Generate basic .cube LUT files for Ralpha Kitchen Sink project
These are 17x17x17 or 33x33x33 3D LUTs in Adobe/Resolve compatible format
"""
import os
import math
def write_cube_header(f, title, size=33):
f.write(f'TITLE "{title}"\n')
f.write(f'LUT_3D_SIZE {size}\n')
f.write('DOMAIN_MIN 0.0 0.0 0.0\n')
f.write('DOMAIN_MAX 1.0 1.0 1.0\n\n')
def clamp(v, min_v=0.0, max_v=1.0):
return max(min_v, min(max_v, v))
def generate_neutral_lut(output_path, size=33):
"""Identity LUT - no change"""
with open(output_path, 'w') as f:
write_cube_header(f, "Neutral", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_teal_orange_lut(output_path, size=33):
"""Teal & Orange Hollywood blockbuster look"""
with open(output_path, 'w') as f:
write_cube_header(f, "Teal Orange", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Luminance
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
# Push shadows toward teal, highlights toward orange
if lum < 0.5:
# Shadows: add teal (cyan-ish)
factor = (0.5 - lum) * 0.3
rf = clamp(rf - factor * 0.2)
gf = clamp(gf + factor * 0.1)
bf = clamp(bf + factor * 0.25)
else:
# Highlights: add orange/warmth
factor = (lum - 0.5) * 0.3
rf = clamp(rf + factor * 0.2)
gf = clamp(gf + factor * 0.05)
bf = clamp(bf - factor * 0.15)
# Slight S-curve contrast
rf = clamp(0.5 + (rf - 0.5) * 1.15)
gf = clamp(0.5 + (gf - 0.5) * 1.15)
bf = clamp(0.5 + (bf - 0.5) * 1.15)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_vintage_lut(output_path, size=33):
"""Faded vintage look with lifted blacks"""
with open(output_path, 'w') as f:
write_cube_header(f, "Vintage Faded", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Lift blacks
lift = 0.08
rf = lift + rf * (1 - lift)
gf = lift + gf * (1 - lift)
bf = lift + bf * (1 - lift)
# Reduce contrast
rf = 0.5 + (rf - 0.5) * 0.85
gf = 0.5 + (gf - 0.5) * 0.85
bf = 0.5 + (bf - 0.5) * 0.85
# Warm tint
rf = clamp(rf * 1.05)
gf = clamp(gf * 1.0)
bf = clamp(bf * 0.9)
# Slight desaturation
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
sat = 0.7
rf = clamp(lum + (rf - lum) * sat)
gf = clamp(lum + (gf - lum) * sat)
bf = clamp(lum + (bf - lum) * sat)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_bw_lut(output_path, size=33):
"""Black and white conversion"""
with open(output_path, 'w') as f:
write_cube_header(f, "Black White", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Luminance with slight contrast boost
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
lum = clamp(0.5 + (lum - 0.5) * 1.2)
f.write(f'{lum:.6f} {lum:.6f} {lum:.6f}\n')
def generate_noir_lut(output_path, size=33):
"""High contrast film noir B&W"""
with open(output_path, 'w') as f:
write_cube_header(f, "Film Noir", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
# Strong S-curve for high contrast
lum = clamp(0.5 + (lum - 0.5) * 1.8)
# Crush blacks slightly
if lum < 0.15:
lum = lum * 0.7
f.write(f'{lum:.6f} {lum:.6f} {lum:.6f}\n')
def generate_warm_lut(output_path, size=33):
"""Warm/golden tones"""
with open(output_path, 'w') as f:
write_cube_header(f, "Warm", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Warm shift
rf = clamp(rf * 1.1 + 0.02)
gf = clamp(gf * 1.02)
bf = clamp(bf * 0.85)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_cool_lut(output_path, size=33):
"""Cool/blue tones"""
with open(output_path, 'w') as f:
write_cube_header(f, "Cool", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Cool shift
rf = clamp(rf * 0.9)
gf = clamp(gf * 0.98)
bf = clamp(bf * 1.1 + 0.02)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_cinematic_lut(output_path, size=33):
"""Cinematic look with crushed blacks and teal/orange"""
with open(output_path, 'w') as f:
write_cube_header(f, "Cinematic", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# S-curve contrast
rf = clamp(0.5 + (rf - 0.5) * 1.3)
gf = clamp(0.5 + (gf - 0.5) * 1.3)
bf = clamp(0.5 + (bf - 0.5) * 1.3)
# Crush blacks
if rf < 0.1: rf *= 0.8
if gf < 0.1: gf *= 0.8
if bf < 0.1: bf *= 0.8
# Teal shadows, orange highlights
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
if lum < 0.4:
bf = clamp(bf + 0.05)
gf = clamp(gf + 0.02)
elif lum > 0.6:
rf = clamp(rf + 0.03)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_bleach_bypass_lut(output_path, size=33):
"""Bleach bypass / desaturated high contrast"""
with open(output_path, 'w') as f:
write_cube_header(f, "Bleach Bypass", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Luminance
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
# Desaturate significantly
sat = 0.4
rf = clamp(lum + (rf - lum) * sat)
gf = clamp(lum + (gf - lum) * sat)
bf = clamp(lum + (bf - lum) * sat)
# High contrast
rf = clamp(0.5 + (rf - 0.5) * 1.5)
gf = clamp(0.5 + (gf - 0.5) * 1.5)
bf = clamp(0.5 + (bf - 0.5) * 1.5)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_sepia_lut(output_path, size=33):
"""Sepia toned monochrome"""
with open(output_path, 'w') as f:
write_cube_header(f, "Sepia", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
# Sepia toning
rf = clamp(lum * 1.2)
gf = clamp(lum * 0.95)
bf = clamp(lum * 0.7)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_vibrant_lut(output_path, size=33):
"""Enhanced saturation and vibrancy"""
with open(output_path, 'w') as f:
write_cube_header(f, "Vibrant", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
# Boost saturation
sat = 1.4
rf = clamp(lum + (rf - lum) * sat)
gf = clamp(lum + (gf - lum) * sat)
bf = clamp(lum + (bf - lum) * sat)
# Slight contrast boost
rf = clamp(0.5 + (rf - 0.5) * 1.1)
gf = clamp(0.5 + (gf - 0.5) * 1.1)
bf = clamp(0.5 + (bf - 0.5) * 1.1)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_matte_lut(output_path, size=33):
"""Low contrast matte look"""
with open(output_path, 'w') as f:
write_cube_header(f, "Matte", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Lift blacks
lift = 0.1
rf = lift + rf * (1 - lift * 2)
gf = lift + gf * (1 - lift * 2)
bf = lift + bf * (1 - lift * 2)
# Reduce contrast
rf = clamp(0.5 + (rf - 0.5) * 0.75)
gf = clamp(0.5 + (gf - 0.5) * 0.75)
bf = clamp(0.5 + (bf - 0.5) * 0.75)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_cross_process_lut(output_path, size=33):
"""Cross-processed look with color shifts"""
with open(output_path, 'w') as f:
write_cube_header(f, "Cross Process", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Color channel shifts
rf = clamp(rf * 1.1 + 0.05)
gf = clamp(gf * 0.95)
bf = clamp(bf * 1.2 - 0.05)
# High contrast
rf = clamp(0.5 + (rf - 0.5) * 1.4)
gf = clamp(0.5 + (gf - 0.5) * 1.3)
bf = clamp(0.5 + (bf - 0.5) * 1.35)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_day_for_night_lut(output_path, size=33):
"""Day for night effect"""
with open(output_path, 'w') as f:
write_cube_header(f, "Day For Night", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Darken overall
rf = rf * 0.4
gf = gf * 0.45
bf = bf * 0.6
# Blue tint
rf = clamp(rf * 0.7)
gf = clamp(gf * 0.85)
bf = clamp(bf * 1.3)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_cyberpunk_lut(output_path, size=33):
"""Neon pink and cyan cyberpunk look"""
with open(output_path, 'w') as f:
write_cube_header(f, "Cyberpunk", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
# High contrast
rf = clamp(0.5 + (rf - 0.5) * 1.4)
gf = clamp(0.5 + (gf - 0.5) * 1.4)
bf = clamp(0.5 + (bf - 0.5) * 1.4)
# Push shadows to cyan/teal
if lum < 0.4:
gf = clamp(gf + 0.1)
bf = clamp(bf + 0.15)
# Push highlights to pink/magenta
elif lum > 0.6:
rf = clamp(rf + 0.15)
bf = clamp(bf + 0.1)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
def generate_film_emulation_luts(output_dir, size=33):
"""Generate film stock emulation LUTs"""
# Kodak Portra 400 - warm skin tones, soft pastels
with open(os.path.join(output_dir, 'LUT_FilmEmulation_01_Portra400.cube'), 'w') as f:
write_cube_header(f, "Kodak Portra 400", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
# Warm up, slight desaturation
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
sat = 0.9
rf = clamp(lum + (rf - lum) * sat + 0.02)
gf = clamp(lum + (gf - lum) * sat)
bf = clamp(lum + (bf - lum) * sat - 0.03)
# Soft contrast
rf = clamp(0.5 + (rf - 0.5) * 0.95)
gf = clamp(0.5 + (gf - 0.5) * 0.95)
bf = clamp(0.5 + (bf - 0.5) * 0.95)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
# Fuji Velvia 50 - ultra saturated, high contrast
with open(os.path.join(output_dir, 'LUT_FilmEmulation_07_Velvia50.cube'), 'w') as f:
write_cube_header(f, "Fuji Velvia 50", size)
for b in range(size):
for g in range(size):
for r in range(size):
rf = r / (size - 1)
gf = g / (size - 1)
bf = b / (size - 1)
lum = 0.299 * rf + 0.587 * gf + 0.114 * bf
# High saturation
sat = 1.5
rf = clamp(lum + (rf - lum) * sat)
gf = clamp(lum + (gf - lum) * sat)
bf = clamp(lum + (bf - lum) * sat)
# High contrast
rf = clamp(0.5 + (rf - 0.5) * 1.3)
gf = clamp(0.5 + (gf - 0.5) * 1.3)
bf = clamp(0.5 + (bf - 0.5) * 1.3)
f.write(f'{rf:.6f} {gf:.6f} {bf:.6f}\n')
if __name__ == '__main__':
output_dir = os.path.dirname(os.path.abspath(__file__))
print("Generating LUT files...")
generate_neutral_lut(os.path.join(output_dir, 'LUT_Neutral.cube'))
print(" Created: LUT_Neutral.cube")
generate_teal_orange_lut(os.path.join(output_dir, 'LUT_Teal_Orange.cube'))
print(" Created: LUT_Teal_Orange.cube")
generate_vintage_lut(os.path.join(output_dir, 'LUT_Vintage_01.cube'))
print(" Created: LUT_Vintage_01.cube")
generate_bw_lut(os.path.join(output_dir, 'LUT_BW_01.cube'))
print(" Created: LUT_BW_01.cube")
generate_noir_lut(os.path.join(output_dir, 'LUT_BW_04_Noir.cube'))
print(" Created: LUT_BW_04_Noir.cube")
generate_warm_lut(os.path.join(output_dir, 'LUT_Warm.cube'))
print(" Created: LUT_Warm.cube")
generate_cool_lut(os.path.join(output_dir, 'LUT_Cool.cube'))
print(" Created: LUT_Cool.cube")
generate_cinematic_lut(os.path.join(output_dir, 'LUT_Cinematic.cube'))
print(" Created: LUT_Cinematic.cube")
generate_bleach_bypass_lut(os.path.join(output_dir, 'LUT_Bleach_Bypass.cube'))
print(" Created: LUT_Bleach_Bypass.cube")
generate_sepia_lut(os.path.join(output_dir, 'LUT_BW_05_Sepia.cube'))
print(" Created: LUT_BW_05_Sepia.cube")
generate_vibrant_lut(os.path.join(output_dir, 'LUT_Vibrant.cube'))
print(" Created: LUT_Vibrant.cube")
generate_matte_lut(os.path.join(output_dir, 'LUT_Matte.cube'))
print(" Created: LUT_Matte.cube")
generate_cross_process_lut(os.path.join(output_dir, 'LUT_Cross_Process.cube'))
print(" Created: LUT_Cross_Process.cube")
generate_day_for_night_lut(os.path.join(output_dir, 'LUT_Day_For_Night.cube'))
print(" Created: LUT_Day_For_Night.cube")
generate_cyberpunk_lut(os.path.join(output_dir, 'LUT_Cyberpunk.cube'))
print(" Created: LUT_Cyberpunk.cube")
generate_film_emulation_luts(output_dir)
print(" Created: Film emulation LUTs")
print("\nDone! Generated LUT files.")