颜色查找表LUT

article/2025/9/14 10:36:25

查找表(LUT,LookUp Table)是图像颜色转换的强大工具,在许多图形和视频编辑器中使用。

2D LUT

CLUT-from-images

2D LUT生成

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 * 64y = g + by * 64img[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 imgidentity_lut = generate_identify_color_matrix()

在这里插入图片描述

2D LUT 用于图像转换

from typing import Tuple
from functools import lru_cache
from math import floor
import numpy as np
import cv2def 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 modex, y, bx, by = color_coordinate(r, g, b)lut_y = y + by * 64lut_x = x + bx * 64dst_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, 0x = 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, 0y = floor(floor(b) / 8.0)x = int(floor(b) - y * 8.0)return x, yimage = cv2.imread('./test.jpg')
lut = cv2.imread('./YV2.JPG')
result_img = lut_apply(image, lut)
cv2.imwrite('./test_YV2_cv.jpg', result_img)
快速方法:
class LUT_WHITEN:def __init__(self, lut):cube64rows = 8cube64size = 64cube256size = 256cubescale = (int)(cube256size / cube64size) #4reshapelut = 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.shapeif _rows == 0 or _cols == 0:continuecube256 = cv2.resize(cube64, (cube256size, cube256size))i = i * cubescalefor k in range(cubescale):reshapelut[i + k] = cube256self.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

3D LUT的常见格式:

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

3D LUT的生成(Hald CLUT)

color-filters-reconstruction

  • 创建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))
    

    为了防止在变换期间可能发生的失真、,如渐晕、划痕、渐变和JPEG伪影。生成的 25 × 25 × 25 25 \times 25 \times 25 25×25×25d的hadl image如下所示(显示了4个):
    25x25x25 look up table

  • 使用图像处理软件对hald image图像进行变换处理,得到同样尺寸的filtered hald image。
    在这里插入图片描述

  • 改进方法,用高斯模糊filtered image进行处理减少噪声。
    如果目标滤波器在局部水平上有严重的失真或图像中心有显著的梯度,则可能出现一些不期望的效果。有问题滤波图像如下所示:
    在这里插入图片描述

     ./bin/convert.py raw/15.Hudson.jpg halds/ --smooth 1.5
    
  • 将滤波后的图像转换为真实的hald image(显示的4个中裁剪了一个)。
    在这里插入图片描述

3D LUT(Hald CLUT)应用于图像(Pillow库)

pillow-lut-tools
Pillow LUT tools
Pillow LUT工具包含用于加载、操作和生成三维查找表的工具,是专为Pillow库设计的。

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

Pillow LUT中包含的函数:

  • load_cube_file从.cube文件格式加载三维查找表
    在这里插入图片描述

  • load_hald_image从Hald图像(通常是.png或.tiff文件)加载三维查找表。
    在这里插入图片描述

  • identity_table返回具有线性分布值的noop查找表。
    在这里插入图片描述

  • rgb_color_enhance根据基本颜色设置的给定值生成三维颜色查找表。
    在这里插入图片描述

  • sample_lut_linear使用线性插值从给定的三维查找表计算新的点值。
    在这里插入图片描述

  • sample_lut_cubic使用三次插值从给定的三维查找表计算新的点值。
    在这里插入图片描述

  • resize_lut使用插值将给定的查找表调整为新大小。
    在这里插入图片描述

  • transform_lut使用另一个表转换给定的查找表并返回结果。
    在这里插入图片描述

  • amplity_lut放大给定的查找表
    在这里插入图片描述

Pillow LUT函数的使用例子:

from PIL import Image
from pillow_lut import load_hald_imagehefe = load_hald_image('./res/hald.6.hefe.png')
im = Image.open('./res/pineapple.jpeg')
im.filter(hefe).save('./res/pineapple.hefe.jpeg')

在这里插入图片描述

from pillow_lut import rgb_color_enhancelut = 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')

