3D Lut 電影級調色演算法 附完整C代碼

来源:https://www.cnblogs.com/cpuimage/archive/2018/05/11/9022815.html
-Advertisement-
Play Games

在前面的文章,我提到過VSCO Cam 的膠片濾鏡演算法實現是3d lut。 那麼3d lut 到底是個什麼東西呢? 或者說它是用來做什麼的? 長話短說,3d lut(全稱 : 3D Lookup table )它是通過建立一個顏色映射表,對圖像的色調進行重調的演算法。 有用於攝像機的效果美化潤色,例如 ...


在前面的文章,我提到過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。

完整示例代碼獻上:

/*
 * 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 c101 = lut3d->lut[next[0]][prev[1]][next[2]];
            c.r = (1 - d.r) * c000.r + (d.r - d.b) * c100.r + (d.b - d.g) * c101.r + (d.g) * c111.r;
            c.g = (1 - d.r) * c000.g + (d.r - d.b) * c100.g + (d.b - d.g) * c101.g + (d.g) * c111.g;
            c.b = (1 - d.r) * c000.b + (d.r - d.b) * c100.b + (d.b - d.g) * c101.b + (d.g) * c111.b;
        } else {
            const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]];
            const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]];
            c.r = (1 - d.b) * c000.r + (d.b - d.r) * c001.r + (d.r - d.g) * c101.r + (d.g) * c111.r;
            c.g = (1 - d.b) * c000.g + (d.b - d.r) * c001.g + (d.r - d.g) * c101.g + (d.g) * c111.g;
            c.b = (1 - d.b) * c000.b + (d.b - d.r) * c001.b + (d.r - d.g) * c101.b + (d.g) * c111.b;
        }
    } else {
        if (d.b > d.g) {
            const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]];
            const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]];
            c.r = (1 - d.b) * c000.r + (d.b - d.g) * c001.r + (d.g - d.r) * c011.r + (d.r) * c111.r;
            c.g = (1 - d.b) * c000.g + (d.b - d.g) * c001.g + (d.g - d.r) * c011.g + (d.r) * c111.g;
            c.b = (1 - d.b) * c000.b + (d.b - d.g) * c001.b + (d.g - d.r) * c011.b + (d.r) * c111.b;
        } else if (d.b > d.r) {
            const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]];
            const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]];
            c.r = (1 - d.g) * c000.r + (d.g - d.b) * c010.r + (d.b - d.r) * c011.r + (d.r) * c111.r;
            c.g = (1 - d.g) * c000.g + (d.g - d.b) * c010.g + (d.b - d.r) * c011.g + (d.r) * c111.g;
            c.b = (1 - d.g) * c000.b + (d.g - d.b) * c010.b + (d.b - d.r) * c011.b + (d.r) * c111.b;
        } else {
            const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]];
            const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]];
            c.r = (1 - d.g) * c000.r + (d.g - d.r) * c010.r + (d.r - d.b) * c110.r + (d.b) * c111.r;
            c.g = (1 - d.g) * c000.g + (d.g - d.r) * c010.g + (d.r - d.b) * c110.g + (d.b) * c111.g;
            c.b = (1 - d.g) * c000.b + (d.g - d.r) * c010.b + (d.r - d.b) * c110.b + (d.b) * c111.b;
        }
    }
    return (c);
}


/**
 * Locale-independent conversion of ASCII isspace.
 */
int _isspace(int c) {
    return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' ||
            c == '\v');
}


/**
 * Clip a signed integer value into the 0-65535 range.
 * @param a value to clip
 * @return clipped value
 */
static uint16_t clip_uint16(int a) {
    if (a & (~0xFFFF))
        return ((~a) >> 31);
    else return (a);
}


/**
 * Clip a signed integer value into the 0-255 range.
 * @param a value to clip
 * @return clipped value
 */
static uint8_t clip_uint8(int a) {
    if (a & (~0xFF))
        return ((~a) >> 31);
    else return (a);
}


static unsigned clip_uintp2(int a, int p) {
    if (a & ~((1 << p) - 1))
        return (-a >> 31 & ((1 << p) - 1));
    else return (a);
}


