cal_psy
1from psychopy import visual, event, core 2import numpy as np 3from cal_lib import GammaFitter 4 5class GrayLevels: 6 """ 7 A class to measure and calibrate monitor gamma using a SpyderX colorimeter and PsychoPy. 8 9 This class allows calibration of the SpyderX device, measurement of luminance levels for 10 various grayscale levels, and fitting of the gamma curve using the provided luminance data. 11 12 Attributes: 13 spyder (object): An instance of the SpyderX class used for photometric measurements. 14 win (psychopy.visual.Window): The PsychoPy window for displaying gray levels. 15 bg_rect (psychopy.visual.Rect): A full-screen rectangle used to simulate background color. 16 """ 17 18 def __init__(self, spyder, fullscr=False): 19 """ 20 Initializes the GrayLevels class. 21 22 Args: 23 spyder (SpyderX): An initialized SpyderX object for luminance measurements. 24 fullscr (bool): Whether to open the PsychoPy window in full-screen mode. 25 Defaults to False. 26 """ 27 self.spyder = spyder 28 self.win = visual.Window([800, 600], color=[0, 0, 0], units="norm", waitBlanking=True, fullscr=fullscr) 29 self.bg_rect = visual.Rect(self.win, width=2, height=2, fillColor=[0, 0, 0], lineColor=None) 30 self.bg_rect.draw() 31 32 def calibrate(self): 33 """ 34 Calibrates the SpyderX device to measure the baseline luminance. 35 36 Displays instructions for the user to close the SpyderX sensor to perform a black 37 level calibration and waits for user confirmation before proceeding. 38 """ 39 print('### GRAYLEVELS ### Spyder calibration', end=' ') 40 instruction = visual.TextStim(self.win, 41 text="Close the SpyderX to measure baseline and press space to start.", 42 color=[1, 1, 1]) 43 self.bg_rect.draw() 44 instruction.draw() 45 self.win.flip() 46 event.waitKeys(keyList=["space"]) 47 48 self.spyder.calibrate() 49 print('DONE') 50 51 def measure(self, pause=1, gamma=None, num_levels=12,wait_user=True): 52 """ 53 Measures luminance levels across a range of grayscale values. 54 55 Displays a series of gray levels on the monitor and uses the SpyderX device to measure 56 the corresponding luminance values. The gamma curve is then fitted using the GammaFitter class. 57 58 Args: 59 pause (float): The time (in seconds) to pause after each gray level display to ensure stabilization. 60 Defaults to 1 second. 61 gamma (float, optional): A predefined gamma value to set for the monitor (for testing the linearity of the monitor after correction). If None, gamma remains unchanged. 62 Defaults to None. This parameter works on Windows only with one monitor or in mirror mode. 63 num_levels (int): The number of gray levels to display and measure. Defaults to 12. 64 wait_user (bool): wait for keypress to start 65 66 Returns: 67 GammaFitter: An instance of the GammaFitter class containing the gamma value and the fit result. 68 """ 69 if gamma is not None: 70 self.win.setGamma(gamma) 71 gray_levels = np.linspace(-1, 1, num_levels) 72 luminance_readings = [] 73 74 if wait_user: 75 instruction = visual.TextStim(self.win, 76 text="Position the SpyderX on the monitor and press space to start.", 77 color=[1, 1, 1]) 78 self.bg_rect.fillColor = [-1, -1, -1] 79 self.bg_rect.draw() 80 instruction.draw() 81 self.win.flip() 82 event.waitKeys(keyList=["space"]) 83 self.bg_rect.fillColor = [-1, -1, -1] 84 self.bg_rect.draw() 85 self.win.flip() 86 core.wait(pause) 87 88 for gray in gray_levels: 89 self.bg_rect.fillColor = [gray, gray, gray] 90 self.bg_rect.draw() 91 self.win.flip() 92 core.wait(pause) # Wait for the screen to stabilize 93 94 XYZ = self.spyder.measure() 95 luminance = XYZ[1] # Y component of XYZ is luminance 96 luminance_readings.append(luminance) 97 print(f"### GRAYLEVELS ### The luminance for gray level {gray:.3f} is {luminance:.3f}") 98 99 gfit = GammaFitter(gray_levels, luminance_readings) 100 gfit.fit() 101 print(f"### GRAYLEVELS ### Monitor Gamma value: {gfit.gamma}") 102 gfit.plot() 103 104 if gamma is not None: 105 # Reset monitor gamma to default 106 self.win.setGamma(1) 107 return gfit 108 109 def close(self): 110 """ 111 Closes the PsychoPy window and releases the SpyderX device. 112 113 Ensures that all resources are properly cleaned up. 114 """ 115 self.win.close() 116 self.spyder.close() 117 118if __name__ == '__main__': 119 from cal_lib import SpyderX 120 libusb_path = r"C:\cancellami\vcpkg\installed\x64-windows\bin\libusb-1.0.dll" # Replace with actual path 121 spyder = SpyderX(libusb_path) 122 gl = GrayLevels(spyder) 123 gl.calibrate() 124 gammas = list() 125 gfit = gl.measure(num_levels=12) 126 gammas.append(gfit.gamma) 127 for i in range(0,3): 128 gfit = gl.measure(num_levels=12,wait_user=False) 129 gammas.append(gfit.gamma) 130 #gl.measure(gamma=np.mean(gammas),num_levels=12,wait_user=False) 131 gl.close() 132 print(f'Display Gamma avg: {np.mean(gammas)}')
6class GrayLevels: 7 """ 8 A class to measure and calibrate monitor gamma using a SpyderX colorimeter and PsychoPy. 9 10 This class allows calibration of the SpyderX device, measurement of luminance levels for 11 various grayscale levels, and fitting of the gamma curve using the provided luminance data. 12 13 Attributes: 14 spyder (object): An instance of the SpyderX class used for photometric measurements. 15 win (psychopy.visual.Window): The PsychoPy window for displaying gray levels. 16 bg_rect (psychopy.visual.Rect): A full-screen rectangle used to simulate background color. 17 """ 18 19 def __init__(self, spyder, fullscr=False): 20 """ 21 Initializes the GrayLevels class. 22 23 Args: 24 spyder (SpyderX): An initialized SpyderX object for luminance measurements. 25 fullscr (bool): Whether to open the PsychoPy window in full-screen mode. 26 Defaults to False. 27 """ 28 self.spyder = spyder 29 self.win = visual.Window([800, 600], color=[0, 0, 0], units="norm", waitBlanking=True, fullscr=fullscr) 30 self.bg_rect = visual.Rect(self.win, width=2, height=2, fillColor=[0, 0, 0], lineColor=None) 31 self.bg_rect.draw() 32 33 def calibrate(self): 34 """ 35 Calibrates the SpyderX device to measure the baseline luminance. 36 37 Displays instructions for the user to close the SpyderX sensor to perform a black 38 level calibration and waits for user confirmation before proceeding. 39 """ 40 print('### GRAYLEVELS ### Spyder calibration', end=' ') 41 instruction = visual.TextStim(self.win, 42 text="Close the SpyderX to measure baseline and press space to start.", 43 color=[1, 1, 1]) 44 self.bg_rect.draw() 45 instruction.draw() 46 self.win.flip() 47 event.waitKeys(keyList=["space"]) 48 49 self.spyder.calibrate() 50 print('DONE') 51 52 def measure(self, pause=1, gamma=None, num_levels=12,wait_user=True): 53 """ 54 Measures luminance levels across a range of grayscale values. 55 56 Displays a series of gray levels on the monitor and uses the SpyderX device to measure 57 the corresponding luminance values. The gamma curve is then fitted using the GammaFitter class. 58 59 Args: 60 pause (float): The time (in seconds) to pause after each gray level display to ensure stabilization. 61 Defaults to 1 second. 62 gamma (float, optional): A predefined gamma value to set for the monitor (for testing the linearity of the monitor after correction). If None, gamma remains unchanged. 63 Defaults to None. This parameter works on Windows only with one monitor or in mirror mode. 64 num_levels (int): The number of gray levels to display and measure. Defaults to 12. 65 wait_user (bool): wait for keypress to start 66 67 Returns: 68 GammaFitter: An instance of the GammaFitter class containing the gamma value and the fit result. 69 """ 70 if gamma is not None: 71 self.win.setGamma(gamma) 72 gray_levels = np.linspace(-1, 1, num_levels) 73 luminance_readings = [] 74 75 if wait_user: 76 instruction = visual.TextStim(self.win, 77 text="Position the SpyderX on the monitor and press space to start.", 78 color=[1, 1, 1]) 79 self.bg_rect.fillColor = [-1, -1, -1] 80 self.bg_rect.draw() 81 instruction.draw() 82 self.win.flip() 83 event.waitKeys(keyList=["space"]) 84 self.bg_rect.fillColor = [-1, -1, -1] 85 self.bg_rect.draw() 86 self.win.flip() 87 core.wait(pause) 88 89 for gray in gray_levels: 90 self.bg_rect.fillColor = [gray, gray, gray] 91 self.bg_rect.draw() 92 self.win.flip() 93 core.wait(pause) # Wait for the screen to stabilize 94 95 XYZ = self.spyder.measure() 96 luminance = XYZ[1] # Y component of XYZ is luminance 97 luminance_readings.append(luminance) 98 print(f"### GRAYLEVELS ### The luminance for gray level {gray:.3f} is {luminance:.3f}") 99 100 gfit = GammaFitter(gray_levels, luminance_readings) 101 gfit.fit() 102 print(f"### GRAYLEVELS ### Monitor Gamma value: {gfit.gamma}") 103 gfit.plot() 104 105 if gamma is not None: 106 # Reset monitor gamma to default 107 self.win.setGamma(1) 108 return gfit 109 110 def close(self): 111 """ 112 Closes the PsychoPy window and releases the SpyderX device. 113 114 Ensures that all resources are properly cleaned up. 115 """ 116 self.win.close() 117 self.spyder.close()
A class to measure and calibrate monitor gamma using a SpyderX colorimeter and PsychoPy.
This class allows calibration of the SpyderX device, measurement of luminance levels for various grayscale levels, and fitting of the gamma curve using the provided luminance data.
Attributes: spyder (object): An instance of the SpyderX class used for photometric measurements. win (psychopy.visual.Window): The PsychoPy window for displaying gray levels. bg_rect (psychopy.visual.Rect): A full-screen rectangle used to simulate background color.
19 def __init__(self, spyder, fullscr=False): 20 """ 21 Initializes the GrayLevels class. 22 23 Args: 24 spyder (SpyderX): An initialized SpyderX object for luminance measurements. 25 fullscr (bool): Whether to open the PsychoPy window in full-screen mode. 26 Defaults to False. 27 """ 28 self.spyder = spyder 29 self.win = visual.Window([800, 600], color=[0, 0, 0], units="norm", waitBlanking=True, fullscr=fullscr) 30 self.bg_rect = visual.Rect(self.win, width=2, height=2, fillColor=[0, 0, 0], lineColor=None) 31 self.bg_rect.draw()
Initializes the GrayLevels class.
Args: spyder (SpyderX): An initialized SpyderX object for luminance measurements. fullscr (bool): Whether to open the PsychoPy window in full-screen mode. Defaults to False.
33 def calibrate(self): 34 """ 35 Calibrates the SpyderX device to measure the baseline luminance. 36 37 Displays instructions for the user to close the SpyderX sensor to perform a black 38 level calibration and waits for user confirmation before proceeding. 39 """ 40 print('### GRAYLEVELS ### Spyder calibration', end=' ') 41 instruction = visual.TextStim(self.win, 42 text="Close the SpyderX to measure baseline and press space to start.", 43 color=[1, 1, 1]) 44 self.bg_rect.draw() 45 instruction.draw() 46 self.win.flip() 47 event.waitKeys(keyList=["space"]) 48 49 self.spyder.calibrate() 50 print('DONE')
Calibrates the SpyderX device to measure the baseline luminance.
Displays instructions for the user to close the SpyderX sensor to perform a black level calibration and waits for user confirmation before proceeding.
52 def measure(self, pause=1, gamma=None, num_levels=12,wait_user=True): 53 """ 54 Measures luminance levels across a range of grayscale values. 55 56 Displays a series of gray levels on the monitor and uses the SpyderX device to measure 57 the corresponding luminance values. The gamma curve is then fitted using the GammaFitter class. 58 59 Args: 60 pause (float): The time (in seconds) to pause after each gray level display to ensure stabilization. 61 Defaults to 1 second. 62 gamma (float, optional): A predefined gamma value to set for the monitor (for testing the linearity of the monitor after correction). If None, gamma remains unchanged. 63 Defaults to None. This parameter works on Windows only with one monitor or in mirror mode. 64 num_levels (int): The number of gray levels to display and measure. Defaults to 12. 65 wait_user (bool): wait for keypress to start 66 67 Returns: 68 GammaFitter: An instance of the GammaFitter class containing the gamma value and the fit result. 69 """ 70 if gamma is not None: 71 self.win.setGamma(gamma) 72 gray_levels = np.linspace(-1, 1, num_levels) 73 luminance_readings = [] 74 75 if wait_user: 76 instruction = visual.TextStim(self.win, 77 text="Position the SpyderX on the monitor and press space to start.", 78 color=[1, 1, 1]) 79 self.bg_rect.fillColor = [-1, -1, -1] 80 self.bg_rect.draw() 81 instruction.draw() 82 self.win.flip() 83 event.waitKeys(keyList=["space"]) 84 self.bg_rect.fillColor = [-1, -1, -1] 85 self.bg_rect.draw() 86 self.win.flip() 87 core.wait(pause) 88 89 for gray in gray_levels: 90 self.bg_rect.fillColor = [gray, gray, gray] 91 self.bg_rect.draw() 92 self.win.flip() 93 core.wait(pause) # Wait for the screen to stabilize 94 95 XYZ = self.spyder.measure() 96 luminance = XYZ[1] # Y component of XYZ is luminance 97 luminance_readings.append(luminance) 98 print(f"### GRAYLEVELS ### The luminance for gray level {gray:.3f} is {luminance:.3f}") 99 100 gfit = GammaFitter(gray_levels, luminance_readings) 101 gfit.fit() 102 print(f"### GRAYLEVELS ### Monitor Gamma value: {gfit.gamma}") 103 gfit.plot() 104 105 if gamma is not None: 106 # Reset monitor gamma to default 107 self.win.setGamma(1) 108 return gfit
Measures luminance levels across a range of grayscale values.
Displays a series of gray levels on the monitor and uses the SpyderX device to measure the corresponding luminance values. The gamma curve is then fitted using the GammaFitter class.
Args: pause (float): The time (in seconds) to pause after each gray level display to ensure stabilization. Defaults to 1 second. gamma (float, optional): A predefined gamma value to set for the monitor (for testing the linearity of the monitor after correction). If None, gamma remains unchanged. Defaults to None. This parameter works on Windows only with one monitor or in mirror mode. num_levels (int): The number of gray levels to display and measure. Defaults to 12. wait_user (bool): wait for keypress to start
Returns: GammaFitter: An instance of the GammaFitter class containing the gamma value and the fit result.
110 def close(self): 111 """ 112 Closes the PsychoPy window and releases the SpyderX device. 113 114 Ensures that all resources are properly cleaned up. 115 """ 116 self.win.close() 117 self.spyder.close()
Closes the PsychoPy window and releases the SpyderX device.
Ensures that all resources are properly cleaned up.