======
Levels
======

Adjusts brightness, contrast, and gamma. This is done using the following
*transfer* function:

.. math::

    \mathsf{output = ( (input - input\_low) / (input\_high - input\_low) )
    ^{(1 / gamma)} * (output\_high - output\_low) + output\_low}

* ``input_low`` and ``input_high`` determine what input pixel values are treated
  as pure black and pure white.
* ``output_low`` and ``output_high`` determine what output values are treated as
  pure black and pure white.
* ``gamma`` controls the degree of non-linearity in the conversion.

This is one of those filters for which it would really be nice to have a GUI.
Since we can't offer a GUI (though `AvsPmod`_ does), we at least make this filter
compatible with `VirtualDub`_'s when the clip is RGB. In that case you should be
able to take the numbers from VirtualDub's Levels dialog and pass them as
parameters to the **Levels** filter and get the same results. Unlike VirtualDub's
filter however, the input and output parameters can be larger than the maximum
value of the pixel format (for example, in 8-bit it can be greater than 255, see
table below).

When processing data in YUV mode, **Levels** only gamma-corrects the luma
information, not the chroma. Gamma correction is really an RGB concept, and is
only approximated here in YUV. If ``gamma=1.0`` (unity), the filter should have
the same effect in both RGB and YUV modes. For adjusting brightness or contrast
in YUV mode, it *may* be better (depending on the effect you are looking for) to
use :doc:`tweak` or :doc:`coloryuv`, because **Levels** changes the chroma of
the clip.

Note in AviSynth+, the parameters ``input_low``, ``input_high``, ``output_low``
and ``output_high``:

* are float instead of int.
* are not `autoscaling`_ – they are relative to the current bit depth:

 .. table::
  :widths: auto

  +--------+------+---------+----------+----------+--------+
  | Bits   | Min. |                               | Max.   |
  +========+======+=========+==========+==========+========+
  | **8**  | 0    | 16      | 128      | 235      | 255    |
  +--------+------+---------+----------+----------+--------+
  | **10** | 0    | 64      | 512      | 940      | 1023   |
  +--------+------+---------+----------+----------+--------+
  | **12** | 0    | 256     | 2048     | 3760     | 4095   |
  +--------+------+---------+----------+----------+--------+
  | **14** | 0    | 1024    | 8192     | 15040    | 16383  |
  +--------+------+---------+----------+----------+--------+
  | **16** | 0    | 4096    | 32768    | 60160    | 65535  |
  +--------+------+---------+----------+----------+--------+
  | **32** | 0    | 16/255  | 128/255  | 235/255  | 1.0    |
  +--------+------+---------+----------+----------+--------+


Syntax and Parameters
----------------------

::

    Levels (clip input, float input_low, float gamma, float input_high, float output_low, float output_high,
            bool "coring", bool "dither")

.. describe:: clip

    Source clip; all color formats supported.

.. describe:: input_low

    | Input values at ``input_low`` or lower are treated as black, and lighter
      colors are darkened proportionally.
    | Therefore, raising ``input_low`` darkens the output.

.. describe:: gamma

    | `Gamma`_ adjustment. See `examples`_.
    | Higher ``gamma`` brightens the output; lower ``gamma`` darkens the output.

.. describe:: input_high

    | Input values at ``input_high`` or higher are treated as *white*, and
      darker colors are brightened proportionally.
    | Therefore, lowering ``input_high`` brightens the output.

.. describe:: output_low

    Dark values brighten to gray as ``output_low`` becomes larger.

.. describe:: output_high

    Light values darken to gray as ``output_high`` becomes smaller.

.. describe:: coring

    For RGB this parameter is ignored and internally set to false. For Y and YUV,
    when true (default),

    #. input clip is clamped to limited range (e.g. in 8-bit, 16-235 for luma
       and 16-240 for chroma);
    #. this clamped input is scaled from limited range back to full range,
    #. the conversion takes place according to the transfer function above, and then
    #. output is scaled back to limited range.

    When false, the conversion takes place according to the transfer function,
    without any scaling.

    .. note::

       ``coring`` was created for VirtualDub compatibility, and it remains true
       by default for compatibility with older scripts.

       In the opinion of some, you should `always use coring=false`_ if you are
       working directly with luma values (whether or not your input is limited
       range).

       Limited range video can be correctly processed with ``coring=false``; for
       example::

            Levels(0, 1.6, 255, 0, 255, coring=true)

       Produces the same result as::

            Levels(16, 1.6, 235, 16, 235, coring=false)

       Except that the output is not clipped to limited range. Black and white
       levels are preserved while adjusting ``gamma``, unlike::

            Levels(0, 1.6, 255, 0, 255, coring=false)

