Color Transformation: Thinning Table Searches

This is an overview of the functionality introduced in Pillow 5.2 : the use of three-dimensional lookup tables (3D LUTs) for color transformation. This technique is widespread in video processing and 3D games, but few graphic libraries could boast of 3D LUT transformations before that.


Three-dimensional lookup tables provide great flexibility in describing color transformations, but most importantly, transformations are performed in the same time, no matter how complex they are.


from PIL import Image, ImageFilter

def washout(r, g, b):
    h, s, v = _rgb_to_hsv(r, g, b)
    if 0.3 < h < 0.7:
        s = 0
    return _hsv_to_rgb(h, s, v)

im = Image.open('./Puffins.jpg')
im = im.filter(ImageFilter.Color3DLUT.generate(17, washout))

A feature entirely written in Python applies to a 16.6-megapixel picture in 75ms.



Working with images is already resource-intensive, so I love algorithms that remove complexity from input parameters. Five years ago, I implemented a Gaussian blur at Pillow , working in the same time for any radius. Not so long ago I talked about how you can reduce the image in constant time with minimal loss of quality. Today I’ll show you what tasks you can use 3D LUT for, what its limitations are and I’m bragging about the achieved performance in Pillow-SIMD.


Do it normally - it won’t be normal


: , . - , ( 0 255). , HSV , Hue, , , Saturation, , , Value, . , , Saturation - , . .


? , , . - , , . API, , , , . .


, , . , , , . , : , , , . API β€” .



- . , . , , , . , , - !


, . . , , RGB #e51288, [229, 18, 136]. β€” , RGB-. :



. 256Β³, 16 , 48 . 48 β€” . , L3 , . , 16 , 16 , , , . ?


, . , -- , . , , , 16Β³ . 12 , L3, L1 .



? β€” . , , ( ) .


, , . , , , . , .


3D LUT


, - . : , , , , . . . , -, . .


: , , , . , , .


, , . , , , . , . , , , - ( , ) .


?


, Pillow. API Color3DLUT PIL.ImageFilter. , :


from PIL.ImageFilter import Color3DLUT

table = [(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0),
         (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]
lut = Color3DLUT(2, table)

β€” . , . :


from PIL import Image
Image.open('in.jpg').filter(lut).save('out.jpg')

, . - , :



β€” - , generate, . , . 3D-.


def transform(r, g, b):
    r, g, b = (max(r, g, b), g, min(r, g, b))
    avg_v = r * 0.2126 + g * 0.7152 + b * 0.0722
    r += (r - avg_v) * 0.6
    g += (g - avg_v) * 0.6
    b += (b - avg_v) * 0.6
    return r, g, b

lut = Color3DLUT.generate(17, transform)
Image.open('in.jpg').filter(lut).save('out.jpg')


- , . : - , . , . Pillow-lut. rgb_color_enhance, :


from pillow_lut import rgb_color_enhance
lut = rgb_color_enhance(
    11, exposure=1, contrast=0.3, vibrance=-0.2, warmth=0.3)
Image.open('in.jpg').filter(lut).save('out.jpg')


, , ? : .cube ( , ) hald- β€” , .



Pillow-lut .


from pillow_lut import load_hald_image 
lut = load_hald_image('hald.6.hefe.png')
Image.open('in.jpg').filter(lut).save('out.jpg')


β€” , , ! , , . rgb_color_enhance , , :


lut = load_hald_image('hald.6.hefe.png')
lut = rgb_color_enhance(
    lut, exposure=1, contrast=0.3, vibrance=-0.2, warmth=0.3)
Image.open('in.jpg').filter(lut).save('out.jpg')


, . rgb_color_enhance. transform_lut, .


, «». ? . , 1, , β€” . . amplify_lut:


from pillow_lut import load_hald_image, amplify_lut
lut = load_hald_image('hald.6.hefe.png')
lut = amplify_lut(lut, scale=3.0)
Image.open('in.jpg').filter(lut).save('out.jpg')



: , . , ? , ( . : ).


, Pillow 5.2 16- ( ). , . 16- .


, 7 . 21 . , , . , ( 6 ), 2,5 , , . , SIMD-.


. ImageMagick GraphicsMagick. β€” LittleCMS β€” icc- . , , . , perf, PrelinEval8, , .



Intel Core i5-8279U.


Pillow-SIMD β€” Pillow, x86 . 100% Pillow . , , , Pillow 5.4, Pillow-SIMD 7.0. Pillow , . .



As can be seen from the test results, the color transformation in Pillow is faster than existing solutions, and with the use of SIMD instructions flies into space. I must say that this can still be slower than a well-optimized implementation of a fairly large number of transformations that could be written in C. But on the side of the search tables, versatility, simplicity and stable time, regardless of the number and complexity of transformations. A simple API allows you to quickly start using color transformations, and a special Pillow-lut library makes it even easier to work.


All Articles