在前面的文章,我提到过VSCO Cam 的胶片滤镜算法实现是3d lut。
那么3d lut 到底是个什么东西呢?
或者说它是用来做什么的?
长话短说,3d lut(全称 : 3D Lookup table )它是通过建立一个颜色映射表,对图像的色调进行重调的算法。
有用于摄像机的效果美化润色,例如一些所谓的数码相机之类的。
也有用于影视后期调色,渲染影视作品的颜色基调等等。
简单的说,你想要把图片上的一些颜色通过你自己的预设给替换掉。
例如红色换成白色,白色换成绿色。
当然这在现实中操作起来非常复杂。
因为 RGB888(8+8+8=24位色):
(2^8)*(2^8)*(2^8)=
256*256*256=16777216
有16M 种颜色,如果采用手工操作的方式一个一个颜色地换,那人还活不活了。
所以就有通过建立映射表进行插值达到逼近这种效果的算法。
它就是3d lut,当然也有2d lut,1d lut。
精度不一,效果不一。
例如:
调节亮度 可以认为是1d lut.
调节对比度 可以认为是 2d lut.
而调节整体的色调最佳肯定是3d lut.
当然2d lut 也是可以做到,但是精度就没有那么高了。
我之前也提到过,市面有不少app是采用2d LUT,毕竟精度不需要那么高。
2d够用了。
但是在摄影界,影视后期这一行当里,3d lut是标配。
相关资料可以参阅:
在VSCO Cam APP中滤镜效果每一档都是一个17*17*17的3d lut预设。
先上个图,大家感受一下。


只是一个例子,效果是看做预设的功底的。
那么3d lut 的实现具体是什么算法呢?
当然据我所知,Trilinear_interpolation 是用得最广泛的一种。
之前做APP滤镜的时候,调研过不少资料。
但是当时发现一些开源项目的实现是有问题的,插值算错坐标之类的。
有一次心血来潮,去翻了翻FFmpeg的代码,居然发现了它也有实现3d lut算法。
嗯,站在巨人的肩膀上。
抽了点时间对FFmpeg中的3d lut 进行了整理。
提取出它的算法,并编写示例。
当然未经过严格验证,应该存在一些小Bugs。
完整示例代码献上:
/* * Copyright (c) 2013 Clément Bœsch * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * 3D Lookup table filter */ #include "browse.h" #define USE_SHELL_OPEN #define STB_IMAGE_STATIC #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" /* ref:https://github.com/nothings/stb/blob/master/stb_image.h */ #define TJE_IMPLEMENTATION #include "tiny_jpeg.h" /* ref:https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h */ #include <math.h> #include <stdbool.h> #include <stdio.h> #include "timing.h" #include <stdint.h> #include <assert.h> #ifndef _MAX_DRIVE #define _MAX_DRIVE 3 #endif #ifndef _MAX_FNAME #define _MAX_FNAME 256 #endif #ifndef _MAX_EXT #define _MAX_EXT 256 #endif #ifndef _MAX_DIR #define _MAX_DIR 256 #endif #ifdef _MSC_VER #endif #ifndef MIN #define MIN(a, b) ( (a) > (b) ? (b) : (a) ) #endif #ifndef _NEAR #define _NEAR(x) ( (int) ( (x) + .5) ) #endif #ifndef PREV #define PREV(x) ( (int) (x) ) #endif #ifndef NEXT #define NEXT(x) (MIN( (int) (x) + 1, lut3d->lutsize - 1 ) ) #endif #ifndef R #define R 0 #endif #ifndef G #define G 1 #endif #ifndef B #define B 2 #endif #ifndef A #define A 3 #endif #ifndef MAX_LEVEL #define MAX_LEVEL 64 #endif enum interp_mode { INTERPOLATE_NEAREST, INTERPOLATE_TRILINEAR, INTERPOLATE_TETRAHEDRAL, NB_INTERP_MODE }; struct rgbvec { float r, g, b; }; /* 3D LUT don't often go up to level 32 */ typedef struct LUT3DContext { uint8_t rgba_map[4]; int step; struct rgbvec lut[MAX_LEVEL][MAX_LEVEL][MAX_LEVEL]; int lutsize; } LUT3DContext; #ifdef _MSC_VER int strcasecmp(const char *s1, char *s2) { while (toupper((unsigned char)*s1) == toupper((unsigned char)*s2++)) if (*s1++ == 0x00) return (0); return (toupper((unsigned char)*s1) - toupper((unsigned char) *--s2)); } #endif static inline float lerpf(float v0, float v1, float f) { return (v0 + (v1 - v0) * f); } static inline struct rgbvec lerp(const struct rgbvec *v0, const struct rgbvec *v1, float f) { struct rgbvec v = { lerpf(v0->r, v1->r, f), lerpf(v0->g, v1->g, f), lerpf(v0->b, v1->b, f) }; return (v); } /** * Get the nearest defined point */ static inline struct rgbvec interp_nearest(const LUT3DContext *lut3d, const struct rgbvec *s) { return (lut3d->lut[_NEAR(s->r)][_NEAR(s->g)][_NEAR(s->b)]); } /** * Interpolate using the 8 vertices of a cube * @see https://en.wikipedia.org/wiki/Trilinear_interpolation */ static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d, const struct rgbvec *s) { const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; const struct rgbvec c000 = lut3d->lut[prev[0]][prev[1]][prev[2]]; const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]]; const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]]; const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]]; const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]]; const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]]; const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]]; const struct rgbvec c111 = lut3d->lut[next[0]][next[1]][next[2]]; const struct rgbvec c00 = lerp(&c000, &c100, d.r); const struct rgbvec c10 = lerp(&c010, &c110, d.r); const struct rgbvec c01 = lerp(&c001, &c101, d.r); const struct rgbvec c11 = lerp(&c011, &c111, d.r); const struct rgbvec c0 = lerp(&c00, &c10, d.g); const struct rgbvec c1 = lerp(&c01, &c11, d.g); const struct rgbvec c = lerp(&c0, &c1, d.b); return (c); } /** * Tetrahedral interpolation. Based on code found in Truelight Software Library paper. * @see http://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf */ static inline struct rgbvec interp_tetrahedral(const LUT3DContext *lut3d, const struct rgbvec *s) { const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; const struct rgbvec c000 = lut3d->lut[prev[0]][prev[1]][prev[2]]; const struct rgbvec c111 = lut3d->lut[next[0]][next[1]][next[2]]; struct rgbvec c; if (d.r > d.g) { if (d.g > d.b) { const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]]; const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]]; c.r = (1 - d.r) * c000.r + (d.r - d.g) * c100.r + (d.g - d.b) * c110.r + (d.b) * c111.r; c.g = (1 - d.r) * c000.g + (d.r - d.g) * c100.g + (d.g - d.b) * c110.g + (d.b) * c111.g; c.b = (1 - d.r) * c000.b + (d.r - d.g) * c100.b + (d.g - d.b) * c110.b + (d.b) * c111.b; } else if (d.r > d.b) { const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]]; const struct rgbvec

3D LUT(3D Lookup Table)是一种颜色映射技术,常用于图像和视频调色。通过建立颜色映射表进行插值逼近,简化大量颜色替换的操作。本文介绍了3D LUT的用途,如摄像机效果美化和影视后期调色,并提及在VSCO Cam中的应用。重点讨论了Trilinear_interpolation作为最常用的插值算法,并分享了一个基于FFmpeg的3D LUT算法实现示例代码,项目地址在https://github.com/cpuimage/Lut3D。
最低0.47元/天 解锁文章
8287

被折叠的 条评论
为什么被折叠?



