/clut

Color Look Up Tables in Python

Primary LanguagePython

Python Color Lookup Tables

This package implements 3d color lookup tables (CLUT) for image manipulation and saving/loading from and to the HaldCLUT format. It was originally created for the purpose of re-creating CLUT filters from a set of edited and un-edited images. When is this necessary? Usually, if one wanted to pull a filter from your favourite app, all that needs to be done is loading an identity HaldCLUT image and applying the desired filter to it. This is not easily possible if the application of a filter happens on a camera instead of an app, since the raw image data is often modified directly. The Ricoh GR for example has a much-loved positive film effect, but one could not apply it to arbitrary images on your computer... until now. Scroll down to see an application example.

Installation

Dependencies: Pillow, numpy, scipy. Optionally scikit-image to use the denoise feature.

Install in PIP editable mode:

  • git clone git@github.com:oiao/clut.git
  • pip install -e clut

For verification run the unit tests with tests/run_tests.sh.

Usage: Basics

import numpy as np
from PIL import Image
from clut import CLUT

Initialize

# Either an identity CLUT
clut = CLUT()
# Or load a HaldCLUT image from file
clut = CLUT('path/to/haldclut.png')

Apply CLUT

im_out = clut('target/img.png') # directly to image files
im_out = clut(np.array(Image.open('target/img.png'))) # or to numpy arrays

Save the modified image

im_out = Image.fromarray(im_out)
im_out.save('modified/img_out.png')

Modify the CLUT

by accessing the r,g,b channels through indexing

clut[120,0,255] = [0, 0, 0] # map rgb[120,0,255] to black

Save the CLUT

clut.save('haldclut.png', size=8)

The resulting image will have a size of (size**2, size**2). Generally, storing with a larger size is recommended (for a color depth of 256, the lossless size is 16, resulting in a ~1.8mb png file).

Usage: CLUT Fitting

from clut import clutfit

Fitting works by providing a series of (unfiltered, filtered) tuples to the clutfit function. For very accurate results, make sure that all image pairs cover as much color of the color space as possible.

images = [
  ('01in.png', '01out.png'),
  ('02in.png', '02out.png'),
  ('03in.png', '03out.png'),
  ]
clut = clutfit(*images)

The resulting instance can then be saved as described above.

Usage: Command Line Interface

The package comes with a CLI that can be accessed through the clut command:

>>> clut -h
usage: clut [-h] [-s] {apply,batch-apply,fit} ...

CLUT Command Line Interface

positional arguments:
  {apply,batch-apply,fit}
    apply               Apply a HaldCLUT image filter to a number of files or
                        directories
    batch-apply         Batch-Apply multiple HaldCLUT image filters to one
                        target image
    fit                 Re-create a HaldCLUT filter from a series of
                        input/output images.

optional arguments:
  -h, --help            show this help message and exit
  -s, --silent          Disable verbose output

You can use the clut COMMAND --help command to get additional help on any of the sub-commands.

Application Example

For the fitting to work, the following needs to be considered:

  • The package requires image pairs (at least one) of unfiltered and filtered images with excatly the same composition. In-camera this can usually be achieved by taking an image without any filters, and applying the filter afterwards
  • Images that cover as much of the complete color space as possible as best suited for fitting
  • Avoid noisy images as much as possible, as this in turn will lead to more artifacts in the CLUT

Lets assume we have the following two image pairs in our folder:

doc
├── 01in.jpg
├── 01out.jpg
├── 02in.jpg
└── 02out.jpg
Unedited Image Edited Image
im01in 01in.jpg im01out 01out.jpg
im02in 02in.jpg im02out 02out.jpg

however, the HaldCLUT for that color mapping is not available to us. We can use the clut package to generate a fit based on the above images:

>>> clut fit --from 01in.jpg 02in.jpg --to 01out.jpg 02out.jpg
Fitting based on 2 image pairs ...

We now have a clutfit.png in the same directory, which can be applied to any image:

>>> clut apply clutfit.png --to 01in.jpg 02in.jpg
01in.jpg ...
02in.jpg ...
Original Edited Image Reconstructed Filter Image
im01in 01out.jpg im01clut 01in_clut.jpg
im02in 02out.jpg im02clut 02in_clut.jpg

Additional notes

  • When working with large images, consider using the clut fit --scale option to significantly speed up the fitting process
  • If your images show visible artifacts after the application of a HaldCLUT that has been previously generated with clut fit, you can try using the denoise option with clut fit --denoise X, where X is a value between 1e-4 and 1e-2. This requires the scikit-image module
  • When in trouble, see clut fit --help