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.
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.
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.