.. _gammaCorrection:

Gamma correcting a monitor
====================================

Monitors typically don't have linear outputs; when you request luminance level of 127, it is not exactly half the luminance of value 254. For experiments that require the luminance values to be linear, a correction needs to be put in place for this nonlinearity which typically involves fitting a power law or gamma (:math:`\gamma`) function to the monitor output values. This process is often refered to as :term:`gamma correction`.

PsychoPy can help you perform gamma correction on your monitor, especially if you have one of the supported photometers/spectroradiometers.

There are various different equations with which to perform gamma correction. The simple equation :eq:`simple` is assumed by most hardware manufacturers and gives a reasonable first approximation to a linear correction. The full gamma correction equation :eq:`full` is more general, and likely more accurate especially where the lowest luminance value of the monitor is bright, but also requires more information. It can only be used in labs that do have access to a photometer or similar device.

Simple gamma correction
---------------------------

The most simple (used by most hardware manufacturers) is this:

.. math:: 
   :label: simple
   
   L(v) = a + kv^\gamma
   
where `L` is the final luminance value, `v` is the requested intensity (ranging 0 to 1), `a`, `k` and :math:`\gamma` are constants for the monitor.

This equation assumes that the luminance where the monitor is set to 'black' (v=0) comes entirely from the surround and is not subject to the same nonlinearity as the monitor. If the monitor itself contributes significantly to `a` then the function may not fit very well and the correction will be poor.

The advantage of this function is that the calibrating system (PsychoPy) does not need to know anything more about the monitor than the gamma value itself (for each gun). For the full gamma equation :eq:`full`, the system needs to know about several additional variables. The look-up table (LUT) values required to give a (roughly) linear luminance output can be generated by:
    
.. math::
    :label: simpleLUT
    
    LUT(i) = i^{1/\gamma}

where `i` is the entry in the LUT, between 0 (black) and 1 (white).


Full gamma correction
---------------------------

For very accurate gamma correction PsychoPy uses a more general form of the equation above, which can separate the contribution of the monitor and the background to the lowest luminance level:

.. math::
    :label: full
    
    L(v) = a + (b+kv)^\gamma

This equation makes no assumption about the origin of the base luminance value, but requires that the system knows the values of `b` and `k` as well as `\gamma`.

The inverse values, required to build the LUT are found by:

.. math::
    :label: fullLUT
    
    LUT(i) = \frac{( (1-i)b^\gamma + i(b+k)^\gamma )^{1/\gamma}-b}{k}
    
This is derived below from Pelli and Zhang [#1]_ for the interested reader. ;-)

And the associated luminance values for each point in the LUT are given by:

.. math::
    
    L(i) = a + (1-i)b^\gamma + i(b+k)^\gamma

.. _deriveInverseGamma:

Deriving the inverse full equation
---------------------------------------

The difficulty with the full gamma equation :eq:`full` is that the presence of the `b` value complicates the issue of calculating the inverse values for the LUT. Pelli and Zhang [#1]_ suggest using the following:

.. math::
    :label: zhangPelliLUT
    
    LUT(i) = \frac{((L-a)^{1/\gamma} - b )}{k}

and then using :math:`LUT(i)=-b/k` in the cases where :math:`L \ge a`.

To use this equation we need to first calculate the linear set of luminance values, `L`, that we are able to produce the current monitor and lighting conditions and /then/ deduce the LUT value needed to generate that luminance value.

We need to insert into the LUT values starting at 0 and ending at 1 (to use the maximum range) that map onto the linear range from the minimum, `m`, to the maximum `M` possible luminance. From the parameters in :eq:`full` it is clear that:

.. math::
    :label: minMax
    
    m = a+b^\gamma
    
    M = a+(b+k)^\gamma
    
Thus, the luminance value, `L` at any given point in the LUT, `i`, is given by
    
.. math::
    :label: lumsLUT
    
    L(i) &= m + (M-m)i \\
         &= a+b^\gamma + (a+(b+k)^\gamma - a - b^\gamma)i \\
         &= a + b^\gamma + ((b+k)^\gamma - b^\gamma)i \\
         &= a + (1-i)b^\gamma + i(b+k)^\gamma
         
where `i` is the position in the LUT as a fraction. 

Now, to generate the LUT as needed we simply take the inverse of :eq:`full`:

.. math::
    :label: fullInv
    
    LUT(x) = \frac{( x -a)^{1/\gamma}-b}{k}
    
and substitute our :math:`L(i)` values from :eq:`lumsLUT`:

.. math::
    :label: fullLUTderived
    
    LUT(i) &= \frac{( a + (1-i)b^\gamma + i(b+k)^\gamma -a)^{1/\gamma}-b}{k}\\
        &= \frac{( (1-i)b^\gamma + i(b+k)^\gamma )^{1/\gamma}-b}{k}

References
--------------

.. [#1] Pelli, D. G., & Zhang, L. (1991) Accurate control of contrast on microcomputer displays. Vision Research 31, 1337-1350.