.. describe:: dither

    When true, `ordered dithering`_ is applied to combat `banding`_.

    Default: false


Examples
--------

::

    # does nothing on a [16,235] clip, but it clamps (or rounds) a [0,255] clip to [16,235]:
    Levels(0, 1, 255, 0, 255)

::

    # the input is scaled from [16,235] to [0,255],
    # the conversion [0,255]->[16,235] takes place (accordingly to the formula),
    # and the output is scaled back from [0,255] to [16,235]:
    # (for example: the luma values in [0,16] are all converted to 30)
    Levels(0, 1, 255, 16, 235)

::

    # gamma-correct image for display in a brighter environment:
    # example: luma of 16 stays 16, 59 is converted to 79, etc.
    Levels(0, 1.3, 255, 0, 255)

::

    # invert the image (make a photo-negative):
    # example: luma of 16 is converted to 235
    Levels(0, 1, 255, 255, 0)

::

    # does nothing on a [0,255] clip; does nothing on a [16,235]:
    Levels(0, 1, 255, 0, 255, coring=false)

::

    # scales a [0,255] clip to [16,235]:
    Levels(0, 1, 255, 16, 235, coring=false)
    # note both luma and chroma components are scaled by the same
    # amount, so it's not exactly the same as ColorYUV(levels="PC->TV")

::

    # scales a [16,235] clip to [0,255]:
    Levels(16, 1, 235, 0, 255, coring=false)
    # note both luma and chroma components are scaled by the same
    # amount, so it's not exactly the same as ColorYUV(levels="TV->PC")

::

    # makes a clip 100% black
    Levels(0, 1.0, 255, 0, 0)

::

    # apply fading on gamma corrected source (same holds for resizing and smoothing)
    clip = ...
    gamma = 2.2
    clip.Levels(0, gamma, 255, 0, 255) # undo gamma (also called gamma correction)
    FadeOut(n)
    Levels(0, 1.0/gamma, 255, 0, 255) # redo gamma
    ## use bit depth >= 10 to avoid banding in dark areas


Changelog
----------

.. table::
    :widths: auto

    +-----------------+----------------------------------------------------------------+
    | Version         | Changes                                                        |
    +=================+================================================================+
    | AviSynth+ r2542 | Added 32 bit float support.                                    |
    +-----------------+----------------------------------------------------------------+
    | AviSynth+ r2487 || Added 10-16 bit support for YUV(A) and planar RGB(A) formats. |
    |                 || Added support for RGB48/64 formats.                           |
    +-----------------+----------------------------------------------------------------+
    | AviSynth 2.6.0  || Added ``dither`` parameter.                                   |
    |                 || Added support for Y8, YV16, YV24 and YV411 formats.           |
    +-----------------+----------------------------------------------------------------+
    | AviSynth 2.5.3  | Added ``coring`` parameter.                                    |
    +-----------------+----------------------------------------------------------------+

$Date: 2025-02-25 17:40:16-05:00 $

.. _AvsPmod:
    https://forum.doom9.org/showthread.php?t=175823
.. _Gamma:
    https://en.wikipedia.org/wiki/Gamma_correction
.. _VirtualDub:
    http://avisynth.nl/index.php/VirtualDub2
.. _autoscaling:
    http://avisynth.nl/index.php/Autoscale_parameter
.. _always use coring=false:
    https://web.archive.org/web/20160825211112/http://forum.doom9.org/showthread.php?p=1722885#post1722885
.. _ordered dithering:
    http://avisynth.nl/index.php/Ordered_dithering
.. _banding:
    https://en.wikipedia.org/wiki/Colour_banding
