在前面的文章,我提到过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是标配。

相关资料可以参阅:

3D LUT调色:单反如何实现电影级调色。

在VSCO Cam APP中滤镜效果每一档都是一个17*17*17的3d lut预设。

先上个图,大家感受一下。

只是一个例子,效果是看做预设的功底的。

那么3d lut 的实现具体是什么算法呢?

Nearest_interpolation

Trilinear_interpolation

Tetrahedral interpolation

当然据我所知,Trilinear_interpolation 是用得最广泛的一种。

之前做APP滤镜的时候,调研过不少资料。

但是当时发现一些开源项目的实现是有问题的,插值算错坐标之类的。

有一次心血来潮,去翻了翻FFmpeg的代码,居然发现了它也有实现3d lut算法。

嗯,站在巨人的肩膀上。

抽了点时间对FFmpeg中的3d lut 进行了整理。

提取出它的算法,并编写示例。

当然未经过严格验证,应该存在一些小Bugs。

完整示例代码献上:

  1. /*
  2. * Copyright (c) 2013 Clément Bœsch
  3. *
  4. * This file is part of FFmpeg.
  5. *
  6. * FFmpeg is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * FFmpeg is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with FFmpeg; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. */
  20. /**
  21. * 3D Lookup table filter
  22. */
  23. #include "browse.h"
  24.  
  25. #define USE_SHELL_OPEN
  26.  
  27. #define STB_IMAGE_STATIC
  28. #define STB_IMAGE_IMPLEMENTATION
  29.  
  30. #include "stb_image.h"
  31. /* ref:https://github.com/nothings/stb/blob/master/stb_image.h */
  32. #define TJE_IMPLEMENTATION
  33.  
  34. #include "tiny_jpeg.h"
  35. /* ref:https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h */
  36. #include <math.h>
  37. #include <stdbool.h>
  38. #include <stdio.h>
  39. #include "timing.h"
  40. #include <stdint.h>
  41. #include <assert.h>
  42.  
  43. #ifndef _MAX_DRIVE
  44. #define _MAX_DRIVE 3
  45. #endif
  46. #ifndef _MAX_FNAME
  47. #define _MAX_FNAME 256
  48. #endif
  49. #ifndef _MAX_EXT
  50. #define _MAX_EXT 256
  51. #endif
  52. #ifndef _MAX_DIR
  53. #define _MAX_DIR 256
  54. #endif
  55. #ifdef _MSC_VER
  56. #endif
  57. #ifndef MIN
  58. #define MIN(a, b) ( (a) > (b) ? (b) : (a) )
  59. #endif
  60. #ifndef _NEAR
  61. #define _NEAR(x) ( (int) ( (x) + .5) )
  62. #endif
  63. #ifndef PREV
  64. #define PREV(x) ( (int) (x) )
  65. #endif
  66. #ifndef NEXT
  67. #define NEXT(x) (MIN( (int) (x) + 1, lut3d->lutsize - 1 ) )
  68. #endif
  69. #ifndef R
  70. #define R 0
  71. #endif
  72. #ifndef G
  73. #define G 1
  74. #endif
  75. #ifndef B
  76. #define B 2
  77. #endif
  78. #ifndef A
  79. #define A 3
  80. #endif
  81. #ifndef MAX_LEVEL
  82. #define MAX_LEVEL 64
  83. #endif
  84.  
  85. enum interp_mode {
  86. INTERPOLATE_NEAREST,
  87. INTERPOLATE_TRILINEAR,
  88. INTERPOLATE_TETRAHEDRAL,
  89. NB_INTERP_MODE
  90. };
  91.  
  92. struct rgbvec {
  93. float r, g, b;
  94. };
  95.  
  96. /* 3D LUT don't often go up to level 32 */
  97.  
  98. typedef struct LUT3DContext {
  99. uint8_t rgba_map[];
  100. int step;
  101. struct rgbvec lut[MAX_LEVEL][MAX_LEVEL][MAX_LEVEL];
  102. int lutsize;
  103. } LUT3DContext;
  104. #ifdef _MSC_VER
  105. int strcasecmp(const char *s1, char *s2) {
  106. while (toupper((unsigned char)*s1) == toupper((unsigned char)*s2++))
  107. if (*s1++ == 0x00)
  108. return ();
  109. return (toupper((unsigned char)*s1) - toupper((unsigned char) *--s2));
  110. }
  111. #endif
  112.  
  113. static inline float lerpf(float v0, float v1, float f) {
  114. return (v0 + (v1 - v0) * f);
  115. }
  116.  
  117. static inline struct rgbvec lerp(const struct rgbvec *v0, const struct rgbvec *v1, float f) {
  118. struct rgbvec v = {
  119. lerpf(v0->r, v1->r, f), lerpf(v0->g, v1->g, f), lerpf(v0->b, v1->b, f)
  120. };
  121. return (v);
  122. }
  123.  
  124. /**
  125. * Get the nearest defined point
  126. */
  127. static inline struct rgbvec interp_nearest(const LUT3DContext *lut3d,
  128. const struct rgbvec *s) {
  129. return (lut3d->lut[_NEAR(s->r)][_NEAR(s->g)][_NEAR(s->b)]);
  130. }
  131.  
  132. /**
  133. * Interpolate using the 8 vertices of a cube
  134. * @see https://en.wikipedia.org/wiki/Trilinear_interpolation
  135. */
  136. static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d,
  137. const struct rgbvec *s) {
  138. const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
  139. const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
  140. const struct rgbvec d = {s->r - prev[], s->g - prev[], s->b - prev[]};
  141. const struct rgbvec c000 = lut3d->lut[prev[]][prev[]][prev[]];
  142. const struct rgbvec c001 = lut3d->lut[prev[]][prev[]][next[]];
  143. const struct rgbvec c010 = lut3d->lut[prev[]][next[]][prev[]];
  144. const struct rgbvec c011 = lut3d->lut[prev[]][next[]][next[]];
  145. const struct rgbvec c100 = lut3d->lut[next[]][prev[]][prev[]];
  146. const struct rgbvec c101 = lut3d->lut[next[]][prev[]][next[]];
  147. const struct rgbvec c110 = lut3d->lut[next[]][next[]][prev[]];
  148. const struct rgbvec c111 = lut3d->lut[next[]][next[]][next[]];
  149. const struct rgbvec c00 = lerp(&c000, &c100, d.r);
  150. const struct rgbvec c10 = lerp(&c010, &c110, d.r);
  151. const struct rgbvec c01 = lerp(&c001, &c101, d.r);
  152. const struct rgbvec c11 = lerp(&c011, &c111, d.r);
  153. const struct rgbvec c0 = lerp(&c00, &c10, d.g);
  154. const struct rgbvec c1 = lerp(&c01, &c11, d.g);
  155. const struct rgbvec c = lerp(&c0, &c1, d.b);
  156. return (c);
  157. }
  158.  
  159. /**
  160. * Tetrahedral interpolation. Based on code found in Truelight Software Library paper.
  161. * @see http://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf
  162. */
  163. static inline struct rgbvec interp_tetrahedral(const LUT3DContext *lut3d,
  164. const struct rgbvec *s) {
  165. const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
  166. const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
  167. const struct rgbvec d = {s->r - prev[], s->g - prev[], s->b - prev[]};
  168. const struct rgbvec c000 = lut3d->lut[prev[]][prev[]][prev[]];
  169. const struct rgbvec c111 = lut3d->lut[next[]][next[]][next[]];
  170. struct rgbvec c;
  171. if (d.r > d.g) {
  172. if (d.g > d.b) {
  173. const struct rgbvec c100 = lut3d->lut[next[]][prev[]][prev[]];
  174. const struct rgbvec c110 = lut3d->lut[next[]][next[]][prev[]];
  175. c.r = ( - d.r) * c000.r + (d.r - d.g) * c100.r + (d.g - d.b) * c110.r + (d.b) * c111.r;
  176. c.g = ( - d.r) * c000.g + (d.r - d.g) * c100.g + (d.g - d.b) * c110.g + (d.b) * c111.g;
  177. c.b = ( - d.r) * c000.b + (d.r - d.g) * c100.b + (d.g - d.b) * c110.b + (d.b) * c111.b;
  178. } else if (d.r > d.b) {
  179. const struct rgbvec c100 = lut3d->lut[next[]][prev[]][prev[]];
  180. const struct rgbvec c101 = lut3d->lut[next[]][prev[]][next[]];
  181. c.r = ( - d.r) * c000.r + (d.r - d.b) * c100.r + (d.b - d.g) * c101.r + (d.g) * c111.r;
  182. c.g = ( - d.r) * c000.g + (d.r - d.b) * c100.g + (d.b - d.g) * c101.g + (d.g) * c111.g;
  183. c.b = ( - d.r) * c000.b + (d.r - d.b) * c100.b + (d.b - d.g) * c101.b + (d.g) * c111.b;
  184. } else {
  185. const struct rgbvec c001 = lut3d->lut[prev[]][prev[]][next[]];
  186. const struct rgbvec c101 = lut3d->lut[next[]][prev[]][next[]];
  187. c.r = ( - d.b) * c000.r + (d.b - d.r) * c001.r + (d.r - d.g) * c101.r + (d.g) * c111.r;
  188. c.g = ( - d.b) * c000.g + (d.b - d.r) * c001.g + (d.r - d.g) * c101.g + (d.g) * c111.g;
  189. c.b = ( - d.b) * c000.b + (d.b - d.r) * c001.b + (d.r - d.g) * c101.b + (d.g) * c111.b;
  190. }
  191. } else {
  192. if (d.b > d.g) {
  193. const struct rgbvec c001 = lut3d->lut[prev[]][prev[]][next[]];
  194. const struct rgbvec c011 = lut3d->lut[prev[]][next[]][next[]];
  195. c.r = ( - d.b) * c000.r + (d.b - d.g) * c001.r + (d.g - d.r) * c011.r + (d.r) * c111.r;
  196. c.g = ( - d.b) * c000.g + (d.b - d.g) * c001.g + (d.g - d.r) * c011.g + (d.r) * c111.g;
  197. c.b = ( - d.b) * c000.b + (d.b - d.g) * c001.b + (d.g - d.r) * c011.b + (d.r) * c111.b;
  198. } else if (d.b > d.r) {
  199. const struct rgbvec c010 = lut3d->lut[prev[]][next[]][prev[]];
  200. const struct rgbvec c011 = lut3d->lut[prev[]][next[]][next[]];
  201. c.r = ( - d.g) * c000.r + (d.g - d.b) * c010.r + (d.b - d.r) * c011.r + (d.r) * c111.r;
  202. c.g = ( - d.g) * c000.g + (d.g - d.b) * c010.g + (d.b - d.r) * c011.g + (d.r) * c111.g;
  203. c.b = ( - d.g) * c000.b + (d.g - d.b) * c010.b + (d.b - d.r) * c011.b + (d.r) * c111.b;
  204. } else {
  205. const struct rgbvec c010 = lut3d->lut[prev[]][next[]][prev[]];
  206. const struct rgbvec c110 = lut3d->lut[next[]][next[]][prev[]];
  207. c.r = ( - d.g) * c000.r + (d.g - d.r) * c010.r + (d.r - d.b) * c110.r + (d.b) * c111.r;
  208. c.g = ( - d.g) * c000.g + (d.g - d.r) * c010.g + (d.r - d.b) * c110.g + (d.b) * c111.g;
  209. c.b = ( - d.g) * c000.b + (d.g - d.r) * c010.b + (d.r - d.b) * c110.b + (d.b) * c111.b;
  210. }
  211. }
  212. return (c);
  213. }
  214.  
  215. /**
  216. * Locale-independent conversion of ASCII isspace.
  217. */
  218. int _isspace(int c) {
  219. return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' ||
  220. c == '\v');
  221. }
  222.  
  223. /**
  224. * Clip a signed integer value into the 0-65535 range.
  225. * @param a value to clip
  226. * @return clipped value
  227. */
  228. static uint16_t clip_uint16(int a) {
  229. if (a & (~0xFFFF))
  230. return ((~a) >> );
  231. else return (a);
  232. }
  233.  
  234. /**
  235. * Clip a signed integer value into the 0-255 range.
  236. * @param a value to clip
  237. * @return clipped value
  238. */
  239. static uint8_t clip_uint8(int a) {
  240. if (a & (~0xFF))
  241. return ((~a) >> );
  242. else return (a);
  243. }
  244.  
  245. static unsigned clip_uintp2(int a, int p) {
  246. if (a & ~(( << p) - ))
  247. return (-a >> & (( << p) - ));
  248. else return (a);
  249. }
  250.  
  251. #define DEFINE_INTERP_FUNC_PLANAR(name, nbits, depth) \
  252. static int interp_ ## nbits ## _ ## name ## _p ## depth( const LUT3DContext * lut3d, uint8_t * indata_g, uint8_t * indata_b, uint8_t * indata_r, uint8_t * indata_a, uint8_t * outdata_g, uint8_t * outdata_b, uint8_t * outdata_r, uint8_t * outdata_a, int width, int height, int linesize ) \
  253. { \
  254. int x, y; \
  255. int direct = (outdata_g == indata_g); \
  256. uint8_t *grow = outdata_g ; \
  257. uint8_t *brow = outdata_b ; \
  258. uint8_t *rrow = outdata_r ; \
  259. uint8_t *arow = outdata_a ; \
  260. const uint8_t *srcgrow = indata_g ; \
  261. const uint8_t *srcbrow = indata_b ; \
  262. const uint8_t *srcrrow = indata_r ; \
  263. const uint8_t *srcarow = indata_a ; \
  264. const float scale = (.f / ( ( << (depth) ) - ) ) * (lut3d->lutsize - ); \
  265. for ( y = ; y < height; y++ ) { \
  266. uint ## nbits ## _t * dstg = (uint ## nbits ## _t *)grow; \
  267. uint ## nbits ## _t * dstb = (uint ## nbits ## _t *)brow; \
  268. uint ## nbits ## _t * dstr = (uint ## nbits ## _t *)rrow; \
  269. uint ## nbits ## _t * dsta = (uint ## nbits ## _t *)arow; \
  270. const uint ## nbits ## _t *srcg = (const uint ## nbits ## _t *)srcgrow; \
  271. const uint ## nbits ## _t *srcb = (const uint ## nbits ## _t *)srcbrow; \
  272. const uint ## nbits ## _t *srcr = (const uint ## nbits ## _t *)srcrrow; \
  273. const uint ## nbits ## _t *srca = (const uint ## nbits ## _t *)srcarow; \
  274. for ( x = ; x < width; x++ ) { \
  275. const struct rgbvec scaled_rgb = { srcr[x] * scale, \
  276. srcg[x] * scale, \
  277. srcb[x] * scale }; \
  278. struct rgbvec vec = interp_ ## name( lut3d, &scaled_rgb ); \
  279. dstr[x] = clip_uintp2( vec.r * (float) ( ( << (depth) ) - ), depth ); \
  280. dstg[x] = clip_uintp2( vec.g * (float) ( ( << (depth) ) - ), depth ); \
  281. dstb[x] = clip_uintp2( vec.b * (float) ( ( << (depth) ) - ), depth ); \
  282. if ( !direct && linesize ) \
  283. dsta[x] = srca[x]; \
  284. } \
  285. grow += linesize; \
  286. brow += linesize; \
  287. rrow += linesize; \
  288. arow += linesize; \
  289. srcgrow += linesize; \
  290. srcbrow += linesize; \
  291. srcrrow += linesize; \
  292. srcarow += linesize; \
  293. } \
  294. return ; \
  295. }
  296.  
  297. DEFINE_INTERP_FUNC_PLANAR(nearest, , )
  298.  
  299. DEFINE_INTERP_FUNC_PLANAR(trilinear, , )
  300.  
  301. DEFINE_INTERP_FUNC_PLANAR(tetrahedral, , )
  302.  
  303. DEFINE_INTERP_FUNC_PLANAR(nearest, , )
  304.  
  305. DEFINE_INTERP_FUNC_PLANAR(trilinear, , )
  306.  
  307. DEFINE_INTERP_FUNC_PLANAR(tetrahedral, , )
  308.  
  309. DEFINE_INTERP_FUNC_PLANAR(nearest, , )
  310.  
  311. DEFINE_INTERP_FUNC_PLANAR(trilinear, , )
  312.  
  313. DEFINE_INTERP_FUNC_PLANAR(tetrahedral, , )
  314.  
  315. DEFINE_INTERP_FUNC_PLANAR(nearest, , )
  316.  
  317. DEFINE_INTERP_FUNC_PLANAR(trilinear, , )
  318.  
  319. DEFINE_INTERP_FUNC_PLANAR(tetrahedral, , )
  320.  
  321. DEFINE_INTERP_FUNC_PLANAR(nearest, , )
  322.  
  323. DEFINE_INTERP_FUNC_PLANAR(trilinear, , )
  324.  
  325. DEFINE_INTERP_FUNC_PLANAR(tetrahedral, , )
  326.  
  327. DEFINE_INTERP_FUNC_PLANAR(nearest, , )
  328.  
  329. DEFINE_INTERP_FUNC_PLANAR(trilinear, , )
  330.  
  331. DEFINE_INTERP_FUNC_PLANAR(tetrahedral, , )
  332.  
  333. #define DEFINE_INTERP_FUNC(name, nbits) \
  334. static int interp_ ## nbits ## _ ## name( LUT3DContext * lut3d, const uint8_t * indata, uint8_t * outdata, int width, int height, int linesize ) \
  335. { \
  336. int x, y; \
  337. const int direct = outdata == indata; \
  338. const int step = lut3d->step; \
  339. const uint8_t r = lut3d->rgba_map[R]; \
  340. const uint8_t g = lut3d->rgba_map[G]; \
  341. const uint8_t b = lut3d->rgba_map[B]; \
  342. const uint8_t a = lut3d->rgba_map[A]; \
  343. uint8_t *dstrow = outdata; \
  344. const uint8_t *srcrow = indata; \
  345. const float scale = (.f / ( ( << nbits) - ) ) * (lut3d->lutsize - ); \
  346. \
  347. for ( y = ; y < height; y++ ) { \
  348. uint ## nbits ## _t * dst = (uint ## nbits ## _t *)dstrow; \
  349. const uint ## nbits ## _t *src = (const uint ## nbits ## _t *)srcrow; \
  350. for ( x = ; x < width * step; x += step ) { \
  351. const struct rgbvec scaled_rgb = { src[x + r] * scale, \
  352. src[x + g] * scale, \
  353. src[x + b] * scale }; \
  354. struct rgbvec vec = interp_ ## name( lut3d, &scaled_rgb ); \
  355. dst[x + r] = clip_uint ## nbits( vec.r * (float) ( ( << nbits) - ) ); \
  356. dst[x + g] = clip_uint ## nbits( vec.g * (float) ( ( << nbits) - ) ); \
  357. dst[x + b] = clip_uint ## nbits( vec.b * (float) ( ( << nbits) - ) ); \
  358. if ( !direct && step == ) \
  359. dst[x + a] = src[x + a]; \
  360. } \
  361. dstrow += linesize; \
  362. srcrow += linesize; \
  363. } \
  364. return ; \
  365. }
  366.  
  367. DEFINE_INTERP_FUNC(nearest, )
  368.  
  369. DEFINE_INTERP_FUNC(trilinear, )
  370.  
  371. DEFINE_INTERP_FUNC(tetrahedral, )
  372.  
  373. DEFINE_INTERP_FUNC(nearest, )
  374.  
  375. DEFINE_INTERP_FUNC(trilinear, )
  376.  
  377. DEFINE_INTERP_FUNC(tetrahedral, )
  378.  
  379. static int skip_line(const char *p) {
  380. while (*p && _isspace(*p))
  381. p++;
  382. return (!*p || *p == '#');
  383. }
  384.  
  385. #ifndef NEXT_LINE
  386. #define NEXT_LINE(loop_cond) do { \
  387. if ( !fgets( line, sizeof(line), f ) ) { \
  388. printf( "Unexpected EOF\n" ); fclose( f ); if ( lut3d ) free( lut3d ); \
  389. return NULL; \
  390. } \
  391. } while ( loop_cond )
  392. #endif
  393.  
  394. #ifndef MAX_LINE_SIZE
  395. #define MAX_LINE_SIZE 512
  396. #endif
  397.  
  398. /* Basically r g and b float values on each line, with a facultative 3DLUTSIZE
  399. * directive; seems to be generated by Davinci */
  400. LUT3DContext *parse_dat(char *filename) {
  401. FILE *f = fopen(filename, "r");
  402. if (f == NULL) return NULL;
  403. LUT3DContext *lut3d = NULL;
  404. char line[MAX_LINE_SIZE];
  405. int i, j, k, size;
  406.  
  407. int lutsize = size = ;
  408.  
  409. NEXT_LINE(skip_line(line));
  410. if (!strncmp(line, "3DLUTSIZE ", )) {
  411. size = strtol(line + , NULL, );
  412. if (size < || size > MAX_LEVEL) {
  413. printf("Too large or invalid 3D LUT size\n");
  414. fclose(f);
  415. return (NULL);
  416. }
  417. lutsize = size;
  418. NEXT_LINE(skip_line(line));
  419. }
  420. if (size != && lut3d == NULL) {
  421. lut3d = (LUT3DContext *) calloc(, sizeof(LUT3DContext));
  422. }
  423. lut3d->lutsize = lutsize;
  424. for (k = ; k < size; k++) {
  425. for (j = ; j < size; j++) {
  426. for (i = ; i < size; i++) {
  427. struct rgbvec *vec = &lut3d->lut[k][j][i];
  428. if (k != || j != || i != )
  429. NEXT_LINE(skip_line(line));
  430. if (sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != ) {
  431. fclose(f);
  432. free(lut3d);
  433. return (NULL);
  434. }
  435. }
  436. }
  437. }
  438. fclose(f);
  439. return (lut3d);
  440. }
  441.  
  442. LUT3DContext *parse_cube(char *filename) {
  443. FILE *f = fopen(filename, "r");
  444. if (f == NULL) return NULL;
  445. char line[MAX_LINE_SIZE];
  446. float min[] = {0.0, 0.0, 0.0};
  447. float max[] = {1.0, 1.0, 1.0};
  448. int lutsize = ;
  449. LUT3DContext *lut3d = NULL;
  450. while (fgets(line, sizeof(line), f)) {
  451. if (!strncmp(line, "LUT_3D_SIZE ", )) {
  452. int i, j, k;
  453. const int size = strtol(line + , NULL, );
  454. if (size < || size > MAX_LEVEL) {
  455. printf("Too large or invalid 3D LUT size\n");
  456. fclose(f);
  457. return (NULL);
  458. }
  459. lutsize = size;
  460. if (size != && lut3d == NULL) {
  461. lut3d = (LUT3DContext *) calloc(, sizeof(LUT3DContext));
  462. }
  463. lut3d->lutsize = lutsize;
  464. for (k = ; k < size; k++) {
  465. for (j = ; j < size; j++) {
  466. for (i = ; i < size; i++) {
  467. struct rgbvec *vec = &lut3d->lut[i][j][k];
  468.  
  469. do {
  470. try_again:
  471. NEXT_LINE();
  472. if (!strncmp(line, "DOMAIN_", )) {
  473. float *vals = NULL;
  474. if (!strncmp(line + , "MIN ", ))
  475. vals = min;
  476. else if (!strncmp(line + , "MAX ", ))
  477. vals = max;
  478. if (!vals) {
  479. fclose(f);
  480. free(lut3d);
  481. return (NULL);
  482. }
  483.  
  484. sscanf(line + , "%f %f %f", vals, vals + , vals + );
  485. //printf("min: %f %f %f | max: %f %f %f\n", min[0], min[1], min[2], max[0], max[1], max[2]);
  486. goto try_again;
  487. }
  488. } while (skip_line(line));
  489. if (sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != ) {
  490. fclose(f);
  491. free(lut3d);
  492. return (NULL);
  493. }
  494. vec->r *= max[] - min[];
  495. vec->g *= max[] - min[];
  496. vec->b *= max[] - min[];
  497. }
  498. }
  499. }
  500. break;
  501. }
  502. }
  503. fclose(f);
  504. return (lut3d);
  505. }
  506.  
  507. /* Assume 17x17x17 LUT with a 16-bit depth */
  508. LUT3DContext *parse_3dl(char *filename) {
  509. FILE *f = fopen(filename, "r");
  510. if (f == NULL) return NULL;
  511. char line[MAX_LINE_SIZE];
  512. int i, j, k;
  513. const int size = ;
  514. const float scale = * * ;
  515.  
  516. int lutsize = size;
  517. LUT3DContext *lut3d = (LUT3DContext *) calloc(, sizeof(LUT3DContext));
  518.  
  519. lut3d->lutsize = lutsize;
  520. NEXT_LINE(skip_line(line));
  521. for (k = ; k < size; k++) {
  522. for (j = ; j < size; j++) {
  523. for (i = ; i < size; i++) {
  524. int r, g, b;
  525. struct rgbvec *vec = &lut3d->lut[k][j][i];
  526.  
  527. NEXT_LINE(skip_line(line));
  528. if (sscanf(line, "%d %d %d", &r, &g, &b) != ) {
  529. fclose(f);
  530. free(lut3d);
  531. return (NULL);
  532. }
  533. vec->r = r / scale;
  534. vec->g = g / scale;
  535. vec->b = b / scale;
  536. }
  537. }
  538. }
  539. fclose(f);
  540. return (lut3d);
  541. }
  542.  
  543. /* Pandora format */
  544. LUT3DContext *parse_m3d(char *filename) {
  545. FILE *f = fopen(filename, "r");
  546. if (f == NULL) return NULL;
  547. float scale;
  548. int i, j, k, size, in = -, out = -;
  549. char line[MAX_LINE_SIZE];
  550. uint8_t rgb_map[] = {, , };
  551.  
  552. while (fgets(line, sizeof(line), f)) {
  553. if (!strncmp(line, "in", ))
  554. in = strtol(line + , NULL, );
  555. else if (!strncmp(line, "out", ))
  556. out = strtol(line + , NULL, );
  557. else if (!strncmp(line, "values", )) {
  558. const char *p = line + ;
  559. #define SET_COLOR(id) do { \
  560. while ( _isspace( *p ) ) \
  561. p++; \
  562. switch ( *p ) { \
  563. case 'r': rgb_map[id] = ; break; \
  564. case 'g': rgb_map[id] = ; break; \
  565. case 'b': rgb_map[id] = ; break; \
  566. } \
  567. while ( *p && !_isspace( *p ) ) \
  568. p++; \
  569. } \
  570. while ( )
  571. SET_COLOR();
  572. SET_COLOR();
  573. SET_COLOR();
  574. break;
  575. }
  576. }
  577.  
  578. if (in == - || out == -) {
  579. printf("in and out must be defined\n");
  580. fclose(f);
  581. return (NULL);
  582. }
  583. if (in < || out < ||
  584. in > MAX_LEVEL * MAX_LEVEL * MAX_LEVEL ||
  585. out > MAX_LEVEL * MAX_LEVEL * MAX_LEVEL) {
  586. printf("invalid in (%d) or out (%d)\n", in, out);
  587. fclose(f);
  588. return (NULL);
  589. }
  590. for (size = ; size * size * size < in; size++);
  591. {}
  592. int lutsize = size;
  593. scale = .f / (out - );
  594. LUT3DContext *lut3d = NULL;
  595. if (size != ) {
  596. lut3d = (LUT3DContext *) calloc(, sizeof(LUT3DContext));
  597. }
  598. lut3d->lutsize = lutsize;
  599. for (k = ; k < size; k++) {
  600. for (j = ; j < size; j++) {
  601. for (i = ; i < size; i++) {
  602. struct rgbvec *vec = &lut3d->lut[k][j][i];
  603.  
  604. float val[];
  605. NEXT_LINE();
  606. if (sscanf(line, "%f %f %f", val, val + , val + ) != ) {
  607. fclose(f);
  608. free(lut3d);
  609. return (NULL);
  610. }
  611. vec->r = val[rgb_map[]] * scale;
  612. vec->g = val[rgb_map[]] * scale;
  613. vec->b = val[rgb_map[]] * scale;
  614. }
  615. }
  616. }
  617. fclose(f);
  618. return (lut3d);
  619. }
  620.  
  621. LUT3DContext *lut3d_load(char *filename) {
  622. int ret = ;
  623. const char *ext;
  624. if (!filename) {
  625. return ();
  626. }
  627. LUT3DContext *lut3d = NULL;
  628.  
  629. ext = strrchr(filename, '.');
  630. if (!ext) {
  631. printf("Unable to guess the format from the extension\n");
  632. goto end;
  633. }
  634. ext++;
  635.  
  636. if (!strcasecmp(ext, "dat")) {
  637. lut3d = parse_dat(filename);
  638. } else if (!strcasecmp(ext, "3dl")) {
  639. lut3d = parse_3dl(filename);
  640. } else if (!strcasecmp(ext, "cube")) {
  641. lut3d = parse_cube(filename);
  642. } else if (!strcasecmp(ext, "m3d")) {
  643. lut3d = parse_m3d(filename);
  644. } else {
  645. printf("Unrecognized '.%s' file type\n", ext);
  646. ret = -;
  647. }
  648. if (!ret && !lut3d->lutsize) {
  649. printf("3D LUT is empty\n");
  650. }
  651. end:
  652. return (lut3d);
  653. }
  654.  
  655. typedef int (action_planar_func)(const LUT3DContext *lut3d, uint8_t *indata_g, uint8_t *indata_b, uint8_t *indata_r,
  656. uint8_t *indata_a, uint8_t *outdata_g, uint8_t *outdata_b, uint8_t *outdata_r,
  657. uint8_t *outdata_a, int width, int height, int linesize);
  658.  
  659. static int apply_planar_lut(char *filename, uint8_t *indata_g, uint8_t *indata_b, uint8_t *indata_r,
  660. uint8_t *indata_a, uint8_t *outdata_g, uint8_t *outdata_b, uint8_t *outdata_r,
  661. uint8_t *outdata_a, int width, int height, int linesize, int depth, int interpolation
  662. ) {
  663. action_planar_func *interp_func = ;
  664.  
  665. LUT3DContext *lut3d = lut3d_load(filename);
  666. if (lut3d == NULL)
  667. return (-);
  668. lut3d->step = depth;
  669. int planar = ; \
  670.  
  671. #define SET_PLANAR_FUNC(name) do { \
  672. if ( planar ) { \
  673. switch ( depth ) { \
  674. case : interp_func = interp_8_ ## name ## _p8; break; \
  675. case : interp_func = interp_16_ ## name ## _p9; break; \
  676. case : interp_func = interp_16_ ## name ## _p10; break; \
  677. case : interp_func = interp_16_ ## name ## _p12; break; \
  678. case : interp_func = interp_16_ ## name ## _p14; break; \
  679. case : interp_func = interp_16_ ## name ## _p16; break; \
  680. } \
  681. } \
  682. } while ( )
  683.  
  684. switch (interpolation) {
  685. case INTERPOLATE_NEAREST:
  686. SET_PLANAR_FUNC(nearest);
  687. break;
  688. case INTERPOLATE_TRILINEAR:
  689. SET_PLANAR_FUNC(trilinear);
  690. break;
  691. case INTERPOLATE_TETRAHEDRAL:
  692. SET_PLANAR_FUNC(tetrahedral);
  693. break;
  694. default:
  695. assert();
  696. }
  697. interp_func(lut3d, indata_g, indata_b, indata_r,
  698. indata_a, outdata_g, outdata_b, outdata_r,
  699. outdata_a, width, height, linesize);
  700. return ();
  701. }
  702.  
  703. typedef int (action_func)(LUT3DContext *lut3d, const uint8_t *indata, uint8_t *outdata, int width, int height,
  704. int linesize);
  705.  
  706. static int
  707. apply_lut(char *filename, const uint8_t *indata, uint8_t *outdata, int width, int height, int linesize, int depth,
  708. int interpolation, int is16bit) {
  709. action_func *interp_func = ;
  710.  
  711. LUT3DContext *lut3d = lut3d_load(filename);
  1. if (lut3d == NULL)
  2. return (-);
      lut3d->rgba_map[0] = 0;
      lut3d->rgba_map[1] = 1;
      lut3d->rgba_map[2] = 2;
      lut3d->rgba_map[3] = 3;
  3. lut3d->step = depth;
  4.  
  5. #define SET_FUNC(name) do { \
  6. if ( is16bit ) { interp_func = interp_16_ ## name; \
  7. } else { interp_func = interp_8_ ## name; } \
  8. } while ( )
  9.  
  10. switch (interpolation) {
  11. case INTERPOLATE_NEAREST:
  12. SET_FUNC(nearest);
  13. break;
  14. case INTERPOLATE_TRILINEAR:
  15. SET_FUNC(trilinear);
  16. break;
  17. case INTERPOLATE_TETRAHEDRAL:
  18. SET_FUNC(tetrahedral);
  19. break;
  20. default:
  21. assert();
  22. }
  23. interp_func(lut3d, indata, outdata, width, height, linesize);
  24. free(lut3d);
  25. return ();
  26. }
  27.  
  28. char saveFile[];
  29.  
  30. unsigned char *loadImage(const char *filename, int *Width, int *Height, int *Channels) {
  31. return (stbi_load(filename, Width, Height, Channels, ));
  32. }
  33.  
  34. void saveImage(const char *filename, int Width, int Height, int Channels, unsigned char *Output) {
  35. memcpy(saveFile + strlen(saveFile), filename, strlen(filename));
  36. *(saveFile + strlen(saveFile) + ) = ;
  37.  
  38. if (!tje_encode_to_file(saveFile, Width, Height, Channels, true, Output)) {
  39. fprintf(stderr, "save JPEG fail.\n");
  40. return;
  41. }
  42.  
  43. #ifdef USE_SHELL_OPEN
  44. browse(saveFile);
  45. #endif
  46. }
  47.  
  48. void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) {
  49. const char *end;
  50. const char *p;
  51. const char *s;
  52. if (path[] && path[] == ':') {
  53. if (drv) {
  54. *drv++ = *path++;
  55. *drv++ = *path++;
  56. *drv = '\0';
  57. }
  58. } else if (drv)
  59. *drv = '\0';
  60. for (end = path; *end && *end != ':';)
  61. end++;
  62. for (p = end; p > path && *--p != '\\' && *p != '/';)
  63. if (*p == '.') {
  64. end = p;
  65. break;
  66. }
  67. if (ext)
  68. for (s = end; (*ext = *s++);)
  69. ext++;
  70. for (p = end; p > path;)
  71. if (*--p == '\\' || *p == '/') {
  72. p++;
  73. break;
  74. }
  75. if (name) {
  76. for (s = p; s < end;)
  77. *name++ = *s++;
  78. *name = '\0';
  79. }
  80. if (dir) {
  81. for (s = path; s < p;)
  82. *dir++ = *s++;
  83. *dir = '\0';
  84. }
  85. }
  86.  
  87. void getCurrentFilePath(const char *filePath, char *saveFile) {
  88. char drive[_MAX_DRIVE];
  89. char dir[_MAX_DIR];
  90. char fname[_MAX_FNAME];
  91. char ext[_MAX_EXT];
  92. splitpath(filePath, drive, dir, fname, ext);
  93. size_t n = strlen(filePath);
  94. memcpy(saveFile, filePath, n);
  95. char *cur_saveFile = saveFile + (n - strlen(ext));
  96. cur_saveFile[] = '_';
  97. cur_saveFile[] = ;
  98. }
  99.  
  100. int main(int argc, char **argv) {
  101. printf("lut 3d demo\n ");
  102. printf("blog:http://cpuimage.cnblogs.com/ \n ");
  103.  
  104. if (argc < ) {
  105. printf("usage: %s 3dlut image \n ", argv[]);
  106. printf("eg: %s 3DLUT d:\\image.jpg \n ", argv[]);
  107.  
  108. return ();
  109. }
  110. char *lutfile = argv[];
  111. char *szfile = argv[];
  112.  
  113. getCurrentFilePath(szfile, saveFile);
  114.  
  115. int Width = ;
  116. int Height = ;
  117. int Channels = ;
  118. unsigned char *inputImage = NULL;
  119.  
  120. double startTime = now();
  121. inputImage = loadImage(szfile, &Width, &Height, &Channels);
  122.  
  123. double nLoadTime = calcElapsed(startTime, now());
  124. printf("load time: %d ms.\n ", (int) (nLoadTime * ));
  125. if ((Channels != ) && (Width != ) && (Height != )) {
  126. unsigned char *outputImg = (unsigned char *) stbi__malloc(Width * Channels * Height * sizeof(unsigned char));
  127. if (inputImage) {
  128. memcpy(outputImg, inputImage, Width * Channels * Height);
  129. } else {
  130. printf("load: %s fail!\n ", szfile);
  131. }
  132. startTime = now();
  133.  
  134. int is16bit = ;
  135. // INTERPOLATE_NEAREST
  136. // INTERPOLATE_TRILINEAR
  137. // INTERPOLATE_TETRAHEDRAL
  138. int interp_mode = INTERPOLATE_TETRAHEDRAL;
  139. apply_lut(lutfile, inputImage, outputImg, Width, Height, Width * Channels, Channels, interp_mode,
  140. is16bit);
  141. double nProcessTime = calcElapsed(startTime, now());
  142.  
  143. printf("process time: %d ms.\n ", (int) (nProcessTime * ));
  144.  
  145. startTime = now();
  146.  
  147. saveImage("_done.jpg", Width, Height, Channels, outputImg);
  148. double nSaveTime = calcElapsed(startTime, now());
  149.  
  150. printf("save time: %d ms.\n ", (int) (nSaveTime * ));
  151.  
  152. if (outputImg) {
  153. stbi_image_free(outputImg);
  154. }
  155.  
  156. if (inputImage) {
  157. stbi_image_free(inputImage);
  158. }
  159. } else {
  160. printf("load: %s fail!\n", szfile);
  161. }
  162.  
  163. getchar();
  164. printf("press any key to exit. \n");
  165.  
  166. return (EXIT_SUCCESS);
  167. }