在这里插入图片描述

from pillow_lut import load_hald_image, rgb_color_enhancehefe = 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')

在这里插入图片描述

3D LUT(Hald CLUT )转换为2D square CLUT

import numpy, cv2hald = 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)

2D LUT和3D LUT之间相互转换

单通道转换示意图,便于理解,3D LUT转 2D LUT容易出错的地方已标红。
在这里插入图片描述

2D LUT到3D LUT的转换

# 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到2D LUT的转换

#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)

参考资料

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


http://chatgpt.dhexx.cn/article/bZyQk7nR.shtml

相关文章

Hadoop HA介绍

1、HA 概述 所谓HA&#xff08;High Available&#xff09;&#xff0c;即高可用&#xff08;7*24小时不中断服务&#xff09;。实现高可用最关键的策略是消除单点故障。Hadoop-HA严格来说应该分成各个组件的HA机制&#xff1a; HDFS的HA和YARN的HA。Hadoop2.0之前&#xff0c…

HAL 库

HAL库 1、初识HAL库 1.1 CMSIS 简介 CMSIS&#xff08;微控制器软件接口标准&#xff09;&#xff1a;Crotex Microcontroller Software Interface Standard&#xff0c;是由ARM和与其合作的芯片厂商、软件工具厂商&#xff0c;共同制定的标准 ARM官方提供的CMSIS规范架构 …

HIDL(HAL interface definition langguage)

HIDL的相关介绍 HIDL的全称是HAL interface definition language&#xff08;硬件抽象层接口定义语言&#xff09;&#xff0c;在此之前 Android 有AIDL&#xff0c;架构在Android binder 之上&#xff0c;用来定义Android 基于Binder通信的Client 与Service之间的接口。HIDL…

内部类

一、非静态内部类。 1、修饰符 非静态内部类有四个作用域&#xff0c;所以有四个修饰符。 private : 只能在外部类的内部使用。 protected : 可被与外部类处于同一个包中的其他类和外部类的子类所访问。 省略 : 只能被与外部类处于同一个包中的其他类访问。 public : 可…

python的类作用_python中类的作用是什么

简单来说&#xff0c;类是一种高级抽象&#xff0c;就是一种高级的数据类型&#xff0c;是对象的蓝图&#xff0c;就是用来定义你要用的对象的属性和行为的。 以下是面向对象简介 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性…

C# 内部类的作用

作用一&#xff1a;限制对类的可访问性 有时候会遇到这样的需求&#xff0c;希望一个类仅能被另一个类&#xff08;以及其派生类&#xff09;访问。 代码 class BaseClass {public class PublicNestedClass { }protected class ProtectedNestedClass { }private class Priva…

内部类详解

1.成员内部类 1.样例 class OutClass {class InnerClass {public String SayHi() {return "你好";}} }2.特点 内部类能够无条件的访问外部类的成员变量&#xff0c;外部类要访问内部类成员变量需要使用new。内部类和外部类有相同名称的变量或者是方法&#xff0c;…

Java 静态内部类作用

需要了解2个概念&#xff1a;内部类和静态修饰符static 1&#xff09;首先&#xff0c;用内部类是因为内部类与所在外部类有一定的关系&#xff0c;往往只有该外部类调用此内部类。所以没有必要专门用一个Java文件存放这个类。 2&#xff09;静态都是用来修饰类的内部成员的。…

java内部类的四大作用

一、内部类的作用 我们为什么需要内部类&#xff1f;或者说内部类为啥要存在&#xff1f;其主要原因有如下几点&#xff1a; 内部类方法可以访问该类定义所在作用域中的数据&#xff0c;包括被 private 修饰的私有数据内部类可以对同一包中的其他类隐藏起来内部类可以解决java …

Flink--- 批处理 / 流处理

