Color lookup table LUT

Lookup table (LUT, LookUp Table) is a powerful tool for image color conversion, used in many graphics and video editors.

2D LUT

CLUT-from-images

2D LUT generation

def generate_identify_color_matrix(width, height, channel):
    img = np.zeros((width, height, channels), dtype=np.uint8)
    for by in range(8):
        for bx in range(8):
            for g in range(64):
                for r in range(64):
                    x = r + bx * 64
                    y = g + by * 64
                    img[y][x][0] = int(r * 255.0 / 63.0 + 0.5)
                    img[y][x][1] = int(g * 255.0 / 63.0 + 0.5)
                    img[y][x][2] = int((bx + by * 8.0) * 255.0 / 63.0 + 0.5)
    return img
 identity_lut = generate_identify_color_matrix()

Insert picture description here

2D LUT for image conversion

from typing import Tuple
from functools import lru_cache
from math import floor
import numpy as np
import cv2

def lut_apply(image, lut):
    img_list = image.tolist()
    # dst_img = image.copy()
    dst_img = np.zeros((image.shape[0], image.shape[1], image.shape[2]), dtype=np.uint8)
    for iy in range(image.shape[0]):
        for ix in range(image.shape[1]):
            b, g, r = img_list[iy][ix] #bgr mode
            x, y, bx, by = color_coordinate(r, g, b)
            lut_y = y + by * 64
            lut_x = x + bx * 64
            dst_img[iy][ix][0] = lut[lut_y][lut_x][0]
            dst_img[iy][ix][1] = lut[lut_y][lut_x][1]
            dst_img[iy][ix][2] = lut[lut_y][lut_x][2]
    return dst_img
@lru_cache(maxsize=512)
def color_coordinate(r, g, b) -> Tuple[int, int, int, int]:
    x, y, bx, by = 0, 0, 0, 0
    x = floor(r / 4.0)
    y = floor(g / 4.0)
    bx, by = blue_coordinate(floor(b / 4.0))
    return x, y, bx, by


@lru_cache(maxsize=64)
def blue_coordinate(b: int) -> Tuple[int, int]:
    assert b >= 0 and b <= 63, 'GOT {}'.format(b)
    x, y = 0, 0
    y = floor(floor(b) / 8.0)
    x = int(floor(b) - y * 8.0)
    return x, y
    
image = cv2.imread('./test.jpg')
lut = cv2.imread('./YV2.JPG')
result_img = lut_apply(image, lut)
cv2.imwrite('./test_YV2_cv.jpg', result_img)
Quick way:
class LUT_WHITEN:
    def __init__(self, lut):
        cube64rows = 8
        cube64size = 64
        cube256size = 256
        cubescale = (int)(cube256size / cube64size) #4

        reshapelut = np.zeros((cube256size, cube256size, cube256size, 3))
        for i in range(cube64size):
            tmp = math.floor(i / cube64rows)
            cx = int((i - tmp * cube64rows) * cube64size)
            cy = int(tmp * cube64size)
            cube64 = lut[cy:cy+cube64size, cx:cx+cube64size]#cube64 in lut(512*512 (512=8*64))
            _rows, _cols, _ = cube64.shape
            if _rows == 0 or _cols == 0:
                continue
            cube256 = cv2.resize(cube64, (cube256size, cube256size))
            i = i * cubescale
            for k in range(cubescale):
                reshapelut[i + k] = cube256
        self.lut = reshapelut
        # print('reshapelut shape:', reshapelut.shape)

    def imageInLut(self, src):
        arr = src.copy()
        bs = arr[:, :, 0]
        gs = arr[:, :, 1]
        rs = arr[:, :, 2]
        arr[:, :] = self.lut[bs, gs, rs]
        return arr

3D LUT

Common formats of 3D LUT:

  • Hald CLUT(.png)
  • 3D LUT(.3dl)
  • Cube LUT (.cube)

3D LUT generation (Hald CLUT)

color-filters-reconstruction

  • Create identity image (3D LUT)

    ./bin/generate.py
    
    def generate_hald(size):
        b, g, r = numpy.mgrid[
            0 : 255 : size**2*1j,
            0 : 255 : size**2*1j,
            0 : 255 : size**2*1j
        ].astype(numpy.uint8)
    
        rgb = numpy.stack((r, g, b), axis=-1)
        return Image.fromarray(rgb.reshape(size**3, size**3, 3))
    

    In order to prevent distortion that may occur during the transformation, such as vignetting, scratches, gradation and JPEG artifacts. Generated 25 × 25 × 25 25 \times 25 \times 2525×25×The hadl image of 2 5 d is as follows (4 are shown):
    25x25x25 look up table

  • Use image processing software to transform the hald image to obtain the filtered hald image of the same size.
    Insert picture description here

  • Improved method, using Gaussian Blur filtered image for processing to reduce noise.
    If the target filter is severely distorted at the local level or the image center has a significant gradient, some undesirable effects may appear. The filtered image in question is as follows:
    Insert picture description here

     ./bin/convert.py raw/15.Hudson.jpg halds/ --smooth 1.5
    
  • Convert the filtered image to a real hald image (one of the 4 shown is cropped).
    Insert picture description here

