avfilter/vf_lut3d: initial float pixel format support
authorMark Reid <mindmark@gmail.com>
Tue, 19 May 2020 02:00:25 +0000 (19:00 -0700)
committerMichael Niedermayer <michael@niedermayer.cc>
Fri, 22 May 2020 22:26:38 +0000 (00:26 +0200)
Reviewed-by: Paul B Mahol <onemda@gmail.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
libavfilter/vf_lut3d.c

index fda85b1..482e239 100644 (file)
  * 3D Lookup table filter
  */
 
+#include "float.h"
+
 #include "libavutil/opt.h"
 #include "libavutil/file.h"
 #include "libavutil/intreadwrite.h"
+#include "libavutil/intfloat.h"
 #include "libavutil/avassert.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/avstring.h"
@@ -73,6 +76,7 @@ typedef struct LUT3DContext {
     int clut_step;
     int clut_bits;
     int clut_planar;
+    int clut_float;
     int clut_width;
     FFFrameSync fs;
 #endif
@@ -91,6 +95,30 @@ typedef struct ThreadData {
         { "tetrahedral", "interpolate values using a tetrahedron",                0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
     { NULL }
 
+#define EXPONENT_MASK 0x7F800000
+#define MANTISSA_MASK 0x007FFFFF
+#define SIGN_MASK     0x7FFFFFFF
+
+static inline float sanitizef(float f)
+{
+    union av_intfloat32 t;
+    t.f = f;
+
+    if ((t.i & EXPONENT_MASK) == EXPONENT_MASK) {
+        if ((t.i & MANTISSA_MASK) != 0) {
+            // NAN
+            return 0.0f;
+        } else if (t.i & SIGN_MASK) {
+            // -INF
+            return FLT_MIN;
+        } else {
+            // +INF
+            return FLT_MAX;
+        }
+    }
+    return f;
+}
+
 static inline float lerpf(float v0, float v1, float f)
 {
     return v0 + (v1 - v0) * f;
@@ -285,6 +313,66 @@ 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_PLANAR_FLOAT(name, depth)                                                   \
+static int interp_##name##_pf##depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)          \
+{                                                                                                      \
+    int x, y;                                                                                          \
+    const LUT3DContext *lut3d = ctx->priv;                                                             \
+    const ThreadData *td = arg;                                                                        \
+    const AVFrame *in  = td->in;                                                                       \
+    const AVFrame *out = td->out;                                                                      \
+    const int direct = out == in;                                                                      \
+    const int slice_start = (in->height *  jobnr   ) / nb_jobs;                                        \
+    const int slice_end   = (in->height * (jobnr+1)) / nb_jobs;                                        \
+    uint8_t *grow = out->data[0] + slice_start * out->linesize[0];                                     \
+    uint8_t *brow = out->data[1] + slice_start * out->linesize[1];                                     \
+    uint8_t *rrow = out->data[2] + slice_start * out->linesize[2];                                     \
+    uint8_t *arow = out->data[3] + slice_start * out->linesize[3];                                     \
+    const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0];                              \
+    const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1];                              \
+    const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2];                              \
+    const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3];                              \
+    const float lutsize = lut3d->lutsize - 1;                                                          \
+    const float scale_r = lut3d->scale.r * lutsize;                                                    \
+    const float scale_g = lut3d->scale.g * lutsize;                                                    \
+    const float scale_b = lut3d->scale.b * lutsize;                                                    \
+                                                                                                       \
+    for (y = slice_start; y < slice_end; y++) {                                                        \
+        float *dstg = (float *)grow;                                                                   \
+        float *dstb = (float *)brow;                                                                   \
+        float *dstr = (float *)rrow;                                                                   \
+        float *dsta = (float *)arow;                                                                   \
+        const float *srcg = (const float *)srcgrow;                                                    \
+        const float *srcb = (const float *)srcbrow;                                                    \
+        const float *srcr = (const float *)srcrrow;                                                    \
+        const float *srca = (const float *)srcarow;                                                    \
+        for (x = 0; x < in->width; x++) {                                                              \
+            const struct rgbvec scaled_rgb = {av_clipf(sanitizef(srcr[x]) * scale_r, 0, lutsize),      \
+                                              av_clipf(sanitizef(srcg[x]) * scale_g, 0, lutsize),      \
+                                              av_clipf(sanitizef(srcb[x]) * scale_b, 0, lutsize)};     \
+            struct rgbvec vec = interp_##name(lut3d, &scaled_rgb);                                     \
+            dstr[x] = vec.r;                                                                           \
+            dstg[x] = vec.g;                                                                           \
+            dstb[x] = vec.b;                                                                           \
+            if (!direct && in->linesize[3])                                                            \
+                dsta[x] = srca[x];                                                                     \
+        }                                                                                              \
+        grow += out->linesize[0];                                                                      \
+        brow += out->linesize[1];                                                                      \
+        rrow += out->linesize[2];                                                                      \
+        arow += out->linesize[3];                                                                      \
+        srcgrow += in->linesize[0];                                                                    \
+        srcbrow += in->linesize[1];                                                                    \
+        srcrrow += in->linesize[2];                                                                    \
+        srcarow += in->linesize[3];                                                                    \
+    }                                                                                                  \
+    return 0;                                                                                          \
+}
+
+DEFINE_INTERP_FUNC_PLANAR_FLOAT(nearest,     32)
+DEFINE_INTERP_FUNC_PLANAR_FLOAT(trilinear,   32)
+DEFINE_INTERP_FUNC_PLANAR_FLOAT(tetrahedral, 32)
+
 #define DEFINE_INTERP_FUNC(name, nbits)                                                             \
 static int interp_##nbits##_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)         \
 {                                                                                                   \
@@ -700,7 +788,8 @@ static int query_formats(AVFilterContext *ctx)
         AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
         AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
         AV_PIX_FMT_GBRP14,
-        AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
+        AV_PIX_FMT_GBRP16,  AV_PIX_FMT_GBRAP16,
+        AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32,
         AV_PIX_FMT_NONE
     };
     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