项目地址:

https://github.com/cpuimage/Lut3D

命令行参数:

lut3d 3d预设文件  图片路径

例如: lut3d ../god.cube ../sample.jpg

用cmake即可进行编译示例代码,详情见CMakeLists.txt。

算法细节就不展开说了,

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是: 
gaozhihan@vip.qq.com

3D Lut 电影级调色算法 附完整C代码的更多相关文章

  1. 音频降噪算法 附完整C代码

    降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...

  2. mser 最大稳定极值区域(文字区域定位)算法 附完整C代码

    mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...

  3. 基于RNN的音频降噪算法 (附完整C代码)

    前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...

  4. 音频自动增益 与 静音检测 算法 附完整C代码

    前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...

  5. 音频自动增益 与 静音检测 算法 附完整C代码【转】

    转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用 ...

  6. 自动曝光修复算法 附完整C代码

    众所周知, 图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了 ...

  7. 基于傅里叶变换的音频重采样算法 (附完整c代码)

    前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...

  8. 磨皮美颜算法 附完整C代码

    前言 2017年底时候写了这篇<集 降噪 美颜 虚化 增强 为一体的极速图像润色算法 附Demo程序> 这也算是学习过程中比较有成就感的一个算法. 自2015年做算法开始到今天,还有个把月 ...

  9. 图片文档倾斜矫正算法 附完整c代码

    2年前在学习图像算法的时候看到一个文档倾斜矫正的算法. 也就是说能将一些文档图像进行旋转矫正, 当然这个算法一般用于一些文档扫描软件做后处理 或者用于ocr 文字识别做前处理. 相关的关键词: 抗倾斜 ...