3D LUT (Hald CLUT) applied to images (Pillow library)

pillow-lut-tools
Pillow LUT tools
Pillow LUT tools contain tools for loading, operating and generating 3D lookup tables, and are designed specifically for the Pillow library.

from PIL import Image
from pillow_lut import load_hald_image

hefe = load_hald_image('./res/test_hald.5.png')
img = Image.open('./res/test.jpg')
img.filter(hefe).save('./res/test.hald.jpg')

Functions included in Pillow LUT:

  • load_cube_file loads 3D lookup table from .cube file format
    Insert picture description here

  • load_hald_image loads a 3D lookup table from a Hald image (usually a .png or .tiff file).
    Insert picture description here

  • identity_table returns a noop lookup table with linearly distributed values.
    Insert picture description here

  • rgb_color_enhance generates a three-dimensional color lookup table based on the given value of the basic color settings.
    Insert picture description here

  • sample_lut_linear uses linear interpolation to calculate new point values ​​from a given three-dimensional lookup table.
    Insert picture description here

  • sample_lut_cubic uses cubic interpolation to calculate new point values ​​from a given three-dimensional lookup table.
    Insert picture description here

  • resize_lut uses interpolation to resize the given lookup table to the new size.
    Insert picture description here

  • transform_lut uses another table to transform the given lookup table and returns the result.
    Insert picture description here

  • amplity_lut amplifies the given lookup table
    Insert picture description here

Pillow LUT function usage example:

from PIL import Image
from pillow_lut import load_hald_image

hefe = load_hald_image('./res/hald.6.hefe.png')
im = Image.open('./res/pineapple.jpeg')
im.filter(hefe).save('./res/pineapple.hefe.jpeg')

Insert picture description here

from pillow_lut import rgb_color_enhance

lut = rgb_color_enhance(11, exposure=0.2, contrast=0.1, vibrance=0.5, gamma=1.3)
im = Image.open('./res/pineapple.jpeg')
im.filter(lut).save('./res/pineapple.enhance.jpeg')

Insert picture description here

from pillow_lut import load_hald_image, rgb_color_enhance

hefe = load_hald_image('./res/hald.6.hefe.png')
lut = rgb_color_enhance(hefe, exposure=0.2, contrast=0.1, vibrance=0.5, gamma=1.3)
im = Image.open('./res/pineapple.jpeg')
im.filter(lut).save('./res/pineapple.hefe.enhance.jpeg')

Insert picture description here

3D LUT (Hald CLUT) converted to 2D square CLUT

import numpy, cv2

hald = cv2.imread('1.Clarendon.png')

size = int(hald.shape[0] ** (1.0/3.0) + .5)
clut = numpy.concatenate([
    numpy.concatenate(im.reshape((size, size, size**2, size**2, 3))[row], axis=1)
    for row in range(size)
])

cv2.imwrite("clut.png", clut)

Conversion between 2D LUT and 3D LUT

The single-channel conversion diagram is easy to understand. The error-prone areas of 3D LUT to 2D LUT have been marked in red.
Insert picture description here

2D LUT to 3D LUT conversion

# 2D LUT(size**3, size**3, 3) ->  3D LUT(size**3, size**3, 3)
clut = cv2.imread('./identity_clut_8.png')
size = int(clut.shape[0] ** (1.0 / 3.0) + 0.5)
clut_result = np.zeros((size ** 2, size ** 2, size ** 2, 3))
for i in range(size ** 2):
    tmp1 = math.floor(i / size)
    cx = int((i - tmp1 * size) * size ** 2)
    cy = int(tmp1 * size ** 2)
    clut_result[i] = clut[cy: cy + size ** 2, cx : cx + size ** 2]

hald = clut_result.reshape((size ** 3, size ** 3, 3))

cv2.imwrite('./identity_hald_{}_test.png'.format(size), hald)

3D LUT to 2D LUT conversion

#3D LUT(size**3, size**3, 3) ->  2D LUT(size**3, size**3, 3)
hald = cv2.imread('./identity_hald_8.png')

size = int(hald.shape[0] ** (1.0/3.0) + .5)
#method1
clut = np.concatenate([
    np.concatenate(hald.reshape((size, size, size**2, size**2, 3))[row], axis=1)
    for row in range(size)
])
cv2.imwrite("2d_identity.png", clut)
#method2
hald_reshape = hald.reshape((size, size, size ** 2, size ** 2, 3))
print('hald_reshape shape:', hald_reshape.shape)
img_list = []
for i in range(size):
    print('hald_reshape[i] shape:', hald_reshape[i].shape)
    tmp = np.concatenate(hald_reshape[i], axis=1)
    print('tmp shape:', tmp.shape)
    img_list.append(tmp)

result = np.concatenate(img_list)
print('result shape:', result.shape)

cv2.imwrite("identity_clut_8_test.png", result)

Reference

color-filters-reconstruction
pillow-lut-tools
Pillow LUT tools
Create your own LUT
CLUT-from-images

Guess you like

Origin blog.csdn.net/studyeboy/article/details/112554067