Convert 3D LUT to hald image
- fmw42
- Posts: 25562
- Joined: 2007-07-02T17:14:51-07:00
- Authentication code: 1152
- Location: Sunnyvale, California, USA
Re: Convert 3D LUT to hald image
IM can read the CUBE LUT and convert to an image. But that image is not a HALD image. We are looking for help to apply the CUBE LUT to process an image or to convert the CUBE LUT into a HALD image.
Re: Convert 3D LUT to hald image
Perfect.. thanks for the input.
I've had a chance to do some further work on it, and yes confirmed on the cubed size for the HALD.
e.g. hald:4 is a resolution of 4**3 x 4**3
I do have a function created now that creates a HALD from a .cube with the option to tell it what HALD level you desire.
I will do a little research and verify the rules for posting code. fmw42 had asked for some code and will post the python function in case it might be useful or permitted.
Due to the process of needing to write a chunk of pixels at a time due to command length limits, the conversion can take several minutes.
Doing comparison on expected results it is 99.99999% accurate, which is more than enough. But will study the process some more in case there is a tweak that could be had.
The python function is aprox 160 lines, so want to research first if that is against the rules to post directly here that many lines.
Thanks everyone for their input.
I've had a chance to do some further work on it, and yes confirmed on the cubed size for the HALD.
e.g. hald:4 is a resolution of 4**3 x 4**3
I do have a function created now that creates a HALD from a .cube with the option to tell it what HALD level you desire.
I will do a little research and verify the rules for posting code. fmw42 had asked for some code and will post the python function in case it might be useful or permitted.
Due to the process of needing to write a chunk of pixels at a time due to command length limits, the conversion can take several minutes.
Doing comparison on expected results it is 99.99999% accurate, which is more than enough. But will study the process some more in case there is a tweak that could be had.
The python function is aprox 160 lines, so want to research first if that is against the rules to post directly here that many lines.
Thanks everyone for their input.
- fmw42
- Posts: 25562
- Joined: 2007-07-02T17:14:51-07:00
- Authentication code: 1152
- Location: Sunnyvale, California, USA
Re: Convert 3D LUT to hald image
It is not too much text to post here.The python function is aprox 160 lines, so want to research first if that is against the rules to post directly here that many lines.
Thanks everyone for their input.
Or you can send directly via email -- see form at https://imagemagick.org/discourse-serve ... ntactadmin
We appreciate any help you can give on this activity.
Re: Convert 3D LUT to hald image
You can post the python script here or email it to snippets @ imagemagick dot org. You need to include a license, either public domain, BSD, or the ImageMagick license so we can adapt it and use it within the ImageMagick source distribution.
Re: Convert 3D LUT to hald image
Here is the Python function for converting a .cube to a HALD image.
Note: It uses a subprocess.call to execute a standard "magick" command that the function builds and executes. The function uses a personal "BASE" module that I use to identify which OS it may be executing on and gives the appropriate "magick" command. In other words, you can replace that directory with the desired magick or magic path as desired.
Note: it loads in the .cube data into a 3D array, and then does a linear interpolate depending upon the desired HALD level.
Note: the "prog" reference can be ignored. It is for in case it is being called from a QT/PySide script with a progressBar widget if desired.
Note: criticism encouraged
Note: It uses a subprocess.call to execute a standard "magick" command that the function builds and executes. The function uses a personal "BASE" module that I use to identify which OS it may be executing on and gives the appropriate "magick" command. In other words, you can replace that directory with the desired magick or magic path as desired.
Note: it loads in the .cube data into a 3D array, and then does a linear interpolate depending upon the desired HALD level.
Note: the "prog" reference can be ignored. It is for in case it is being called from a QT/PySide script with a progressBar widget if desired.
Note: criticism encouraged
Code: Select all
import sys
import re
import subprocess
import traceback
def cube_to_hald(cube_file, hald_file=None, hald_size=7, prog=None):
"""
Turn that Cube into a HALD file
Do one chunk at a time.
If no hald_file given, create it next to the .cube.
If title in the .cube, name the HALD after that.
Otherwise the file given.
"""
if not cube_file:
return None
lut_types = ('LUT_3D_SIZE', 'LUT_1D_SIZE')
use_title = False
title = None
if not hald_file:
cube, ext = os.path.splitext(cube_file)
hald_file = '{0}.exr'.format(cube)
use_title = True
if sys.platform == 'win32':
cube_file = cube_file.replace('/', '\\')
hald_file = hald_file.replace('/', '\\')
else:
cube_file = cube_file.replace('\\', '/')
hald_file = hald_file.replace('\\', '/')
if not os.path.isfile(cube_file):
return None
fileID = open(cube_file, 'r')
dump = fileID.readlines()
fileID.close()
dimension = None
out_data = [[[[0.0] * 3 for x in range(hald_size**2)] for y in range(hald_size**2)] for z in range(hald_size**2)]
r = g = b = 0
for line in dump:
each = line.strip()
if not each:
continue
if each.startswith('#'):
continue
if dimension:
buf = each.split(' ')
data[b][g][r] = (float(buf[0]), float(buf[1]), float(buf[2]))
r += 1
if r >= dimension:
r = 0
g += 1
if g >= dimension:
g = 0
b += 1
if each.startswith('TITLE '):
title = each[6:].replace(r'"', '')
if each.startswith(lut_types):
buf = each.split(' ')
dimension = int(buf[1])
data = [[[[0.0] * 3 for x in range(dimension)] for y in range(dimension)] for z in range(dimension)]
continue
# Update hald filename and create directory accordingly
hald_dir = os.path.dirname(hald_file)
if use_title and title:
hald_file = '{0}/{0}.exr'.format(hald_dir, title)
if sys.platform == 'win32':
hald_file = hald_file.replace('/', '\\')
if not os.path.isdir(hald_dir):
os.makedirs(hald_dir)
# Data loaded, build the HALD
pVal = 0
if prog:
fmt = 'Interpolating HALD file (%v of %m)'
prog.setFormat(fmt)
prog.setRange(0, int(hald_size ** 2))
else:
sys.stdout.write('\n')
length = dimension - 1
factor = 1.0 / ((hald_size ** 2) - 1)
for b in range(hald_size ** 2):
for g in range(hald_size ** 2):
for r in range(hald_size ** 2):
r_position = (r * factor) * length
r_factor, r_index = math.modf(r_position)
r_index = int(r_index)
if r_index == length:
r_next = r_index
else:
r_next = r_index + 1
g_position = (g * factor) * length
g_factor, g_index = math.modf(g_position)
g_index = int(g_index)
if g_index == length:
g_next = g_index
else:
g_next = g_index + 1
b_position = (b * factor) * length
b_factor, b_index = math.modf(b_position)
b_index = int(b_index)
if b_index == length:
b_next = b_index
else:
b_next = b_index + 1
out_data[b][g][r] = (data[b_index][g_index][r_index][0] + (data[b_index][g_index][r_next][0] - data[b_index][g_index][r_index][0]) * r_factor,
data[b_index][g_index][r_index][1] + (data[b_index][g_next][r_index][1] - data[b_index][g_index][r_index][1]) * g_factor,
data[b_index][g_index][r_index][2] + (data[b_next][g_index][r_index][2] - data[b_index][g_index][r_index][2]) * b_factor)
if prog:
pVal += 1
prog.setValue(pVal)
else:
sys.stdout.write('.')
# Now create the results as a HALD image file.
cmd = [r'"{0}" convert'.format(BASE.APP_PATHS['MAGICK'])]
cmd.append('-size {0}x{0} xc:#000000000000 -depth 16'.format(str(hald_size ** 3)))
cmd.append(r'"{0}"'.format(hald_file))
try:
subprocess.call(' '.join(cmd), shell=True)
except Exception as err:
traceback.print_exc()
return None
pVal = 0
if prog:
fmt = 'Building HALD file (%v of %m)'
prog.setFormat(fmt)
prog.setRange(0, int(hald_size ** 2))
else:
sys.stdout.write('\n')
for b in range(hald_size ** 2):
for g in range(hald_size ** 2):
cmd = [r'"{0}" convert'.format(BASE.APP_PATHS['MAGICK'])]
cmd.append(r'"{0}"'.format(hald_file))
for r in range(hald_size ** 2):
value = tuple([(hex(int(float(x) * 65535)).split('x')[-1].zfill(4)) for x in out_data[b][g][r]])
x = (g % hald_size) * (hald_size ** 2) + r
y = (b * hald_size) + ((g / hald_size) % (hald_size ** 2))
cmd.append(r'-fill "#{0}{1}{2}"'.format(*value))
cmd.append(r'-draw "color {0}, {1} point"'.format(x, y))
cmd.append('-depth 16')
cmd.append(r'"{0}"'.format(hald_file))
try:
subprocess.call(' '.join(cmd), shell=True)
except Exception as err:
traceback.print_exc()
return None
if prog:
pVal += 1
prog.setValue(pVal)
else:
sys.stdout.write('.')
if prog:
fmt = 'HALD Conversion Complete'
prog.setFormat(fmt)
prog.setValue(0)
else:
sys.stdout.write('\ncomplete\n')
return hald_file
- fmw42
- Posts: 25562
- Joined: 2007-07-02T17:14:51-07:00
- Authentication code: 1152
- Location: Sunnyvale, California, USA
Re: Convert 3D LUT to hald image
In IM 7.0.8.20 there is now a conversion form a CUBE LUT to a HALD Image. For example
the [6] converts to a HALD:6 image. See
https://imagemagick.org/Usage/color_mods/#hald-clut
Code: Select all
magick cube:FG_CineVibrant.cube[6] hald6.png
https://imagemagick.org/Usage/color_mods/#hald-clut