#define DEFINE_INTERP_FUNC_PLANAR(name, nbits, depth)                             \
    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 ) \
    {                                                       \
        int        x, y;                                             \
        int        direct        = (outdata_g == indata_g);                                    \
        uint8_t        *grow        = outdata_g  ;                   \
        uint8_t        *brow        = outdata_b  ;                   \
        uint8_t        *rrow        = outdata_r  ;                   \
        uint8_t        *arow        = outdata_a  ;                   \
        const uint8_t    *srcgrow    = indata_g  ;               \
        const uint8_t    *srcbrow    = indata_b  ;               \
        const uint8_t    *srcrrow    = indata_r  ;               \
        const uint8_t    *srcarow    = indata_a  ;               \
        const float    scale        = (1.f / ( (1 << (depth) ) - 1) ) * (lut3d->lutsize - 1);                \
        for ( y = 0; y < height; y++ ) {                            \
            uint ## nbits ## _t * dstg    = (uint ## nbits ## _t *)grow;                             \
            uint ## nbits ## _t * dstb    = (uint ## nbits ## _t *)brow;                             \
            uint ## nbits ## _t * dstr    = (uint ## nbits ## _t *)rrow;                             \
            uint ## nbits ## _t * dsta    = (uint ## nbits ## _t *)arow;                             \
            const    uint ## nbits ## _t *srcg = (const uint ## nbits ## _t *)srcgrow;                 \
            const    uint ## nbits ## _t *srcb = (const uint ## nbits ## _t *)srcbrow;                 \
            const    uint ## nbits ## _t *srcr = (const uint ## nbits ## _t *)srcrrow;                 \
            const    uint ## nbits ## _t *srca = (const uint ## nbits ## _t *)srcarow;                 \
            for ( x = 0; x < width; x++ ) {                                     \
                const struct rgbvec    scaled_rgb = { srcr[x] * scale,                        \
                                       srcg[x] * scale,                        \
                                       srcb[x] * scale };                     \
                struct rgbvec        vec = interp_ ## name( lut3d, &scaled_rgb );                     \
                dstr[x] = clip_uintp2( vec.r * (float) ( (1 << (depth) ) - 1), depth );                 \
                dstg[x] = clip_uintp2( vec.g * (float) ( (1 << (depth) ) - 1), depth );                 \
                dstb[x] = clip_uintp2( vec.b * (float) ( (1 << (depth) ) - 1), depth );                 \
                if ( !direct && linesize )                                  \
                    dsta[x] = srca[x];                                       \
            }                                                        \
            grow    += linesize;                                      \
            brow    += linesize;                                       \
            rrow    += linesize;                                       \
            arow    += linesize;                                        \
            srcgrow += linesize;                                     \
            srcbrow += linesize;                                     \
            srcrrow += linesize;                                      \
            srcarow += linesize;                                     \
        }                                                        \
        return 0;                                               \
    }

DEFINE_INTERP_FUNC_PLANAR(nearest, 8, 8)

DEFINE_INTERP_FUNC_PLANAR(trilinear, 8, 8)

DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 8, 8)

DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 9)

DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 9)

DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 9)

DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 10)

DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 10)

DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 10)

DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 12)

DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 12)

DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 12)

DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 14)

DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 14)

DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 14)

DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 16)

DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 16)

DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 16)

#define DEFINE_INTERP_FUNC(name, nbits)                                  \
    static int interp_ ## nbits ## _ ## name( LUT3DContext * lut3d, const uint8_t * indata, uint8_t * outdata, int width, int height, int linesize )     \
    {                                                    \
        int        x, y;                                                 \
        const int    direct    = outdata == indata;                                       \
        const int    step    = lut3d->step;                                     \
        const uint8_t    r    = lut3d->rgba_map[R];                                \
        const uint8_t    g    = lut3d->rgba_map[G];                                \
        const uint8_t    b    = lut3d->rgba_map[B];                                \
        const uint8_t    a    = lut3d->rgba_map[A];                                \
        uint8_t        *dstrow = outdata;                \
        const uint8_t    *srcrow = indata;               \
        const float    scale    = (1.f / ( (1 << nbits) - 1) ) * (lut3d->lutsize - 1);                  \
                                                    \
        for ( y = 0; y < height; y++ ) {                             \
            uint ## nbits ## _t * dst = (uint ## nbits ## _t *)dstrow;                         \
            const uint ## nbits ## _t *src = (const uint ## nbits ## _t *)srcrow;                    \
            for ( x = 0; x < width * step; x += step ) {                          \
                const struct rgbvec    scaled_rgb = { src[x + r] * scale,                     \
                                       src[x + g] * scale,                     \
                                       src[x + b] * scale };                      \
                struct rgbvec        vec = interp_ ## name( lut3d, &scaled_rgb );                      \
                dst[x + r]    = clip_uint ## nbits( vec.r * (float) ( (1 << nbits) - 1) );              \
                dst[x + g]    = clip_uint ## nbits( vec.g * (float) ( (1 << nbits) - 1) );              \
                dst[x + b]    = clip_uint ## nbits( vec.b * (float) ( (1 << nbits) - 1) );              \
                if ( !direct && step == 4 )                                  \
                    dst[x + a] = src[x + a];                                \
            }                                                \
            dstrow    += linesize;                                     \
            srcrow    += linesize;                                     \
        }                                                \
        return 0;                                            \
    }

