# Script for manual gamma calibration

**URL:** https://discourse.psychopy.org/t/script-for-manual-gamma-calibration/43750
**Category:** Coding
**Created:** [February 5, 2025, 12:58pm UTC](https://discourse.psychopy.org/t/script-for-manual-gamma-calibration/43750 "2025-02-05T12:58:15Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![Reuben](https://avatars.discourse-cdn.com/v4/letter/r/54ee81/32.png) [@Reuben](https://discourse.psychopy.org/u/Reuben)
#### Post date: [February 5, 2025, 12:58pm UTC](https://discourse.psychopy.org/t/script-for-manual-gamma-calibration/43750/1 "2025-02-05T12:58:15Z")

</div>

Just thought I would add the script I made for the purposes of calibrating gamma manually. It just makes boxes of grey level from min to max in as many steps as specified.

```from
from psychopy.hardware import keyboard
import pyglet
import numpy as np

kb = keyboard.Keyboard()
screens = pyglet.canvas.get_display().get_screens()
screen = len(screens) -1
win = visual.Window(color=(0, 0, 0), screen = screen, fullscr = True)

### Change number of steps ###
nLevels = 11
###

def calibration(nLevels):
    grey_vals = np.linspace(-1, 1, nLevels)
    for i in range(nLevels):
        grey_val = grey_vals[i]
        box = visual.Rect(win, width = 0.5, height = 0.5, fillColor = [grey_val,grey_val,grey_val])
        box.draw()
        win.flip()
        kb.waitKeys()

calibration(nLevels)

```

no question, just might save someone 5mins.

---

<div class="post-metadata">

### Author: ![Reuben](https://avatars.discourse-cdn.com/v4/letter/r/54ee81/32.png) [@Reuben](https://discourse.psychopy.org/u/Reuben)
#### Post date: [February 7, 2025, 10:00am UTC](https://discourse.psychopy.org/t/script-for-manual-gamma-calibration/43750/2 "2025-02-07T10:00:20Z")

</div>

Updating this because I could not get any of the calibrations to actually work. This was due to an error with setGammaRamp which I’ve seen reported many times but couldn’t find a solution that actually worked.

Here is a script which will manually apply the gamma ramp, tested and working for Windows11.

```auto
import ctypes
import numpy as np
import win32api
import win32gui

# Load gdi32.dll
gdi32 = ctypes.WinDLL("gdi32")
user32 = ctypes.WinDLL("user32")

monitors = win32api.EnumDisplayMonitors()
if len(monitors) == 1:
    monitor_info = win32api.GetMonitorInfo(monitors[0][0])
    hdc = win32gui.CreateDC("DISPLAY", monitor_info['Device'], None)
elif len(monitors) == 2:
    monitor_info = win32api.GetMonitorInfo(monitors[1][0])
    hdc = win32gui.CreateDC("DISPLAY", monitor_info['Device'], None)
else:
    raise RuntimeError("No monitors detected or more than two monitors detected.")

# Define the LUT structure: an array of 256 RGB triplets (WORD type)
class GAMMARAMP(ctypes.Structure):
    _fields_ = [("red", ctypes.c_ushort * 256),
                ("green", ctypes.c_ushort * 256),
                ("blue", ctypes.c_ushort * 256)]

def set_gamma(value, min_luminance=0, max_luminance=300):
    """Set screen gamma correction with specified gamma value and luminance range (in cd/m²)."""
    if not (0.1 <= value <= 5.0):
        raise ValueError("Gamma value must be between 0.1 and 5.0")
    if min_luminance < 0 or max_luminance <= min_luminance:
        raise ValueError("Invalid luminance range")

    gamma_ramp = GAMMARAMP()

    # Normalize min/max luminance to 0-65535 range
    min_val = int((min_luminance / 300) * 65535)
    max_val = int((max_luminance / 300) * 65535)

    # Generate LUT based on gamma value and luminance limits
    for i in range(256):
        gamma = ((i / 255.0) ** (1.0 / value)) * (max_val - min_val) + min_val
        gamma = min(65535, max(0, int(gamma)))
        gamma_ramp.red[i] = ctypes.c_ushort(gamma)
        gamma_ramp.green[i] = ctypes.c_ushort(gamma)
        gamma_ramp.blue[i] = ctypes.c_ushort(gamma)

    # Apply gamma correction
    success = gdi32.SetDeviceGammaRamp(hdc, ctypes.byref(gamma_ramp))

    if not success:
        raise RuntimeError("Failed to set gamma correction")

# Example Usage
set_gamma(2.2, min_luminance=20, max_luminance=250)

```

The gamma will reset any time the display settings are updated- e.g. computer is restarted, refresh rate changed etc. etc.