随机推荐

  1. Caffe框架,了解三个文件

    不知道从什么时候开始,Deep Learning成为了各个领域研究的热点,也不知道从什么时候开始,2015CVPR的文章出现了很多Deep Learning的文章,更不知道从什么时候开始,三维重建各个 ...

  2. “基于数据仓库的广东省高速公路一张网过渡期通行数据及异常分析系统"已被《计算机时代》录用

       今天收到<计算机时代>编辑部寄来的稿件录用通知,本人撰写的论文"基于数据仓库的广东省高速公路一张网过渡期通行数据及异常分析系统",已被<计算机时代>录 ...

  3. 深入理解Android IPC机制之Binder机制

    Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...

  4. Leetcode_136_Single Number

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42713315 Given an array of inte ...

  5. window环境下搭建react native及相关插件

    可以先浏览一下中文翻译的开发文档具体了解一下关于React Native,想要查看官方文档可以点http://facebook.github.io/react-native/docs/getting- ...

  6. Android高级控件(一)——ListView绑定CheckBox实现全选,增加和删除等功能

    Android高级控件(一)--ListView绑定CheckBox实现全选,增加和删除等功能 这个控件还是挺复杂的,也是项目中应该算是比较常用的了,所以写了一个小Demo来讲讲,主要是自定义adap ...

  7. 第一个Polymer应用 - (4)收尾工作

    原文链接: Step 4: Finishing touches翻译日期: 2014年7月8日翻译人员: 铁锚在本节中,会在卡片上添加收藏按钮,并可以通过切换选项卡(tabs)连接到不同的 <po ...

  8. mt6577驱动开发 笔记版

    3 Preloader & Uboot 3.1 Preloader 3.1.1Preloader结构 Preloader的主题结构在文件:"alps\mediatek\platfor ...

  9. VS2010中使用Jquery调用Wcf服务读取数据库记录

    VS2010中使用Jquery调用Wcf服务读取数据库记录 开发环境:Window Servere 2008 +SQL SERVE 2008 R2+ IIS7 +VS2010+Jquery1.3.2 ...

  10. Apriori和FPTree

    Apriori算法和FPTree算法都是数据挖掘中的关联规则挖掘算法,处理的都是最简单的单层单维布尔关联规则. Apriori算法 Apriori算法是一种最有影响的挖掘布尔关联规则频繁项集的算法.是 ...