@@ -711,18 +800,19 @@ static int query_formats(AVFilterContext *ctx)
 
 static int config_input(AVFilterLink *inlink)
 {
-    int depth, is16bit, planar;
+    int depth, is16bit, isfloat, planar;
     LUT3DContext *lut3d = inlink->dst->priv;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
 
     depth = desc->comp[0].depth;
     is16bit = desc->comp[0].depth > 8;
     planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR;
+    isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT;
     ff_fill_rgba_map(lut3d->rgba_map, inlink->format);
     lut3d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit);
 
 #define SET_FUNC(name) do {                                     \
-    if (planar) {                                               \
+    if (planar && !isfloat) {                                   \
         switch (depth) {                                        \
         case  8: lut3d->interp = interp_8_##name##_p8;   break; \
         case  9: lut3d->interp = interp_16_##name##_p9;  break; \
@@ -731,6 +821,7 @@ static int config_input(AVFilterLink *inlink)
         case 14: lut3d->interp = interp_16_##name##_p14; break; \
         case 16: lut3d->interp = interp_16_##name##_p16; break; \
         }                                                       \
+    } else if (isfloat) { lut3d->interp = interp_##name##_pf32; \
     } else if (is16bit) { lut3d->interp = interp_16_##name;     \
     } else {       lut3d->interp = interp_8_##name; }           \
 } while (0)
@@ -970,6 +1061,39 @@ static void update_clut_planar(LUT3DContext *lut3d, const AVFrame *frame)
     }
 }
 
