[FFmpeg-devel] [PATCH] Add a gamma flag to exr loader to avoid banding

Clément Bœsch u at pkh.me
Mon Apr 28 01:14:00 CEST 2014


On Sun, Apr 27, 2014 at 07:39:57PM -0300, Gonzalo Garramuno wrote:
> On 27/04/14 13:48, Michael Niedermayer wrote:
> >On Sun, Apr 27, 2014 at 01:28:15PM -0300, Gonzalo Garramuno wrote:
> >
> >you can do the gamma correction in the LUT for 16bit too
> >
> >[...]
> >
> 
> Good idea.  And here it is.   The gamma table for the half float path.
> Now hopefully the powers that be accept the patch.

> diff --git a/Changelog b/Changelog
> index daaa1ea..a726823 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
>  releases are sorted from youngest to oldest.
>  
>  version <next>:
> +- EXR loader supports a gamma flag

"Entries are sorted chronologically from oldest to youngest"

>  - AC3 fixed-point decoding
>  - shuffleplanes filter
>  - subfile protocol
> diff --git a/libavcodec/exr.c b/libavcodec/exr.c
> index 084025a..55313f4 100644
> --- a/libavcodec/exr.c
> +++ b/libavcodec/exr.c
> @@ -31,9 +31,11 @@
>   */
>  
>  #include <zlib.h>
> +#include <float.h>
>  
>  #include "libavutil/imgutils.h"
>  #include "libavutil/opt.h"
> +#include "libavutil/intfloat.h"
>  
>  #include "avcodec.h"
>  #include "bytestream.h"
> @@ -77,6 +79,7 @@ typedef struct EXRThreadData {
>      uint16_t *lut;
>  } EXRThreadData;
>  
> +
>  typedef struct EXRContext {
>      AVClass *class;
>      AVFrame *picture;
> @@ -106,8 +109,14 @@ typedef struct EXRContext {
>      EXRThreadData *thread_data;
>  
>      const char *layer;
> +
> +    float gamma;
> +
> +    uint16_t gamma_table[32767];
> +
>  } EXRContext;
>  
> +
>  /**
>   * Convert from 32-bit float as uint32_t to uint16_t.
>   *
> @@ -128,6 +137,23 @@ static inline uint16_t exr_flt2uint(uint32_t v)
>      return (v + (1 << 23)) >> (127 + 7 - exp);
>  }
>  
> +/*
> +  Safely convert a half float to a full float
> + */
> +static inline union av_intfloat32 half_to_float_full(uint16_t h)
> +{
> +    static const union av_intfloat32 magic = { (254 - 15) << 23 };
> +    static const union av_intfloat32 was_infnan = { (127 + 16) << 23 };
> +    union  av_intfloat32 o;
> +
> +    o.i = (h & 0x7fff) << 13; // exponent/mantissa bits
> +    o.f *= magic.f; // exponent adjust
> +    if (o.f >= was_infnan.f) // make sure Inf/NaN survive
> +        o.i |= 255 << 23;
> +    o.i |= (h & 0x8000) << 16; // sign bit
> +    return o;
> +}
> +
>  /**
>   * Convert from 16-bit float as uint16_t to uint16_t.
>   *
> @@ -772,6 +798,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
>      int bxmin = s->xmin * 2 * s->desc->nb_components;
>      int i, x, buf_size = s->buf_size;
>      int ret;
> +    float one_gamma = 1.0f / s->gamma;
>  
>      line_offset = AV_RL64(s->gb.buffer + jobnr * 8);
>      // Check if the buffer has the required bytes needed from the offset
> @@ -851,31 +878,40 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
>          if (s->pixel_type == EXR_FLOAT) {
>              // 32-bit
>              for (x = 0; x < xdelta; x++) {
> -                *ptr_x++ = exr_flt2uint(bytestream_get_le32(&r));
> -                *ptr_x++ = exr_flt2uint(bytestream_get_le32(&g));
> -                *ptr_x++ = exr_flt2uint(bytestream_get_le32(&b));
> +                union av_intfloat32 t;
> +                t.i = bytestream_get_le32(&r);;
> +                t.f = powf(t.f, one_gamma);
> +                *ptr_x++ = exr_flt2uint(t.i);
> +
> +                t.i = bytestream_get_le32(&g);
> +                t.f = powf(t.f, one_gamma);
> +                *ptr_x++ = exr_flt2uint(t.i);
> +
> +                t.i = bytestream_get_le32(&b);
> +                t.f = powf(t.f, one_gamma);
> +                *ptr_x++ = exr_flt2uint(t.i);
>                  if (channel_buffer[3])
>                      *ptr_x++ = exr_flt2uint(bytestream_get_le32(&a));
>              }
> -        } else {
> -            // 16-bit
> -            for (x = 0; x < xdelta; x++) {
> -                *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&r));
> -                *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&g));
> -                *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&b));
> -                if (channel_buffer[3])
> -                    *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&a));
> -            }
> -        }
> -
> -        // Zero out the end if xmax+1 is not w
> -        memset(ptr_x, 0, axmax);
> -
> -        channel_buffer[0] += s->scan_line_size;
> -        channel_buffer[1] += s->scan_line_size;
> -        channel_buffer[2] += s->scan_line_size;
> -        if (channel_buffer[3])
> -            channel_buffer[3] += s->scan_line_size;
> +         } else {
> +             // 16-bit
> +             for (x = 0; x < xdelta; x++) {
> +                 *ptr_x++ = s->gamma_table[bytestream_get_le16(&r)];
> +                 *ptr_x++ = s->gamma_table[bytestream_get_le16(&g)];
> +                 *ptr_x++ = s->gamma_table[bytestream_get_le16(&b)];
> +                 if (channel_buffer[3])
> +                      *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&a));
> +             }
> +         }
> +
> +         // Zero out the end if xmax+1 is not w
> +         memset(ptr_x, 0, axmax);
> +
> +         channel_buffer[0] += s->scan_line_size;
> +         channel_buffer[1] += s->scan_line_size;
> +         channel_buffer[2] += s->scan_line_size;
> +         if (channel_buffer[3])
> +             channel_buffer[3] += s->scan_line_size;
>      }
>  
>      return 0;
> @@ -1261,7 +1297,11 @@ static int decode_frame(AVCodecContext *avctx, void *data,
>  
>  static av_cold int decode_init(AVCodecContext *avctx)
>  {
> +    uint16_t i;
> +    union av_intfloat32 t;
>      EXRContext *s = avctx->priv_data;
> +    float one_gamma = 1.0f / s->gamma;
> +    av_log(avctx, AV_LOG_INFO, "gamma %f.\n", s->gamma);
>  
>      s->avctx              = avctx;
>      s->xmin               = ~0;
> @@ -1280,6 +1320,13 @@ static av_cold int decode_init(AVCodecContext *avctx)
>      s->w                  = 0;
>      s->h                  = 0;
>  

> +    for ( i = 0; i < 32767; ++i )
> +    {

style: "for (i = 0; i < 32767; i++) {"

> +       t = half_to_float_full(i);
> +       t.f = powf(t.f,one_gamma);

> +       s->gamma_table[i] = exr_flt2uint( t.i ); 

trailing whitespace, style, and indent

> +    }
> +
>      // allocate thread data, used for non EXR_RAW compreesion types
>      s->thread_data = av_mallocz_array(avctx->thread_count, sizeof(EXRThreadData));
>      if (!s->thread_data)
> @@ -1322,6 +1369,8 @@ static av_cold int decode_end(AVCodecContext *avctx)
>  static const AVOption options[] = {
>      { "layer", "Set the decoding layer", OFFSET(layer),
>          AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VD },
> +    { "gamma", "Set the float gamma value when decoding", OFFSET(gamma),
> +        AV_OPT_TYPE_FLOAT, { .dbl = 2.2 }, 0.001, FLT_MAX, VD },
>      { NULL },
>  };
>  


-- 
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20140428/1a8f1127/attachment.asc>


More information about the ffmpeg-devel mailing list