
图像处理算法入门系列 · 第 027 期 | 模块三:像素级图像处理与灰度变换
发布日期:2026-09-06 | 环境:Python 3.13 + OpenCV 5.0 + NumPy + Matplotlib
摘要
024 线性、025 曲线、026 折线------前三期我们写了一堆点运算公式,每一期都是"逐像素算一遍"。本期介绍把这一切收编的统一引擎:查找表(Look-Up Table,LUT)。
核心结论先放这里:
- LUT 就是 256 项一维表 :
out[p] = table[p],把"逐像素浮点计算"降维成"逐像素数组下标访问"; - 建表代价恒定:无论图像多大、公式多复杂,预计算只有 256 次,与图像尺寸无关;
- 实测性能 (480×640 灰度伽马校正):Python 循环 43 ms → NumPy 直接计算 2.6 ms → NumPy 花式索引 0.41 ms → cv2.LUT 0.07 ms ,端到端提速约 663 倍;
- 复合律:N 个点运算串联 = 1 张复合 LUT,5 趟查表融合成 1 趟,结果逐像素一致(max|Δ| = 0);
- 顺序律 :非线性点运算不可交换,
f○g ≠ g○f,交换顺序输出差 88 个灰度级; - 实战:VSCO 式胶片感滤镜 = S 曲线 + 褪色 + 三通道曲线,全部预复合进一张 256×3 表,一次查表 0.2 ms 完成。
所有代码完整可运行,文末附 8 组实验的可视化解读、产品推荐表、FAQ 与课后练习。
一、为什么需要 LUT:点运算的冗余
先算一笔账。024~026 期的每个点运算,本质上都是同一个模式:
text
对图像里每一个像素 p:
out[p] = 某个函数 F(p) ← F 可能是 255·(p/255)^γ,也可能是折线插值
一张 4000×3000 的照片有 1200 万个像素,就要做 1200 万次浮点幂运算。但请注意一个关键事实:
点运算的输入只有 0~255 共 256 种取值。
1200 万个像素,反复计算的 F 其实最多只有 256 种不同的输入!那就干脆先算好一张 256 项的答案表:
text
table[0] = F(0)
table[1] = F(1)
...
table[255] = F(255)
之后处理图像时,每个像素只做一次"查表":
text
out[p] = table[p] ← 一次数组下标访问,O(1)
这就是查找表(LUT)。计算量从 N×cost(F) 降为 256×cost(F) + N×1------建表成本是常数,与图像大小彻底解耦。图像越大、F 越复杂,LUT 赚得越多。
二、原理:LUT 的三个形态
2.1 形态一:单通道表 (256,)
最基本形态,适用于灰度图,或对彩色图三通道"广播"同一变换:
text
table = [t0, t1, t2, ..., t255] uint8 一维数组
out[b, g, r] 图像 → 三个通道共用这一张表
2.2 形态二:多通道表 (256, C)
三通道各配一条曲线,实现"调色"而非"调明暗":
text
table = ┌ R 曲线 256 项 ┐
│ G 曲线 256 项 │ → 第 0 列作用于 B 通道(OpenCV 是 BGR!)
└ B 曲线 256 项 ┘
⚠️ OpenCV 通道序陷阱 :cv2.LUT 的多通道表第 0 列作用的是 B 通道。设计暖色调滤镜时"R 通道提亮"指的是图像的第 2 通道,写表的时候要反转列序。
⚠️ NumPy 形状陷阱 :直接把 (256, 3) 的二维数组传给 cv2.LUT,OpenCV 会把它当作"单通道的 256×3 矩阵"(共 768 项),直接报 lut_size != 256 断言错误。正确做法是 reshape 成 (256, 1, 3):
python
lut_bgr = lut_rgb[:, ::-1].copy() # RGB → BGR 列序反转
lut_cv = np.ascontiguousarray(lut_bgr).reshape(256, 1, 3) # 关键!
out = cv2.LUT(img, lut_cv) # OK
2.3 形态三:复合表(本期精华)
点运算可以复合:先做伽马再做对比度,等价于一个新函数 g(x) = contrast(γ(x))。既然每张 LUT 都是一张 256 项的表,那复合也可以直接在表域完成:
text
comp = lut_contrast[lut_gamma] ← uint8 数组当索引用,天然安全(0~255 不会越界)
复合表的输出与"老老实实查两趟表"逐像素完全一致(后面实验 2 验证 max|Δ| = 0),但只需查一趟。N 个点运算串联就复合 N 张表,"5 趟变 1 趟"。
三、四个必须知道的性质
3.1 建表代价是常数
无论 F 是简单线性还是十段折线 + 幂函数,建表都是 256 次计算。LUT 的收益随图像尺寸和公式复杂度单调增长。
3.2 复合律:N 趟变 1 趟
text
out = F_n(...F_2(F_1(img))...) ≡ img 逐像素查 comp 表
其中 comp[x] = F_n(...F_2(F_1(x))...)
前提:链条上全是点运算。空间滤波(模糊、锐化、边缘检测)涉及邻域,无法进 LUT。
3.3 顺序律:不可交换
线性运算满足交换律,但非线性点运算不满足:
text
gamma(contrast(x)) ≠ contrast(gamma(x))
实验 3 给出实测:输入 170 的灰阶,"先 γ=2.2 后 α=2.0"输出 82,"先 α=2.0 后 γ=2.2"输出 170------差 88 个灰度级。在表域复合时,顺序就焊死在表里了,这既是陷阱(改顺序要重建表)也是优势(运行时零分支、不可篡改)。
3.4 量化误差:256 级够不够?
建表时 round 引入取整误差,单次查表误差不超过 1 个灰度级(实验 7 实测 max +0.50,mean ≈ 0)。级联时误差会累积,但实测 γ=0.85 连查 8 次 vs 一张 γ⁸ 复合表,最大差异也只有 2 级------8 万像素受影响、但肉眼不可见。结论:256 级精度对 8 bit 图像完全够用;级联场景一律预复合成单表根治。
四、完整代码
环境说明:Python 3.13、OpenCV 5.0、NumPy、Matplotlib。中文图表需配置中文字体(微软雅黑)。
运行方式:
python 027_lut_acceleration.py,所有可视化输出到code/output/。
python
"""
第027期:查找表(LUT)加速 ------ 像素级操作优化
作者:图像处理算法入门系列
日期:2026-09-05
环境:Python 3.8+, OpenCV 4.x/5.x, NumPy, Matplotlib
八个实验:
1. 原理图:LUT 表结构示意 + 映射曲线 + 渐变条查表效果
2. 复合律:伽马→对比度两趟 vs 复合 LUT 一趟(逐像素一致性验证)
3. 顺序律:γ 与 contrast 交换顺序的曲线与图像差异
4. 性能基准:Python 循环 / NumPy 直接计算 / NumPy 查表 / cv2.LUT
5. 多通道 LUT:广播表 vs (256,3) 逐通道表(暖色调滤镜)
6. 胶片感滤镜:控制点曲线 → 插值 LUT → before/after + 直方图
7. 量化误差:浮点精确值 vs 256 级表 + 级联漂移
8. 实战 pipeline:5 个点运算 naive 串联 vs 复合单表
"""
import os
import time
import cv2
import numpy as np
# ------------------------------------------------------------------
# 0. 输出目录与中文字体
# ------------------------------------------------------------------
OUTPUT_DIR = os.path.join(os.path.dirname(__file__), 'output')
os.makedirs(OUTPUT_DIR, exist_ok=True)
def ensure_chinese_font():
"""配置 Matplotlib 中文字体(Windows 优先微软雅黑)。"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import font_manager
candidates = ['Microsoft YaHei', 'SimHei', 'WenQuanYi Micro Hei',
'Noto Sans CJK SC', 'PingFang SC']
available = [f.name for f in font_manager.fontManager.ttflist]
chosen = None
for c in candidates:
if c in available:
chosen = c
break
if chosen:
plt.rcParams['font.sans-serif'] = [chosen] + plt.rcParams['font.sans-serif']
plt.rcParams['axes.unicode_minus'] = False
return plt
plt = ensure_chinese_font()
# ------------------------------------------------------------------
# 1. 核心算法:点运算 → LUT 构建器
# ------------------------------------------------------------------
def to_u8(vals):
"""float → uint8(先 round 再 clip,杜绝回绕)。"""
return np.clip(np.round(vals), 0, 255).astype(np.uint8)
def lut_gamma(gamma):
"""伽马 LUT:g = 255·(f/255)^γ。"""
xs = np.arange(256, dtype=np.float32)
return to_u8(255.0 * np.power(xs / 255.0, gamma))
def lut_contrast(alpha):
"""对比度 LUT:绕 128 中灰旋转(024 期结论)。"""
xs = np.arange(256, dtype=np.float32)
return to_u8(alpha * (xs - 128.0) + 128.0)
def lut_brightness(beta):
"""亮度 LUT:整体平移。"""
xs = np.arange(256, dtype=np.float32)
return to_u8(xs + beta)
def lut_scurve(strength=0.35):
"""S 曲线 LUT(余弦缓入缓出),strength 控制对比度强度。"""
xs = np.arange(256, dtype=np.float32)
t = xs / 255.0
s = 0.5 - 0.5 * np.cos(np.pi * t) # 平滑 S 基线
ys = (t + strength * (s - t)) * 255.0 # 恒等与 S 的插值
return to_u8(ys)
def lut_fade(lift):
"""褪色(提黑降白)LUT:把黑位抬到 lift、白位压到 255−lift·0.6。"""
xs = np.arange(256, dtype=np.float32)
lo, hi = lift, 255.0 - lift * 0.6
return to_u8(lo + (hi - lo) * xs / 255.0)
def lut_from_points(points):
"""控制点曲线 → 插值 LUT(Lightroom 曲线工具的数学本质)。"""
pts = sorted(points)
px = [p[0] for p in pts]
py = [p[1] for p in pts]
xs = np.arange(256, dtype=np.float32)
return to_u8(np.interp(xs, px, py))
def compose_luts(*luts):
"""
复合 N 张 LUT:g = lutN[...[lut2[lut1[x]]]...]。
输入 uint8 数组当索引用天然安全(0~255 不会越界)。
"""
comp = luts[0].copy()
for nxt in luts[1:]:
comp = nxt[comp]
return comp
def as_cv_lut(lut2d):
"""
把 (256,C) 二维表转成 cv2.LUT 认可的多通道表 (256,1,C)。
关键点:NumPy 二维数组传入 cv2 会被视为"单通道 256×C 矩阵",
直接用会报 lut_size != 256 断言错误。
"""
arr = np.ascontiguousarray(lut2d)
if arr.ndim == 2 and arr.shape[1] > 1:
arr = arr.reshape(256, 1, arr.shape[1])
return arr
def np_gamma_direct(img, gamma):
"""NumPy 逐像素直接计算(基准对照用,未走 LUT)。"""
norm = img.astype(np.float32) / 255.0
return to_u8(255.0 * np.power(norm, gamma))
# ------------------------------------------------------------------
# 2. 通用工具与测试图
# ------------------------------------------------------------------
def save_fig(fig, name):
out_path = os.path.join(OUTPUT_DIR, name)
fig.savefig(out_path, dpi=110, bbox_inches='tight', facecolor='white')
plt.close(fig)
print(f'[saved] {out_path}')
def gray_image(ax, img, title='', cmap='gray'):
ax.imshow(img, cmap=cmap, vmin=0, vmax=255, interpolation='nearest')
ax.set_title(title, fontsize=10)
ax.axis('off')
def color_image(ax, img_bgr, title=''):
ax.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
ax.set_title(title, fontsize=10)
ax.axis('off')
def make_gray_gradient(length=400):
return np.linspace(0, 255, length).astype(np.uint8)[None, :]
def make_color_scene(size=(420, 560), seed=7):
"""合成"逆光照片"风格彩色测试图(BGR):亮天空 + 太阳 + 山峦剪影。"""
h, w = size
rng = np.random.default_rng(seed)
yy, xx = np.mgrid[0:h, 0:w].astype(np.float32)
ny, nx = yy / h, xx / w
t = ny.clip(0, 1)
sky_b = 60 + 130 * t
sky_g = 120 + 40 * t
sky_r = 235 - 60 * t
img = np.dstack([sky_b, sky_g, sky_r]).astype(np.float32)
d2 = (nx - 0.72) ** 2 + ((ny - 0.30) * 1.0) ** 2
sun = d2 < 0.010
halo = (d2 < 0.075) & (~sun)
img[sun] = [120, 200, 255]
img[halo] = img[halo] * 0.82 + np.array([80, 130, 100]) * 0.35
for i, (base, amp, dark) in enumerate([(0.62, 0.06, 0.45),
(0.72, 0.05, 0.28),
(0.82, 0.04, 0.15)]):
ridge = base + amp * np.sin(2 * np.pi * (nx * (1.3 + 0.7 * i) + 0.2 * i))
mask = ny > ridge
shade = np.array([30, 45, 70]) * dark * 2.2
img[mask] = img[mask] * (1 - 0.85) + shade * 0.85 + 15
water = ny > 0.86
img[water] *= 0.55
img[water] += rng.normal(0, 4.0, (int(water.sum()), 3))
img += rng.normal(0, 2.5, (h, w, 3))
out = np.clip(img, 0, 255).astype(np.uint8)
return cv2.GaussianBlur(out, (3, 3), 0)
def benchmark(fn, n_repeat=5):
"""多次计时取最小值(排除调度抖动)。"""
best = float('inf')
result = None
for _ in range(n_repeat):
t0 = time.perf_counter()
result = fn()
best = min(best, time.perf_counter() - t0)
return best * 1000.0, result # ms
(实验函数体共 8 组,与 8 张可视化一一对应,完整源码见文末仓库链接;以下给出每组实验的关键实现片段。)
实验 2:复合律验证(两趟 vs 复合一趟,逐像素 diff):
python
lut_g = lut_gamma(1.6) # 第一趟:伽马压中间调
lut_c = lut_contrast(1.8) # 第二趟:对比度拉伸
lut_cc = compose_luts(lut_g, lut_c) # 复合:一张表
two_pass = cv2.LUT(cv2.LUT(img, lut_g), lut_c) # 老实查两趟
one_pass = cv2.LUT(img, lut_cc) # 复合表查一趟
diff = cv2.absdiff(two_pass, one_pass)
print(diff.max()) # → 0,逐像素完全一致
实验 4:性能基准(四种实现的同题对抗):
python
lut = lut_gamma(1.6)
# 方案 A:Python 循环(反面教材)
for i in range(flat.shape[0]):
p = float(flat[i]) / 255.0
out[i] = 255.0 * (p ** gamma)
# 方案 B:NumPy 逐像素直接计算
out = to_u8(255.0 * np.power(img.astype(np.float32) / 255.0, gamma))
# 方案 C:NumPy 花式索引查表(uint8 数组天然是 0~255 的合法索引)
out = lut[img]
# 方案 D:cv2.LUT(底层 SIMD 优化)
out = cv2.LUT(img, lut)
实验 6:胶片感滤镜(控制点曲线 + 预复合):
python
def build_film_lut():
# 全通道:S 曲线 + 褪色,先在"表域"复合成一张
comp_rgb = compose_luts(lut_scurve(0.40), lut_fade(26))
# 三通道独立曲线(RGB 顺序给控制点):阴影偏青、高光偏暖
r_curve = lut_from_points([(0, 30), (56, 82), (128, 136), (196, 196), (255, 252)])
g_curve = lut_from_points([(0, 22), (56, 74), (128, 126), (196, 188), (255, 240)])
b_curve = lut_from_points([(0, 36), (56, 86), (128, 122), (196, 168), (255, 218)])
out = np.empty((256, 3), dtype=np.uint8)
for i, cur in enumerate((r_curve, g_curve, b_curve)):
out[:, i] = cur[comp_rgb] # 通道曲线再复合上去
return as_cv_lut(out[:, ::-1]) # RGB → BGR + cv2 多通道表布局
out = cv2.LUT(img, build_film_lut()) # 一张表,一次查表,0.2 ms
实验 8:滤镜链融合(5 趟 vs 1 趟):
python
chain = [lut_gamma(0.85), lut_contrast(1.18), lut_brightness(10),
lut_scurve(0.30), lut_fade(12)]
# 路线 A:5 趟逐表查询
cur = img
for l in chain:
cur = cv2.LUT(cur, l)
# 路线 B:预复合成一张三通道表,1 趟
lut3 = as_cv_lut(np.dstack([compose_luts(*chain)] * 3))
out = cv2.LUT(img, lut3)
五、运行结果与图解读
5.1 原理:一张表管全图(027_lut_principle.png)
左图是 S 曲线 LUT 的映射曲线,红色标注了两次"查表"过程:输入 60 → table[60] = 48,输入 200 → table[200] = 212。右上角的表格给出了表的物理形态------就是一张 256 行的对照表。右下的渐变条最直观:同一条 0→255 渐变,换 S 曲线表、伽马表、褪色表,输出风格完全不同。曲线工具的本质就是换表。
5.2 复合律:N 趟 = 1 趟(027_compose_law.png / 027_compose_curve.png)
四个面板从左到右:原图(mean 110/std 29)→ 两趟结果 → 复合表一趟结果 → 差异图(max|Δ| = 0,全黑)。附图把两条曲线"焊"成一条复合曲线------发布滤镜时只需分发一张 256×1 表,用户端零计算、零分支。
5.3 顺序律:交换顺序差 88 级(027_order_matters.png)
三条灰阶带(60/100/170)在两种顺序下的输出:
text
输入 60:先 γ 后 α → 0; 先 α 后 γ → 0
输入 100:先 γ 后 α → 0; 先 α 后 γ → 16
输入 170:先 γ 后 α → 82; 先 α 后 γ → 170 ← 差 88 级!
差异图 max|Δ| = 88。结论:设计滤镜链时顺序必须在建表前想清楚。
5.4 性能基准:663 倍差距(027_benchmark.png)
480×640 灰度图做伽马校正(γ=1.6),四方案耗时(5 次取最小):
| 方案 | 耗时 | 相对 cv2.LUT |
|---|---|---|
| Python 循环 | 43.1 ms | 663× |
| NumPy 直接计算(np.power) | 2.6 ms | 40× |
NumPy 花式索引 lut[img] |
0.41 ms | 6× |
| cv2.LUT | 0.07 ms | 1× |
一致性检验:三种向量化方案与 cv2.LUT 的输出 max|Δ| = 0(Python 循环是浮点未取整版本,差异 ≤1 级)。彩色 420×560 图查 256×3 胶片表也只要 0.23 ms------4K 视频 30fps 也绰绰有余。
5.5 多通道 LUT:调明暗 vs 调颜色(027_channel_lut.png)
上排:广播表(三通道共用一条对比度曲线)只改明暗、色彩平衡不动;逐通道表(R↑ γ=0.88 / G 中性 / B↓ γ=1.25)重塑色彩,画面整体转暖。下排是三条通道曲线的分解。这就是"调色"与"调光"在 LUT 层面的分水岭。
5.6 实战:VSCO 式胶片感(027_film_look.png)
左上原图(合成逆光场景)→ 右上加滤镜:黑位被抬到 30 左右("空气感"褪色)、高光偏暖、阴影偏青。左下是复合曲线(S 曲线 × 褪色 × 通道曲线,全部焊在一条曲线上),右下直方图验证黑位右移、蓝通道高光左移。整个滤镜 = 一张 256×3 表 + 一次查表,0.2 ms。
5.7 量化误差:≤1 级,级联 ≤2 级(027_quant_error.png)
左图:γ=0.5 时浮点精确值与 256 级表输出的误差直方图,max +0.50、mean −0.003------全部落在 ±1 级内。中右两图:γ=0.85 连查 8 次 vs 一张 γ⁸=0.272 复合表,max|Δ| = 2、78662 个像素受影响但肉眼不可辨。256 级精度对 8 bit 图像足够;级联漂移用预复合根治。
5.8 滤镜链融合:3.4× 提速 + 逐像素一致(027_multipass_speedup.png)
5 步点运算滤镜链(伽马→对比度→亮度→S 曲线→褪色):逐表 5 趟 0.72 ms vs 复合 1 趟 0.21 ms,提速 3.4×,max|Δ| = 0。注意 naive 路线本身已经很快(都是 cv2.LUT),融合的意义在于:省 4 次全图内存读写、滤镜发布物从 5 张表变 1 张表、运行时零分支。
六、移动端 / 专业级产品推荐
| 类别 | 产品 | 平台 | LUT 相关能力 | 参考价格 |
|---|---|---|---|---|
| 移动端 | VSCO | iOS/Android | 胶片滤镜即预设 LUT,支持强度调节 | 免费+订阅 ¥38/月 |
| 移动端 | Lightroom Mobile | iOS/Android | 曲线工具 + DNG 配置文件(相机 LUT) | 免费+订阅 |
| 移动端 | 醒图 | iOS/Android | 调色预设、曲线工具 | 免费 |
| 专业级 | 3D LUT Creator | Win/Mac | 工业级 LUT 制作与体积色分析,支持导出 .cube | 约 $99 |
| 专业级 | DaVinci Resolve | Win/Mac/Linux | 调色行业标准,LUT 管理与节点式复合 | 免费版/Studio $295 |
| 专业级 | Color Efex Pro (Nik Collection) | Win/Mac | 经典滤镜组,本质是复杂复合 LUT | $149(套装) |
| 开源/免费 | HaldCLUT | 全平台 | 开源 LUT 图像格式,GIMP/Photoshop 通用 | 免费 |
| 开源/免费 | GIMP 曲线工具 | 全平台 | 控制点曲线 → 即时生成 LUT | 免费 |
| 开发者 | OpenCV cv2.LUT | 全平台 | 本期主角,.cube LUT 解析后可直接查表 | 免费(Apache 2.0) |
| 开发者 | LUTPY / colour-science | Python | .cube/.3dl LUT 读写与色彩科学计算 | 免费 |
选型建议:想理解滤镜原理 → 用 GIMP 曲线工具拖几个控制点导出对比;想做滤镜创作者 → 3D LUT Creator 是最专业的 LUT 生产工具;程序化批量应用 → OpenCV + .cube 解析(下一期实战会用上)。
七、FAQ
Q1:cv2.LUT 只能处理 uint8 图像吗?浮点图怎么办?
是。cv2.LUT 要求输入 uint8/int8 配 256 项表,或 uint16/int16 配 65536 项表。浮点图像(如 HDR、深度图)没有"只有 256 种取值"的性质,查表前需量化,或直接用 NumPy 向量化计算(np.interp、np.power)。
Q2:lut[img] 和 cv2.LUT(img, lut) 哪个好?
结果完全一致(实验 4 验证 max|Δ|=0)。cv2.LUT 走 SIMD 指令快约 6 倍,是首选;lut[img] 的价值在于①不需要额外依赖即可工作,②是理解"复合表"的钥匙------lut_c[lut_g] 就是用这个语法在表域做复合。
Q3:哪些操作能进 LUT,哪些不能?
判断标准:输出是否只依赖当前像素自己的输入值。伽马、对比度、亮度、曲线、色相旋转(配合 HSV)、通道混合矩阵------能进;模糊、锐化、边缘检测、形态学------依赖邻域,不能进;直方图均衡化------表依赖整图统计,只能"先统计后查表",表是每图一份而非全局一份。
Q4:.cube LUT 文件是什么?和本期的 256 项表什么关系?
.cube 是工业标准的 3D LUT (通常 17³~65³ 项,把 RGB 三维空间整体映射),而本期是 1D LUT(每通道独立 256 项)。1D LUT 不能表达"绿色→橙色"这类跨通道映射;3D LUT 可以。下一阶段讲到彩色图像处理时会展开 3D LUT。
Q5:滤镜 App 里"强度 50%"是怎么实现的?
两种方案:① 对输出做线性混合 out = (1−t)·img + t·filtered(一趟逐像素运算,又是个点运算);② 在表域直接插值 table_t = (1−t)·identity + t·table,仍是一次查表,更快。VSCO/醒图的滤镜强度滑杆本质就是方案②。
Q6:为什么我的 (256,3) 表传给 cv2.LUT 报 lut_size 错误?
NumPy 二维数组传给 OpenCV 会变成"单通道 256×3 矩阵",总元素 768 ≠ 256 触发断言。必须 reshape 成 (256, 1, 3) 让 OpenCV 识别为"3 通道、256 项"的表,见第三节 as_cv_lut。
八、课后练习
- 基础 :为 026 期的窗宽窗位变换写一个
window_width/window_level双滑杆实时预览(提示:拖动滑杆时只重建 256 项表 + 一次cv2.LUT,体验"医学影像实时调窗 = 换一张表")。 - 进阶 :实现
.cube1D LUT 的加载器(解析LUT_1D_SIZE与 256 行r g b数据),把本期胶片滤镜导出为 .cube 文件,并在 GIMP 里加载验证效果一致。 - 挑战 :实现"滤镜强度"滑杆的表域插值版本(
table_t = (1−t)·arange(256) + t·table),与"线性混合图像"版本对比:耗时差多少?输出是否逐像素一致?
九、下期预告
第028期:阶段实战------自动照片增强器开发
模块三收官之作:把 021 直方图、022/023 均衡化、024/025/026 三种灰度变换、本期 LUT 全部串起来,开发一个"一键增强"流水线------自动判断曝光状态 → 选择修复策略(伽马/CLAHE/对比度拉伸)→ 全程 LUT 加速 → 支持批量处理。手机"一键美化"按钮背后的一切,明天亲手实现。