DEFINE_INTERP_FUNC(nearest, 8)

DEFINE_INTERP_FUNC(trilinear, 8)

DEFINE_INTERP_FUNC(tetrahedral, 8)

DEFINE_INTERP_FUNC(nearest, 16)

DEFINE_INTERP_FUNC(trilinear, 16)

DEFINE_INTERP_FUNC(tetrahedral, 16)


static int skip_line(const char *p) {
    while (*p && _isspace(*p))
        p++;
    return (!*p || *p == '#');
}

#ifndef NEXT_LINE
#define NEXT_LINE(loop_cond) do {                  \
        if ( !fgets( line, sizeof(line), f ) ) {            \
            printf( "Unexpected EOF\n" );    fclose( f );  if ( lut3d ) free( lut3d );          \
            return NULL;                 \
        }                            \
} while ( loop_cond )
#endif

#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 512
#endif

/* Basically r g and b float values on each line, with a facultative 3DLUTSIZE
 * directive; seems to be generated by Davinci */
LUT3DContext *parse_dat(char *filename) {
    FILE *f = fopen(filename, "r");
    if (f == NULL) return NULL;
    LUT3DContext *lut3d = NULL;
    char line[MAX_LINE_SIZE];
    int i, j, k, size;

    int lutsize = size = 33;

    NEXT_LINE(skip_line(line));
    if (!strncmp(line, "3DLUTSIZE ", 10)) {
        size = strtol(line + 10, NULL, 0);
        if (size < 2 || size > MAX_LEVEL) {
            printf("Too large or invalid 3D LUT size\n");
            fclose(f);
            return (NULL);
        }
        lutsize = size;
        NEXT_LINE(skip_line(line));
    }
    if (size != 0 && lut3d == NULL) {
        lut3d = (LUT3DContext *) calloc(1, sizeof(LUT3DContext));
    }
    lut3d->lutsize = lutsize;
    for (k = 0; k < size; k++) {
        for (j = 0; j < size; j++) {
            for (i = 0; i < size; i++) {
                struct rgbvec *vec = &lut3d->lut[k][j][i];
                if (k != 0 || j != 0 || i != 0)
                    NEXT_LINE(skip_line(line));
                if (sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) {
                    fclose(f);
                    free(lut3d);
                    return (NULL);
                }
            }
        }
    }
    fclose(f);
    return (lut3d);
}


LUT3DContext *parse_cube(char *filename) {
    FILE *f = fopen(filename, "r");
    if (f == NULL) return NULL;
    char line[MAX_LINE_SIZE];
    float min[3] = {0.0, 0.0, 0.0};
    float max[3] = {1.0, 1.0, 1.0};
    int lutsize = 0;
    LUT3DContext *lut3d = NULL;
    while (fgets(line, sizeof(line), f)) {
        if (!strncmp(line, "LUT_3D_SIZE ", 12)) {
            int i, j, k;
            const int size = strtol(line + 12, NULL, 0);
            if (size < 2 || size > MAX_LEVEL) {
                printf("Too large or invalid 3D LUT size\n");
                fclose(f);
                return (NULL);
            }
            lutsize = size;
            if (size != 0 && lut3d == NULL) {
                lut3d = (LUT3DContext *) calloc(1, sizeof(LUT3DContext));
            }
            lut3d->lutsize = lutsize;
            for (k = 0; k < size; k++) {
                for (j = 0; j < size; j++) {
                    for (i = 0; i < size; i++) {
                        struct rgbvec *vec = &lut3d->lut[i][j][k];

                        do {
                            try_again:
                            NEXT_LINE(0);
                            if (!strncmp(line, "DOMAIN_", 7)) {
                                float *vals = NULL;
                                if (!strncmp(line + 7, "MIN ", 4))
                                    vals = min;
                                else if (!strncmp(line + 7, "MAX ", 4))
                                    vals = max;
                                if (!vals) {
                                    fclose(f);
                                    free(lut3d);
                                    return (NULL);
                                }

                                sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2);
                                //printf("min: %f %f %f | max: %f %f %f\n",     min[0], min[1], min[2], max[0], max[1], max[2]);
                                goto try_again;
                            }
                        } while (skip_line(line));
                        if (sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) {
                            fclose(f);
                            free(lut3d);
                            return (NULL);
                        }
                        vec->r *= max[0] - min[0];
                        vec->g *= max[1] - min[1];
                        vec->b *= max[2] - min[2];
                    }
                }
            }
            break;
        }
    }
    fclose(f);
    return (lut3d);
}


