From 4a9c230c2604ae63e91224eee5b1a141cd7bb20c Mon Sep 17 00:00:00 2001 From: Shinya Ito Date: Wed, 21 Feb 2024 10:43:40 -0800 Subject: [PATCH] Making physical spacing optional for drifting gratings + faster separable FilterNet (#346) * Making physical_spacing of moving gratings optional + faster separable FilterNet by ignoring zeros in the kernel. * Changing the variable name + adding documentation 'physical_spacing' to 'degrees_per_pixel'. --- bmtk/simulator/filternet/filtersimulator.py | 2 +- bmtk/simulator/filternet/lgnmodel/cursor.py | 5 ++++- bmtk/simulator/filternet/lgnmodel/movie.py | 15 ++++++++------- docs/autodocs/source/filternet.rst | 4 +++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/bmtk/simulator/filternet/filtersimulator.py b/bmtk/simulator/filternet/filtersimulator.py index cd08e2c3b..dbd3025e7 100644 --- a/bmtk/simulator/filternet/filtersimulator.py +++ b/bmtk/simulator/filternet/filtersimulator.py @@ -73,7 +73,7 @@ def add_movie(self, movie_type, params): elif movie_type == 'graiting': init_params = FilterSimulator.find_params(['row_size', 'col_size', 'frame_rate'], **params) - create_params = FilterSimulator.find_params(['gray_screen_dur', 'cpd', 'temporal_f', 'theta', 'contrast'], + create_params = FilterSimulator.find_params(['gray_screen_dur', 'cpd', 'temporal_f', 'theta', 'contrast', 'degrees_per_pixel'], **params) create_params['gray_screen_dur'] /= 1000.0 diff --git a/bmtk/simulator/filternet/lgnmodel/cursor.py b/bmtk/simulator/filternet/lgnmodel/cursor.py index 113f3bd87..95c2958d3 100644 --- a/bmtk/simulator/filternet/lgnmodel/cursor.py +++ b/bmtk/simulator/filternet/lgnmodel/cursor.py @@ -193,7 +193,10 @@ def evaluate(self, threshold=0): # Convolve every frame in the movie with the spatial filter. First find the range of rows (range min and max) # and columns in the filter that are above threshold, that way only portion of movie/filter are multiplied # together - nonzero_inds = np.where(np.abs(full_spatial_kernel[0, :, :]) >= threshold) + + # Using > instead of >= makes the code faster if there are many zeros in the + # spatial kernel. This will not affect the results. + nonzero_inds = np.where(np.abs(full_spatial_kernel[0, :, :]) > threshold) rm, rM = nonzero_inds[0].min(), nonzero_inds[0].max() cm, cM = nonzero_inds[1].min(), nonzero_inds[1].max() convolution_answer_sep_spatial = (self.movie.data[:, rm:rM+1, cm:cM+1] * diff --git a/bmtk/simulator/filternet/lgnmodel/movie.py b/bmtk/simulator/filternet/lgnmodel/movie.py index c446b6ff8..a08b85f20 100755 --- a/bmtk/simulator/filternet/lgnmodel/movie.py +++ b/bmtk/simulator/filternet/lgnmodel/movie.py @@ -253,7 +253,7 @@ def __init__(self, row_size, col_size, frame_rate=1000.0): self.frame_rate = float(frame_rate) # in Hz def create_movie(self, t_min=0, t_max=1, gray_screen_dur=0, cpd=0.05, temporal_f=4, theta=45, - phase=0., contrast=1.0, row_size_new=None, col_size_new=None): + phase=0., contrast=1.0, degrees_per_pixel=None, row_size_new=None, col_size_new=None): """Create the grating movie with the desired parameters :param t_min: start time in seconds @@ -271,9 +271,10 @@ def create_movie(self, t_min=0, t_max=1, gray_screen_dur=0, cpd=0.05, temporal_f assert contrast <= 1, "Contrast must be <= 1" assert contrast > 0, "Contrast must be > 0" - physical_spacing = 1.0 / (float(cpd) * 10) # To make sure no aliasing occurs - self.row_range = np.linspace(0, self.row_size, int(self.row_size/physical_spacing), endpoint=True) - self.col_range = np.linspace(0, self.col_size, int(self.col_size/physical_spacing), endpoint=True) + if degrees_per_pixel is None: # default behavior when not specified + degrees_per_pixel = 1.0 / (float(cpd) * 10) # To make sure no aliasing occurs + self.row_range = np.linspace(0, self.row_size, int(self.row_size/degrees_per_pixel), endpoint=True) + self.col_range = np.linspace(0, self.col_size, int(self.col_size/degrees_per_pixel), endpoint=True) numberFramesNeeded = int(round(self.frame_rate * (t_max - gray_screen_dur))) + 1 time_range = np.linspace(0, t_max - gray_screen_dur, numberFramesNeeded, endpoint=True) @@ -316,9 +317,9 @@ def create_movie(self, t_looming=1, gray_screen_dur=0.5): :param t_looming: duration of time looming :param gray_screen_dur: """ - physical_spacing = 1.0 # To make sure no aliasing occurs - self.row_range = np.linspace(0, self.row_size, int(self.row_size/physical_spacing), endpoint=True) - self.col_range = np.linspace(0, self.col_size, int(self.col_size/physical_spacing), endpoint=True) + degrees_per_pixel = 1.0 # To make sure no aliasing occurs + self.row_range = np.linspace(0, self.row_size, int(self.row_size/degrees_per_pixel), endpoint=True) + self.col_range = np.linspace(0, self.col_size, int(self.col_size/degrees_per_pixel), endpoint=True) loomingFramesNeeded = int(round(self.frame_rate * t_looming)) grayScreenFrames = int(round(self.frame_rate * gray_screen_dur)) time_range = np.linspace(0, t_looming, loomingFramesNeeded, endpoint=True) diff --git a/docs/autodocs/source/filternet.rst b/docs/autodocs/source/filternet.rst index 254aa7042..01d82b87e 100644 --- a/docs/autodocs/source/filternet.rst +++ b/docs/autodocs/source/filternet.rst @@ -68,7 +68,8 @@ Plays a drifting grating across the screen "temporal_f": 4.0, "contrast": 0.8, "theta": 45.0, - "phase": 0.0 + "phase": 0.0, + "degrees_per_pixel": 1.0 } } @@ -79,6 +80,7 @@ Plays a drifting grating across the screen * theta: orientation angle, in degrees (default: 45.0) * phase: temporal phase, in degrees (default: 0.0) * contrast: the maximum constrast, must be between 0 and 1.0 (default: 1.0) +* degrees_per_pixel: sampling pitch of the movie in degrees per pixel (default: 1 / (cpd * 10)) Full Field Flash