目录 Flink的主要特点 Flink 和 Spark Streaming 搭建maven工程 FlinkTutorial 添加Scala框架 和 Scala文件夹 Flink-批处理wordcount Flink---流处理wordcount Flink 是一个框架和分布式的处理引擎&#xff0c;用于对无界和有界数据流进行状态计算。 传统数据处理架构 事…

流数据处理利器

流处理 (Stream processing) 是一种计算机编程范式&#xff0c;其允许给定一个数据序列 (流处理数据源)&#xff0c;一系列数据操作 (函数) 被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率&#xff0c;允许他们编写有效、干净和简洁的代码。 流数据处理在…

流处理(Stream)和批处理(Batch)

1 流处理和批处理的概念 在程序计算当中&#xff0c;同一节点或者不同节点之间的数据的传递是实时传递还是延迟传递&#xff0c;这就引出了两个概念。其实在非大数据领域这两个概念所起的作用也是很有限&#xff0c;但是在大数据领域&#xff0c;处理上亿级别的时候&#xff0…

管道模式 流处理

&#xff08;一&#xff09;介绍 管道这个名字源于自来水厂的原水处理过程。原水要经过管道&#xff0c;一层层地过滤、沉淀、去杂质、消毒&#xff0c;到管道另一端形成纯净水。我们不应该把所有原水的过滤都放在一个管道中去提纯&#xff0c;而应该把处理过程进行划分&#…

Flink流处理API大合集:掌握所有flink流处理技术,看这一篇就够了

大家好&#xff0c;我是百思不得小赵。 创作时间&#xff1a;2022 年 5 月 18 日 博客主页&#xff1a; &#x1f50d;点此进入博客主页 —— 新时代的农民工 &#x1f64a; —— 换一种思维逻辑去看待这个世界 &#x1f440; 今天是加入CSDN的第1172天。觉得有帮助麻烦&#x…

Python流处理

转自 &#xff1a;https://www.toutiao.com/a6589000256896107015/?tt_frommobile_qq&utm_campaignclient_share&timestamp1534156143&appnews_article&utm_sourcemobile_qq&iid40708017633&utm_mediumtoutiao_ios&group_id6589000256896107015 F…

Stream流式处理

Stream流的三类方法 获取Stream&#xff1a;流创建一条流水线,并把数据放到流水线上准备。 中间方法&#xff1a;流水线上的操作一次操作完毕之后,还可以继续进行其他操作。 终结方法&#xff1a;一个Stream流只能有一个终结方法是流水线上的最后一个操作。 生成Stream流的…

流数据处理与分析

环境 名称 版本 系统 Ubuntu 18.04.4 LTS 内存 7.5GiB 处理器 Intel Core i7-8565U CPU 1.80GHz *8 图形 Intel UHD Graphics&#xff08;Whiskey Lake 3*8 GT2&#xff09; GNOME 3.28.2 操作系统类型 64位 磁盘 251.0 GB Storm 2.1.0 Zookeeper…

流处理系统

文章目录 引言如何发送事件流流处理不可靠的时钟容错总结 引言 清楚数据的类型有助于我们设计一个性能更高&#xff0c;更有针对性的数据系统&#xff0c;比如在线系统&#xff0c;离线系统&#xff08;批处理&#xff09;。近实时系统(流处理)等等。比如说批处理系统&#xf…

流处理简介

一. 流式处理简介 在我接触到java8流式处理的时候&#xff0c;我的第一感觉是流式处理让集合操作变得简洁了许多&#xff0c;通常我们需要多行代码才能完成的操作&#xff0c;借助于流式处理可以在一行中实现。比如我们希望对一个包含整数的集合中筛选出所有的偶数&#xff0c;…

【节点流和处理流】

节点流和处理流 基本介绍 节点流可以从特定数据源读取数据&#xff0c;如FileReader、FileWriter处理流&#xff1a;是对一个已存在的流的连接和封装&#xff0c;通过所封装的流的功能调用实现数据读写。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一…