/* Assume 17x17x17 LUT with a 16-bit depth   */
LUT3DContext *parse_3dl(char *filename) {
    FILE *f = fopen(filename, "r");
    if (f == NULL) return NULL;
    char line[MAX_LINE_SIZE];
    int i, j, k;
    const int size = 17;
    const float scale = 16 * 16 * 16;

    int lutsize = size;
    LUT3DContext *lut3d = (LUT3DContext *) calloc(1, sizeof(LUT3DContext));

    lut3d->lutsize = lutsize;
    NEXT_LINE(skip_line(line));
    for (k = 0; k < size; k++) {
        for (j = 0; j < size; j++) {
            for (i = 0; i < size; i++) {
                int r, g, b;
                struct rgbvec *vec = &lut3d->lut[k][j][i];

                NEXT_LINE(skip_line(line));
                if (sscanf(line, "%d %d %d", &r, &g, &b) != 3) {
                    fclose(f);
                    free(lut3d);
                    return (NULL);
                }
                vec->r = r / scale;
                vec->g = g / scale;
                vec->b = b / scale;
            }
        }
    }
    fclose(f);
    return (lut3d);
}


/* Pandora format */
LUT3DContext *parse_m3d(char *filename) {
    FILE *f = fopen(filename, "r");
    if (f == NULL) return NULL;
    float scale;
    int i, j, k, size, in = -1, out = -1;
    char line[MAX_LINE_SIZE];
    uint8_t rgb_map[3] = {0, 1, 2};

    while (fgets(line, sizeof(line), f)) {
        if (!strncmp(line, "in", 2))
            in = strtol(line + 2, NULL, 0);
        else if (!strncmp(line, "out	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近也是挺煩的,博客園做為程式員的家園,其實不假。雖然現在寫出的隨筆,看的人少。就當自娛自樂了。煩惱就是矛盾引起的,人很多想法都會被外界環境影響。比如一個思考很久的決定,當事情真發生時,考慮過多,受到外界環境的影響就改變了。 面試真的靠技巧,雖然你很NB,但是面試官不知道,不給你機會,也是白搭。真正 ...
  • JSPs /admin/* BASIC ...
  • 單體應用架構 架構總感覺理我很遠,有時候感覺很迷茫。今天起我把我認識到的三種架構寫出來,一是希望沉澱一下自己所學的東西,二是希望有人能指出我的不足指出,向大家學習。 第一篇 單體應用架構我會總結出單體架構的優缺點,和一般我在經歷過的項目中單體架構所用到的技術,以及我需掌握的知識。 第二篇 垂直應用架 ...
  • 課程是按照真實企業級開發項目流程進行講解,通過學習此課程可以體會到真實的大型大數據項目開發流程,學完此課程可以熟練掌握大數據技術,java web技術,docker虛擬化技術,分散式技術,緩存技術,linux等。 ...
  • 雙擊 勾上藍色保存 ...
  • 驗證客戶端鏈接的合法性 如果你想在分散式系統中實現一個簡單的客戶端鏈接認證功能,又不像SSL那麼複雜, 那麼可以利用hmac+加鹽的方式來實現。 SocketServer是標準庫中的一個高級模塊(python3.x中重命名為socketserver), 它的目標是簡化很多樣板代碼,它們是創建網路客... ...
  • Java開源生鮮電商平臺-商品表的設計(源碼可下載) 任何一個電商,無論是B2C還是B2B的電商,商品表的設計關係到整個系統架構的核心。 1. 商品基本信息表:用單詞:goods做為商品表 2. 商品分類信息表: 說明:商品分類信息表存在父子級關係,採用parent_id來做父類,預設是0表示頂級。 ...
  • ref:https://www.cnblogs.com/dongguacai/p/6030187.html http://www.360doc.com/content/15/0511/14/12726874_469670444.shtml https://blog.csdn.net/honghail ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...