def whites_illusion_gil(shape, ppd, contrast, frequency, mean_lum=.5, start='low'): """ Create a version of White's illusion on a square wave, in the style used by Gilchrist (2006, p. 281) Parameters ---------- shape : tuple of 2 numbers The shape of the stimulus in degrees of visual angle. (y,x) ppd : number the number of pixels in one degree of visual angle contrast : number in [0,1] the contrast of the grating, defined as (max_luminance - min_luminance) / mean_luminance frequency : number the spatial frequency of the wave in cycles per degree mean_lum : number the mean luminance of the grating, i.e. (max_lum + min_lum) / 2. The average luminance of the actual stimulus can differ slightly from this value if the stimulus is not an integer of cycles big. start : string in ['high', 'low'] (optional) specifies if the wave starts with a high or low value. Default is 'high'. Returns ------- stim : 2D ndarray the stimulus References ---------- Gilchrist A (2006). Seeing Black and White. New York, New York, USA: Oxford University Press. """ stim = square_wave(shape, ppd, contrast, frequency, mean_lum, 'half', start) half_cycle = int(degrees_to_pixels(1. / frequency / 2, ppd) + .5) on_dark_idx = [i for i in range(int(half_cycle * 2.5), int(stim.shape[1] - half_cycle * .5)) if stim[0, i] < mean_lum] on_light_idx = [i for i in range(int(half_cycle * 1.5), int(stim.shape[1] - half_cycle * 1.5)) if stim[0, i] > mean_lum] stim[stim.shape[0] / 5: stim.shape[0] / 5 * 2, on_light_idx] = mean_lum stim[stim.shape[0] / 5 * 3: stim.shape[0] / 5 * 4, on_dark_idx] = mean_lum # randomize border cutoff max_cut = stim.shape[0] / 10 bg = stim[0, half_cycle] for start_idx in range(0 if start is 'low' else half_cycle, stim.shape[1] - half_cycle, 2 * half_cycle): stim[0 : np.random.randint(max_cut), start_idx : start_idx + half_cycle] = bg stim[stim.shape[0] - np.random.randint(max_cut):, start_idx : start_idx + half_cycle] = bg return stim
def whites_illusion_bmcc(shape, ppd, contrast, frequency, mean_lum=.5, start='high'): """ Create a version of White's illusion on a square wave, in the style used by Blakeslee and McCourt (1999). Parameters ---------- shape : tuple of 2 numbers The shape of the stimulus in degrees of visual angle. (y,x) ppd : number the number of pixels in one degree of visual angle contrast : number in [0,1] the contrast of the grating, defined as (max_luminance - min_luminance) / mean_luminance frequency : number the spatial frequency of the wave in cycles per degree mean_lum : number the mean luminance of the grating, i.e. (max_lum + min_lum) / 2. The average luminance of the actual stimulus can differ slightly from this value if the stimulus is not an integer of cycles big. start : string in ['high', 'low'] (optional) specifies if the wave starts with a high or low value. Default is 'high'. Returns ------- stim : 2D ndarray the stimulus References ---------- Blakeslee B, McCourt ME (1999). A multiscale spatial filtering account of the White effect, simultaneous brightness contrast and grating induction. Vision research 39(26):4361-77. """ stim = square_wave(shape, ppd, contrast, frequency, mean_lum, 'full', start) half_cycle = int(degrees_to_pixels(1. / frequency / 2, ppd) + .5) stim[stim.shape[0] / 3:stim.shape[0] / 3 * 2, stim.shape[1] / 2 - 2 * half_cycle:stim.shape[1] / 2 - half_cycle] = mean_lum stim[stim.shape[0] / 3:stim.shape[0] / 3 * 2, stim.shape[1] / 2 + half_cycle:stim.shape[1] / 2 + 2 * half_cycle] = mean_lum return stim
def __init__(self, spatial_scales=3. / 2. ** np.arange(-2,5), orientations=np.arange(0,180,30), pixels_per_degree=30, weights_slope=.1): """ Create an ODOG model instance. Parameters ---------- spatial_scales : list of numbers, optional the spatial frequencies of the filters. Each number corresponds to one DOG filter and specifies the center size, i.e. the distance from zero-crossing to zero-crossing, in degrees of visual angle. Default is an octave range from 12 to 3/16. orientations : list of numbers, optional the orientations of the filters in degrees. 0 degrees is a vertical filter, 90 degrees is a horizontal one. Default is 30deg steps from 0 to 150. pixels_per_degree : number, optional specifies how many pixels fit within one degree of visual angle in the experimental setup. Default is 30. weights_slope : number, optional the slope of the log-log function that relates a filter spatial frequency to its weight in the summation. Default is 0.1. """ # determine standard deviation from zero_crossing distance, # assuming that the surround sigma is 2 times center sigma. center_sigmas = np.array(spatial_scales) center_sigmas = (center_sigmas / 2.) / (2 * np.sqrt(2 * np.log(2) / 3)) center_sigmas = degrees_to_pixels(center_sigmas, pixels_per_degree) self.multiscale_filters = [] for angle in orientations: scale_filters = [] for center_sigma in center_sigmas: scale_filters.append( difference_of_gaussians((center_sigma, 2 * center_sigma), center_sigma, angle)) self.multiscale_filters.append(scale_filters) self.scale_weights = np.exp(weights_slope * np.log(spatial_scales)) self.spatial_scales = spatial_scales self.orientations = orientations
def whites_illusion_bmcc(shape, ppd, contrast, frequency, mean_lum=.5, start='high'): """ Create a version of White's illusion on a square wave, in the style used by Blakeslee and McCourt (1999). Parameters ---------- shape : tuple of 2 numbers The shape of the stimulus in degrees of visual angle. (y,x) ppd : number the number of pixels in one degree of visual angle contrast : number in [0,1] the contrast of the grating, defined as (max_luminance - min_luminance) / mean_luminance frequency : number the spatial frequency of the wave in cycles per degree mean_lum : number the mean luminance of the grating, i.e. (max_lum + min_lum) / 2. The average luminance of the actual stimulus can differ slightly from this value if the stimulus is not an integer of cycles big. start : string in ['high', 'low'] (optional) specifies if the wave starts with a high or low value. Default is 'high'. Returns ------- stim : 2D ndarray the stimulus References ---------- Blakeslee B, McCourt ME (1999). A multiscale spatial filtering account of the White effect, simultaneous brightness contrast and grating induction. Vision research 39(26):4361-77. """ stim = square_wave(shape, ppd, contrast, frequency, mean_lum, 'full', start) half_cycle = int(degrees_to_pixels(1. / frequency / 2, ppd) + .5) stim[stim.shape[0] / 3: stim.shape[0] / 3 * 2, stim.shape[1] / 2 - 2 * half_cycle: stim.shape[1] / 2 - half_cycle] = mean_lum stim[stim.shape[0] / 3: stim.shape[0] / 3 * 2, stim.shape[1] / 2 + half_cycle: stim.shape[1] / 2 + 2 * half_cycle] = mean_lum return stim
def whites_illusion_gil(shape, ppd, contrast, frequency, mean_lum=.5, start='low'): """ Create a version of White's illusion on a square wave, in the style used by Gilchrist (2006, p. 281) Parameters ---------- shape : tuple of 2 numbers The shape of the stimulus in degrees of visual angle. (y,x) ppd : number the number of pixels in one degree of visual angle contrast : number in [0,1] the contrast of the grating, defined as (max_luminance - min_luminance) / mean_luminance frequency : number the spatial frequency of the wave in cycles per degree mean_lum : number the mean luminance of the grating, i.e. (max_lum + min_lum) / 2. The average luminance of the actual stimulus can differ slightly from this value if the stimulus is not an integer of cycles big. start : string in ['high', 'low'] (optional) specifies if the wave starts with a high or low value. Default is 'high'. Returns ------- stim : 2D ndarray the stimulus References ---------- Gilchrist A (2006). Seeing Black and White. New York, New York, USA: Oxford University Press. """ stim = square_wave(shape, ppd, contrast, frequency, mean_lum, 'half', start) half_cycle = int(degrees_to_pixels(1. / frequency / 2, ppd) + .5) on_dark_idx = [ i for i in range(int(half_cycle * 2.5), int(stim.shape[1] - half_cycle * .5)) if stim[0, i] < mean_lum ] on_light_idx = [ i for i in range(int(half_cycle * 1.5), int(stim.shape[1] - half_cycle * 1.5)) if stim[0, i] > mean_lum ] stim[stim.shape[0] / 5:stim.shape[0] / 5 * 2, on_light_idx] = mean_lum stim[stim.shape[0] / 5 * 3:stim.shape[0] / 5 * 4, on_dark_idx] = mean_lum # randomize border cutoff max_cut = stim.shape[0] / 10 bg = stim[0, half_cycle] for start_idx in range(0 if start is 'low' else half_cycle, stim.shape[1] - half_cycle, 2 * half_cycle): stim[0:np.random.randint(max_cut), start_idx:start_idx + half_cycle] = bg stim[stim.shape[0] - np.random.randint(max_cut):, start_idx:start_idx + half_cycle] = bg return stim
def square_wave(shape, ppd, contrast, frequency, mean_lum=.5, period='ignore', start='high'): """ Create a horizontal square wave of given spatial frequency. Parameters ---------- shape : tuple of 2 numbers The shape of the stimulus in degrees of visual angle. (y,x) ppd : number the number of pixels in one degree of visual angle contrast : number in [0,1] the contrast of the grating, defined as (max_luminance - min_luminance) / mean_luminance frequency : number the spatial frequency of the wave in cycles per degree mean_lum : number the mean luminance of the grating, i.e. (max_lum + min_lum) / 2. The average luminance of the actual stimulus can differ slightly from this value if the stimulus is not an integer of cycles big. period : string in ['ignore', 'full', 'half'] (optional) specifies if the period of the wave is taken into account when determining exact stimulus dimensions. 'ignore' simply converts degrees to pixesl 'full' rounds down to garuantee a full period 'half' adds a half period to the size 'full' would yield. Default is 'ignore'. start : string in ['high', 'low'] (optional) specifies if the wave starts with a high or low value. Default is 'high'. Returns ------- stim : 2D ndarray the square wave stimulus """ if not period in ['ignore', 'full', 'half']: raise TypeError('size not understood: %s' % period) if not start in ['high', 'low']: raise TypeError('start value not understood: %s' % start) if frequency > ppd / 2: raise ValueError('The frequency is limited to 1/2 cycle per pixel.') shape = degrees_to_pixels(np.array(shape), ppd).astype(int) pixels_per_cycle = int(degrees_to_pixels(1. / frequency / 2, ppd) + .5) * 2 if period is 'full': shape[1] = shape[1] / pixels_per_cycle * pixels_per_cycle elif period is 'half': shape[1] = shape[1] / pixels_per_cycle * pixels_per_cycle + \ pixels_per_cycle / 2 diff = type(mean_lum)(contrast * mean_lum) high = mean_lum + diff low = mean_lum - diff stim = np.ones(shape) * (low if start is 'high' else high) index = [ i + j for i in range(pixels_per_cycle / 2) for j in range(0, shape[1], pixels_per_cycle) if i + j < shape[1] ] stim[:, index] = low if start is 'low' else high return stim
def square_wave(shape, ppd, contrast, frequency, mean_lum=.5, period='ignore', start='high'): """ Create a horizontal square wave of given spatial frequency. Parameters ---------- shape : tuple of 2 numbers The shape of the stimulus in degrees of visual angle. (y,x) ppd : number the number of pixels in one degree of visual angle contrast : number in [0,1] the contrast of the grating, defined as (max_luminance - min_luminance) / mean_luminance frequency : number the spatial frequency of the wave in cycles per degree mean_lum : number the mean luminance of the grating, i.e. (max_lum + min_lum) / 2. The average luminance of the actual stimulus can differ slightly from this value if the stimulus is not an integer of cycles big. period : string in ['ignore', 'full', 'half'] (optional) specifies if the period of the wave is taken into account when determining exact stimulus dimensions. 'ignore' simply converts degrees to pixesl 'full' rounds down to garuantee a full period 'half' adds a half period to the size 'full' would yield. Default is 'ignore'. start : string in ['high', 'low'] (optional) specifies if the wave starts with a high or low value. Default is 'high'. Returns ------- stim : 2D ndarray the square wave stimulus """ if not period in ['ignore', 'full', 'half']: raise TypeError('size not understood: %s' % period) if not start in ['high', 'low']: raise TypeError('start value not understood: %s' % start) if frequency > ppd / 2: raise ValueError('The frequency is limited to 1/2 cycle per pixel.') shape = degrees_to_pixels(np.array(shape), ppd).astype(int) pixels_per_cycle = int(degrees_to_pixels(1. / frequency / 2, ppd) + .5) * 2 if period is 'full': shape[1] = shape[1] / pixels_per_cycle * pixels_per_cycle elif period is 'half': shape[1] = shape[1] / pixels_per_cycle * pixels_per_cycle + \ pixels_per_cycle / 2 diff = type(mean_lum)(contrast * mean_lum) high = mean_lum + diff low = mean_lum - diff stim = np.ones(shape) * (low if start is 'high' else high) index = [i + j for i in range(pixels_per_cycle / 2) for j in range(0, shape[1], pixels_per_cycle) if i + j < shape[1]] stim[:, index] = low if start is 'low' else high return stim