Contribrute a stimulus (set)#
stimupy contains both a wide range of generalized stimulus-functions
that can generate many parameterizated stimulus images,
and several complete sets of stimuli
that all come from the same source.
Development, organization and maintenance of these sets can be arduous,
and we are committed to providing a high-quality and well-tested suite of functions.
Thus, stimupy is a curated and maintained library of stimuli
(stimulus-functions, to be precise),
not a repository or platform for uploading stimuli.
We welcome contributions to this library of stimuli,
and stimulus sets, from the literature.
Add a stimulus set#
If you wish to contribute a complete set of stimuli from a single paper, please follow these instructions for organizing and formatting the code.
A paper should have the following features:
Each paper is identified using a
paper_key: a cite-key like indicator, usually some combination of author names/initials and publication date.For
modelfestwe made an exception.
All stimuli belonging a
papershould be in a single python module, i.e.,.pycode-file; this file is found understimupy/papers/<paper_key>.pyOnly stimuli that
stimupyfunctionality should be included in the<paper_key>.pyWe have made a select few exceptions to this rule
Each stimulus should be its own function
Each function should only take a
ppdargument; it should replicate the exact size and geometry of the stimulus, allowing only the resolution (i.e., pixels per degree) to be changed. This argument should have an actual default value, which corresponds to theppdused in the original project.Thus, different parameterizations of the “same” stimulus (e.g. different geometries, polarities, etc.) should be separate functions.
As a result, each function should run without any arguments, e.g.
stim = stimupy.papers.RHS2007.WE_thick()
The output stim-
dicts must contain at least theimg, but should also contain relevant stimulus parameters.The output stim-
dicts may contain additional information (keys), such as experimental results, if they do not take up too much space.Each function should have a NumPy-style docstring, which at least documents the
ppdargument and the returned dict as follows:""" <short summary of stimulus function> Parameters ---------- ppd : int Resolution of stimulus in pixels per degree. (default: 32) Returns ------- dict of str dict with the stimulus (key: "img") and additional keys containing stimulus parameters """
Ideally it also provides detailed information of which exact stimulus it is, e.g., through reference to a specific figure in the original paper.
The paper-module should also contain an
__all__attribute, which contains a list of all function names of the stimuli in the paper. Anything in here is considered a stimulus, anything not in here will not be available outside the paper module itself.The paper-module should have a NumPy style docstring as well, documenting at least the full bibliographical reference for the source of the stimuli.
An example#
Tip
This file is included as stimupy/papers/example.py
and can be imported as stimupy.papers.example
"""Example paper module (citation information here)
This set only serves as an example / template
for how to set up a stimulus-set-module
Attributes
----------
__all__ (list of str): list of all stimulus-functions,
these are exported by this module when executing
>>> from stimupy.papers.example import *
References
----------
reference information here
"""
from stimupy.components import combine_masks, draw_regions, shapes
# Define original size resolution parameters
VISUAL_SIZE = (10, 12)
PPD = 10
__all__ = [
"my_bullseye",
"my_inverse_bullseye",
]
# Helper function:
def bullseye_geometry(ppd=PPD):
"""Helper function to create the bullseye geometry
This function itself is not a stimulus,
and will not be shown in `stimupy.papers.my_paper`
Parameters
----------
ppd : int
Resolution of stimulus in pixels per degree. (default: 10)
"""
# Create center (target) disc:
disc = shapes.disc(
visual_size=VISUAL_SIZE, ppd=ppd, radius=2, intensity_disc=0.5, intensity_background=0.5
)
# Create first ring, white:
ring_1 = shapes.ring(
visual_size=VISUAL_SIZE, ppd=ppd, radii=(2, 3), intensity_ring=1, intensity_background=0.5
)
# Create second ring, black:
ring_2 = shapes.ring(
visual_size=VISUAL_SIZE, ppd=ppd, radii=(3, 4), intensity_ring=0, intensity_background=0.5
)
bullseye_mask = combine_masks(disc["ring_mask"], ring_1["ring_mask"], ring_2["ring_mask"])
return bullseye_mask
# New stimulus function:
def my_bullseye(ppd=PPD):
"""My bullseye stimulus: grey on white on black
Parameters
----------
ppd : int
Resolution of stimulus in pixels per degree. (default: 32)
Returns
-------
dict of str
dict with the stimulus (key: "img")
and additional keys containing stimulus parameters
"""
# Call geometry helper function
bullseye_mask = bullseye_geometry(ppd=ppd)
bullseye_img = draw_regions(
mask=bullseye_mask, intensities=[0.5, 1, 0], intensity_background=0.5
)
# Package into stim-dict, adding parameter information
stim = {
"img": bullseye_img,
"visual_size": VISUAL_SIZE,
"ppd": ppd,
"radii": (2, 3, 4),
"intensities": (0.5, 1, 0),
"intensity_background": 0.5,
}
# Output
return stim
# Second stimulus function:
def my_inverse_bullseye(ppd=PPD):
"""My other bullseye stimulus: grey on black on white
Parameters
----------
ppd : int
Resolution of stimulus in pixels per degree. (default: 10)
Returns
-------
dict of str
dict with the stimulus (key: "img")
and additional keys containing stimulus parameters
"""
# Call geometry helper function
bullseye_mask = bullseye_geometry(ppd=ppd)
bullseye_img = draw_regions(
mask=bullseye_mask, intensities=[0.5, 0, 1], intensity_background=0.5
)
# Package into stim-dict, adding parameter information
stim = {
"img": bullseye_img,
"visual_size": VISUAL_SIZE,
"ppd": ppd,
"radii": (2, 3, 4),
"intensities": (0.5, 0, 1),
"intensity_background": 0.5,
"note": "Here is some additional information: I like this stimulus!",
}
# Output
return stim
Testing#
For each paper, there is a corresponding tests/papers/<paper_key>.py file
containing regression tests code.
These tests generate each stimulus from the paper-module,
and compares the produce img (and maskss) to a hash of the img (and masks).
The hashed imgs for all stimuli is stored in a corresponding
tests/papers/<paper_key>.json JSON file.
The JSON files can be generated by running
the tests/papers.gen_ground_truth.py script.
See stimupy/tests/papers/paper_tests_template.py for a template;
to create this regression test suite for your own paper,
replace paper_key with the <paper_key>
Additional features#
The paper-modules already included in stimupy have a couple quality-of-life features
which can be copied into a new module.
A function to generate all stimuli in the set:
def gen_all(ppd=PPD, skip=False):
stims = {} # save the stimulus-dicts in a larger dict, with name as key
for stim_name in __all__:
print(f"Generating RHS2007.{stim_name}")
# Get a reference to the actual function
func = globals()[stim_name]
try:
stim = func(ppd=ppd, pad=pad)
# Accumulate
stims[stim_name] = stim
except NotImplementedError as e:
if not skip:
raise e
# Skip stimuli that aren't implemented
print("-- not implemented")
pass
return stims
A code block that gets executed when the .py file is run as a script
(rather than imported), and shows an overview of all stimuli in this set:
if __name__ == "__main__":
from stimupy.utils import plot_stimuli
stims = gen_all(pad=True, skip=True)
plot_stimuli(stims, mask=True)
Edit or add a stimulus function#
To contribute a stimulus function, add it to the corresponding module
Contributing back to stimupy#
Edit code/documentation
Commit & Push changes to your fork
We use conventional commit messages. For bugfixes, please start your commit message(s) with
fix: .... For add features (e.g., stimuli, set), please start your commit message(s) withfeat: ....
Pull request from your fork to our repository
GitHub Actions will automatically run tests and linters
If linters fail, run
black,pyupgradeandflake8– either separately or all together throughpre-commit:pre-commit run --all-files
Changes will be reviewed by one of the maintainers