+static void update_clut_float(LUT3DContext *lut3d, const AVFrame *frame)
+{
+    const uint8_t *datag = frame->data[0];
+    const uint8_t *datab = frame->data[1];
+    const uint8_t *datar = frame->data[2];
+    const int glinesize  = frame->linesize[0];
+    const int blinesize  = frame->linesize[1];
+    const int rlinesize  = frame->linesize[2];
+    const int w = lut3d->clut_width;
+    const int level = lut3d->lutsize;
+    const int level2 = lut3d->lutsize2;
+
+    int i, j, k, x = 0, y = 0;
+
+    for (k = 0; k < level; k++) {
+        for (j = 0; j < level; j++) {
+            for (i = 0; i < level; i++) {
+                const float *gsrc = (const float *)(datag + y*glinesize);
+                const float *bsrc = (const float *)(datab + y*blinesize);
+                const float *rsrc = (const float *)(datar + y*rlinesize);
+                struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k];
+                vec->r = rsrc[x];
+                vec->g = gsrc[x];
+                vec->b = bsrc[x];
+                if (++x == w) {
+                    x = 0;
+                    y++;
+                }
+            }
+        }
+    }
+}
+
 static int config_output(AVFilterLink *outlink)
 {
     AVFilterContext *ctx = outlink->src;
@@ -1004,6 +1128,7 @@ static int config_clut(AVFilterLink *inlink)
 
     lut3d->clut_bits = desc->comp[0].depth;
     lut3d->clut_planar = av_pix_fmt_count_planes(inlink->format) > 1;
+    lut3d->clut_float = desc->flags & AV_PIX_FMT_FLAG_FLOAT;
 
     lut3d->clut_step = av_get_padded_bits_per_pixel(desc) >> 3;
     ff_fill_rgba_map(lut3d->clut_rgba_map, inlink->format);
@@ -1049,7 +1174,9 @@ static int update_apply_clut(FFFrameSync *fs)
         return ret;
     if (!second)
         return ff_filter_frame(ctx->outputs[0], master);
-    if (lut3d->clut_planar)
+    if (lut3d->clut_float)
+        update_clut_float(ctx->priv, second);
+    else if (lut3d->clut_planar)
         update_clut_planar(ctx->priv, second);
     else
         update_clut_packed(ctx->priv, second);
@@ -1477,6 +1604,72 @@ DEFINE_INTERP_FUNC_PLANAR_1D(cosine,      16, 16)
 DEFINE_INTERP_FUNC_PLANAR_1D(cubic,       16, 16)
 DEFINE_INTERP_FUNC_PLANAR_1D(spline,      16, 16)
 
+#define DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(name, depth)                      \
+static int interp_1d_##name##_pf##depth(AVFilterContext *ctx,                \
+                                                 void *arg, int jobnr,       \
+                                                 int nb_jobs)                \
+{                                                                            \
+    int x, y;                                                                \
+    const LUT1DContext *lut1d = ctx->priv;                                   \
+    const ThreadData *td = arg;                                              \
+    const AVFrame *in  = td->in;                                             \
+    const AVFrame *out = td->out;                                            \
+    const int direct = out == in;                                            \
+    const int slice_start = (in->height *  jobnr   ) / nb_jobs;              \
+    const int slice_end   = (in->height * (jobnr+1)) / nb_jobs;              \
+    uint8_t *grow = out->data[0] + slice_start * out->linesize[0];           \
+    uint8_t *brow = out->data[1] + slice_start * out->linesize[1];           \
+    uint8_t *rrow = out->data[2] + slice_start * out->linesize[2];           \
+    uint8_t *arow = out->data[3] + slice_start * out->linesize[3];           \
+    const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0];    \
+    const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1];    \
+    const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2];    \
+    const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3];    \
+    const float lutsize = lut1d->lutsize - 1;                                \
+    const float scale_r = lut1d->scale.r * lutsize;                          \
+    const float scale_g = lut1d->scale.g * lutsize;                          \
+    const float scale_b = lut1d->scale.b * lutsize;                          \
+                                                                             \
+    for (y = slice_start; y < slice_end; y++) {                              \
+        float *dstg = (float *)grow;                                         \
+        float *dstb = (float *)brow;                                         \
+        float *dstr = (float *)rrow;                                         \
+        float *dsta = (float *)arow;                                         \
+        const float *srcg = (const float *)srcgrow;                          \
+        const float *srcb = (const float *)srcbrow;                          \
+        const float *srcr = (const float *)srcrrow;                          \
+        const float *srca = (const float *)srcarow;                          \
+        for (x = 0; x < in->width; x++) {                                    \
+            float r = av_clipf(sanitizef(srcr[x]) * scale_r, 0.0f, lutsize); \
+            float g = av_clipf(sanitizef(srcg[x]) * scale_g, 0.0f, lutsize); \
+            float b = av_clipf(sanitizef(srcb[x]) * scale_b, 0.0f, lutsize); \
+            r = interp_1d_##name(lut1d, 0, r);                               \
+            g = interp_1d_##name(lut1d, 1, g);                               \
+            b = interp_1d_##name(lut1d, 2, b);                               \
+            dstr[x] = r;                                                     \
+            dstg[x] = g;                                                     \
+            dstb[x] = b;                                                     \
+            if (!direct && in->linesize[3])                                  \
+                dsta[x] = srca[x];                                           \
+        }                                                                    \
+        grow += out->linesize[0];                                            \
+        brow += out->linesize[1];                                            \
+        rrow += out->linesize[2];                                            \
+        arow += out->linesize[3];                                            \
+        srcgrow += in->linesize[0];                                          \
+        srcbrow += in->linesize[1];                                          \
+        srcrrow += in->linesize[2];                                          \
+        srcarow += in->linesize[3];                                          \
+    }                                                                        \
+    return 0;                                                                \
+}
+
+DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(nearest, 32)
+DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(linear,  32)
+DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cosine,  32)
+DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cubic,   32)
+DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(spline,  32)
+
 #define DEFINE_INTERP_FUNC_1D(name, nbits)                                   \
 static int interp_1d_##nbits##_##name(AVFilterContext *ctx, void *arg,       \
                                       int jobnr, int nb_jobs)                \
@@ -1537,18 +1730,19 @@ DEFINE_INTERP_FUNC_1D(spline,      16)
 
 static int config_input_1d(AVFilterLink *inlink)
 {
-    int depth, is16bit, planar;
+    int depth, is16bit, isfloat, planar;
     LUT1DContext *lut1d = inlink->dst->priv;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
 
     depth = desc->comp[0].depth;
     is16bit = desc->comp[0].depth > 8;
     planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR;
+    isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT;
     ff_fill_rgba_map(lut1d->rgba_map, inlink->format);
     lut1d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit);
 
 #define SET_FUNC_1D(name) do {                                     \
-    if (planar) {                                                  \
+    if (planar && !isfloat) {                                      \
         switch (depth) {                                           \
         case  8: lut1d->interp = interp_1d_8_##name##_p8;   break; \
         case  9: lut1d->interp = interp_1d_16_##name##_p9;  break; \
@@ -1557,6 +1751,7 @@ static int config_input_1d(AVFilterLink *inlink)
         case 14: lut1d->interp = interp_1d_16_##name##_p14; break; \
         case 16: lut1d->interp = interp_1d_16_##name##_p16; break; \
         }                                                          \
+    } else if (isfloat) { lut1d->interp = interp_1d_##name##_pf32; \
     } else if (is16bit) { lut1d->interp = interp_1d_16_##name;     \
     } else {              lut1d->interp = interp_1d_8_##name; }    \
 } while (0)