Monitor color calibration

Dear team
I am trying to figure out how to calibrate color on my monitor manually (the Konica Minolta photometer I have borrowed CS200 does not connect). As I understand it, the first step is to acquire the measurements for phosphor spectra and enter it as part of the calibration using setSpectra. However I am not sure what type and how many measurements are expected here. Is dominant wavelength a valid input? Sorry for the naive question. Thanks for any help!

Hi There,

Note I am probably providing more info here than needed but it is for future users who may find this post.

Here is a post that might help get started (be warned I made this quite a long time ago in my PhD!) it also uses a PR655 for automated color calibration so it is a little different, but still hopefully the resource is useful for future readers finding this post.

For manually calibrating a monitor without a photometer (i.e. perceptual calibration) this guide might also be helpful as it points towards demos in the PsychoPy coder view that can help.

For manual calibration with a photometer that is not supported out of box by PsychoPy (more relevant to your post here). It is first useful to know the calculations performed by PsychoPy’s monitor centre and then useful to know the lines of code in the source code where PsychoPy does this automatically, specifically here is where the linearised values are formed given the non linear measurements and gamma and here is where gamma is estimated.

To demonstrate here is a very rough script that could be run in PsychoPy coder. Note that i first made a new monitor in my monitor centre called “gamma_test” and saved it with default values.

'''
A demonstration to help understanding of gamma and the functions used by PsychoPy
'''
from psychopy import monitors, visual
import numpy as np
import matplotlib.pyplot as plt

# Generate x-values (20 points between 0 and 1). Imagine this is the value you give to a polygonc color in PsychoPy
# e.g. color = [0,0,0] then [0.2, 0.2, 0.2] and so on to [1, 1, 1] for all guns 
# OR color = [0,0,0] then [0.2, 0, 0] and so on to [1, 0,0] for one color gun
x_values = np.linspace(0, 1, 20)

#Simulate some pre calubration measured values (e.g. values that follow an exponential not a linear function)
# Note these values range between 0 nand 1, in reality these will differ.
def exponential_function(x):
    return np.exp(x / 1) / np.exp(1)

y_values = exponential_function(x_values)


# plot the precalibration measurements
plt.plot(x_values, y_values, marker='o', linestyle='', label='Simulated pre calibration data', color='black', alpha = 0.5)
plt.xlabel('PsychoPy color gun value')
plt.ylabel('Measured Luminance')


pre_calibration_lums = y_values
# calculate the linear values for the desiredLums calculation
linear_values = np.linspace(0, 1, 20)

# fetch the monitors available in monitor centre
names = monitors.getAllMonitors()
for thisName in names:
    
    thisMon = monitors.Monitor(thisName)
    
    if thisName == "gamma_test": # I set up a monitor in my monitor centre called "gamma_test" for the purposes of this demo
        
        #set the precalibration luminance levels
        thisMon.setLumsPre(pre_calibration_lums)
        # set the levels that were used to measure luminance pre calibration
        thisMon.setLevelsPre(x_values)
        
        # create a class for managing the gamma table see https://github.com/psychopy/psychopy/blob/dev/psychopy/monitors/calibTools.py#L651C7-L651C22
        # and derive a gamma table
        gammaTable = monitors.GammaCalculator(inputs = x_values, lums = pre_calibration_lums)
        
        # fetch what the value of gamma is 
        # note this uses the fitGammFun https://github.com/psychopy/psychopy/blob/dev/psychopy/monitors/calibTools.py#L708
        thisgamma = gammaTable.gamma
        print('gamma:', gammaTable.gamma)
        
        # fetch what linearization method is being used https://github.com/psychopy/psychopy/blob/dev/psychopy/monitors/calibTools.py#L547
        print(thisMon.getLinearizeMethod())
        
        linearized_vals = thisMon.lineariseLums(desiredLums = linear_values, overrideGamma = thisgamma)
        print(thisMon.lineariseLums(desiredLums = linear_values, overrideGamma = thisgamma))

plt.plot(x_values, linearized_vals, marker='o', linestyle='', label='gamma corrected', color='red', alpha = 1)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Plot of simulated calibration data')
plt.legend()
plt.grid(True)
plt.show()

This generates this plot:
Figure_1

For your own manual calibration, could you try replacing y_values with your actual pre-calibration measurements? and also replace linear_values with your actual desired linear values i.e. your min to max in linear steps. You would do this 3 times (once per colour gun, in this example there are 20 sample points). The print statement print('gamma:', gammaTable.gamma) should show you the gamma for each gun.

note I haven’t tested this yet with an actual photometer, but hopefully it is useful to get started

Hope this helps,
Becca

Hi Becca
Thanks so much! I managed the gamma correction. Now I am wondering how do I do the chromatic calibration, which is needed to create the DKL2RGB conversion matrix? It looks like PsycoPy is looking for “phosphor Spectra” in nm and “RGB power” .

https://psychopy.org/_modules/psychopy/monitors/calibTools.html#Monitor.setSpectra

I am just unsure what input would be appropriate here. For phosphor spectra, would dominant wavelength be appropriate? For RGB power I have no idea what that means. Thanks so much for helping with this