diff --git a/README.md b/README.md index a661b298..a2016b28 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,24 @@ -# Stimuli +# Stimupy Contains submodules for -- creating different brightness illusions ([illusions](stimuli/illusions/)) -- replicating illusions in certain published papers ([papers](stimuli/papers/)) -- creating 2D patterns or renderings of 3D checkerboards with transparent -layers covering part of the image ([transparency](stimuli/transparency/)) -- various functions that calculate contrast metrics ([contrast_metrics](stimuli/contrest_metrics/)) -- some helper functions for padding, resizing, computing Munsell values, and -converting pixel values to degrees of visual angle ([utils](stimuli/utils/)) -- (creating different random and deterministic textures ([texture](stimuli/texture/)) [yet to be fixed]) +- drawing basic visual stimulus components ([components](stimupy/components/)) +- creating different (brightness) illusions ([illusions](stimupy/illusions/)) +- replicating illusions in certain published papers ([papers](stimupy/papers/)) +converting pixel values to degrees of visual angle ([utils](stimupy/utils/)) -For details, please refer to the source directory (stimuli/), the respective subdirectories and the docstrings. +For details, please refer to the source directory (stimupy/), +the respective subdirectories and the docstrings. ## Dependencies -- Required: numpy, matplotlib, PIL -- Optional: - - [PovRay](http://www.povray.org/) (to render variegated checkerboards - submodule transparency.CheckerboardFactory]) - - rpy2 and R (to render textures with specific spatial properties - submodule texture) - +- Required: numpy, scipy, matplotlib + ## Installation First clone the repository via ```shell script git clone https://github.com/computational-psychology/stimuli.git -``` +``` Then run `pip install .` at the root of the repository. The repository may then be removed again. @@ -34,12 +28,10 @@ This makes changes to files immediately usable, rather than having to reinstall the package after every change. ## Importing -To use in your own code, import the modules. See READMEs in stimuli/ for example usages. +To use in your own code, import (from) the modules. ```python -from stimuli import lightness -from stimuli import illusions -from stimuli.transparency import TextureFactory -from stimuli.transparency import CheckerboardFactory -from stimuli import contrast_metrics as cm -from stimuli import utils +from stimupy import components +from stimupy import illusions +from stimupy import papers +from stimupy import utils ``` diff --git a/demo/basic_shapes.ipynb b/demo/basic_shapes.ipynb index 08e26b2a..32864821 100644 --- a/demo/basic_shapes.ipynb +++ b/demo/basic_shapes.ipynb @@ -1,325 +1,325 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Basic shapes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rectangle" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.shapes import rectangle\n", - "help(rectangle)\n", - "\n", - "# Define widgets\n", - "w_height = iw.IntSlider(value=4, min=1, max=10, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=4, min=1, max=10, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_rect_height = iw.IntSlider(value=2, min=1, max=10, description=\"height [deg]\")\n", - "w_rect_width = iw.IntSlider(value=2, min=1, max=10, description=\"width [deg]\")\n", - "\n", - "w_rect_posx = iw.FloatSlider(value=0.0, min=-5.0, max=5.0, description=\"horz. position\")\n", - "w_rect_posy = iw.FloatSlider(value=0.0, min=-5.0, max=5.0, description=\"vert. position\")\n", - "\n", - "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_im_size = iw.HBox([w_height, w_width, w_ppd])\n", - "b_rect_size = iw.HBox([w_rect_height, w_rect_width])\n", - "b_post = iw.HBox([w_rect_posx, w_rect_posy])\n", - "b_intensities = iw.HBox([w_intensities])\n", - "ui = iw.VBox([b_im_size, b_rect_size, b_post, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_rect(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " rect_height=None,\n", - " rect_width=None,\n", - " pos_x=0.0,\n", - " pos_y=0.0,\n", - " orientation='horizontal',\n", - " intensities=(0.0, 1.0)\n", - "):\n", - " stim = rectangle(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " rectangle_size=(rect_height, rect_width),\n", - " rectangle_position=(pos_y, pos_x),\n", - " intensity_rectangle=intensities[1],\n", - " intensity_background=intensities[0]\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_rect,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"rect_height\": w_rect_height,\n", - " \"rect_width\": w_rect_width,\n", - " \"pos_x\": w_rect_posx,\n", - " \"pos_y\": w_rect_posy,\n", - " \"intensities\": w_intensities,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Triangle" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.shapes import triangle\n", - "help(triangle)\n", - "\n", - "# Define widgets\n", - "w_height = iw.IntSlider(value=4, min=1, max=10, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=4, min=1, max=10, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_im_size = iw.HBox([w_height, w_width, w_ppd])\n", - "b_intensities = iw.HBox([w_intensities])\n", - "ui = iw.VBox([b_im_size, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_triangle(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " intensities=(0.0, 1.0)\n", - "):\n", - " stim = triangle(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " intensity_triangle=intensities[1],\n", - " intensity_background=intensities[0]\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_triangle,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"intensities\": w_intensities,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parallelogram" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.shapes import parallelogram\n", - "help(parallelogram)\n", - "\n", - "# Define widgets\n", - "w_height = iw.IntSlider(value=4.0, min=1, max=10, description=\"heigh [deg]\")\n", - "w_width = iw.IntSlider(value=4.0, min=1, max=10, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_depth = iw.FloatSlider(value=2.0, min=-3.0, max=3.0, description=\"depth [deg]\")\n", - "\n", - "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='vertical', button_style='', description=\"orientation\")\n", - "\n", - "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_height, w_width, w_ppd])\n", - "b_intensities = iw.HBox([w_intensities])\n", - "ui = iw.VBox([b_size, w_depth, w_orientation, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_parallelogram(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " depth=None,\n", - " orientation='horizontal',\n", - " intensities=(0.0, 1.0)\n", - "):\n", - " stim = parallelogram(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " parallelogram_depth=depth,\n", - " orientation=orientation,\n", - " intensity_parallelogram=intensities[1],\n", - " intensity_background=intensities[0]\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_parallelogram,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"depth\": w_depth,\n", - " \"intensities\": w_intensities,\n", - " \"orientation\": w_orientation,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Cross" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.shapes import cross\n", - "help(cross)\n", - "\n", - "# Define widgets\n", - "w_height = iw.IntSlider(value=4.0, min=1, max=10, description=\"heigh [deg]\")\n", - "w_width = iw.IntSlider(value=4.0, min=1, max=10, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_cross_thick = iw.FloatSlider(value=0.5, min=0.0, max=5.0, description=\"thickness [deg]\")\n", - "w_cross_hratio = iw.FloatSlider(value=1.0, min=0.0, max=5.0, description=\"horz. arm ratio\")\n", - "w_cross_vratio = iw.FloatSlider(value=1.0, min=0.0, max=5.0, description=\"vert. arm ratio\")\n", - "\n", - "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_height, w_width, w_ppd])\n", - "b_intensities = iw.HBox([w_intensities])\n", - "b_cross = iw.HBox([w_cross_thick, w_cross_hratio, w_cross_vratio])\n", - "ui = iw.VBox([b_size, b_cross, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_cross(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " thickness=None,\n", - " hratio=1.0,\n", - " vratio=1.0,\n", - " intensities=(0.0, 1.0)\n", - "):\n", - " stim = cross(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " cross_thickness=thickness,\n", - " cross_arm_ratios=(vratio, hratio),\n", - " intensity_cross=intensities[1],\n", - " intensity_background=intensities[0]\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_cross,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"thickness\": w_cross_thick,\n", - " \"vratio\": w_cross_vratio,\n", - " \"hratio\": w_cross_hratio,\n", - " \"intensities\": w_intensities,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basic shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rectangle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.shapes import rectangle\n", + "help(rectangle)\n", + "\n", + "# Define widgets\n", + "w_height = iw.IntSlider(value=4, min=1, max=10, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=4, min=1, max=10, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_rect_height = iw.IntSlider(value=2, min=1, max=10, description=\"height [deg]\")\n", + "w_rect_width = iw.IntSlider(value=2, min=1, max=10, description=\"width [deg]\")\n", + "\n", + "w_rect_posx = iw.FloatSlider(value=0.0, min=-5.0, max=5.0, description=\"horz. position\")\n", + "w_rect_posy = iw.FloatSlider(value=0.0, min=-5.0, max=5.0, description=\"vert. position\")\n", + "\n", + "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_im_size = iw.HBox([w_height, w_width, w_ppd])\n", + "b_rect_size = iw.HBox([w_rect_height, w_rect_width])\n", + "b_post = iw.HBox([w_rect_posx, w_rect_posy])\n", + "b_intensities = iw.HBox([w_intensities])\n", + "ui = iw.VBox([b_im_size, b_rect_size, b_post, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_rect(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " rect_height=None,\n", + " rect_width=None,\n", + " pos_x=0.0,\n", + " pos_y=0.0,\n", + " orientation='horizontal',\n", + " intensities=(0.0, 1.0)\n", + "):\n", + " stim = rectangle(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " rectangle_size=(rect_height, rect_width),\n", + " rectangle_position=(pos_y, pos_x),\n", + " intensity_rectangle=intensities[1],\n", + " intensity_background=intensities[0]\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_rect,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"rect_height\": w_rect_height,\n", + " \"rect_width\": w_rect_width,\n", + " \"pos_x\": w_rect_posx,\n", + " \"pos_y\": w_rect_posy,\n", + " \"intensities\": w_intensities,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Triangle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.shapes import triangle\n", + "help(triangle)\n", + "\n", + "# Define widgets\n", + "w_height = iw.IntSlider(value=4, min=1, max=10, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=4, min=1, max=10, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_im_size = iw.HBox([w_height, w_width, w_ppd])\n", + "b_intensities = iw.HBox([w_intensities])\n", + "ui = iw.VBox([b_im_size, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_triangle(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " intensities=(0.0, 1.0)\n", + "):\n", + " stim = triangle(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " intensity_triangle=intensities[1],\n", + " intensity_background=intensities[0]\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_triangle,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"intensities\": w_intensities,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parallelogram" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.shapes import parallelogram\n", + "help(parallelogram)\n", + "\n", + "# Define widgets\n", + "w_height = iw.IntSlider(value=4.0, min=1, max=10, description=\"heigh [deg]\")\n", + "w_width = iw.IntSlider(value=4.0, min=1, max=10, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_depth = iw.FloatSlider(value=2.0, min=-3.0, max=3.0, description=\"depth [deg]\")\n", + "\n", + "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='vertical', button_style='', description=\"orientation\")\n", + "\n", + "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_height, w_width, w_ppd])\n", + "b_intensities = iw.HBox([w_intensities])\n", + "ui = iw.VBox([b_size, w_depth, w_orientation, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_parallelogram(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " depth=None,\n", + " orientation='horizontal',\n", + " intensities=(0.0, 1.0)\n", + "):\n", + " stim = parallelogram(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " parallelogram_depth=depth,\n", + " orientation=orientation,\n", + " intensity_parallelogram=intensities[1],\n", + " intensity_background=intensities[0]\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_parallelogram,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"depth\": w_depth,\n", + " \"intensities\": w_intensities,\n", + " \"orientation\": w_orientation,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cross" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.shapes import cross\n", + "help(cross)\n", + "\n", + "# Define widgets\n", + "w_height = iw.IntSlider(value=4.0, min=1, max=10, description=\"heigh [deg]\")\n", + "w_width = iw.IntSlider(value=4.0, min=1, max=10, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_cross_thick = iw.FloatSlider(value=0.5, min=0.0, max=5.0, description=\"thickness [deg]\")\n", + "w_cross_hratio = iw.FloatSlider(value=1.0, min=0.0, max=5.0, description=\"horz. arm ratio\")\n", + "w_cross_vratio = iw.FloatSlider(value=1.0, min=0.0, max=5.0, description=\"vert. arm ratio\")\n", + "\n", + "w_intensities = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_height, w_width, w_ppd])\n", + "b_intensities = iw.HBox([w_intensities])\n", + "b_cross = iw.HBox([w_cross_thick, w_cross_hratio, w_cross_vratio])\n", + "ui = iw.VBox([b_size, b_cross, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_cross(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " thickness=None,\n", + " hratio=1.0,\n", + " vratio=1.0,\n", + " intensities=(0.0, 1.0)\n", + "):\n", + " stim = cross(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " cross_thickness=thickness,\n", + " cross_arm_ratios=(vratio, hratio),\n", + " intensity_cross=intensities[1],\n", + " intensity_background=intensities[0]\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_cross,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"thickness\": w_cross_thick,\n", + " \"vratio\": w_cross_vratio,\n", + " \"hratio\": w_cross_hratio,\n", + " \"intensities\": w_intensities,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/angular.ipynb b/demo/illusions/angular.ipynb index 0539b031..d1527583 100644 --- a/demo/illusions/angular.ipynb +++ b/demo/illusions/angular.ipynb @@ -1,489 +1,489 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Angular (radial) components & illusions: wedges, wheel-of-fortune/pin-wheel" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Angular, or radial, stimuli are in some way defined by angles respective to the center." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "\n", - "from stimuli.utils import plot_stim, plot_stimuli\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# angles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each point in the image has an angle to the center point --\n", - "`components.angular.img_angles(...)` generates the matrix of these angles in an image\n", - "of given resolution (`visua_size`,`ppd`,`shape`).\n", - "The convention here is that of the unit-circle:\n", - "3 o'clock is $0$ and $360$ degrees ($0$ and $2\\pi$ radians),\n", - "angle increases going counterclockwise." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.components import image_base\n", - "\n", - "base = image_base(visual_size=(8,8),ppd=32, rotation=0)\n", - "\n", - "plt.imshow(base[\"angular\"], cmap=\"coolwarm\")\n", - "plt.colorbar()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## angular_segments" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An image can be divided into `angular_segments`\n", - "by defining the upper-limits on the angles in each segment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.angular import angular_segments\n", - "\n", - "stim = angular_segments(angles=[45, 360], intensity_segments=(0.0, 1.0), visual_size=(8,8), ppd=32)\n", - "\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As many segments, of any (non-negative) size, can be defined as desired --\n", - "and each segment is individually masked:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "stim = angular_segments(angles=[45, 90, 100, 110, 200, 300, 360], intensity_segments=np.linspace(0.25,1,10), visual_size=(8,8), ppd=32)\n", - "\n", - "plt.subplot(1,2,1)\n", - "plot_stim(stim, stim_name=\"angular segments\")\n", - "plt.subplot(1,2,2)\n", - "plot_stim(stim, mask=True, stim_name=\"mask per segment\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## wedge\n", - "Angular segment, espcially their masks, can be used to cut out regions of other\n", - "types of stimuli.\n", - "For example: by masking everything but one angular segment of a circle,\n", - "one can create a `wedge(...)`:\n", - "this requires defining the angular-width of the wedge andoptionally a rotation \n", - "(i.e., the lower limit on the angles in the wedge)\n", - "and of course the outer- (and optinally, inner-) radius of the circle:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.angular import wedge\n", - "\n", - "help(wedge)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_angle = iw.IntSlider(value=45, min=0, max=360, description=\"angle\")\n", - "w_rotation = iw.IntSlider(value=45, min=0, max=360, description=\"rotate\")\n", - "w_radii = iw.FloatRangeSlider(\n", - " value=[1.0, 2.0], min=0.1, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", - ")\n", - "\n", - "w_idisc = iw.FloatSlider(value=1.0, min=0.0, max=1.0, description=\"intensity disc\")\n", - "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", - "\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_intensities = iw.HBox([w_idisc, w_iback])\n", - "ui = iw.VBox([b_size, w_angle, w_rotation, w_radii, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_wedge(\n", - " width=None,\n", - " rotation=None,\n", - " length=None,\n", - " ppd=None,\n", - " radii=None,\n", - " intensity_disc=None,\n", - " intensity_background=None,\n", - "):\n", - "\n", - " stim = wedge(\n", - " width=width,\n", - " rotation=rotation,\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " radius=radii[1],\n", - " inner_radius=radii[0],\n", - " intensity_background=intensity_background,\n", - " intensity=intensity_disc,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_wedge,\n", - " {\n", - " \"width\": w_angle,\n", - " \"rotation\": w_rotation,\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"radii\": w_radii,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_disc\": w_idisc,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## angular_grating\n", - "If the full 306 degrees is divided into a number of equal-width segments\n", - "this can also be considered an `angular.grating(...)` (or radial grating).\n", - "The angular `segment_width` is also a measure of the angular `frequency`,\n", - "as is the `n_segments` -- any of these can be defined:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.angular import grating\n", - "\n", - "help(grating)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_rotation = iw.FloatSlider(value=45, min=0, max=360, description=\"rotate\")\n", - "w_n_segments = iw.IntSlider(value=6, min=2, max=20, step=2, description=\"n segments\")\n", - "\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "ui = iw.VBox([b_size, w_rotation, w_n_segments])\n", - "\n", - "# Function for showing stim\n", - "def show_grating(\n", - " rotation=None,\n", - " length=None,\n", - " ppd=None,\n", - " n_segments=None,\n", - "):\n", - "\n", - " stim = grating(\n", - " rotation=rotation,\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " n_segments=n_segments,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_grating,\n", - " {\n", - " \"rotation\": w_rotation,\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"n_segments\": w_n_segments,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## pinwheel\n", - "Combining this with a circular `disc` component\n", - "now divides a circle into a set of equal-size wedges,\n", - "forming a `pinwheel`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.angular import pinwheel\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_rotation = iw.FloatSlider(value=45, min=0, max=360, description=\"rotate\")\n", - "w_n_segments = iw.IntSlider(value=6, min=2, max=20, step=2, description=\"n segments\")\n", - "w_radii = iw.FloatRangeSlider(\n", - " value=[0.0, 2.0], min=0.0, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", - ")\n", - "w_intensities = iw.FloatRangeSlider(\n", - " value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\"\n", - ")\n", - "\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "ui = iw.VBox([b_size, w_rotation, w_n_segments, w_radii, w_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_pinwheel(\n", - " rotation=None,\n", - " length=None,\n", - " ppd=None,\n", - " n_segments=None,\n", - " radii=None,\n", - " intensities=None,\n", - "):\n", - "\n", - " stim = pinwheel(\n", - " rotation=rotation,\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " radius=radii[1],\n", - " inner_radius=radii[0],\n", - " n_segments=n_segments,\n", - " intensity_segments=intensities,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_pinwheel,\n", - " {\n", - " \"rotation\": w_rotation,\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"n_segments\": w_n_segments,\n", - " \"radii\": w_radii,\n", - " \"intensities\": w_intensities,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## illusions.angular.radial_white\n", - "Target(s) can be embedded in such a pinwheel,\n", - "to create a `radial_white`'s-like illusion" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.angular import radial_white\n", - "\n", - "help(radial_white)\n", - "\n", - "# Define widgets\n", - "# Resolution\n", - "w_length = iw.IntSlider(value=8, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "# Pinwheel\n", - "w_rotation = iw.FloatSlider(value=45, min=0, max=360, description=\"rotate\")\n", - "w_n_segments = iw.IntSlider(value=6, min=2, max=20, step=2, description=\"n segments\")\n", - "w_radii = iw.FloatRangeSlider(\n", - " value=[0.0, 2.0], min=0.0, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", - ")\n", - "w_intensities = iw.FloatRangeSlider(\n", - " value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\"\n", - ")\n", - "\n", - "# Target\n", - "w_twidth = iw.FloatSlider(value=2, min=0, max=4, description=\"width target [deg]\")\n", - "w_tcenter = iw.FloatSlider(value=2.5, min=0, max=4, description=\"center target [deg]\")\n", - "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_seg = iw.HBox([w_n_segments, w_radii, w_rotation, w_intensities])\n", - "b_target = iw.HBox([w_tidx, w_twidth, w_tcenter, w_itarget])\n", - "ui = iw.VBox([b_size, b_seg, b_target, w_intensities])\n", - "\n", - "def show_radial(\n", - " length=None,\n", - " ppd=None,\n", - " n_segments=None,\n", - " rotation=None,\n", - " target_indices=None,\n", - " target_width=None,\n", - " target_center=None,\n", - " intensity_segments=None,\n", - " intensity_background=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = radial_white(\n", - " visual_size=length,\n", - " ppd=ppd,\n", - " n_segments=n_segments,\n", - " rotation=rotation,\n", - " target_indices=target_indices,\n", - " target_width=target_width,\n", - " target_center=target_center,\n", - " intensity_background=intensity_background,\n", - " intensity_segments=intensity_segments,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_radial,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"n_segments\": w_n_segments,\n", - " \"rotation\": w_rotation,\n", - " \"target_indices\": w_tidx,\n", - " \"target_width\": w_twidth,\n", - " \"target_center\": w_tcenter,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_segments\": w_intensities,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is possible to place as many targets as desired\n", - "by providing a list of `target_indices`.\n", - "The indices refer to the segments starting with `1` at the 3 o'clock position\n", - "(affected by `rotation`) and then going counter-clockwise.\n", - "Different targets can also have differnt `_width`, `_center` and `intensity_`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 32.0,\n", - " \"n_segments\": 10,\n", - " \"rotation\": 0,\n", - " \"intensity_background\": 0.5,\n", - "}\n", - "\n", - "stim1 = radial_white(**params, target_indices=(2, 5), target_width=2.0, target_center=2.5, intensity_target=0.5)\n", - "stim2 = radial_white(**params, target_indices=(1, 5, 6, 9), target_width=[2.0, 1.0], target_center=[3.0, 4.0], intensity_target=[0.25, 0.4, 0.6, 0.75])\n", - "\n", - "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Angular (radial) components & illusions: wedges, wheel-of-fortune/pin-wheel" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Angular, or radial, stimuli are in some way defined by angles respective to the center." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "\n", + "from stimupy.utils import plot_stim, plot_stimuli\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# angles" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each point in the image has an angle to the center point --\n", + "`components.angular.img_angles(...)` generates the matrix of these angles in an image\n", + "of given resolution (`visua_size`,`ppd`,`shape`).\n", + "The convention here is that of the unit-circle:\n", + "3 o'clock is $0$ and $360$ degrees ($0$ and $2\\pi$ radians),\n", + "angle increases going counterclockwise." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.components import image_base\n", + "\n", + "base = image_base(visual_size=(8,8),ppd=32, rotation=0)\n", + "\n", + "plt.imshow(base[\"angular\"], cmap=\"coolwarm\")\n", + "plt.colorbar()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## angular_segments" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An image can be divided into `angular_segments`\n", + "by defining the upper-limits on the angles in each segment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.angular import angular_segments\n", + "\n", + "stim = angular_segments(angles=[45, 360], intensity_segments=(0.0, 1.0), visual_size=(8,8), ppd=32)\n", + "\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As many segments, of any (non-negative) size, can be defined as desired --\n", + "and each segment is individually masked:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stim = angular_segments(angles=[45, 90, 100, 110, 200, 300, 360], intensity_segments=np.linspace(0.25,1,10), visual_size=(8,8), ppd=32)\n", + "\n", + "plt.subplot(1,2,1)\n", + "plot_stim(stim, stim_name=\"angular segments\")\n", + "plt.subplot(1,2,2)\n", + "plot_stim(stim, mask=True, stim_name=\"mask per segment\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## wedge\n", + "Angular segment, espcially their masks, can be used to cut out regions of other\n", + "types of stimupy.\n", + "For example: by masking everything but one angular segment of a circle,\n", + "one can create a `wedge(...)`:\n", + "this requires defining the angular-width of the wedge andoptionally a rotation \n", + "(i.e., the lower limit on the angles in the wedge)\n", + "and of course the outer- (and optinally, inner-) radius of the circle:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.angular import wedge\n", + "\n", + "help(wedge)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_angle = iw.IntSlider(value=45, min=0, max=360, description=\"angle\")\n", + "w_rotation = iw.IntSlider(value=45, min=0, max=360, description=\"rotate\")\n", + "w_radii = iw.FloatRangeSlider(\n", + " value=[1.0, 2.0], min=0.1, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", + ")\n", + "\n", + "w_idisc = iw.FloatSlider(value=1.0, min=0.0, max=1.0, description=\"intensity disc\")\n", + "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", + "\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_intensities = iw.HBox([w_idisc, w_iback])\n", + "ui = iw.VBox([b_size, w_angle, w_rotation, w_radii, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_wedge(\n", + " width=None,\n", + " rotation=None,\n", + " length=None,\n", + " ppd=None,\n", + " radii=None,\n", + " intensity_disc=None,\n", + " intensity_background=None,\n", + "):\n", + "\n", + " stim = wedge(\n", + " width=width,\n", + " rotation=rotation,\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " radius=radii[1],\n", + " inner_radius=radii[0],\n", + " intensity_background=intensity_background,\n", + " intensity=intensity_disc,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_wedge,\n", + " {\n", + " \"width\": w_angle,\n", + " \"rotation\": w_rotation,\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"radii\": w_radii,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_disc\": w_idisc,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## angular_grating\n", + "If the full 306 degrees is divided into a number of equal-width segments\n", + "this can also be considered an `angular.grating(...)` (or radial grating).\n", + "The angular `segment_width` is also a measure of the angular `frequency`,\n", + "as is the `n_segments` -- any of these can be defined:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.angular import grating\n", + "\n", + "help(grating)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_rotation = iw.FloatSlider(value=45, min=0, max=360, description=\"rotate\")\n", + "w_n_segments = iw.IntSlider(value=6, min=2, max=20, step=2, description=\"n segments\")\n", + "\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "ui = iw.VBox([b_size, w_rotation, w_n_segments])\n", + "\n", + "# Function for showing stim\n", + "def show_grating(\n", + " rotation=None,\n", + " length=None,\n", + " ppd=None,\n", + " n_segments=None,\n", + "):\n", + "\n", + " stim = grating(\n", + " rotation=rotation,\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " n_segments=n_segments,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_grating,\n", + " {\n", + " \"rotation\": w_rotation,\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"n_segments\": w_n_segments,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## pinwheel\n", + "Combining this with a circular `disc` component\n", + "now divides a circle into a set of equal-size wedges,\n", + "forming a `pinwheel`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.angular import pinwheel\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_rotation = iw.FloatSlider(value=45, min=0, max=360, description=\"rotate\")\n", + "w_n_segments = iw.IntSlider(value=6, min=2, max=20, step=2, description=\"n segments\")\n", + "w_radii = iw.FloatRangeSlider(\n", + " value=[0.0, 2.0], min=0.0, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", + ")\n", + "w_intensities = iw.FloatRangeSlider(\n", + " value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\"\n", + ")\n", + "\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "ui = iw.VBox([b_size, w_rotation, w_n_segments, w_radii, w_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_pinwheel(\n", + " rotation=None,\n", + " length=None,\n", + " ppd=None,\n", + " n_segments=None,\n", + " radii=None,\n", + " intensities=None,\n", + "):\n", + "\n", + " stim = pinwheel(\n", + " rotation=rotation,\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " radius=radii[1],\n", + " inner_radius=radii[0],\n", + " n_segments=n_segments,\n", + " intensity_segments=intensities,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_pinwheel,\n", + " {\n", + " \"rotation\": w_rotation,\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"n_segments\": w_n_segments,\n", + " \"radii\": w_radii,\n", + " \"intensities\": w_intensities,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## illusions.angular.radial_white\n", + "Target(s) can be embedded in such a pinwheel,\n", + "to create a `radial_white`'s-like illusion" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.angular import radial_white\n", + "\n", + "help(radial_white)\n", + "\n", + "# Define widgets\n", + "# Resolution\n", + "w_length = iw.IntSlider(value=8, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "# Pinwheel\n", + "w_rotation = iw.FloatSlider(value=45, min=0, max=360, description=\"rotate\")\n", + "w_n_segments = iw.IntSlider(value=6, min=2, max=20, step=2, description=\"n segments\")\n", + "w_radii = iw.FloatRangeSlider(\n", + " value=[0.0, 2.0], min=0.0, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", + ")\n", + "w_intensities = iw.FloatRangeSlider(\n", + " value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\"\n", + ")\n", + "\n", + "# Target\n", + "w_twidth = iw.FloatSlider(value=2, min=0, max=4, description=\"width target [deg]\")\n", + "w_tcenter = iw.FloatSlider(value=2.5, min=0, max=4, description=\"center target [deg]\")\n", + "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_seg = iw.HBox([w_n_segments, w_radii, w_rotation, w_intensities])\n", + "b_target = iw.HBox([w_tidx, w_twidth, w_tcenter, w_itarget])\n", + "ui = iw.VBox([b_size, b_seg, b_target, w_intensities])\n", + "\n", + "def show_radial(\n", + " length=None,\n", + " ppd=None,\n", + " n_segments=None,\n", + " rotation=None,\n", + " target_indices=None,\n", + " target_width=None,\n", + " target_center=None,\n", + " intensity_segments=None,\n", + " intensity_background=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = radial_white(\n", + " visual_size=length,\n", + " ppd=ppd,\n", + " n_segments=n_segments,\n", + " rotation=rotation,\n", + " target_indices=target_indices,\n", + " target_width=target_width,\n", + " target_center=target_center,\n", + " intensity_background=intensity_background,\n", + " intensity_segments=intensity_segments,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_radial,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"n_segments\": w_n_segments,\n", + " \"rotation\": w_rotation,\n", + " \"target_indices\": w_tidx,\n", + " \"target_width\": w_twidth,\n", + " \"target_center\": w_tcenter,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_segments\": w_intensities,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is possible to place as many targets as desired\n", + "by providing a list of `target_indices`.\n", + "The indices refer to the segments starting with `1` at the 3 o'clock position\n", + "(affected by `rotation`) and then going counter-clockwise.\n", + "Different targets can also have differnt `_width`, `_center` and `intensity_`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 32.0,\n", + " \"n_segments\": 10,\n", + " \"rotation\": 0,\n", + " \"intensity_background\": 0.5,\n", + "}\n", + "\n", + "stim1 = radial_white(**params, target_indices=(2, 5), target_width=2.0, target_center=2.5, intensity_target=0.5)\n", + "stim2 = radial_white(**params, target_indices=(1, 5, 6, 9), target_width=[2.0, 1.0], target_center=[3.0, 4.0], intensity_target=[0.25, 0.4, 0.6, 0.75])\n", + "\n", + "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/benary_cross.ipynb b/demo/illusions/benary_cross.ipynb index 40d9233a..21481bda 100644 --- a/demo/illusions/benary_cross.ipynb +++ b/demo/illusions/benary_cross.ipynb @@ -1,897 +1,897 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# benarys_cross_generalized\n", - "\n", - "Most general version of the function that creates a Benarys-cross-like stimulus." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.benary_cross import benarys_cross_generalized" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (21., 21.),\n", - " \"ppd\": 18.0,\n", - " \"cross_thickness\": 5.0,\n", - " \"target_size\": (2.0, 2.0),\n", - " \"target_type\": \"r\",\n", - " \"target_orientation\": 0,\n", - " \"target_x\": (6.0, 19.0),\n", - " \"target_y\": (6.0, 8.0),\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim = benarys_cross_generalized(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The main advantage of `benarys_cross_generalized()` is that you can add as many targets as you like by adding values to the lists that you pass to `target_type`, `target_ori`, `target_posx` and/or `target_posy`.\n", - "Furthermore, you can either add rectangular targets (`target_type=\"r\"`) or triangular targets (`target_type=\"t\"`).\n", - "\n", - "Keep in mind that the number of elements for each of those input variables needs be 1 or you need to have as many elements as you want to have targets.\n", - "\n", - "In this most general version of Benarys cross, the user needs to define the target placement (i.e. calculate the values for `target_posy` and `target_posx`)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (21., 21.),\n", - " \"ppd\": 18.0,\n", - " \"cross_thickness\": 5.0,\n", - " \"target_size\": (2.0, 2.0),\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim1 = benarys_cross_generalized(**params,\n", - " target_type=\"r\",\n", - " target_orientation=0,\n", - " target_x=(2, 2, 8, 8, 11, 11, 17, 17),\n", - " target_y=(8, 11, 6, 13, 6, 13, 8, 11),\n", - " )\n", - "\n", - "stim2 = benarys_cross_generalized(**params,\n", - " target_type=(\"r\", \"r\", \"t\", \"t\", \"t\", \"t\", \"r\", \"r\"),\n", - " target_orientation=(45, 45, 270, 0, 180, 90, 45, 45),\n", - " target_x=(2, 2, 8, 8, 11, 11, 17, 17),\n", - " target_y=(6.7, 11.7, 6, 13, 6, 13, 6.7, 11.7),\n", - " )\n", - "\n", - "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_cross_thickness = iw.IntSlider(value=5, min=1, max=10, description=\"cross thickness [deg]\")\n", - "w_theight = iw.IntSlider(value=2, min=1, max=4, description=\"target height [deg]\")\n", - "w_twidth = iw.IntSlider(value=2, min=1, max=4, description=\"target width [deg]\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "w_ori = iw.IntSlider(value=0, min=0, max=360, description=\"target orientation [deg]\")\n", - "w_posx = iw.IntSlider(value=6, min=0, max=10, description=\"target x-position [deg]\")\n", - "w_posy = iw.IntSlider(value=6, min=0, max=10, description=\"target y-position [deg]\")\n", - "w_place = iw.HBox([w_posx, w_posy, w_ori])\n", - "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_cross_thickness, w_tsize, w_place, w_intensities])\n", - "\n", - "def show_benary(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " cross_thickness=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " target_type=\"r\",\n", - " target_orientation=None,\n", - " target_x=None,\n", - " target_y=None,\n", - " intensity_background=None,\n", - " intensity_cross=None,\n", - " intensity_target=None, \n", - "):\n", - "\n", - " stim = benarys_cross_generalized(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " cross_thickness=cross_thickness,\n", - " target_size=(target_height, target_width),\n", - " target_type=target_type,\n", - " target_orientation=[target_orientation],\n", - " target_x=target_x,\n", - " target_y=target_y,\n", - " intensity_background=intensity_background,\n", - " intensity_cross=intensity_cross,\n", - " intensity_target=intensity_target, \n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_benary,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"cross_thickness\": w_cross_thickness,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"target_orientation\": w_ori,\n", - " \"target_x\": w_posx,\n", - " \"target_y\": w_posy,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_cross\": w_icross,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# benarys_cross_rectangles\n", - "\n", - "More user-friendly version of Benarys cross with two rectangular targets and default placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.benary_cross import benarys_cross_rectangles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (21., 21.),\n", - " \"ppd\": 18.0,\n", - " \"cross_thickness\": 5.0,\n", - " \"target_size\": (2.0, 2.0),\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim = benarys_cross_rectangles(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_cross_thickness = iw.IntSlider(value=5, min=1, max=10, description=\"cross thickness [deg]\")\n", - "w_theight = iw.IntSlider(value=2, min=1, max=4, description=\"target height [deg]\")\n", - "w_twidth = iw.IntSlider(value=2, min=1, max=4, description=\"target width [deg]\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_cross_thickness, w_tsize, w_intensities])\n", - "\n", - "def show_benary(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " cross_thickness=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " intensity_background=None,\n", - " intensity_cross=None,\n", - " intensity_target=None, \n", - "):\n", - "\n", - " stim = benarys_cross_rectangles(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " cross_thickness=cross_thickness,\n", - " target_size=(target_height, target_width),\n", - " intensity_background=intensity_background,\n", - " intensity_cross=intensity_cross,\n", - " intensity_target=intensity_target, \n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_benary,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"cross_thickness\": w_cross_thickness,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_cross\": w_icross,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# benarys_cross_triangles\n", - "\n", - "More user-friendly version of Benarys cross with two triangular targets and default placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.benary_cross import benarys_cross_triangles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (21., 21.),\n", - " \"ppd\": 18.0,\n", - " \"cross_thickness\": 5.0,\n", - " \"target_size\": 3.,\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim = benarys_cross_triangles(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_cross_thickness = iw.IntSlider(value=5, min=1, max=10, description=\"cross thickness [deg]\")\n", - "w_tsize = iw.IntSlider(value=3, min=1, max=5, description=\"target height [deg]\")\n", - "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_cross_thickness, w_tsize, w_intensities])\n", - "\n", - "def show_benary(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " cross_thickness=None,\n", - " target_size=None,\n", - " intensity_background=None,\n", - " intensity_cross=None,\n", - " intensity_target=None, \n", - "):\n", - "\n", - " stim = benarys_cross_triangles(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " cross_thickness=cross_thickness,\n", - " target_size=target_size,\n", - " intensity_background=intensity_background,\n", - " intensity_cross=intensity_cross,\n", - " intensity_target=intensity_target, \n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_benary,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"cross_thickness\": w_cross_thickness,\n", - " \"target_size\": w_tsize,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_cross\": w_icross,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# todorovic_benary_generalized\n", - "General version of the function that creates Todorovic's variations of Benarys cross.\n", - "The use of this function is very similar to `benarys_cross_generalized()`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.benary_cross import todorovic_benary_generalized" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (16., 16.),\n", - " \"ppd\": 10.0,\n", - " \"L_width\": 2.0,\n", - " \"target_size\": (2.0, 2.0),\n", - " \"target_type\": \"r\",\n", - " \"target_orientation\": 0,\n", - " \"target_x\": (2.0, 12.0),\n", - " \"target_y\": (6.0, 8.0),\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim = todorovic_benary_generalized(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As for `benarys_cross_generalized()`, the main advantage this function is the flexibility of the target placement.\n", - "You can add as many targets as you like by adapting `target_type`, `target_ori`, `target_posx` and/or `target_posy`.\n", - "\n", - "Again, keep in mind that the number of list elements for these input variables needs be 1 or you need to have as many elements as you want to have targets." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (16., 16.),\n", - " \"ppd\": 10.0,\n", - " \"L_width\": 2.0,\n", - " \"target_size\": (2.0, 2.0),\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim1 = todorovic_benary_generalized(**params,\n", - " target_type=\"r\",\n", - " target_orientation=0,\n", - " target_x=(2, 2, 2, 2, 2, 5.3, 8.6, 12, 12, 12),\n", - " target_y=(2, 5, 8, 11, 14, 8, 8, 8, 11, 14)\n", - " )\n", - "\n", - "stim2 = todorovic_benary_generalized(**params,\n", - " target_type=(\"r\", \"t\", \"t\", \"r\"),\n", - " target_orientation=(0, 45, 225, 0),\n", - " target_x=(2, 5, 8, 12),\n", - " target_y=(6, 8, 6.7, 8),\n", - " )\n", - "\n", - "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=20, min=10, max=30, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=20, min=10, max=30, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_lwidth = iw.IntSlider(value=5, min=1, max=10, description=\"L width [deg]\")\n", - "w_theight = iw.IntSlider(value=3, min=1, max=5, description=\"target height [deg]\")\n", - "w_twidth = iw.IntSlider(value=3, min=1, max=5, description=\"target width [deg]\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "w_ori = iw.IntSlider(value=0, min=0, max=360, description=\"target orientation [deg]\")\n", - "w_posx = iw.IntSlider(value=5, min=0, max=10, description=\"target x-position [deg]\")\n", - "w_posy = iw.IntSlider(value=7, min=0, max=10, description=\"target y-position [deg]\")\n", - "w_place = iw.HBox([w_posx, w_posy, w_ori])\n", - "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_lwidth, w_tsize, w_place, w_intensities])\n", - "\n", - "def show_benary(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " L_width=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " target_type=\"r\",\n", - " target_orientation=None,\n", - " target_x=None,\n", - " target_y=None,\n", - " intensity_background=None,\n", - " intensity_cross=None,\n", - " intensity_target=None, \n", - "):\n", - "\n", - " stim = todorovic_benary_generalized(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " L_width=L_width,\n", - " target_size=(target_height, target_width),\n", - " target_type=target_type,\n", - " target_orientation=[target_orientation],\n", - " target_x=target_x,\n", - " target_y=target_y,\n", - " intensity_background=intensity_background,\n", - " intensity_cross=intensity_cross,\n", - " intensity_target=intensity_target, \n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_benary,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"L_width\": w_lwidth,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"target_orientation\": w_ori,\n", - " \"target_x\": w_posx,\n", - " \"target_y\": w_posy,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_cross\": w_icross,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# todorovic_benary_rectangles\n", - "\n", - "More user-friendly version of Todorovic Benary with two rectangular targets and default placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.benary_cross import todorovic_benary_rectangles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (21., 21.),\n", - " \"ppd\": 18.0,\n", - " \"L_width\": 5.0,\n", - " \"target_size\": (3.0, 3.0),\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim = todorovic_benary_rectangles(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_lwidth = iw.IntSlider(value=5, min=1, max=10, description=\"L width [deg]\")\n", - "w_theight = iw.IntSlider(value=3, min=1, max=4, description=\"target height [deg]\")\n", - "w_twidth = iw.IntSlider(value=3, min=1, max=4, description=\"target width [deg]\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_lwidth, w_tsize, w_intensities])\n", - "\n", - "def show_benary(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " L_width=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " intensity_background=None,\n", - " intensity_cross=None,\n", - " intensity_target=None, \n", - "):\n", - "\n", - " stim = todorovic_benary_rectangles(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " L_width=L_width,\n", - " target_size=(target_height, target_width),\n", - " intensity_background=intensity_background,\n", - " intensity_cross=intensity_cross,\n", - " intensity_target=intensity_target, \n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_benary,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"L_width\": w_lwidth,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_cross\": w_icross,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# todorovic_benary_triangles\n", - "More user-friendly version of Todorovic Benary with two triangular targets and default placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.benary_cross import todorovic_benary_triangles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (21., 21.),\n", - " \"ppd\": 18.0,\n", - " \"L_width\": 5.0,\n", - " \"target_size\": 3.,\n", - " \"intensity_background\": 1.0,\n", - " \"intensity_cross\": 0.0,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "\n", - "\n", - "stim = todorovic_benary_triangles(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_lwidth = iw.IntSlider(value=5, min=1, max=10, description=\"L width [deg]\")\n", - "w_tsize = iw.IntSlider(value=3, min=1, max=4, description=\"target size [deg]\")\n", - "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_lwidth, w_tsize, w_intensities])\n", - "\n", - "def show_benary(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " L_width=None,\n", - " target_size=None,\n", - " intensity_background=None,\n", - " intensity_cross=None,\n", - " intensity_target=None, \n", - "):\n", - "\n", - " stim = todorovic_benary_triangles(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " L_width=L_width,\n", - " target_size=target_size,\n", - " intensity_background=intensity_background,\n", - " intensity_cross=intensity_cross,\n", - " intensity_target=intensity_target, \n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_benary,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"L_width\": w_lwidth,\n", - " \"target_size\": w_tsize,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_cross\": w_icross,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# benarys_cross_generalized\n", + "\n", + "Most general version of the function that creates a Benarys-cross-like stimulus." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.benary_cross import benarys_cross_generalized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (21., 21.),\n", + " \"ppd\": 18.0,\n", + " \"cross_thickness\": 5.0,\n", + " \"target_size\": (2.0, 2.0),\n", + " \"target_type\": \"r\",\n", + " \"target_orientation\": 0,\n", + " \"target_x\": (6.0, 19.0),\n", + " \"target_y\": (6.0, 8.0),\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim = benarys_cross_generalized(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The main advantage of `benarys_cross_generalized()` is that you can add as many targets as you like by adding values to the lists that you pass to `target_type`, `target_ori`, `target_posx` and/or `target_posy`.\n", + "Furthermore, you can either add rectangular targets (`target_type=\"r\"`) or triangular targets (`target_type=\"t\"`).\n", + "\n", + "Keep in mind that the number of elements for each of those input variables needs be 1 or you need to have as many elements as you want to have targets.\n", + "\n", + "In this most general version of Benarys cross, the user needs to define the target placement (i.e. calculate the values for `target_posy` and `target_posx`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (21., 21.),\n", + " \"ppd\": 18.0,\n", + " \"cross_thickness\": 5.0,\n", + " \"target_size\": (2.0, 2.0),\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim1 = benarys_cross_generalized(**params,\n", + " target_type=\"r\",\n", + " target_orientation=0,\n", + " target_x=(2, 2, 8, 8, 11, 11, 17, 17),\n", + " target_y=(8, 11, 6, 13, 6, 13, 8, 11),\n", + " )\n", + "\n", + "stim2 = benarys_cross_generalized(**params,\n", + " target_type=(\"r\", \"r\", \"t\", \"t\", \"t\", \"t\", \"r\", \"r\"),\n", + " target_orientation=(45, 45, 270, 0, 180, 90, 45, 45),\n", + " target_x=(2, 2, 8, 8, 11, 11, 17, 17),\n", + " target_y=(6.7, 11.7, 6, 13, 6, 13, 6.7, 11.7),\n", + " )\n", + "\n", + "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_cross_thickness = iw.IntSlider(value=5, min=1, max=10, description=\"cross thickness [deg]\")\n", + "w_theight = iw.IntSlider(value=2, min=1, max=4, description=\"target height [deg]\")\n", + "w_twidth = iw.IntSlider(value=2, min=1, max=4, description=\"target width [deg]\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "w_ori = iw.IntSlider(value=0, min=0, max=360, description=\"target orientation [deg]\")\n", + "w_posx = iw.IntSlider(value=6, min=0, max=10, description=\"target x-position [deg]\")\n", + "w_posy = iw.IntSlider(value=6, min=0, max=10, description=\"target y-position [deg]\")\n", + "w_place = iw.HBox([w_posx, w_posy, w_ori])\n", + "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_cross_thickness, w_tsize, w_place, w_intensities])\n", + "\n", + "def show_benary(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " cross_thickness=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " target_type=\"r\",\n", + " target_orientation=None,\n", + " target_x=None,\n", + " target_y=None,\n", + " intensity_background=None,\n", + " intensity_cross=None,\n", + " intensity_target=None, \n", + "):\n", + "\n", + " stim = benarys_cross_generalized(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " cross_thickness=cross_thickness,\n", + " target_size=(target_height, target_width),\n", + " target_type=target_type,\n", + " target_orientation=[target_orientation],\n", + " target_x=target_x,\n", + " target_y=target_y,\n", + " intensity_background=intensity_background,\n", + " intensity_cross=intensity_cross,\n", + " intensity_target=intensity_target, \n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_benary,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"cross_thickness\": w_cross_thickness,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"target_orientation\": w_ori,\n", + " \"target_x\": w_posx,\n", + " \"target_y\": w_posy,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_cross\": w_icross,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# benarys_cross_rectangles\n", + "\n", + "More user-friendly version of Benarys cross with two rectangular targets and default placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.benary_cross import benarys_cross_rectangles" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (21., 21.),\n", + " \"ppd\": 18.0,\n", + " \"cross_thickness\": 5.0,\n", + " \"target_size\": (2.0, 2.0),\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim = benarys_cross_rectangles(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_cross_thickness = iw.IntSlider(value=5, min=1, max=10, description=\"cross thickness [deg]\")\n", + "w_theight = iw.IntSlider(value=2, min=1, max=4, description=\"target height [deg]\")\n", + "w_twidth = iw.IntSlider(value=2, min=1, max=4, description=\"target width [deg]\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_cross_thickness, w_tsize, w_intensities])\n", + "\n", + "def show_benary(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " cross_thickness=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " intensity_background=None,\n", + " intensity_cross=None,\n", + " intensity_target=None, \n", + "):\n", + "\n", + " stim = benarys_cross_rectangles(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " cross_thickness=cross_thickness,\n", + " target_size=(target_height, target_width),\n", + " intensity_background=intensity_background,\n", + " intensity_cross=intensity_cross,\n", + " intensity_target=intensity_target, \n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_benary,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"cross_thickness\": w_cross_thickness,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_cross\": w_icross,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# benarys_cross_triangles\n", + "\n", + "More user-friendly version of Benarys cross with two triangular targets and default placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.benary_cross import benarys_cross_triangles" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (21., 21.),\n", + " \"ppd\": 18.0,\n", + " \"cross_thickness\": 5.0,\n", + " \"target_size\": 3.,\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim = benarys_cross_triangles(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_cross_thickness = iw.IntSlider(value=5, min=1, max=10, description=\"cross thickness [deg]\")\n", + "w_tsize = iw.IntSlider(value=3, min=1, max=5, description=\"target height [deg]\")\n", + "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_cross_thickness, w_tsize, w_intensities])\n", + "\n", + "def show_benary(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " cross_thickness=None,\n", + " target_size=None,\n", + " intensity_background=None,\n", + " intensity_cross=None,\n", + " intensity_target=None, \n", + "):\n", + "\n", + " stim = benarys_cross_triangles(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " cross_thickness=cross_thickness,\n", + " target_size=target_size,\n", + " intensity_background=intensity_background,\n", + " intensity_cross=intensity_cross,\n", + " intensity_target=intensity_target, \n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_benary,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"cross_thickness\": w_cross_thickness,\n", + " \"target_size\": w_tsize,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_cross\": w_icross,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# todorovic_benary_generalized\n", + "General version of the function that creates Todorovic's variations of Benarys cross.\n", + "The use of this function is very similar to `benarys_cross_generalized()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.benary_cross import todorovic_benary_generalized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (16., 16.),\n", + " \"ppd\": 10.0,\n", + " \"L_width\": 2.0,\n", + " \"target_size\": (2.0, 2.0),\n", + " \"target_type\": \"r\",\n", + " \"target_orientation\": 0,\n", + " \"target_x\": (2.0, 12.0),\n", + " \"target_y\": (6.0, 8.0),\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim = todorovic_benary_generalized(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As for `benarys_cross_generalized()`, the main advantage this function is the flexibility of the target placement.\n", + "You can add as many targets as you like by adapting `target_type`, `target_ori`, `target_posx` and/or `target_posy`.\n", + "\n", + "Again, keep in mind that the number of list elements for these input variables needs be 1 or you need to have as many elements as you want to have targets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (16., 16.),\n", + " \"ppd\": 10.0,\n", + " \"L_width\": 2.0,\n", + " \"target_size\": (2.0, 2.0),\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim1 = todorovic_benary_generalized(**params,\n", + " target_type=\"r\",\n", + " target_orientation=0,\n", + " target_x=(2, 2, 2, 2, 2, 5.3, 8.6, 12, 12, 12),\n", + " target_y=(2, 5, 8, 11, 14, 8, 8, 8, 11, 14)\n", + " )\n", + "\n", + "stim2 = todorovic_benary_generalized(**params,\n", + " target_type=(\"r\", \"t\", \"t\", \"r\"),\n", + " target_orientation=(0, 45, 225, 0),\n", + " target_x=(2, 5, 8, 12),\n", + " target_y=(6, 8, 6.7, 8),\n", + " )\n", + "\n", + "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=20, min=10, max=30, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=20, min=10, max=30, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_lwidth = iw.IntSlider(value=5, min=1, max=10, description=\"L width [deg]\")\n", + "w_theight = iw.IntSlider(value=3, min=1, max=5, description=\"target height [deg]\")\n", + "w_twidth = iw.IntSlider(value=3, min=1, max=5, description=\"target width [deg]\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "w_ori = iw.IntSlider(value=0, min=0, max=360, description=\"target orientation [deg]\")\n", + "w_posx = iw.IntSlider(value=5, min=0, max=10, description=\"target x-position [deg]\")\n", + "w_posy = iw.IntSlider(value=7, min=0, max=10, description=\"target y-position [deg]\")\n", + "w_place = iw.HBox([w_posx, w_posy, w_ori])\n", + "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_lwidth, w_tsize, w_place, w_intensities])\n", + "\n", + "def show_benary(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " L_width=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " target_type=\"r\",\n", + " target_orientation=None,\n", + " target_x=None,\n", + " target_y=None,\n", + " intensity_background=None,\n", + " intensity_cross=None,\n", + " intensity_target=None, \n", + "):\n", + "\n", + " stim = todorovic_benary_generalized(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " L_width=L_width,\n", + " target_size=(target_height, target_width),\n", + " target_type=target_type,\n", + " target_orientation=[target_orientation],\n", + " target_x=target_x,\n", + " target_y=target_y,\n", + " intensity_background=intensity_background,\n", + " intensity_cross=intensity_cross,\n", + " intensity_target=intensity_target, \n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_benary,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"L_width\": w_lwidth,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"target_orientation\": w_ori,\n", + " \"target_x\": w_posx,\n", + " \"target_y\": w_posy,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_cross\": w_icross,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# todorovic_benary_rectangles\n", + "\n", + "More user-friendly version of Todorovic Benary with two rectangular targets and default placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.benary_cross import todorovic_benary_rectangles" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (21., 21.),\n", + " \"ppd\": 18.0,\n", + " \"L_width\": 5.0,\n", + " \"target_size\": (3.0, 3.0),\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim = todorovic_benary_rectangles(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_lwidth = iw.IntSlider(value=5, min=1, max=10, description=\"L width [deg]\")\n", + "w_theight = iw.IntSlider(value=3, min=1, max=4, description=\"target height [deg]\")\n", + "w_twidth = iw.IntSlider(value=3, min=1, max=4, description=\"target width [deg]\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_lwidth, w_tsize, w_intensities])\n", + "\n", + "def show_benary(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " L_width=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " intensity_background=None,\n", + " intensity_cross=None,\n", + " intensity_target=None, \n", + "):\n", + "\n", + " stim = todorovic_benary_rectangles(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " L_width=L_width,\n", + " target_size=(target_height, target_width),\n", + " intensity_background=intensity_background,\n", + " intensity_cross=intensity_cross,\n", + " intensity_target=intensity_target, \n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_benary,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"L_width\": w_lwidth,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_cross\": w_icross,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# todorovic_benary_triangles\n", + "More user-friendly version of Todorovic Benary with two triangular targets and default placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.benary_cross import todorovic_benary_triangles" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (21., 21.),\n", + " \"ppd\": 18.0,\n", + " \"L_width\": 5.0,\n", + " \"target_size\": 3.,\n", + " \"intensity_background\": 1.0,\n", + " \"intensity_cross\": 0.0,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "\n", + "\n", + "stim = todorovic_benary_triangles(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=21, min=10, max=30, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=21, min=10, max=30, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_lwidth = iw.IntSlider(value=5, min=1, max=10, description=\"L width [deg]\")\n", + "w_tsize = iw.IntSlider(value=3, min=1, max=4, description=\"target size [deg]\")\n", + "w_iback = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icross = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity cross\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icross, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_lwidth, w_tsize, w_intensities])\n", + "\n", + "def show_benary(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " L_width=None,\n", + " target_size=None,\n", + " intensity_background=None,\n", + " intensity_cross=None,\n", + " intensity_target=None, \n", + "):\n", + "\n", + " stim = todorovic_benary_triangles(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " L_width=L_width,\n", + " target_size=target_size,\n", + " intensity_background=intensity_background,\n", + " intensity_cross=intensity_cross,\n", + " intensity_target=intensity_target, \n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_benary,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"L_width\": w_lwidth,\n", + " \"target_size\": w_tsize,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_cross\": w_icross,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/checkerboard.ipynb b/demo/illusions/checkerboard.ipynb index fe83beb1..7542dd5e 100644 --- a/demo/illusions/checkerboard.ipynb +++ b/demo/illusions/checkerboard.ipynb @@ -1,325 +1,325 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import IPython\n", - "import ipywidgets as widgets\n", - "from stimuli.illusions.checkerboards import checkerboard\n", - "from stimuli.utils import plot_stimuli, plot_stim\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization of a checkerboard stimulus" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": None,\n", - " \"ppd\": 32,\n", - " \"shape\": None,\n", - " \"frequency\": None,\n", - " \"board_shape\": (5, 8),\n", - " \"check_visual_size\": 2.0,\n", - " \"targets\": ((2, 1), (2, 6)),\n", - " \"extend_targets\": False,\n", - " \"period\": \"ignore\",\n", - " \"rotation\": 0,\n", - " \"intensity_checks\": (0.0, 1.0),\n", - " \"intensity_target\": 0.5,\n", - "}\n", - "stim = checkerboard(**params)\n", - "plot_stim(stim)\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_ppd = widgets.FloatSlider(value=10, min=1, max=32, step=0.5, description=\"PPD\")\n", - "w_intensities = widgets.FloatRangeSlider(\n", - " value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"Intensity range\"\n", - ")\n", - "w_board_height = widgets.IntSlider(value=3, min=1, max=10, description=\"height board\")\n", - "w_board_width = widgets.IntSlider(value=3, min=1, max=10, description=\"width board\")\n", - "w_board_shape = widgets.HBox([w_board_height, w_board_width])\n", - "w_check_height = widgets.FloatSlider(\n", - " value=2.0, min=0.25, max=4.0, step=0.25, description=\"height check\"\n", - ")\n", - "w_check_width = widgets.FloatSlider(\n", - " value=2.0, min=0.25, max=4.0, step=0.25, description=\"width check\"\n", - ")\n", - "w_check_size = widgets.HBox([w_check_height, w_check_width])\n", - "w_target_int = widgets.FloatSlider(\n", - " value=0.5, min=0.0, max=1.0, step=0.1, description=\"target intensity\"\n", - ")\n", - "w_target_row = widgets.IntSlider(\n", - " value=0, min=0, max=w_board_width.value, description=\"targets row\"\n", - ")\n", - "w_target_col1 = widgets.IntSlider(\n", - " value=0, min=0, max=w_board_width.value, description=\"target 1 column\"\n", - ")\n", - "w_target_col2 = widgets.IntSlider(\n", - " value=0, min=0, max=w_board_width.value, description=\"target 2 column\"\n", - ")\n", - "w_targets = widgets.HBox([w_target_row, w_target_col1, w_target_col2])\n", - "\n", - "ui = widgets.VBox([w_board_shape, w_check_size, w_ppd, w_intensities, w_target_int, w_targets])\n", - "\n", - "\n", - "def update_board(*args):\n", - " w_target_col1.max = w_board_width.value - 1\n", - " w_target_col2.max = w_board_width.value - 1\n", - "\n", - "\n", - "w_board_width.observe(update_board, \"value\")\n", - "\n", - "\n", - "def show_checkerboard(\n", - " shape=None,\n", - " ppd=None,\n", - " visual_size=None,\n", - " board_height=None,\n", - " board_width=None,\n", - " check_height=None,\n", - " check_width=None,\n", - " target_row=None,\n", - " target1_col=None,\n", - " target2_col=None,\n", - " extend_targets=False,\n", - " intensity_range=(0.0, 1.0),\n", - " intensity_target=0.5,\n", - "):\n", - " board_shape = (board_height, board_width)\n", - " check_visual_size = (check_height, check_width)\n", - " intensity_high = intensity_range[0]\n", - " intensity_low = intensity_range[1]\n", - " targets = [(target_row, target1_col), (target_row, target2_col)]\n", - " stim = checkerboard(\n", - " visual_size=visual_size,\n", - " ppd=ppd,\n", - " shape=shape,\n", - " frequency=None,\n", - " board_shape=board_shape,\n", - " check_visual_size=check_visual_size,\n", - " targets=targets,\n", - " extend_targets=extend_targets,\n", - " period=\"ignore\",\n", - " rotation=0,\n", - " intensity_checks=(intensity_low, intensity_high),\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = widgets.interactive_output(\n", - " show_checkerboard,\n", - " {\n", - " \"ppd\": w_ppd,\n", - " \"board_height\": w_board_height,\n", - " \"board_width\": w_board_width,\n", - " \"check_height\": w_check_height,\n", - " \"check_width\": w_check_width,\n", - " \"target_row\": w_target_row,\n", - " \"target1_col\": w_target_col1,\n", - " \"target2_col\": w_target_col2,\n", - " \"intensity_range\": w_intensities,\n", - " \"intensity_target\": w_target_int,\n", - " },\n", - ")\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Generate set of parameterizations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define a _list_ of options for each parameter.\n", - "# Note that if the parameter is already a sequence (e.g., board_shape=(3,7))\n", - "# it now becomes a _list_ of sequences (e.g., [ (3,7), (3,10)] )\n", - "param_lists = {\n", - " # \"shape\": [None],\n", - " \"ppd\": [32],\n", - " # \"visual_size\": [None],\n", - " \"board_shape\": [(3, 10), (3, 7), (5, 10), (7, 7)],\n", - " \"check_visual_size\": [2.0, 4.0],\n", - " \"targets\": [((1, 1), (1, 4)), ((1, 1), (1, 6)), ((1, 1), (1, 8))],\n", - " # \"extend_targets\": [False],\n", - " # \"intensity_low\": [0.0],\n", - " # \"intensity_high\": [1.0],\n", - " \"intensity_target\": [0.25, 0.5, 0.75],\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Generator for each dictionary that is a unique combination\n", - "# of all the parameter options\n", - "def product_dict(**kwargs):\n", - " keys = kwargs.keys()\n", - " vals = kwargs.values()\n", - " for instance in itertools.product(*vals):\n", - " yield dict(zip(keys, instance))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = list(product_dict(**param_lists))\n", - "params[3]\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "IPython.display.display(pd.DataFrame(params))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "template_name = \"checkerboard_{check_visual_size}_{board_shape}_{targets}_{intensity_target}\"\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "stims = {}\n", - "for d in params:\n", - " try:\n", - " stims[template_name.format(**d)] = checkerboard(**d)\n", - " except ValueError:\n", - " next\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_stimuli(stims)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import IPython\n", + "import ipywidgets as widgets\n", + "from stimupy.illusions.checkerboards import checkerboard\n", + "from stimupy.utils import plot_stimuli, plot_stim\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization of a checkerboard stimulus" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": None,\n", + " \"ppd\": 32,\n", + " \"shape\": None,\n", + " \"frequency\": None,\n", + " \"board_shape\": (5, 8),\n", + " \"check_visual_size\": 2.0,\n", + " \"targets\": ((2, 1), (2, 6)),\n", + " \"extend_targets\": False,\n", + " \"period\": \"ignore\",\n", + " \"rotation\": 0,\n", + " \"intensity_checks\": (0.0, 1.0),\n", + " \"intensity_target\": 0.5,\n", + "}\n", + "stim = checkerboard(**params)\n", + "plot_stim(stim)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_ppd = widgets.FloatSlider(value=10, min=1, max=32, step=0.5, description=\"PPD\")\n", + "w_intensities = widgets.FloatRangeSlider(\n", + " value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"Intensity range\"\n", + ")\n", + "w_board_height = widgets.IntSlider(value=3, min=1, max=10, description=\"height board\")\n", + "w_board_width = widgets.IntSlider(value=3, min=1, max=10, description=\"width board\")\n", + "w_board_shape = widgets.HBox([w_board_height, w_board_width])\n", + "w_check_height = widgets.FloatSlider(\n", + " value=2.0, min=0.25, max=4.0, step=0.25, description=\"height check\"\n", + ")\n", + "w_check_width = widgets.FloatSlider(\n", + " value=2.0, min=0.25, max=4.0, step=0.25, description=\"width check\"\n", + ")\n", + "w_check_size = widgets.HBox([w_check_height, w_check_width])\n", + "w_target_int = widgets.FloatSlider(\n", + " value=0.5, min=0.0, max=1.0, step=0.1, description=\"target intensity\"\n", + ")\n", + "w_target_row = widgets.IntSlider(\n", + " value=0, min=0, max=w_board_width.value, description=\"targets row\"\n", + ")\n", + "w_target_col1 = widgets.IntSlider(\n", + " value=0, min=0, max=w_board_width.value, description=\"target 1 column\"\n", + ")\n", + "w_target_col2 = widgets.IntSlider(\n", + " value=0, min=0, max=w_board_width.value, description=\"target 2 column\"\n", + ")\n", + "w_targets = widgets.HBox([w_target_row, w_target_col1, w_target_col2])\n", + "\n", + "ui = widgets.VBox([w_board_shape, w_check_size, w_ppd, w_intensities, w_target_int, w_targets])\n", + "\n", + "\n", + "def update_board(*args):\n", + " w_target_col1.max = w_board_width.value - 1\n", + " w_target_col2.max = w_board_width.value - 1\n", + "\n", + "\n", + "w_board_width.observe(update_board, \"value\")\n", + "\n", + "\n", + "def show_checkerboard(\n", + " shape=None,\n", + " ppd=None,\n", + " visual_size=None,\n", + " board_height=None,\n", + " board_width=None,\n", + " check_height=None,\n", + " check_width=None,\n", + " target_row=None,\n", + " target1_col=None,\n", + " target2_col=None,\n", + " extend_targets=False,\n", + " intensity_range=(0.0, 1.0),\n", + " intensity_target=0.5,\n", + "):\n", + " board_shape = (board_height, board_width)\n", + " check_visual_size = (check_height, check_width)\n", + " intensity_high = intensity_range[0]\n", + " intensity_low = intensity_range[1]\n", + " targets = [(target_row, target1_col), (target_row, target2_col)]\n", + " stim = checkerboard(\n", + " visual_size=visual_size,\n", + " ppd=ppd,\n", + " shape=shape,\n", + " frequency=None,\n", + " board_shape=board_shape,\n", + " check_visual_size=check_visual_size,\n", + " targets=targets,\n", + " extend_targets=extend_targets,\n", + " period=\"ignore\",\n", + " rotation=0,\n", + " intensity_checks=(intensity_low, intensity_high),\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = widgets.interactive_output(\n", + " show_checkerboard,\n", + " {\n", + " \"ppd\": w_ppd,\n", + " \"board_height\": w_board_height,\n", + " \"board_width\": w_board_width,\n", + " \"check_height\": w_check_height,\n", + " \"check_width\": w_check_width,\n", + " \"target_row\": w_target_row,\n", + " \"target1_col\": w_target_col1,\n", + " \"target2_col\": w_target_col2,\n", + " \"intensity_range\": w_intensities,\n", + " \"intensity_target\": w_target_int,\n", + " },\n", + ")\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generate set of parameterizations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define a _list_ of options for each parameter.\n", + "# Note that if the parameter is already a sequence (e.g., board_shape=(3,7))\n", + "# it now becomes a _list_ of sequences (e.g., [ (3,7), (3,10)] )\n", + "param_lists = {\n", + " # \"shape\": [None],\n", + " \"ppd\": [32],\n", + " # \"visual_size\": [None],\n", + " \"board_shape\": [(3, 10), (3, 7), (5, 10), (7, 7)],\n", + " \"check_visual_size\": [2.0, 4.0],\n", + " \"targets\": [((1, 1), (1, 4)), ((1, 1), (1, 6)), ((1, 1), (1, 8))],\n", + " # \"extend_targets\": [False],\n", + " # \"intensity_low\": [0.0],\n", + " # \"intensity_high\": [1.0],\n", + " \"intensity_target\": [0.25, 0.5, 0.75],\n", + "}\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generator for each dictionary that is a unique combination\n", + "# of all the parameter options\n", + "def product_dict(**kwargs):\n", + " keys = kwargs.keys()\n", + " vals = kwargs.values()\n", + " for instance in itertools.product(*vals):\n", + " yield dict(zip(keys, instance))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = list(product_dict(**param_lists))\n", + "params[3]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "IPython.display.display(pd.DataFrame(params))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "template_name = \"checkerboard_{check_visual_size}_{board_shape}_{targets}_{intensity_target}\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stims = {}\n", + "for d in params:\n", + " try:\n", + " stims[template_name.format(**d)] = checkerboard(**d)\n", + " except ValueError:\n", + " next\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot_stimuli(stims)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/circular.ipynb b/demo/illusions/circular.ipynb index 6cfd4d42..eb046aad 100644 --- a/demo/illusions/circular.ipynb +++ b/demo/illusions/circular.ipynb @@ -1,554 +1,554 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Circular components & illusions (discs, rings, circular gratings, radial/wheel-of-fortune)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## disc" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.circular import disc\n", - "help(disc)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "w_radius = iw.FloatSlider(value=1.0, min=0.1, max=4.0, description=\"radius (outer)\")\n", - "w_idisc = iw.FloatSlider(value=0.0, min=0.0, max=1.0, description=\"intensity disc\")\n", - "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", - "\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_intensities = iw.HBox([w_idisc, w_iback])\n", - "ui = iw.VBox([b_size, w_radius, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_disc(\n", - " length=None,\n", - " ppd=None,\n", - " radius=None,\n", - " intensity_disc=None,\n", - " intensity_background=None,\n", - "):\n", - "\n", - " stim = disc(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " radius=radius,\n", - " intensity_background=intensity_background,\n", - " intensity=intensity_disc,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_disc,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"radius\": w_radius,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_disc\": w_idisc,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## annulus, ring" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.circular import annulus\n", - "\n", - "help(annulus)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_radii = iw.FloatRangeSlider(\n", - " value=[1.0, 2.0], min=0.1, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", - ")\n", - "\n", - "w_idisc = iw.FloatSlider(value=0.0, min=0.0, max=1.0, description=\"intensity disc\")\n", - "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", - "\n", - "\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_intensities = iw.HBox([w_idisc, w_iback])\n", - "ui = iw.VBox([b_size, w_radii, b_intensities])\n", - "\n", - "\n", - "# Function for showing stim\n", - "def show_disc(\n", - " length=None,\n", - " ppd=None,\n", - " radii=None,\n", - " intensity_disc=None,\n", - " intensity_background=None,\n", - "):\n", - "\n", - " stim = annulus(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " radii=radii,\n", - " intensity_background=intensity_background,\n", - " intensity=intensity_disc,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_disc,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"radii\": w_radii,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_disc\": w_idisc,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## disc_and_rings" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.circular import disc_and_rings\n", - "\n", - "help(disc_and_rings)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_radius1 = iw.FloatSlider(value=0.5, min=0.1, max=3.0, description=\"1 radius [deg]\")\n", - "w_radius2 = iw.FloatSlider(value=1., min=0.1, max=3.0, description=\"2 radius [deg]\")\n", - "w_radius3 = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"3 radius [deg]\")\n", - "\n", - "\n", - "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", - "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_radii = iw.HBox([w_radius1, w_radius2, w_radius3])\n", - "b_intensities = iw.HBox([w_iback, w_irings])\n", - "ui = iw.VBox([b_size, b_radii, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_disc_rings(\n", - " length=None,\n", - " ppd=None,\n", - " radius1=None,\n", - " radius2=None,\n", - " radius3=None,\n", - " intensity_rings=None,\n", - " intensity_background=None,\n", - "):\n", - " stim = disc_and_rings(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " radii=[radius1, radius2, radius3],\n", - " intensity_background=intensity_background,\n", - " intensity_rings=intensity_rings,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_disc_rings,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"radius1\": w_radius1,\n", - " \"radius2\": w_radius2,\n", - " \"radius3\": w_radius3,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_rings\": w_irings,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## circular_grating" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.circular import grating\n", - "\n", - "help(grating)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=1, min=0.1, max=3.0, description=\"frequency\")\n", - "w_nrings = iw.IntSlider(value=4, min=1, max=10, description=\"n_rings\")\n", - "w_ring_width = iw.FloatSlider(value=0.5, min=0.1, max=3.0, description=\"ring_width\")\n", - "\n", - "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", - "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_intensities = iw.HBox([w_iback, w_irings])\n", - "b_rings = iw.HBox([w_frequency, w_nrings])\n", - "ui = iw.VBox([b_size, w_frequency, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_circular(\n", - " length=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " # ring_width=None,\n", - " #n_rings=None,\n", - " intensity_rings=None,\n", - " intensity_background=None,\n", - "):\n", - " stim = grating(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " # ring_width=ring_width,\n", - " #n_rings=n_rings,\n", - " intensity_background=intensity_background,\n", - " intensity_rings=intensity_rings,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_circular,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " # \"ring_width\": w_ring_width,\n", - " #\"n_rings\": w_nrings,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_rings\": w_irings,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Specification of `frequency`, `ring_width`, or `n_rings`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "stim_freq = grating(\n", - " visual_size=(8, 8),\n", - " ppd=32,\n", - " frequency=1.0,\n", - ")\n", - "stim_width = grating(\n", - " visual_size=(8, 8),\n", - " ppd=32,\n", - " ring_width=0.5,\n", - ")\n", - "stim_n = grating(\n", - " visual_size=(8, 8),\n", - " ppd=32,\n", - " n_rings=8,\n", - ")\n", - "plt.subplot(1,3,1)\n", - "plot_stim(stim_freq, stim_name=f\"frequency={stim_freq['frequency']}\")\n", - "\n", - "plt.subplot(1,3,2)\n", - "plot_stim(stim_width, stim_name=f\"ring_width={stim_width['ring_width']}\")\n", - "\n", - "plt.subplot(1,3,3)\n", - "plot_stim(stim_n, stim_name=f\"n_rings={stim_n['n_rings']}\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# circular_white" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.circular import circular_white\n", - "\n", - "help(circular_white)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is possible to place as many targets as there are rings by providing a list of `target_indices`.\n", - "The indices refer to the number of the respective ring going from inside to outside." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 40.0,\n", - " \"frequency\": 1.5,\n", - " \"intensity_rings\": (0., 1.),\n", - " \"intensity_background\": 0.5,\n", - " \"intensity_target\": 0.5\n", - "}\n", - "\n", - "stim1 = circular_white(**params, target_indices=(4, 6, 8, 10))\n", - "stim2 = circular_white(**params, target_indices=(3, 5, 7, 9))\n", - "\n", - "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=1, min=0.1, max=3.0, description=\"frequency\")\n", - "w_target_idx = iw.IntSlider(value=1, min=0, max=8, description=\"idx target\")\n", - "\n", - "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"background intensity\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"target intensity\")\n", - "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_intensities = iw.HBox([w_itarget, w_iback, w_irings])\n", - "b_rings = iw.HBox([w_frequency, w_target_idx])\n", - "ui = iw.VBox([b_size, b_rings, b_intensities])\n", - "\n", - "def show_circular(\n", - " length=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " target_indices=None,\n", - " intensity_rings=None,\n", - " intensity_background=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = circular_white(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " intensity_background=intensity_background,\n", - " intensity_rings=intensity_rings,\n", - " intensity_target=intensity_target,\n", - " target_indices=target_indices\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_circular,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"target_indices\": w_target_idx,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_rings\": w_irings,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# circular_bullseye\n", - "\n", - "This function produces the same stimulus as `circular_white()` but always with a single central target." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.circular import circular_bullseye\n", - "\n", - "help(circular_bullseye)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=1, min=0.1, max=3.0, description=\"frequency\")\n", - "\n", - "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"background intensity\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"target intensity\")\n", - "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_intensities = iw.HBox([w_itarget, w_iback, w_irings])\n", - "b_rings = iw.HBox([w_frequency, w_nrings])\n", - "ui = iw.VBox([b_size, w_frequency, b_intensities])\n", - "\n", - "def show_bullseye(\n", - " length=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " intensity_rings=None,\n", - " intensity_background=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = circular_bullseye(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " intensity_background=intensity_background,\n", - " intensity_rings=intensity_rings,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_bullseye,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_rings\": w_irings,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Circular components & illusions (discs, rings, circular gratings, radial/wheel-of-fortune)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## disc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.circular import disc\n", + "help(disc)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "w_radius = iw.FloatSlider(value=1.0, min=0.1, max=4.0, description=\"radius (outer)\")\n", + "w_idisc = iw.FloatSlider(value=0.0, min=0.0, max=1.0, description=\"intensity disc\")\n", + "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", + "\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_intensities = iw.HBox([w_idisc, w_iback])\n", + "ui = iw.VBox([b_size, w_radius, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_disc(\n", + " length=None,\n", + " ppd=None,\n", + " radius=None,\n", + " intensity_disc=None,\n", + " intensity_background=None,\n", + "):\n", + "\n", + " stim = disc(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " radius=radius,\n", + " intensity_background=intensity_background,\n", + " intensity=intensity_disc,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_disc,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"radius\": w_radius,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_disc\": w_idisc,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## annulus, ring" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.circular import annulus\n", + "\n", + "help(annulus)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_radii = iw.FloatRangeSlider(\n", + " value=[1.0, 2.0], min=0.1, max=5.0, step=0.1, description=\"radii (inner,outer)\"\n", + ")\n", + "\n", + "w_idisc = iw.FloatSlider(value=0.0, min=0.0, max=1.0, description=\"intensity disc\")\n", + "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", + "\n", + "\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_intensities = iw.HBox([w_idisc, w_iback])\n", + "ui = iw.VBox([b_size, w_radii, b_intensities])\n", + "\n", + "\n", + "# Function for showing stim\n", + "def show_disc(\n", + " length=None,\n", + " ppd=None,\n", + " radii=None,\n", + " intensity_disc=None,\n", + " intensity_background=None,\n", + "):\n", + "\n", + " stim = annulus(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " radii=radii,\n", + " intensity_background=intensity_background,\n", + " intensity=intensity_disc,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_disc,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"radii\": w_radii,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_disc\": w_idisc,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## disc_and_rings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.circular import disc_and_rings\n", + "\n", + "help(disc_and_rings)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_radius1 = iw.FloatSlider(value=0.5, min=0.1, max=3.0, description=\"1 radius [deg]\")\n", + "w_radius2 = iw.FloatSlider(value=1., min=0.1, max=3.0, description=\"2 radius [deg]\")\n", + "w_radius3 = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"3 radius [deg]\")\n", + "\n", + "\n", + "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", + "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_radii = iw.HBox([w_radius1, w_radius2, w_radius3])\n", + "b_intensities = iw.HBox([w_iback, w_irings])\n", + "ui = iw.VBox([b_size, b_radii, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_disc_rings(\n", + " length=None,\n", + " ppd=None,\n", + " radius1=None,\n", + " radius2=None,\n", + " radius3=None,\n", + " intensity_rings=None,\n", + " intensity_background=None,\n", + "):\n", + " stim = disc_and_rings(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " radii=[radius1, radius2, radius3],\n", + " intensity_background=intensity_background,\n", + " intensity_rings=intensity_rings,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_disc_rings,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"radius1\": w_radius1,\n", + " \"radius2\": w_radius2,\n", + " \"radius3\": w_radius3,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_rings\": w_irings,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## circular_grating" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.circular import grating\n", + "\n", + "help(grating)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=1, min=0.1, max=3.0, description=\"frequency\")\n", + "w_nrings = iw.IntSlider(value=4, min=1, max=10, description=\"n_rings\")\n", + "w_ring_width = iw.FloatSlider(value=0.5, min=0.1, max=3.0, description=\"ring_width\")\n", + "\n", + "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"intensity background\")\n", + "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_intensities = iw.HBox([w_iback, w_irings])\n", + "b_rings = iw.HBox([w_frequency, w_nrings])\n", + "ui = iw.VBox([b_size, w_frequency, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_circular(\n", + " length=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " # ring_width=None,\n", + " #n_rings=None,\n", + " intensity_rings=None,\n", + " intensity_background=None,\n", + "):\n", + " stim = grating(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " # ring_width=ring_width,\n", + " #n_rings=n_rings,\n", + " intensity_background=intensity_background,\n", + " intensity_rings=intensity_rings,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_circular,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " # \"ring_width\": w_ring_width,\n", + " #\"n_rings\": w_nrings,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_rings\": w_irings,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Specification of `frequency`, `ring_width`, or `n_rings`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stim_freq = grating(\n", + " visual_size=(8, 8),\n", + " ppd=32,\n", + " frequency=1.0,\n", + ")\n", + "stim_width = grating(\n", + " visual_size=(8, 8),\n", + " ppd=32,\n", + " ring_width=0.5,\n", + ")\n", + "stim_n = grating(\n", + " visual_size=(8, 8),\n", + " ppd=32,\n", + " n_rings=8,\n", + ")\n", + "plt.subplot(1,3,1)\n", + "plot_stim(stim_freq, stim_name=f\"frequency={stim_freq['frequency']}\")\n", + "\n", + "plt.subplot(1,3,2)\n", + "plot_stim(stim_width, stim_name=f\"ring_width={stim_width['ring_width']}\")\n", + "\n", + "plt.subplot(1,3,3)\n", + "plot_stim(stim_n, stim_name=f\"n_rings={stim_n['n_rings']}\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# circular_white" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.circular import circular_white\n", + "\n", + "help(circular_white)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is possible to place as many targets as there are rings by providing a list of `target_indices`.\n", + "The indices refer to the number of the respective ring going from inside to outside." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 40.0,\n", + " \"frequency\": 1.5,\n", + " \"intensity_rings\": (0., 1.),\n", + " \"intensity_background\": 0.5,\n", + " \"intensity_target\": 0.5\n", + "}\n", + "\n", + "stim1 = circular_white(**params, target_indices=(4, 6, 8, 10))\n", + "stim2 = circular_white(**params, target_indices=(3, 5, 7, 9))\n", + "\n", + "plot_stimuli({\"Example 1\": stim1, \"Example 2\": stim2})\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=1, min=0.1, max=3.0, description=\"frequency\")\n", + "w_target_idx = iw.IntSlider(value=1, min=0, max=8, description=\"idx target\")\n", + "\n", + "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"background intensity\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"target intensity\")\n", + "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_intensities = iw.HBox([w_itarget, w_iback, w_irings])\n", + "b_rings = iw.HBox([w_frequency, w_target_idx])\n", + "ui = iw.VBox([b_size, b_rings, b_intensities])\n", + "\n", + "def show_circular(\n", + " length=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " target_indices=None,\n", + " intensity_rings=None,\n", + " intensity_background=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = circular_white(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " intensity_background=intensity_background,\n", + " intensity_rings=intensity_rings,\n", + " intensity_target=intensity_target,\n", + " target_indices=target_indices\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_circular,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"target_indices\": w_target_idx,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_rings\": w_irings,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# circular_bullseye\n", + "\n", + "This function produces the same stimulus as `circular_white()` but always with a single central target." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.circular import circular_bullseye\n", + "\n", + "help(circular_bullseye)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=5, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=1, min=0.1, max=3.0, description=\"frequency\")\n", + "\n", + "w_iback = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"background intensity\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0.0, max=1.0, description=\"target intensity\")\n", + "w_irings = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_intensities = iw.HBox([w_itarget, w_iback, w_irings])\n", + "b_rings = iw.HBox([w_frequency, w_nrings])\n", + "ui = iw.VBox([b_size, w_frequency, b_intensities])\n", + "\n", + "def show_bullseye(\n", + " length=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " intensity_rings=None,\n", + " intensity_background=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = circular_bullseye(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " intensity_background=intensity_background,\n", + " intensity_rings=intensity_rings,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_bullseye,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_rings\": w_irings,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/cornsweet.ipynb b/demo/illusions/cornsweet.ipynb index 3e355630..b3a62f78 100644 --- a/demo/illusions/cornsweet.ipynb +++ b/demo/illusions/cornsweet.ipynb @@ -1,169 +1,169 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# cornsweet" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.cornsweet import cornsweet" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"intensity_max\": 1.,\n", - " \"intensity_min\": 0.,\n", - " \"intensity_plateau\": 0.5,\n", - " \"ramp_width\": 3,\n", - " \"exponent\": 2.75,\n", - "}\n", - "\n", - "stim = cornsweet(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=10, min=10, max=30, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=10, min=10, max=30, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_rwidth = iw.FloatSlider(value=5., min=0.1, max=10.0, description=\"ramp width\")\n", - "w_exp = iw.FloatSlider(value=2.75, min=0.5, max=5.0, description=\"ramp exponent\")\n", - "w_ramp = iw.HBox([w_rwidth, w_exp])\n", - "w_imax = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"max intensity\")\n", - "w_imin = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"min intensity\")\n", - "w_iplateau = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity plateau\")\n", - "w_intensities = iw.HBox([w_imin, w_imax, w_iplateau])\n", - "\n", - "ui = iw.VBox([w_size, w_ramp, w_intensities])\n", - "\n", - "def show_cornsweet(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " intensity_min=None,\n", - " intensity_max=None,\n", - " intensity_plateau=None,\n", - " ramp_width=None,\n", - " exponent=None,\n", - "):\n", - "\n", - " stim = cornsweet(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " intensity_min=intensity_min,\n", - " intensity_max=intensity_max,\n", - " intensity_plateau=intensity_plateau,\n", - " ramp_width=ramp_width,\n", - " exponent=exponent\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_cornsweet,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"intensity_min\": w_imin,\n", - " \"intensity_max\": w_imax,\n", - " \"intensity_plateau\": w_iplateau,\n", - " \"ramp_width\": w_rwidth,\n", - " \"exponent\": w_exp,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.13" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# cornsweet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.cornsweet import cornsweet" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"intensity_max\": 1.,\n", + " \"intensity_min\": 0.,\n", + " \"intensity_plateau\": 0.5,\n", + " \"ramp_width\": 3,\n", + " \"exponent\": 2.75,\n", + "}\n", + "\n", + "stim = cornsweet(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=10, min=10, max=30, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=10, min=10, max=30, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=1, max=64, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_rwidth = iw.FloatSlider(value=5., min=0.1, max=10.0, description=\"ramp width\")\n", + "w_exp = iw.FloatSlider(value=2.75, min=0.5, max=5.0, description=\"ramp exponent\")\n", + "w_ramp = iw.HBox([w_rwidth, w_exp])\n", + "w_imax = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"max intensity\")\n", + "w_imin = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"min intensity\")\n", + "w_iplateau = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity plateau\")\n", + "w_intensities = iw.HBox([w_imin, w_imax, w_iplateau])\n", + "\n", + "ui = iw.VBox([w_size, w_ramp, w_intensities])\n", + "\n", + "def show_cornsweet(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " intensity_min=None,\n", + " intensity_max=None,\n", + " intensity_plateau=None,\n", + " ramp_width=None,\n", + " exponent=None,\n", + "):\n", + "\n", + " stim = cornsweet(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " intensity_min=intensity_min,\n", + " intensity_max=intensity_max,\n", + " intensity_plateau=intensity_plateau,\n", + " ramp_width=ramp_width,\n", + " exponent=exponent\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_cornsweet,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"intensity_min\": w_imin,\n", + " \"intensity_max\": w_imax,\n", + " \"intensity_plateau\": w_iplateau,\n", + " \"ramp_width\": w_rwidth,\n", + " \"exponent\": w_exp,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.13" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/frames.ipynb b/demo/illusions/frames.ipynb index 048b79a2..a157912b 100644 --- a/demo/illusions/frames.ipynb +++ b/demo/illusions/frames.ipynb @@ -1,297 +1,297 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Square frames" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Frames (as grating)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.frame import square_wave\n", - "help(square_wave)\n", - "\n", - "# Function for showing stim\n", - "def show_square_wave(\n", - " length=None,\n", - " visual_angle=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " n_phases=None,\n", - " phase_width=None,\n", - " period=\"ignore\",\n", - " use_params=None,\n", - " intensity_elements=(1.0, 0.0),\n", - "):\n", - " stim = square_wave(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_frames=n_phases if \"n_phases\" in use_params else None,\n", - " frame_width=phase_width if \"phase_width\" in use_params else None,\n", - " period=period,\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Size widgets\n", - "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "# Grating widgets\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", - "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", - "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", - "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", - "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", - "w_ielements = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_resolution = iw.HBox([w_length, w_ppd])\n", - "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period])\n", - "ui = iw.VBox([b_resolution, b_grating_params])\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_square_wave,\n", - " {\n", - " \"visual_angle\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"n_phases\": w_phases,\n", - " \"phase_width\": w_phase_width,\n", - " \"period\": w_period,\n", - " \"use_params\": w_use_which,\n", - " \"intensity_elements\": w_ielements,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## illusions.frames" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.frames import frames as square_wave_illusion\n", - "help(square_wave_illusion)\n", - "\n", - "# Function for showing stim\n", - "def show_square_wave_illusion(\n", - " length=None,\n", - " visual_angle=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " n_phases=None,\n", - " phase_width=None,\n", - " period=\"ignore\",\n", - " use_params=None,\n", - " intensity_elements=(1.0, 0.0),\n", - " target_indices=2,\n", - " intensity_target=0.5\n", - "):\n", - " stim = square_wave_illusion(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_frames=n_phases if \"n_phases\" in use_params else None,\n", - " frame_width=phase_width if \"phase_width\" in use_params else None,\n", - " period=period,\n", - " target_indices=target_indices,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "\n", - "# Size widgets\n", - "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "# Grating widgets\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", - "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", - "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", - "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", - "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", - "w_ielements = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Target widges\n", - "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "\n", - "# Layout\n", - "b_resolution = iw.HBox([w_length, w_ppd])\n", - "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period, w_ielements])\n", - "b_target = iw.HBox([w_tidx, w_itarget])\n", - "ui = iw.VBox([b_resolution, b_grating_params, b_target])\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_square_wave_illusion,\n", - " {\n", - " \"visual_angle\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"n_phases\": w_phases,\n", - " \"phase_width\": w_phase_width,\n", - " \"period\": w_period,\n", - " \"use_params\": w_use_which,\n", - " \"intensity_elements\": w_ielements,\n", - " \"target_indices\": w_tidx,\n", - " \"intensity_target\": w_itarget,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## ilusions.bullseye" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.frames import bullseye\n", - "help(bullseye)\n", - "\n", - "# Function for showing stim\n", - "def show_bullseye(\n", - " length=None,\n", - " visual_angle=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " n_phases=None,\n", - " phase_width=None,\n", - " period=\"ignore\",\n", - " use_params=None,\n", - " intensity_elements=(1.0, 0.0),\n", - " intensity_target=0.5\n", - "):\n", - " stim = bullseye(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_frames=n_phases if \"n_phases\" in use_params else None,\n", - " frame_width=phase_width if \"phase_width\" in use_params else None,\n", - " period=period,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "\n", - "# Size widgets\n", - "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "# Grating widgets\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", - "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", - "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", - "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", - "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", - "w_ielements = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Target widges\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "\n", - "# Layout\n", - "b_resolution = iw.HBox([w_length, w_ppd])\n", - "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period, w_ielements])\n", - "b_target = iw.HBox([w_itarget])\n", - "ui = iw.VBox([b_resolution, b_grating_params, b_target])\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_bullseye,\n", - " {\n", - " \"visual_angle\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"n_phases\": w_phases,\n", - " \"phase_width\": w_phase_width,\n", - " \"period\": w_period,\n", - " \"use_params\": w_use_which,\n", - " \"intensity_elements\": w_ielements,\n", - " \"intensity_target\": w_itarget,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Square frames" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Frames (as grating)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.frame import square_wave\n", + "help(square_wave)\n", + "\n", + "# Function for showing stim\n", + "def show_square_wave(\n", + " length=None,\n", + " visual_angle=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " n_phases=None,\n", + " phase_width=None,\n", + " period=\"ignore\",\n", + " use_params=None,\n", + " intensity_elements=(1.0, 0.0),\n", + "):\n", + " stim = square_wave(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_frames=n_phases if \"n_phases\" in use_params else None,\n", + " frame_width=phase_width if \"phase_width\" in use_params else None,\n", + " period=period,\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Size widgets\n", + "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "# Grating widgets\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", + "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", + "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", + "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", + "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", + "w_ielements = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_resolution = iw.HBox([w_length, w_ppd])\n", + "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period])\n", + "ui = iw.VBox([b_resolution, b_grating_params])\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_square_wave,\n", + " {\n", + " \"visual_angle\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"n_phases\": w_phases,\n", + " \"phase_width\": w_phase_width,\n", + " \"period\": w_period,\n", + " \"use_params\": w_use_which,\n", + " \"intensity_elements\": w_ielements,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## illusions.frames" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.frames import frames as square_wave_illusion\n", + "help(square_wave_illusion)\n", + "\n", + "# Function for showing stim\n", + "def show_square_wave_illusion(\n", + " length=None,\n", + " visual_angle=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " n_phases=None,\n", + " phase_width=None,\n", + " period=\"ignore\",\n", + " use_params=None,\n", + " intensity_elements=(1.0, 0.0),\n", + " target_indices=2,\n", + " intensity_target=0.5\n", + "):\n", + " stim = square_wave_illusion(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_frames=n_phases if \"n_phases\" in use_params else None,\n", + " frame_width=phase_width if \"phase_width\" in use_params else None,\n", + " period=period,\n", + " target_indices=target_indices,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "\n", + "# Size widgets\n", + "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "# Grating widgets\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", + "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", + "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", + "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", + "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", + "w_ielements = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Target widges\n", + "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "\n", + "# Layout\n", + "b_resolution = iw.HBox([w_length, w_ppd])\n", + "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period, w_ielements])\n", + "b_target = iw.HBox([w_tidx, w_itarget])\n", + "ui = iw.VBox([b_resolution, b_grating_params, b_target])\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_square_wave_illusion,\n", + " {\n", + " \"visual_angle\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"n_phases\": w_phases,\n", + " \"phase_width\": w_phase_width,\n", + " \"period\": w_period,\n", + " \"use_params\": w_use_which,\n", + " \"intensity_elements\": w_ielements,\n", + " \"target_indices\": w_tidx,\n", + " \"intensity_target\": w_itarget,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ilusions.bullseye" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.frames import bullseye\n", + "help(bullseye)\n", + "\n", + "# Function for showing stim\n", + "def show_bullseye(\n", + " length=None,\n", + " visual_angle=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " n_phases=None,\n", + " phase_width=None,\n", + " period=\"ignore\",\n", + " use_params=None,\n", + " intensity_elements=(1.0, 0.0),\n", + " intensity_target=0.5\n", + "):\n", + " stim = bullseye(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_frames=n_phases if \"n_phases\" in use_params else None,\n", + " frame_width=phase_width if \"phase_width\" in use_params else None,\n", + " period=period,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "\n", + "# Size widgets\n", + "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "# Grating widgets\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", + "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", + "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", + "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", + "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", + "w_ielements = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Target widges\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "\n", + "# Layout\n", + "b_resolution = iw.HBox([w_length, w_ppd])\n", + "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period, w_ielements])\n", + "b_target = iw.HBox([w_itarget])\n", + "ui = iw.VBox([b_resolution, b_grating_params, b_target])\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_bullseye,\n", + " {\n", + " \"visual_angle\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"n_phases\": w_phases,\n", + " \"phase_width\": w_phase_width,\n", + " \"period\": w_period,\n", + " \"use_params\": w_use_which,\n", + " \"intensity_elements\": w_ielements,\n", + " \"intensity_target\": w_itarget,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/grating.ipynb b/demo/illusions/grating.ipynb index e7195389..c2f54a26 100644 --- a/demo/illusions/grating.ipynb +++ b/demo/illusions/grating.ipynb @@ -1,364 +1,364 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Grating components, illusions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## square_wave grating" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.grating import square_wave\n", - "help(square_wave)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=1.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", - "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", - "\n", - "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", - "\n", - "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_frequency = iw.HBox([w_frequency, w_period])\n", - "b_intensities = iw.HBox([w_ibars])\n", - "ui = iw.VBox([b_size, b_frequency, w_orientation, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_grating(\n", - " length=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " intensity_bars=None,\n", - " period=\"ignore\",\n", - " orientation='horizontal',\n", - "):\n", - " stim = square_wave(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " intensity_bars=intensity_bars,\n", - " period=period,\n", - " orientation=orientation,\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_grating,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"intensity_bars\": w_ibars,\n", - " \"period\": w_period,\n", - " \"orientation\": w_orientation,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## illusions.grating\n", - "A (square-wave) grating, with target(s) embedded in it" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.grating import square_wave\n", - "help(square_wave)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=1.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", - "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", - "\n", - "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", - "\n", - "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", - "\n", - "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_frequency = iw.HBox([w_frequency, w_period])\n", - "b_intensities = iw.HBox([w_ibars, w_itarget])\n", - "ui = iw.VBox([b_size, b_frequency, w_orientation, w_tidx, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_grating(\n", - " length=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " intensity_bars=None,\n", - " period=\"ignore\",\n", - " orientation='horizontal',\n", - " target_indices=2,\n", - " intensity_target=0.5\n", - "):\n", - " stim = square_wave(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " intensity_bars=intensity_bars,\n", - " period=period,\n", - " orientation=orientation,\n", - " target_indices=target_indices,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_grating,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"intensity_bars\": w_ibars,\n", - " \"period\": w_period,\n", - " \"orientation\": w_orientation,\n", - " \"target_indices\": w_tidx,\n", - " \"intensity_target\": w_itarget\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## illusions.grating_induction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.grating import grating_induction\n", - "help(grating_induction)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=6, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", - "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", - "\n", - "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", - "\n", - "w_twidth = iw.FloatSlider(value=2, min=0, max=4, description=\"width target\")\n", - "\n", - "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_frequency = iw.HBox([w_frequency, w_period])\n", - "b_intensities = iw.HBox([w_ibars, w_itarget])\n", - "ui = iw.VBox([b_size, b_frequency, w_orientation, w_twidth, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_GI(\n", - " length=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " intensity_bars=None,\n", - " period=\"ignore\",\n", - " orientation='horizontal',\n", - " target_width=2,\n", - " intensity_target=0.5,\n", - "):\n", - " stim = grating_induction(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " intensity_bars=intensity_bars,\n", - " period=period,\n", - " orientation=orientation,\n", - " target_width=target_width,\n", - " intensity_target=intensity_target\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_GI,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"intensity_bars\": w_ibars,\n", - " \"period\": w_period,\n", - " \"orientation\": w_orientation,\n", - " \"target_width\": w_twidth,\n", - " \"intensity_target\": w_itarget,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## illusions.grating_grating_shifted" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.grating import grating_grating_shifted\n", - "help(grating_grating_shifted)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=6, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", - "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", - "\n", - "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", - "\n", - "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", - "\n", - "w_shiftwidth = iw.FloatSlider(value=2, min=0, max=4, description=\"width target\")\n", - "\n", - "\n", - "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "\n", - "\n", - "# Layout\n", - "b_size = iw.HBox([w_length, w_ppd])\n", - "b_frequency = iw.HBox([w_frequency, w_period])\n", - "b_intensities = iw.HBox([w_ibars, w_itarget])\n", - "ui = iw.VBox([b_size, b_frequency, w_orientation, w_tidx, w_shiftwidth, b_intensities])\n", - "\n", - "# Function for showing stim\n", - "def show_ggs(\n", - " length=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " intensity_bars=None,\n", - " period=\"ignore\",\n", - " orientation='horizontal',\n", - " target_indices=2,\n", - " intensity_target=0.5,\n", - " shifted_width=2.0\n", - "):\n", - " stim = grating_grating_shifted(\n", - " visual_size=(length, length),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " intensity_bars=intensity_bars,\n", - " period=period,\n", - " orientation=orientation,\n", - " target_indices=target_indices,\n", - " intensity_target=intensity_target,\n", - " shifted_width=shifted_width,\n", - " )\n", - " plot_stim(stim, mask=False)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_ggs,\n", - " {\n", - " \"length\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"intensity_bars\": w_ibars,\n", - " \"period\": w_period,\n", - " \"orientation\": w_orientation,\n", - " \"target_indices\": w_tidx,\n", - " \"intensity_target\": w_itarget,\n", - " \"shifted_width\": w_shiftwidth,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Grating components, illusions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## square_wave grating" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.grating import square_wave\n", + "help(square_wave)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=1.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", + "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", + "\n", + "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", + "\n", + "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_frequency = iw.HBox([w_frequency, w_period])\n", + "b_intensities = iw.HBox([w_ibars])\n", + "ui = iw.VBox([b_size, b_frequency, w_orientation, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_grating(\n", + " length=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " intensity_bars=None,\n", + " period=\"ignore\",\n", + " orientation='horizontal',\n", + "):\n", + " stim = square_wave(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " intensity_bars=intensity_bars,\n", + " period=period,\n", + " orientation=orientation,\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_grating,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"intensity_bars\": w_ibars,\n", + " \"period\": w_period,\n", + " \"orientation\": w_orientation,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## illusions.grating\n", + "A (square-wave) grating, with target(s) embedded in it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.grating import square_wave\n", + "help(square_wave)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=1.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", + "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", + "\n", + "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", + "\n", + "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", + "\n", + "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_frequency = iw.HBox([w_frequency, w_period])\n", + "b_intensities = iw.HBox([w_ibars, w_itarget])\n", + "ui = iw.VBox([b_size, b_frequency, w_orientation, w_tidx, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_grating(\n", + " length=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " intensity_bars=None,\n", + " period=\"ignore\",\n", + " orientation='horizontal',\n", + " target_indices=2,\n", + " intensity_target=0.5\n", + "):\n", + " stim = square_wave(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " intensity_bars=intensity_bars,\n", + " period=period,\n", + " orientation=orientation,\n", + " target_indices=target_indices,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_grating,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"intensity_bars\": w_ibars,\n", + " \"period\": w_period,\n", + " \"orientation\": w_orientation,\n", + " \"target_indices\": w_tidx,\n", + " \"intensity_target\": w_itarget\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## illusions.grating_induction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.grating import grating_induction\n", + "help(grating_induction)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=6, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", + "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", + "\n", + "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", + "\n", + "w_twidth = iw.FloatSlider(value=2, min=0, max=4, description=\"width target\")\n", + "\n", + "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_frequency = iw.HBox([w_frequency, w_period])\n", + "b_intensities = iw.HBox([w_ibars, w_itarget])\n", + "ui = iw.VBox([b_size, b_frequency, w_orientation, w_twidth, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_GI(\n", + " length=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " intensity_bars=None,\n", + " period=\"ignore\",\n", + " orientation='horizontal',\n", + " target_width=2,\n", + " intensity_target=0.5,\n", + "):\n", + " stim = grating_induction(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " intensity_bars=intensity_bars,\n", + " period=period,\n", + " orientation=orientation,\n", + " target_width=target_width,\n", + " intensity_target=intensity_target\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_GI,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"intensity_bars\": w_ibars,\n", + " \"period\": w_period,\n", + " \"orientation\": w_orientation,\n", + " \"target_width\": w_twidth,\n", + " \"intensity_target\": w_itarget,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## illusions.grating_grating_shifted" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.grating import grating_grating_shifted\n", + "help(grating_grating_shifted)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=6, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency [cpd]\")\n", + "w_period = iw.ToggleButtons(options=['ignore', 'full', 'half'], value='ignore', button_style='', description=\"ensure period\")\n", + "\n", + "w_orientation = iw.ToggleButtons(options=['horizontal','vertical'], value='horizontal', button_style='', description=\"orientation\")\n", + "\n", + "w_tidx = iw.IntSlider(value=2, min=1, max=20, description=\"idx target\")\n", + "\n", + "w_shiftwidth = iw.FloatSlider(value=2, min=0, max=4, description=\"width target\")\n", + "\n", + "\n", + "w_ibars = iw.FloatRangeSlider(value=[0.0, 1.0], min=0.0, max=1.0, step=0.1, description=\"intensities\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "\n", + "\n", + "# Layout\n", + "b_size = iw.HBox([w_length, w_ppd])\n", + "b_frequency = iw.HBox([w_frequency, w_period])\n", + "b_intensities = iw.HBox([w_ibars, w_itarget])\n", + "ui = iw.VBox([b_size, b_frequency, w_orientation, w_tidx, w_shiftwidth, b_intensities])\n", + "\n", + "# Function for showing stim\n", + "def show_ggs(\n", + " length=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " intensity_bars=None,\n", + " period=\"ignore\",\n", + " orientation='horizontal',\n", + " target_indices=2,\n", + " intensity_target=0.5,\n", + " shifted_width=2.0\n", + "):\n", + " stim = grating_grating_shifted(\n", + " visual_size=(length, length),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " intensity_bars=intensity_bars,\n", + " period=period,\n", + " orientation=orientation,\n", + " target_indices=target_indices,\n", + " intensity_target=intensity_target,\n", + " shifted_width=shifted_width,\n", + " )\n", + " plot_stim(stim, mask=False)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_ggs,\n", + " {\n", + " \"length\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"intensity_bars\": w_ibars,\n", + " \"period\": w_period,\n", + " \"orientation\": w_orientation,\n", + " \"target_indices\": w_tidx,\n", + " \"intensity_target\": w_itarget,\n", + " \"shifted_width\": w_shiftwidth,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/grating_parameters.ipynb b/demo/illusions/grating_parameters.ipynb index 3cf19eff..0cd54c4a 100644 --- a/demo/illusions/grating_parameters.ipynb +++ b/demo/illusions/grating_parameters.ipynb @@ -1,316 +1,316 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Image dimensions, gratings" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dimensions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components import image_base\n", - "help(image_base)\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "w_rotation = iw.IntSlider(value=0, min=0, max=360, description=\"rotation\")\n", - "w_originv = iw.FloatSlider(value=4.0, min=0.0, max=10.0, description=\"v. origin [deg]\")\n", - "w_originh = iw.FloatSlider(value=4.0, min=0.0, max=10.0, description=\"h. origin [deg]\")\n", - "\n", - "# Layout\n", - "b_resolution = iw.HBox([w_length, w_ppd])\n", - "b_center = iw.HBox([w_originv, w_originh])\n", - "ui = iw.VBox([b_resolution, w_rotation, b_center])\n", - "\n", - "# Function for showing\n", - "def show_image_base(\n", - " visual_angle=None,\n", - " ppd=None,\n", - " rotation=None,\n", - " vcenter=None,\n", - " hcenter=None,\n", - "):\n", - " base = image_base(visual_size=visual_angle, ppd=ppd, rotation=rotation, origin=(vcenter, hcenter))\n", - "\n", - " stimuli = {key: {\"img\": value} for key, value in base.items() if key in [\"horizontal\", \"vertical\", \"angular\", \"radial\", \"cityblock\"]}\n", - "\n", - " plot_stimuli(stimuli, vmin=None, vmax=None)\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_image_base,\n", - " {\n", - " \"visual_angle\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"rotation\": w_rotation,\n", - " \"vcenter\": w_originv,\n", - " \"hcenter\": w_originh,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Gratings are parameterized along single dimension" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components import resolve_grating_params\n", - "help(resolve_grating_params)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", - "w_phases = iw.IntSlider(value=4, min=0, max=10, description=\"N phases\")\n", - "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", - "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", - "\n", - "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", - "\n", - "# Layout\n", - "b_resolution = iw.HBox([w_length, w_ppd])\n", - "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period])\n", - "ui = iw.VBox([b_resolution, b_grating_params])\n", - "\n", - "# Function for resolving\n", - "def resolve_params(\n", - " length=None,\n", - " visual_angle=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " n_phases=None,\n", - " phase_width=None,\n", - " period=\"ignore\",\n", - " use_params=None,\n", - "):\n", - " params = resolve_grating_params(\n", - " length=length,\n", - " visual_angle=visual_angle,\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_phases=n_phases if \"n_phases\" in use_params else None,\n", - " phase_width=phase_width if \"phase_width\" in use_params else None,\n", - " period=period,\n", - " )\n", - " print(params)\n", - "\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " resolve_params,\n", - " {\n", - " \"visual_angle\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"n_phases\": w_phases,\n", - " \"phase_width\": w_phase_width,\n", - " \"period\": w_period,\n", - " \"use_params\": w_use_which,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.components.grating import square_wave as linear\n", - "from stimuli.components.frame import square_wave as frames\n", - "from stimuli.components.circular import grating as circular\n", - "from stimuli.components.angular import grating as angular\n", - "from stimuli.components.angular import pinwheel\n", - "\n", - "# Define widgets\n", - "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", - "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", - "\n", - "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", - "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", - "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", - "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", - "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", - "\n", - "# Layout\n", - "b_resolution = iw.HBox([w_length, w_ppd])\n", - "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period])\n", - "ui = iw.VBox([b_resolution, b_grating_params])\n", - "\n", - "# Function for resolving\n", - "def show_gratings(\n", - " length=None,\n", - " visual_angle=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " n_phases=None,\n", - " phase_width=None,\n", - " period=\"ignore\",\n", - " use_params=None,\n", - "):\n", - " stimuli = {\n", - " \"horizontal\": linear(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_bars=n_phases if \"n_phases\" in use_params else None,\n", - " bar_width=phase_width if \"phase_width\" in use_params else None,\n", - " period=period,\n", - " orientation=\"horizontal\",\n", - " ),\n", - " \"vertical\": linear(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_bars=n_phases if \"n_phases\" in use_params else None,\n", - " bar_width=phase_width if \"phase_width\" in use_params else None,\n", - " period=period,\n", - " orientation=\"vertical\",\n", - " ),\n", - " \"frames\": frames(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_frames=n_phases if \"n_phases\" in use_params else None,\n", - " frame_width=phase_width if \"phase_width\" in use_params else None,\n", - " period=period,\n", - " ),\n", - " \"circular\": circular(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_rings=n_phases if \"n_phases\" in use_params else None,\n", - " ring_width=phase_width if \"phase_width\" in use_params else None,\n", - " #period=period,\n", - " ),\n", - " \"angular\": angular(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_segments=n_phases if \"n_phases\" in use_params else None,\n", - " segment_width=phase_width if \"phase_width\" in use_params else None,\n", - " ),\n", - " \"pinwheel\": pinwheel(\n", - " shape=(length, length),\n", - " visual_size=(visual_angle, visual_angle),\n", - " ppd=ppd,\n", - " frequency=frequency if \"frequency\" in use_params else None,\n", - " n_segments=n_phases if \"n_phases\" in use_params else None,\n", - " segment_width=phase_width if \"phase_width\" in use_params else None,\n", - " )\n", - " }\n", - " plot_stimuli(stimuli)\n", - "\n", - "\n", - "# Set interactivity\n", - "out = iw.interactive_output(\n", - " show_gratings,\n", - " {\n", - " \"visual_angle\": w_length,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_frequency,\n", - " \"n_phases\": w_phases,\n", - " \"phase_width\": w_phase_width,\n", - " \"period\": w_period,\n", - " \"use_params\": w_use_which,\n", - " },\n", - ")\n", - "\n", - "# Show\n", - "display(ui, out)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": true, - "title_cell": "Contents", - "title_sidebar": "Contents", - "toc_cell": true, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": true - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Image dimensions, gratings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dimensions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components import image_base\n", + "help(image_base)\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "w_rotation = iw.IntSlider(value=0, min=0, max=360, description=\"rotation\")\n", + "w_originv = iw.FloatSlider(value=4.0, min=0.0, max=10.0, description=\"v. origin [deg]\")\n", + "w_originh = iw.FloatSlider(value=4.0, min=0.0, max=10.0, description=\"h. origin [deg]\")\n", + "\n", + "# Layout\n", + "b_resolution = iw.HBox([w_length, w_ppd])\n", + "b_center = iw.HBox([w_originv, w_originh])\n", + "ui = iw.VBox([b_resolution, w_rotation, b_center])\n", + "\n", + "# Function for showing\n", + "def show_image_base(\n", + " visual_angle=None,\n", + " ppd=None,\n", + " rotation=None,\n", + " vcenter=None,\n", + " hcenter=None,\n", + "):\n", + " base = image_base(visual_size=visual_angle, ppd=ppd, rotation=rotation, origin=(vcenter, hcenter))\n", + "\n", + " stimuli = {key: {\"img\": value} for key, value in base.items() if key in [\"horizontal\", \"vertical\", \"angular\", \"radial\", \"cityblock\"]}\n", + "\n", + " plot_stimuli(stimuli, vmin=None, vmax=None)\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_image_base,\n", + " {\n", + " \"visual_angle\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"rotation\": w_rotation,\n", + " \"vcenter\": w_originv,\n", + " \"hcenter\": w_originh,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Gratings are parameterized along single dimension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components import resolve_grating_params\n", + "help(resolve_grating_params)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", + "w_phases = iw.IntSlider(value=4, min=0, max=10, description=\"N phases\")\n", + "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", + "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", + "\n", + "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", + "\n", + "# Layout\n", + "b_resolution = iw.HBox([w_length, w_ppd])\n", + "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period])\n", + "ui = iw.VBox([b_resolution, b_grating_params])\n", + "\n", + "# Function for resolving\n", + "def resolve_params(\n", + " length=None,\n", + " visual_angle=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " n_phases=None,\n", + " phase_width=None,\n", + " period=\"ignore\",\n", + " use_params=None,\n", + "):\n", + " params = resolve_grating_params(\n", + " length=length,\n", + " visual_angle=visual_angle,\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_phases=n_phases if \"n_phases\" in use_params else None,\n", + " phase_width=phase_width if \"phase_width\" in use_params else None,\n", + " period=period,\n", + " )\n", + " print(params)\n", + "\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " resolve_params,\n", + " {\n", + " \"visual_angle\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"n_phases\": w_phases,\n", + " \"phase_width\": w_phase_width,\n", + " \"period\": w_period,\n", + " \"use_params\": w_use_which,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.components.grating import square_wave as linear\n", + "from stimupy.components.frame import square_wave as frames\n", + "from stimupy.components.circular import grating as circular\n", + "from stimupy.components.angular import grating as angular\n", + "from stimupy.components.angular import pinwheel\n", + "\n", + "# Define widgets\n", + "w_length = iw.IntSlider(value=8.0, min=1, max=10, description=\"heigh/width [deg]\")\n", + "w_ppd = iw.IntSlider(value=32, min=1, max=64, description=\"ppd\")\n", + "\n", + "w_frequency = iw.FloatSlider(value=2.0, min=0.1, max=3.0, description=\"frequency\")\n", + "w_phases = iw.IntSlider(value=6, min=0, max=10, description=\"N phases\")\n", + "w_phase_width = iw.FloatSlider(value=1.0, min=0., max=4.0, description=\"phase_width\")\n", + "w_use_which = iw.SelectMultiple(options=['frequency', 'n_phases', 'phase_width'], value=['n_phases'], description='use param(s):')\n", + "w_period = iw.ToggleButtons(options=[\"ignore\", \"full\", \"half\"], value=\"ignore\", button_style=\"\", description=\"ensure period\")\n", + "\n", + "# Layout\n", + "b_resolution = iw.HBox([w_length, w_ppd])\n", + "b_grating_params = iw.VBox([iw.HBox([w_use_which, iw.VBox([w_frequency, w_phases, w_phase_width])]),w_period])\n", + "ui = iw.VBox([b_resolution, b_grating_params])\n", + "\n", + "# Function for resolving\n", + "def show_gratings(\n", + " length=None,\n", + " visual_angle=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " n_phases=None,\n", + " phase_width=None,\n", + " period=\"ignore\",\n", + " use_params=None,\n", + "):\n", + " stimuli = {\n", + " \"horizontal\": linear(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_bars=n_phases if \"n_phases\" in use_params else None,\n", + " bar_width=phase_width if \"phase_width\" in use_params else None,\n", + " period=period,\n", + " orientation=\"horizontal\",\n", + " ),\n", + " \"vertical\": linear(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_bars=n_phases if \"n_phases\" in use_params else None,\n", + " bar_width=phase_width if \"phase_width\" in use_params else None,\n", + " period=period,\n", + " orientation=\"vertical\",\n", + " ),\n", + " \"frames\": frames(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_frames=n_phases if \"n_phases\" in use_params else None,\n", + " frame_width=phase_width if \"phase_width\" in use_params else None,\n", + " period=period,\n", + " ),\n", + " \"circular\": circular(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_rings=n_phases if \"n_phases\" in use_params else None,\n", + " ring_width=phase_width if \"phase_width\" in use_params else None,\n", + " #period=period,\n", + " ),\n", + " \"angular\": angular(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_segments=n_phases if \"n_phases\" in use_params else None,\n", + " segment_width=phase_width if \"phase_width\" in use_params else None,\n", + " ),\n", + " \"pinwheel\": pinwheel(\n", + " shape=(length, length),\n", + " visual_size=(visual_angle, visual_angle),\n", + " ppd=ppd,\n", + " frequency=frequency if \"frequency\" in use_params else None,\n", + " n_segments=n_phases if \"n_phases\" in use_params else None,\n", + " segment_width=phase_width if \"phase_width\" in use_params else None,\n", + " )\n", + " }\n", + " plot_stimuli(stimuli)\n", + "\n", + "\n", + "# Set interactivity\n", + "out = iw.interactive_output(\n", + " show_gratings,\n", + " {\n", + " \"visual_angle\": w_length,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_frequency,\n", + " \"n_phases\": w_phases,\n", + " \"phase_width\": w_phase_width,\n", + " \"period\": w_period,\n", + " \"use_params\": w_use_which,\n", + " },\n", + ")\n", + "\n", + "# Show\n", + "display(ui, out)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.5 64-bit ('stimuli-dev')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Contents", + "title_sidebar": "Contents", + "toc_cell": true, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": true + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/hermann.ipynb b/demo/illusions/hermann.ipynb index 6e06b35c..4fa850ab 100644 --- a/demo/illusions/hermann.ipynb +++ b/demo/illusions/hermann.ipynb @@ -1,172 +1,172 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# hermann" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.hermann import hermann_grid" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"element_size\": (2., 2., 0.2),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_grid\": 1.,\n", - "}\n", - "\n", - "stim = hermann_grid(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=10, min=1, max=20, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=10, min=1, max=20, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_eheight = iw.FloatSlider(value=2., min=0.1, max=4.0, description=\"element height\")\n", - "w_ewidth = iw.FloatSlider(value=2., min=0.1, max=4.0, description=\"element width\")\n", - "w_ethick = iw.FloatSlider(value=0.2, min=0.1, max=0.5, description=\"element thickness\")\n", - "w_element = iw.HBox([w_eheight, w_ewidth, w_ethick])\n", - "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_igrid = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity grid\")\n", - "w_intensities = iw.HBox([w_iback, w_igrid])\n", - "\n", - "ui = iw.VBox([w_size, w_element, w_intensities])\n", - "\n", - "def show_hermann(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " element_height=None,\n", - " element_width=None,\n", - " element_thickness=None,\n", - " intensity_background=None,\n", - " intensity_grid=None,\n", - "):\n", - "\n", - " stim = hermann_grid(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " element_size=(element_height, element_width, element_thickness),\n", - " intensity_background=intensity_background,\n", - " intensity_grid=intensity_grid,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_hermann,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"element_height\": w_eheight,\n", - " \"element_width\": w_ewidth,\n", - " \"element_thickness\": w_ethick,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_grid\": w_igrid,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.13" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# hermann" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.hermann import hermann_grid" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"element_size\": (2., 2., 0.2),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_grid\": 1.,\n", + "}\n", + "\n", + "stim = hermann_grid(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=10, min=1, max=20, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=10, min=1, max=20, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_eheight = iw.FloatSlider(value=2., min=0.1, max=4.0, description=\"element height\")\n", + "w_ewidth = iw.FloatSlider(value=2., min=0.1, max=4.0, description=\"element width\")\n", + "w_ethick = iw.FloatSlider(value=0.2, min=0.1, max=0.5, description=\"element thickness\")\n", + "w_element = iw.HBox([w_eheight, w_ewidth, w_ethick])\n", + "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_igrid = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity grid\")\n", + "w_intensities = iw.HBox([w_iback, w_igrid])\n", + "\n", + "ui = iw.VBox([w_size, w_element, w_intensities])\n", + "\n", + "def show_hermann(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " element_height=None,\n", + " element_width=None,\n", + " element_thickness=None,\n", + " intensity_background=None,\n", + " intensity_grid=None,\n", + "):\n", + "\n", + " stim = hermann_grid(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " element_size=(element_height, element_width, element_thickness),\n", + " intensity_background=intensity_background,\n", + " intensity_grid=intensity_grid,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_hermann,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"element_height\": w_eheight,\n", + " \"element_width\": w_ewidth,\n", + " \"element_thickness\": w_ethick,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_grid\": w_igrid,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.13" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/sbc.ipynb b/demo/illusions/sbc.ipynb index f808a5bc..c0bf3ecb 100644 --- a/demo/illusions/sbc.ipynb +++ b/demo/illusions/sbc.ipynb @@ -1,558 +1,558 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# simultaneous_contrast_generalized\n", - "\n", - "Most general version of the simultaneous brightness contrast with flexible target placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.sbc import simultaneous_contrast_generalized" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (6., 6.),\n", - " \"ppd\": 10.0,\n", - " \"target_size\": (2., 2.),\n", - " \"target_position\": (2., 2.),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - " \n", - "stim = simultaneous_contrast_generalized(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=7, min=1, max=16, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=7, min=1, max=16, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", - "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "w_tposx = iw.FloatSlider(value=2., min=0., max=10.0, description=\"target posx\")\n", - "w_tposy = iw.FloatSlider(value=2., min=0., max=10.0, description=\"target posy\")\n", - "w_tpos = iw.HBox([w_tposx, w_tposy])\n", - "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_tsize, w_tpos, w_intensities])\n", - "\n", - "def show_sbc(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " target_x=None,\n", - " target_y=None,\n", - " intensity_background=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = simultaneous_contrast_generalized(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " target_size=(target_height, target_width),\n", - " target_position=(target_y, target_x),\n", - " intensity_background=intensity_background,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_sbc,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"target_x\": w_tposx,\n", - " \"target_y\": w_tposy,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# simultaneous_contrast\n", - "\n", - "Simultaneous brightness contrast with always central target." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.sbc import simultaneous_contrast" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (6., 6.),\n", - " \"ppd\": 10.0,\n", - " \"target_size\": (2., 2.),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - " \n", - "stim = simultaneous_contrast(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=7, min=1, max=16, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=7, min=1, max=16, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", - "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_tsize, w_intensities])\n", - "\n", - "def show_sbc(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " intensity_background=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = simultaneous_contrast(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " target_size=(target_height, target_width),\n", - " intensity_background=intensity_background,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_sbc,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# sbc_with_dots" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.sbc import sbc_with_dots" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"ppd\": 10.0,\n", - " \"n_dots\": (8, 9),\n", - " \"dot_radius\": 3.,\n", - " \"distance\": 1.,\n", - " \"target_shape\": (2., 3.),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_dots\": 1.,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - " \n", - "stim = sbc_with_dots(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", - "w_ndotsy = iw.IntSlider(value=5, min=1, max=10, description=\"n dots y\")\n", - "w_ndotsx = iw.IntSlider(value=5, min=1, max=10, description=\"n dots x\")\n", - "w_ndots = iw.HBox([w_ndotsy, w_ndotsx])\n", - "\n", - "w_dotradius = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"dot radius\")\n", - "w_dotdist = iw.FloatSlider(value=1., min=0.1, max=3.0, description=\"dot distance\")\n", - "w_dotsize = iw.HBox([w_dotradius, w_dotdist])\n", - "\n", - "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", - "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "\n", - "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_idots = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity dots\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_idots, w_itarget])\n", - "\n", - "ui = iw.VBox([w_ppd, w_ndots, w_dotsize, w_tsize, w_intensities])\n", - "\n", - "def show_sbc(\n", - " ppd=None,\n", - " ndotsy=None,\n", - " ndotsx=None,\n", - " dot_radius=None,\n", - " dot_distance=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " intensity_background=None,\n", - " intensity_dots=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = sbc_with_dots(\n", - " ppd=ppd,\n", - " n_dots=(ndotsy, ndotsx),\n", - " dot_radius=dot_radius,\n", - " distance=dot_distance,\n", - " target_shape=(target_height, target_width),\n", - " intensity_background=intensity_background,\n", - " intensity_dots=intensity_dots,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_sbc,\n", - " {\n", - " \"ppd\": w_ppd,\n", - " \"ndotsy\": w_ndotsy,\n", - " \"ndotsx\": w_ndotsx,\n", - " \"dot_radius\": w_dotradius,\n", - " \"dot_distance\": w_dotdist,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_dots\": w_idots,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# dotted_sbc" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.sbc import dotted_sbc" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"ppd\": 10.0,\n", - " \"n_dots\": (8, 9),\n", - " \"dot_radius\": 3.,\n", - " \"distance\": 1.,\n", - " \"target_shape\": (2., 3.),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_dots\": 1.,\n", - " \"intensity_target\": 0.5,\n", - "}\n", - " \n", - "stim = dotted_sbc(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", - "w_ndotsy = iw.IntSlider(value=5, min=1, max=10, description=\"n dots y\")\n", - "w_ndotsx = iw.IntSlider(value=5, min=1, max=10, description=\"n dots x\")\n", - "w_ndots = iw.HBox([w_ndotsy, w_ndotsx])\n", - "\n", - "w_dotradius = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"dot radius\")\n", - "w_dotdist = iw.FloatSlider(value=1., min=0.1, max=3.0, description=\"dot distance\")\n", - "w_dotsize = iw.HBox([w_dotradius, w_dotdist])\n", - "\n", - "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", - "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "\n", - "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_idots = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity dots\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_idots, w_itarget])\n", - "\n", - "ui = iw.VBox([w_ppd, w_ndots, w_dotsize, w_tsize, w_intensities])\n", - "\n", - "def show_sbc(\n", - " ppd=None,\n", - " ndotsy=None,\n", - " ndotsx=None,\n", - " dot_radius=None,\n", - " dot_distance=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " intensity_background=None,\n", - " intensity_dots=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = dotted_sbc(\n", - " ppd=ppd,\n", - " n_dots=(ndotsy, ndotsx),\n", - " dot_radius=dot_radius,\n", - " distance=dot_distance,\n", - " target_shape=(target_height, target_width),\n", - " intensity_background=intensity_background,\n", - " intensity_dots=intensity_dots,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_sbc,\n", - " {\n", - " \"ppd\": w_ppd,\n", - " \"ndotsy\": w_ndotsy,\n", - " \"ndotsx\": w_ndotsx,\n", - " \"dot_radius\": w_dotradius,\n", - " \"dot_distance\": w_dotdist,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_dots\": w_idots,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# simultaneous_contrast_generalized\n", + "\n", + "Most general version of the simultaneous brightness contrast with flexible target placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.sbc import simultaneous_contrast_generalized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (6., 6.),\n", + " \"ppd\": 10.0,\n", + " \"target_size\": (2., 2.),\n", + " \"target_position\": (2., 2.),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + " \n", + "stim = simultaneous_contrast_generalized(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=7, min=1, max=16, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=7, min=1, max=16, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", + "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "w_tposx = iw.FloatSlider(value=2., min=0., max=10.0, description=\"target posx\")\n", + "w_tposy = iw.FloatSlider(value=2., min=0., max=10.0, description=\"target posy\")\n", + "w_tpos = iw.HBox([w_tposx, w_tposy])\n", + "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_tsize, w_tpos, w_intensities])\n", + "\n", + "def show_sbc(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " target_x=None,\n", + " target_y=None,\n", + " intensity_background=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = simultaneous_contrast_generalized(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " target_size=(target_height, target_width),\n", + " target_position=(target_y, target_x),\n", + " intensity_background=intensity_background,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_sbc,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"target_x\": w_tposx,\n", + " \"target_y\": w_tposy,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# simultaneous_contrast\n", + "\n", + "Simultaneous brightness contrast with always central target." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.sbc import simultaneous_contrast" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (6., 6.),\n", + " \"ppd\": 10.0,\n", + " \"target_size\": (2., 2.),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + " \n", + "stim = simultaneous_contrast(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=7, min=1, max=16, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=7, min=1, max=16, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", + "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_tsize, w_intensities])\n", + "\n", + "def show_sbc(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " intensity_background=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = simultaneous_contrast(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " target_size=(target_height, target_width),\n", + " intensity_background=intensity_background,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_sbc,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# sbc_with_dots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.sbc import sbc_with_dots" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"ppd\": 10.0,\n", + " \"n_dots\": (8, 9),\n", + " \"dot_radius\": 3.,\n", + " \"distance\": 1.,\n", + " \"target_shape\": (2., 3.),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_dots\": 1.,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + " \n", + "stim = sbc_with_dots(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", + "w_ndotsy = iw.IntSlider(value=5, min=1, max=10, description=\"n dots y\")\n", + "w_ndotsx = iw.IntSlider(value=5, min=1, max=10, description=\"n dots x\")\n", + "w_ndots = iw.HBox([w_ndotsy, w_ndotsx])\n", + "\n", + "w_dotradius = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"dot radius\")\n", + "w_dotdist = iw.FloatSlider(value=1., min=0.1, max=3.0, description=\"dot distance\")\n", + "w_dotsize = iw.HBox([w_dotradius, w_dotdist])\n", + "\n", + "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", + "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "\n", + "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_idots = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity dots\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_idots, w_itarget])\n", + "\n", + "ui = iw.VBox([w_ppd, w_ndots, w_dotsize, w_tsize, w_intensities])\n", + "\n", + "def show_sbc(\n", + " ppd=None,\n", + " ndotsy=None,\n", + " ndotsx=None,\n", + " dot_radius=None,\n", + " dot_distance=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " intensity_background=None,\n", + " intensity_dots=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = sbc_with_dots(\n", + " ppd=ppd,\n", + " n_dots=(ndotsy, ndotsx),\n", + " dot_radius=dot_radius,\n", + " distance=dot_distance,\n", + " target_shape=(target_height, target_width),\n", + " intensity_background=intensity_background,\n", + " intensity_dots=intensity_dots,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_sbc,\n", + " {\n", + " \"ppd\": w_ppd,\n", + " \"ndotsy\": w_ndotsy,\n", + " \"ndotsx\": w_ndotsx,\n", + " \"dot_radius\": w_dotradius,\n", + " \"dot_distance\": w_dotdist,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_dots\": w_idots,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# dotted_sbc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.sbc import dotted_sbc" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"ppd\": 10.0,\n", + " \"n_dots\": (8, 9),\n", + " \"dot_radius\": 3.,\n", + " \"distance\": 1.,\n", + " \"target_shape\": (2., 3.),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_dots\": 1.,\n", + " \"intensity_target\": 0.5,\n", + "}\n", + " \n", + "stim = dotted_sbc(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", + "w_ndotsy = iw.IntSlider(value=5, min=1, max=10, description=\"n dots y\")\n", + "w_ndotsx = iw.IntSlider(value=5, min=1, max=10, description=\"n dots x\")\n", + "w_ndots = iw.HBox([w_ndotsy, w_ndotsx])\n", + "\n", + "w_dotradius = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"dot radius\")\n", + "w_dotdist = iw.FloatSlider(value=1., min=0.1, max=3.0, description=\"dot distance\")\n", + "w_dotsize = iw.HBox([w_dotradius, w_dotdist])\n", + "\n", + "w_theight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target height\")\n", + "w_twidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"target width\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "\n", + "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_idots = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity dots\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_idots, w_itarget])\n", + "\n", + "ui = iw.VBox([w_ppd, w_ndots, w_dotsize, w_tsize, w_intensities])\n", + "\n", + "def show_sbc(\n", + " ppd=None,\n", + " ndotsy=None,\n", + " ndotsx=None,\n", + " dot_radius=None,\n", + " dot_distance=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " intensity_background=None,\n", + " intensity_dots=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = dotted_sbc(\n", + " ppd=ppd,\n", + " n_dots=(ndotsy, ndotsx),\n", + " dot_radius=dot_radius,\n", + " distance=dot_distance,\n", + " target_shape=(target_height, target_width),\n", + " intensity_background=intensity_background,\n", + " intensity_dots=intensity_dots,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_sbc,\n", + " {\n", + " \"ppd\": w_ppd,\n", + " \"ndotsy\": w_ndotsy,\n", + " \"ndotsx\": w_ndotsx,\n", + " \"dot_radius\": w_dotradius,\n", + " \"dot_distance\": w_dotdist,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_dots\": w_idots,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/todorovic.ipynb b/demo/illusions/todorovic.ipynb index 5bb729e0..79abe3ef 100644 --- a/demo/illusions/todorovic.ipynb +++ b/demo/illusions/todorovic.ipynb @@ -1,547 +1,547 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# todorovic_rectangle_generalized\n", - "\n", - "General version of the Todorovic stimulus with rectangular target and with flexible target and cover placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.todorovic import todorovic_rectangle_generalized" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"target_size\": (4., 4.),\n", - " \"target_position\": (3., 3.),\n", - " \"covers_size\": (2., 2.),\n", - " \"covers_x\": (2.0, 6.0, 2.0, 6.0),\n", - " \"covers_y\": (2.0, 6.0, 6.0, 2.0),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - " \"intensity_covers\": 1.,\n", - "}\n", - " \n", - "stim = todorovic_rectangle_generalized(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With this general form of the stimulus, it is possible to add as many covers as desired by adding more cover coordinates in `covers_x` and `covers_y`.\n", - "\n", - "Keep in mind that you need as many elements in`covers_x` as in `covers_y`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"target_size\": (4., 4.),\n", - " \"target_position\": (3., 3.),\n", - " \"covers_size\": (2., 2.),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - " \"intensity_covers\": 1.,\n", - "}\n", - " \n", - "stim1 = todorovic_rectangle_generalized(**params,\n", - " covers_x=(1., 4., 7.),\n", - " covers_y=(1., 4., 7.))\n", - "\n", - "stim2 = todorovic_rectangle_generalized(**params,\n", - " covers_x=(2., 2., 6., 2., 6., 6.),\n", - " covers_y=(2., 4., 6., 6., 4., 2.))\n", - "\n", - "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# todorovic_rectangle\n", - "\n", - "Todorovic stimulus with rectangular target and central target and cover placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.todorovic import todorovic_rectangle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"target_size\": (4., 4.),\n", - " \"covers_size\": (2., 2.),\n", - " \"covers_offset\": (2., 2.),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - " \"intensity_covers\": 1.,\n", - "}\n", - " \n", - "stim = todorovic_rectangle(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In case you want everything to be symmetric, it is sufficient to define one element of all size and position-related variables." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": 10.,\n", - " \"ppd\": 10.0,\n", - " \"target_size\": 4.,\n", - " \"covers_size\": 2.,\n", - " \"covers_offset\": 2.,\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - " \"intensity_covers\": 1.,\n", - "}\n", - " \n", - "stim = todorovic_rectangle(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=12, min=5, max=16, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=12, min=5, max=16, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "\n", - "w_theight = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"target height\")\n", - "w_twidth = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"target width\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth])\n", - "\n", - "w_cheight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover height\")\n", - "w_cwidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover width\")\n", - "w_csize = iw.HBox([w_cheight, w_cwidth])\n", - "\n", - "w_coffy = iw.FloatSlider(value=2., min=1., max=4.0, description=\"cover offset y\")\n", - "w_coffx = iw.FloatSlider(value=2., min=1., max=4.0, description=\"cover offset x\")\n", - "w_coff = iw.HBox([w_coffy, w_coffx])\n", - "\n", - "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icovers = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity covers\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icovers, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_tsize, w_csize, w_coff, w_intensities])\n", - "\n", - "def show_todorovic(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " target_height=None,\n", - " target_width=None,\n", - " cover_height=None,\n", - " cover_width=None,\n", - " cover_offset_y=None,\n", - " cover_offset_x=None,\n", - " intensity_background=None,\n", - " intensity_covers=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = todorovic_rectangle(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " target_size=(target_height, target_width),\n", - " covers_size=(cover_height, cover_width),\n", - " covers_offset=(cover_offset_y, cover_offset_x),\n", - " intensity_background=intensity_background,\n", - " intensity_covers=intensity_covers,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_todorovic,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"target_height\": w_theight,\n", - " \"target_width\": w_twidth,\n", - " \"cover_height\": w_cheight,\n", - " \"cover_width\": w_cwidth,\n", - " \"cover_offset_y\": w_coffy,\n", - " \"cover_offset_x\": w_coffx,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_covers\": w_icovers,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# todorovic_cross_generalized\n", - "\n", - "General version of the Todorovic stimulus with cross-like target and with flexible target and cover placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.todorovic import todorovic_cross_generalized" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"cross_size\": (3., 4.),\n", - " \"cross_thickness\": 2.,\n", - " \"covers_size\": (2., 2.),\n", - " \"covers_x\": (2.0, 6.0, 2.0, 6.0),\n", - " \"covers_y\": (2.0, 6.0, 6.0, 2.0),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - " \"intensity_covers\": 1.,\n", - "}\n", - " \n", - "stim = todorovic_cross_generalized(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": 10.,\n", - " \"ppd\": 10.0,\n", - " \"cross_size\": 4.,\n", - " \"cross_thickness\": 2.,\n", - " \"covers_size\": 2.,\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - " \"intensity_covers\": 1.,\n", - "}\n", - " \n", - "stim1 = todorovic_cross_generalized(**params,\n", - " covers_x=(1., 4., 7.),\n", - " covers_y=(1., 4., 7.))\n", - "\n", - "stim2 = todorovic_cross_generalized(**params,\n", - " covers_x=(2., 2., 6., 2., 6., 6.),\n", - " covers_y=(2., 4., 6., 6., 4., 2.))\n", - "\n", - "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# todorovic_cross\n", - "Todorovic stimulus with cross-like target and central target and cover placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.todorovic import todorovic_cross" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"cross_size\": (4., 3.),\n", - " \"cross_thickness\": 2.,\n", - " \"covers_size\": (2., 2.),\n", - " \"intensity_background\": 0.,\n", - " \"intensity_target\": 0.5,\n", - " \"intensity_covers\": 1.,\n", - "}\n", - " \n", - "stim = todorovic_cross(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=12, min=5, max=16, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=12, min=5, max=16, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "\n", - "w_theight = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"cross height\")\n", - "w_twidth = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"cross width\")\n", - "w_tthick = iw.FloatSlider(value=2., min=0.1, max=3.0, description=\"cross thickness\")\n", - "w_tsize = iw.HBox([w_theight, w_twidth, w_tthick])\n", - "\n", - "w_cheight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover height\")\n", - "w_cwidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover width\")\n", - "w_csize = iw.HBox([w_cheight, w_cwidth])\n", - "\n", - "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_icovers = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity covers\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_iback, w_icovers, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_tsize, w_csize, w_intensities])\n", - "\n", - "def show_todorovic(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " cross_height=None,\n", - " cross_width=None,\n", - " cross_thickness=None,\n", - " cover_height=None,\n", - " cover_width=None,\n", - " intensity_background=None,\n", - " intensity_covers=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = todorovic_cross(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " cross_size=(cross_height, cross_width),\n", - " cross_thickness=cross_thickness,\n", - " covers_size=(cover_height, cover_width),\n", - " intensity_background=intensity_background,\n", - " intensity_covers=intensity_covers,\n", - " intensity_target=intensity_target,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_todorovic,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"cross_height\": w_theight,\n", - " \"cross_width\": w_twidth,\n", - " \"cross_thickness\": w_tthick,\n", - " \"cover_height\": w_cheight,\n", - " \"cover_width\": w_cwidth,\n", - " \"intensity_background\": w_iback,\n", - " \"intensity_covers\": w_icovers,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# todorovic_rectangle_generalized\n", + "\n", + "General version of the Todorovic stimulus with rectangular target and with flexible target and cover placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.todorovic import todorovic_rectangle_generalized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"target_size\": (4., 4.),\n", + " \"target_position\": (3., 3.),\n", + " \"covers_size\": (2., 2.),\n", + " \"covers_x\": (2.0, 6.0, 2.0, 6.0),\n", + " \"covers_y\": (2.0, 6.0, 6.0, 2.0),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + " \"intensity_covers\": 1.,\n", + "}\n", + " \n", + "stim = todorovic_rectangle_generalized(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With this general form of the stimulus, it is possible to add as many covers as desired by adding more cover coordinates in `covers_x` and `covers_y`.\n", + "\n", + "Keep in mind that you need as many elements in`covers_x` as in `covers_y`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"target_size\": (4., 4.),\n", + " \"target_position\": (3., 3.),\n", + " \"covers_size\": (2., 2.),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + " \"intensity_covers\": 1.,\n", + "}\n", + " \n", + "stim1 = todorovic_rectangle_generalized(**params,\n", + " covers_x=(1., 4., 7.),\n", + " covers_y=(1., 4., 7.))\n", + "\n", + "stim2 = todorovic_rectangle_generalized(**params,\n", + " covers_x=(2., 2., 6., 2., 6., 6.),\n", + " covers_y=(2., 4., 6., 6., 4., 2.))\n", + "\n", + "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# todorovic_rectangle\n", + "\n", + "Todorovic stimulus with rectangular target and central target and cover placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.todorovic import todorovic_rectangle" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"target_size\": (4., 4.),\n", + " \"covers_size\": (2., 2.),\n", + " \"covers_offset\": (2., 2.),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + " \"intensity_covers\": 1.,\n", + "}\n", + " \n", + "stim = todorovic_rectangle(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In case you want everything to be symmetric, it is sufficient to define one element of all size and position-related variables." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": 10.,\n", + " \"ppd\": 10.0,\n", + " \"target_size\": 4.,\n", + " \"covers_size\": 2.,\n", + " \"covers_offset\": 2.,\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + " \"intensity_covers\": 1.,\n", + "}\n", + " \n", + "stim = todorovic_rectangle(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=12, min=5, max=16, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=12, min=5, max=16, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "\n", + "w_theight = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"target height\")\n", + "w_twidth = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"target width\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth])\n", + "\n", + "w_cheight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover height\")\n", + "w_cwidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover width\")\n", + "w_csize = iw.HBox([w_cheight, w_cwidth])\n", + "\n", + "w_coffy = iw.FloatSlider(value=2., min=1., max=4.0, description=\"cover offset y\")\n", + "w_coffx = iw.FloatSlider(value=2., min=1., max=4.0, description=\"cover offset x\")\n", + "w_coff = iw.HBox([w_coffy, w_coffx])\n", + "\n", + "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icovers = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity covers\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icovers, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_tsize, w_csize, w_coff, w_intensities])\n", + "\n", + "def show_todorovic(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " target_height=None,\n", + " target_width=None,\n", + " cover_height=None,\n", + " cover_width=None,\n", + " cover_offset_y=None,\n", + " cover_offset_x=None,\n", + " intensity_background=None,\n", + " intensity_covers=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = todorovic_rectangle(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " target_size=(target_height, target_width),\n", + " covers_size=(cover_height, cover_width),\n", + " covers_offset=(cover_offset_y, cover_offset_x),\n", + " intensity_background=intensity_background,\n", + " intensity_covers=intensity_covers,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_todorovic,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"target_height\": w_theight,\n", + " \"target_width\": w_twidth,\n", + " \"cover_height\": w_cheight,\n", + " \"cover_width\": w_cwidth,\n", + " \"cover_offset_y\": w_coffy,\n", + " \"cover_offset_x\": w_coffx,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_covers\": w_icovers,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# todorovic_cross_generalized\n", + "\n", + "General version of the Todorovic stimulus with cross-like target and with flexible target and cover placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.todorovic import todorovic_cross_generalized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"cross_size\": (3., 4.),\n", + " \"cross_thickness\": 2.,\n", + " \"covers_size\": (2., 2.),\n", + " \"covers_x\": (2.0, 6.0, 2.0, 6.0),\n", + " \"covers_y\": (2.0, 6.0, 6.0, 2.0),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + " \"intensity_covers\": 1.,\n", + "}\n", + " \n", + "stim = todorovic_cross_generalized(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": 10.,\n", + " \"ppd\": 10.0,\n", + " \"cross_size\": 4.,\n", + " \"cross_thickness\": 2.,\n", + " \"covers_size\": 2.,\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + " \"intensity_covers\": 1.,\n", + "}\n", + " \n", + "stim1 = todorovic_cross_generalized(**params,\n", + " covers_x=(1., 4., 7.),\n", + " covers_y=(1., 4., 7.))\n", + "\n", + "stim2 = todorovic_cross_generalized(**params,\n", + " covers_x=(2., 2., 6., 2., 6., 6.),\n", + " covers_y=(2., 4., 6., 6., 4., 2.))\n", + "\n", + "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# todorovic_cross\n", + "Todorovic stimulus with cross-like target and central target and cover placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.todorovic import todorovic_cross" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"cross_size\": (4., 3.),\n", + " \"cross_thickness\": 2.,\n", + " \"covers_size\": (2., 2.),\n", + " \"intensity_background\": 0.,\n", + " \"intensity_target\": 0.5,\n", + " \"intensity_covers\": 1.,\n", + "}\n", + " \n", + "stim = todorovic_cross(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=12, min=5, max=16, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=12, min=5, max=16, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=32, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "\n", + "w_theight = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"cross height\")\n", + "w_twidth = iw.FloatSlider(value=4., min=0.1, max=6.0, description=\"cross width\")\n", + "w_tthick = iw.FloatSlider(value=2., min=0.1, max=3.0, description=\"cross thickness\")\n", + "w_tsize = iw.HBox([w_theight, w_twidth, w_tthick])\n", + "\n", + "w_cheight = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover height\")\n", + "w_cwidth = iw.FloatSlider(value=3., min=0.1, max=6.0, description=\"cover width\")\n", + "w_csize = iw.HBox([w_cheight, w_cwidth])\n", + "\n", + "w_iback = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_icovers = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity covers\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_iback, w_icovers, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_tsize, w_csize, w_intensities])\n", + "\n", + "def show_todorovic(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " cross_height=None,\n", + " cross_width=None,\n", + " cross_thickness=None,\n", + " cover_height=None,\n", + " cover_width=None,\n", + " intensity_background=None,\n", + " intensity_covers=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = todorovic_cross(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " cross_size=(cross_height, cross_width),\n", + " cross_thickness=cross_thickness,\n", + " covers_size=(cover_height, cover_width),\n", + " intensity_background=intensity_background,\n", + " intensity_covers=intensity_covers,\n", + " intensity_target=intensity_target,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_todorovic,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"cross_height\": w_theight,\n", + " \"cross_width\": w_twidth,\n", + " \"cross_thickness\": w_tthick,\n", + " \"cover_height\": w_cheight,\n", + " \"cover_width\": w_cwidth,\n", + " \"intensity_background\": w_iback,\n", + " \"intensity_covers\": w_icovers,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/wedding_cake.ipynb b/demo/illusions/wedding_cake.ipynb index 88be5342..64a34d66 100644 --- a/demo/illusions/wedding_cake.ipynb +++ b/demo/illusions/wedding_cake.ipynb @@ -1,252 +1,252 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# wedding_cake_stimulus" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.wedding_cake import wedding_cake_stimulus" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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z36mSy+99Hs8FQBAT9PtkPryyy914403+m+ffq9o5syZWrVqlSTpnXfekTFGM2bM6HB/t9utd955R88884yam5s1cOBALViwIOA9JwAApPMIqXHjxskYc9Yxc+fO1dy5czvtGz16tHbs2NHdhwUAXIRCenZfqEVFRSkzM1MNDQ1Ol4Juam5uVlVVVZd/8Fzs3G43azxMscaDI6xDatiwYfrkk0/k8/mcLgXdVFJSonvvvVeNjY1Ol2K14cOHs8bDFGs8OMI6pKKiopScnOx0GTgPlZWVcrlcTpdhvaioKD4TGKZY48HBpToAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANbq5XQB58MYI0mqq6tzuBKcr8bGRv/v0RZtbW2qq6uTz+dzuhT0AKzxszv9/3dXc+Qyts3iOTh69KjS09OdLgMA8D0dOXJE/fv3P2N/WIaUz+dTWVmZrrrqKh05ckTx8fFOl3TO6urqlJ6eTt0XULjWTt0XFnVfWMYY1dfXy+v1KiLizO88heXLfREREbrsssskSfHx8WH1izmNui+8cK2dui8s6r5wEhISuhzDiRMAAGsRUgAAa4VtSLndbi1atEhut9vpUrqFui+8cK2dui8s6rZTWJ44AQC4OITtkRQAoOcjpAAA1iKkAADWIqQAANYipAAA1grbkFqxYoUyMzMVHR2t7OxsffHFF06X5FdUVKRrr71WcXFxSklJ0R133KGysrKAMePGjZPL5QrYHnroIYcq/j/PPPNMh7quvPJKf39TU5Py8/PVt29fxcbGatq0aaqurnaw4u9kZmZ2qNvlcik/P1+SPfO9fft2TZkyRV6vVy6XS+vWrQvoN8bo6aefVlpamnr37q0JEyZo//79AWNOnjypvLw8xcfHKzExUbNnz1ZDQ4Njdbe2tmrhwoUaMWKEYmJi5PV6dd999+nYsWMB++jsd7R06dKQ1t1V7ZI0a9asDnVNmjQpYIxtcy6p0/Xucrm0fPly/xin5jyYwjKk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xnz++efmhRdeMKWlpaa8vNy89dZbpl+/fua+++4Lad1d1V5fX2+eeOIJU1xcbCoqKszGjRvN6NGjzeWXX26ampr8+7Btzk+rra01ffr0Ma+88kqH+zs558EUliFljDEvv/yyycjIMFFRUea6664zO3bscLokP0mdbitXrjTGGHP48GEzduxYk5SUZNxutxkyZIh58sknTW1trbOFG2Puvvtuk5aWZqKiosxll11m7r77bnPgwAF//9///nfzyCOPmEsvvdT06dPH/OQnPzGVlZUOVvx//vjHPxpJpqysLKDdpvnesmVLp2tj5syZxpjvTkN/6qmnTGpqqnG73Wb8+PEdns+JEyfMjBkzTGxsrImPjzf333+/qa+vd6zuioqKM675LVu2GGOM2b17t8nOzjYJCQkmOjraDBs2zDz77LMBQeBE7adOnTI333yz6devn7nkkkvMgAEDzJw5czr8wWvbnJ/2n//5n6Z3796mpqamw/2dnPNg4npSAABrhd17UgCAiwchBQCwFiEFALAWIQUAsBYhBQCwFiEFALAWIQUAsBYhBQCwFiEFALAWIQUAsBYhBQCw1v8Dpv6tq5qQnEoAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 20.0,\n", - " \"L_size\": (3., 3., 1.),\n", - " \"target_height\": 0.5,\n", - " \"target_indices1\": ((0, 0), (0, 1)),\n", - " \"target_indices2\": ((2, 0), (2, 1)),\n", - " \"intensity_grating\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - "}\n", - " \n", - "stim = wedding_cake_stimulus(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "cc664f38560b46d2a59aba73d148e452", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(HBox(children=(IntSlider(value=10, description='height [deg]', max=16, min=5), IntSlider(value=…" - ] - }, - "metadata": {}, - "output_type": "display_data" + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# wedding_cake_stimulus" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.wedding_cake import wedding_cake_stimulus" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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z36mSy+99Hs8FQBAT9PtkPryyy914403+m+ffq9o5syZWrVqlSTpnXfekTFGM2bM6HB/t9utd955R88884yam5s1cOBALViwIOA9JwAApPMIqXHjxskYc9Yxc+fO1dy5czvtGz16tHbs2NHdhwUAXIRCenZfqEVFRSkzM1MNDQ1Ol4Juam5uVlVVVZd/8Fzs3G43azxMscaDI6xDatiwYfrkk0/k8/mcLgXdVFJSonvvvVeNjY1Ol2K14cOHs8bDFGs8OMI6pKKiopScnOx0GTgPlZWVcrlcTpdhvaioKD4TGKZY48HBpToAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANYipAAA1iKkAADWIqQAANbq5XQB58MYI0mqq6tzuBKcr8bGRv/v0RZtbW2qq6uTz+dzuhT0AKzxszv9/3dXc+Qyts3iOTh69KjS09OdLgMA8D0dOXJE/fv3P2N/WIaUz+dTWVmZrrrqKh05ckTx8fFOl3TO6urqlJ6eTt0XULjWTt0XFnVfWMYY1dfXy+v1KiLizO88heXLfREREbrsssskSfHx8WH1izmNui+8cK2dui8s6r5wEhISuhzDiRMAAGsRUgAAa4VtSLndbi1atEhut9vpUrqFui+8cK2dui8s6rZTWJ44AQC4OITtkRQAoOcjpAAA1iKkAADWIqQAANYipAAA1grbkFqxYoUyMzMVHR2t7OxsffHFF06X5FdUVKRrr71WcXFxSklJ0R133KGysrKAMePGjZPL5QrYHnroIYcq/j/PPPNMh7quvPJKf39TU5Py8/PVt29fxcbGatq0aaqurnaw4u9kZmZ2qNvlcik/P1+SPfO9fft2TZkyRV6vVy6XS+vWrQvoN8bo6aefVlpamnr37q0JEyZo//79AWNOnjypvLw8xcfHKzExUbNnz1ZDQ4Njdbe2tmrhwoUaMWKEYmJi5PV6dd999+nYsWMB++jsd7R06dKQ1t1V7ZI0a9asDnVNmjQpYIxtcy6p0/Xucrm0fPly/xin5jyYwjKk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xnz++efmhRdeMKWlpaa8vNy89dZbpl+/fua+++4Lad1d1V5fX2+eeOIJU1xcbCoqKszGjRvN6NGjzeWXX26ampr8+7Btzk+rra01ffr0Ma+88kqH+zs558EUliFljDEvv/yyycjIMFFRUea6664zO3bscLokP0mdbitXrjTGGHP48GEzduxYk5SUZNxutxkyZIh58sknTW1trbOFG2Puvvtuk5aWZqKiosxll11m7r77bnPgwAF//9///nfzyCOPmEsvvdT06dPH/OQnPzGVlZUOVvx//vjHPxpJpqysLKDdpvnesmVLp2tj5syZxpjvTkN/6qmnTGpqqnG73Wb8+PEdns+JEyfMjBkzTGxsrImPjzf333+/qa+vd6zuioqKM675LVu2GGOM2b17t8nOzjYJCQkmOjraDBs2zDz77LMBQeBE7adOnTI333yz6devn7nkkkvMgAEDzJw5czr8wWvbnJ/2n//5n6Z3796mpqamw/2dnPNg4npSAABrhd17UgCAiwchBQCwFiEFALAWIQUAsBYhBQCwFiEFALAWIQUAsBYhBQCwFiEFALAWIQUAsBYhBQCw1v8Dpv6tq5qQnEoAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 20.0,\n", + " \"L_size\": (3., 3., 1.),\n", + " \"target_height\": 0.5,\n", + " \"target_indices1\": ((0, 0), (0, 1)),\n", + " \"target_indices2\": ((2, 0), (2, 1)),\n", + " \"intensity_grating\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + "}\n", + " \n", + "stim = wedding_cake_stimulus(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "cc664f38560b46d2a59aba73d148e452", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(HBox(children=(IntSlider(value=10, description='height [deg]', max=16, min=5), IntSlider(value=…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3fcf3712a6da441abab49f82f74bc66a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=10, min=5, max=16, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=10, min=5, max=16, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=10, max=30, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "\n", + "w_lheight = iw.FloatSlider(value=3, min=1, max=5, description=\"L-height [deg]\")\n", + "w_lwidth = iw.FloatSlider(value=3, min=1, max=5, description=\"L-width [deg]\")\n", + "w_lthick = iw.FloatSlider(value=0.5, min=0.1, max=3, description=\"L-thickness [deg]\")\n", + "w_lsize = iw.HBox([w_lheight, w_lwidth, w_lthick])\n", + "\n", + "w_theight = iw.FloatSlider(value=0.2, min=0.1, max=1., description=\"target height\")\n", + "w_tidx1 = iw.IntSlider(value=0, min=0, max=5, description=\"target index 1\")\n", + "w_tidx2 = iw.IntSlider(value=0, min=-3, max=3, description=\"target index 2\")\n", + "w_target = iw.HBox([w_theight, w_tidx1, w_tidx2])\n", + "\n", + "ui = iw.VBox([w_size, w_lsize, w_target])\n", + "\n", + "def show_wedding_cake(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " Lheight=None,\n", + " Lwidth=None,\n", + " Lthick=None,\n", + " target_height=None,\n", + " tidx1=None,\n", + " tidx2=None,\n", + "):\n", + "\n", + " stim = wedding_cake_stimulus(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " L_size=(Lheight, Lwidth, Lthick),\n", + " target_height=target_height,\n", + " target_indices1=((tidx1, tidx2),),\n", + " target_indices2=None,\n", + " intensity_grating=(0., 1.),\n", + " intensity_target=0.5,\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_wedding_cake,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"Lheight\": w_lheight,\n", + " \"Lwidth\": w_lwidth,\n", + " \"Lthick\": w_lthick,\n", + " \"target_height\": w_theight,\n", + " \"tidx1\": w_tidx1,\n", + " \"tidx2\": w_tidx2,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3fcf3712a6da441abab49f82f74bc66a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=10, min=5, max=16, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=10, min=5, max=16, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=10, max=30, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "\n", - "w_lheight = iw.FloatSlider(value=3, min=1, max=5, description=\"L-height [deg]\")\n", - "w_lwidth = iw.FloatSlider(value=3, min=1, max=5, description=\"L-width [deg]\")\n", - "w_lthick = iw.FloatSlider(value=0.5, min=0.1, max=3, description=\"L-thickness [deg]\")\n", - "w_lsize = iw.HBox([w_lheight, w_lwidth, w_lthick])\n", - "\n", - "w_theight = iw.FloatSlider(value=0.2, min=0.1, max=1., description=\"target height\")\n", - "w_tidx1 = iw.IntSlider(value=0, min=0, max=5, description=\"target index 1\")\n", - "w_tidx2 = iw.IntSlider(value=0, min=-3, max=3, description=\"target index 2\")\n", - "w_target = iw.HBox([w_theight, w_tidx1, w_tidx2])\n", - "\n", - "ui = iw.VBox([w_size, w_lsize, w_target])\n", - "\n", - "def show_wedding_cake(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " Lheight=None,\n", - " Lwidth=None,\n", - " Lthick=None,\n", - " target_height=None,\n", - " tidx1=None,\n", - " tidx2=None,\n", - "):\n", - "\n", - " stim = wedding_cake_stimulus(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " L_size=(Lheight, Lwidth, Lthick),\n", - " target_height=target_height,\n", - " target_indices1=((tidx1, tidx2),),\n", - " target_indices2=None,\n", - " intensity_grating=(0., 1.),\n", - " intensity_target=0.5,\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_wedding_cake,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"Lheight\": w_lheight,\n", - " \"Lwidth\": w_lwidth,\n", - " \"Lthick\": w_lthick,\n", - " \"target_height\": w_theight,\n", - " \"tidx1\": w_tidx1,\n", - " \"tidx2\": w_tidx2,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/illusions/whites.ipynb b/demo/illusions/whites.ipynb index 2d95407c..c9e8194b 100644 --- a/demo/illusions/whites.ipynb +++ b/demo/illusions/whites.ipynb @@ -1,540 +1,540 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# white_generalized\n", - "\n", - "General version of the White stimulus with flexible number of targets and target placement." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.whites import white_generalized" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (8., 8.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"target_indices\": (2, 5),\n", - " \"target_center_offsets\": 0,\n", - " \"target_heights\": 2.,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\"\n", - "}\n", - " \n", - "stim = white_generalized(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The main advantage of `white_generalized()` is that you can add as many targets as you like by adding values to the lists that you pass to `target_indices`.\n", - "In addition, you can define the vertical distance from the center of the stimulus for each target by adding values to `target_center_offsets`, and change their individual sizes by adding values to `target_heights`.\n", - "\n", - "Keep in mind that the number of elements for each of those input variables needs be 1 or you need to have as many elements as you want to have targets." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (8., 8.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\"\n", - "}\n", - "stim1 = white_generalized(**params,\n", - " target_indices=(1, 3, 5, -2, -4, -6),\n", - " target_center_offsets=(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5),\n", - " target_heights=1.5)\n", - "\n", - "stim2 = white_generalized(**params,\n", - " target_indices=(1, 2, 3, 4, 5, 6),\n", - " target_center_offsets=(-3., -2., -1, -0., 1., 2.),\n", - " target_heights=(0.25, 0.5, 1., 1.5, 2., 3))\n", - "\n", - "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=10, min=5, max=16, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=10, min=5, max=16, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "\n", - "w_freq = iw.FloatSlider(value=0.5, min=0.1, max=2.0, description=\"frequency\")\n", - "\n", - "w_tsize = iw.FloatSlider(value=2., min=0.5, max=6.0, description=\"target height\")\n", - "w_toff = iw.FloatSlider(value=0., min=-3, max=3, description=\"target center offset\")\n", - "w_target = iw.HBox([w_tsize, w_toff])\n", - "\n", - "w_ibar1 = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", - "w_ibar2 = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity covers\")\n", - "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", - "w_intensities = iw.HBox([w_ibar1, w_ibar2, w_itarget])\n", - "\n", - "ui = iw.VBox([w_size, w_freq, w_target, w_intensities])\n", - "\n", - "def show_white(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " frequency=None,\n", - " target_heights=None,\n", - " target_center_offsets=None,\n", - " intensity_bar1=None,\n", - " intensity_bar2=None,\n", - " intensity_target=None,\n", - "):\n", - "\n", - " stim = white_generalized(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " frequency=frequency,\n", - " target_indices=3,\n", - " target_heights=target_heights,\n", - " target_center_offsets=target_center_offsets,\n", - " intensity_bars=(intensity_bar1, intensity_bar2),\n", - " intensity_target=intensity_target,\n", - " period=\"ignore\",\n", - " )\n", - " plot_stim(stim)\n", - "\n", - "\n", - "out = iw.interactive_output(show_white,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"frequency\": w_freq,\n", - " \"target_heights\": w_tsize,\n", - " \"target_center_offsets\": w_toff,\n", - " \"intensity_bar1\": w_ibar1,\n", - " \"intensity_bar2\": w_ibar2,\n", - " \"intensity_target\": w_itarget,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# white\n", - "\n", - "This function is the same as `white_generalized()` with the only exception that the targets are always placed in the vertical center." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.whites import white" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (8., 8.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"target_indices\": (2, 5),\n", - " \"target_height\": 2.,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\"\n", - "}\n", - " \n", - "stim = white(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# white_two_rows\n", - "This function is the same as `white_generalized()` with the exception that the user can define top and bottom target indices seperately (using `target_indices_top` and `target_indices_bottom`) given a `target_center_offset`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.whites import white_two_rows" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (8., 8.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"target_indices_top\": (1, 3),\n", - " \"target_indices_bottom\": (4, 6),\n", - " \"target_center_offset\": 1.,\n", - " \"target_height\": 1.,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\"\n", - "}\n", - " \n", - "stim = white_two_rows(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# white_anderson\n", - "\n", - "Anderson variation of White's stimulus with default target placement.\n", - "Similar use as `white_two_rows()` but with additional variables for stripes (`stripe_height`, `stripe_center_offset` and `intensity_stripes`)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.whites import white_anderson" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"target_indices_top\": 3,\n", - " \"target_indices_bottom\": -4,\n", - " \"target_center_offset\": 2.,\n", - " \"target_height\": 2.,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\",\n", - " \"stripe_height\": 2.,\n", - " \"stripe_center_offset\": 3.,\n", - " \"intensity_stripes\": (0., 1.),\n", - "}\n", - " \n", - "stim = white_anderson(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"target_indices_top\": 3,\n", - " \"target_indices_bottom\": -4,\n", - " \"target_height\": 2.,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\",\n", - " \"intensity_stripes\": (0., 1.),\n", - "}\n", - " \n", - "stim1 = white_anderson(**params,\n", - " target_center_offset=3.,\n", - " stripe_height=3.,\n", - " stripe_center_offset=2.)\n", - "\n", - "stim2 = white_anderson(**params,\n", - " target_center_offset=2.,\n", - " stripe_height=1.,\n", - " stripe_center_offset=2.)\n", - "\n", - "\n", - "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# white_howe\n", - "\n", - "Howe variation of White's stimulus with default target placement.\n", - "Same use as `white_anderson()` but with `stripe_height=target_height` and `stripe_center_offset=target_center_offset`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.whites import white_howe" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"target_indices_top\": 3,\n", - " \"target_indices_bottom\": -4,\n", - " \"target_center_offset\": 2.,\n", - " \"target_height\": 2.,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\",\n", - " \"intensity_stripes\": (0., 1.),\n", - "}\n", - " \n", - "stim = white_howe(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# white_yazdanbakhsh\n", - "\n", - "Yazdanbakhsh variation of White's stimulus with default target placement.\n", - "Same use as `white_howe()` but with additional parameter `gap_size` which defines the size of the gap between the target and the bar it is placed on both sides of the target in deg." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.illusions.whites import white_yazdanbakhsh" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10., 10.),\n", - " \"ppd\": 10.0,\n", - " \"frequency\": 0.5,\n", - " \"target_indices_top\": 3,\n", - " \"target_indices_bottom\": -4,\n", - " \"target_center_offset\": 2.,\n", - " \"target_height\": 2.,\n", - " \"intensity_bars\": (0., 1.),\n", - " \"intensity_target\": 0.5,\n", - " \"period\": \"ignore\",\n", - " \"intensity_stripes\": (0., 1.),\n", - " \"gap_size\": 0.5,\n", - "}\n", - " \n", - "stim = white_yazdanbakhsh(**params)\n", - "plot_stim(stim)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# white_generalized\n", + "\n", + "General version of the White stimulus with flexible number of targets and target placement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.whites import white_generalized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (8., 8.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"target_indices\": (2, 5),\n", + " \"target_center_offsets\": 0,\n", + " \"target_heights\": 2.,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\"\n", + "}\n", + " \n", + "stim = white_generalized(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The main advantage of `white_generalized()` is that you can add as many targets as you like by adding values to the lists that you pass to `target_indices`.\n", + "In addition, you can define the vertical distance from the center of the stimulus for each target by adding values to `target_center_offsets`, and change their individual sizes by adding values to `target_heights`.\n", + "\n", + "Keep in mind that the number of elements for each of those input variables needs be 1 or you need to have as many elements as you want to have targets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (8., 8.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\"\n", + "}\n", + "stim1 = white_generalized(**params,\n", + " target_indices=(1, 3, 5, -2, -4, -6),\n", + " target_center_offsets=(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5),\n", + " target_heights=1.5)\n", + "\n", + "stim2 = white_generalized(**params,\n", + " target_indices=(1, 2, 3, 4, 5, 6),\n", + " target_center_offsets=(-3., -2., -1, -0., 1., 2.),\n", + " target_heights=(0.25, 0.5, 1., 1.5, 2., 3))\n", + "\n", + "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=10, min=5, max=16, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=10, min=5, max=16, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=10, min=1, max=30, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "\n", + "w_freq = iw.FloatSlider(value=0.5, min=0.1, max=2.0, description=\"frequency\")\n", + "\n", + "w_tsize = iw.FloatSlider(value=2., min=0.5, max=6.0, description=\"target height\")\n", + "w_toff = iw.FloatSlider(value=0., min=-3, max=3, description=\"target center offset\")\n", + "w_target = iw.HBox([w_tsize, w_toff])\n", + "\n", + "w_ibar1 = iw.FloatSlider(value=0.0, min=0., max=1.0, description=\"intensity background\")\n", + "w_ibar2 = iw.FloatSlider(value=1.0, min=0., max=1.0, description=\"intensity covers\")\n", + "w_itarget = iw.FloatSlider(value=0.5, min=0., max=1.0, description=\"intensity target\")\n", + "w_intensities = iw.HBox([w_ibar1, w_ibar2, w_itarget])\n", + "\n", + "ui = iw.VBox([w_size, w_freq, w_target, w_intensities])\n", + "\n", + "def show_white(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " frequency=None,\n", + " target_heights=None,\n", + " target_center_offsets=None,\n", + " intensity_bar1=None,\n", + " intensity_bar2=None,\n", + " intensity_target=None,\n", + "):\n", + "\n", + " stim = white_generalized(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " frequency=frequency,\n", + " target_indices=3,\n", + " target_heights=target_heights,\n", + " target_center_offsets=target_center_offsets,\n", + " intensity_bars=(intensity_bar1, intensity_bar2),\n", + " intensity_target=intensity_target,\n", + " period=\"ignore\",\n", + " )\n", + " plot_stim(stim)\n", + "\n", + "\n", + "out = iw.interactive_output(show_white,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"frequency\": w_freq,\n", + " \"target_heights\": w_tsize,\n", + " \"target_center_offsets\": w_toff,\n", + " \"intensity_bar1\": w_ibar1,\n", + " \"intensity_bar2\": w_ibar2,\n", + " \"intensity_target\": w_itarget,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# white\n", + "\n", + "This function is the same as `white_generalized()` with the only exception that the targets are always placed in the vertical center." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.whites import white" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (8., 8.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"target_indices\": (2, 5),\n", + " \"target_height\": 2.,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\"\n", + "}\n", + " \n", + "stim = white(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# white_two_rows\n", + "This function is the same as `white_generalized()` with the exception that the user can define top and bottom target indices seperately (using `target_indices_top` and `target_indices_bottom`) given a `target_center_offset`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.whites import white_two_rows" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (8., 8.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"target_indices_top\": (1, 3),\n", + " \"target_indices_bottom\": (4, 6),\n", + " \"target_center_offset\": 1.,\n", + " \"target_height\": 1.,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\"\n", + "}\n", + " \n", + "stim = white_two_rows(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# white_anderson\n", + "\n", + "Anderson variation of White's stimulus with default target placement.\n", + "Similar use as `white_two_rows()` but with additional variables for stripes (`stripe_height`, `stripe_center_offset` and `intensity_stripes`)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.whites import white_anderson" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"target_indices_top\": 3,\n", + " \"target_indices_bottom\": -4,\n", + " \"target_center_offset\": 2.,\n", + " \"target_height\": 2.,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\",\n", + " \"stripe_height\": 2.,\n", + " \"stripe_center_offset\": 3.,\n", + " \"intensity_stripes\": (0., 1.),\n", + "}\n", + " \n", + "stim = white_anderson(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"target_indices_top\": 3,\n", + " \"target_indices_bottom\": -4,\n", + " \"target_height\": 2.,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\",\n", + " \"intensity_stripes\": (0., 1.),\n", + "}\n", + " \n", + "stim1 = white_anderson(**params,\n", + " target_center_offset=3.,\n", + " stripe_height=3.,\n", + " stripe_center_offset=2.)\n", + "\n", + "stim2 = white_anderson(**params,\n", + " target_center_offset=2.,\n", + " stripe_height=1.,\n", + " stripe_center_offset=2.)\n", + "\n", + "\n", + "plot_stimuli({\"Example 1:\": stim1, \"Example 2:\": stim2})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# white_howe\n", + "\n", + "Howe variation of White's stimulus with default target placement.\n", + "Same use as `white_anderson()` but with `stripe_height=target_height` and `stripe_center_offset=target_center_offset`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.whites import white_howe" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"target_indices_top\": 3,\n", + " \"target_indices_bottom\": -4,\n", + " \"target_center_offset\": 2.,\n", + " \"target_height\": 2.,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\",\n", + " \"intensity_stripes\": (0., 1.),\n", + "}\n", + " \n", + "stim = white_howe(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# white_yazdanbakhsh\n", + "\n", + "Yazdanbakhsh variation of White's stimulus with default target placement.\n", + "Same use as `white_howe()` but with additional parameter `gap_size` which defines the size of the gap between the target and the bar it is placed on both sides of the target in deg." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.illusions.whites import white_yazdanbakhsh" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10., 10.),\n", + " \"ppd\": 10.0,\n", + " \"frequency\": 0.5,\n", + " \"target_indices_top\": 3,\n", + " \"target_indices_bottom\": -4,\n", + " \"target_center_offset\": 2.,\n", + " \"target_height\": 2.,\n", + " \"intensity_bars\": (0., 1.),\n", + " \"intensity_target\": 0.5,\n", + " \"period\": \"ignore\",\n", + " \"intensity_stripes\": (0., 1.),\n", + " \"gap_size\": 0.5,\n", + "}\n", + " \n", + "stim = white_yazdanbakhsh(**params)\n", + "plot_stim(stim)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/noises/narrowband.ipynb b/demo/noises/narrowband.ipynb index f1b3a5a9..281563d0 100644 --- a/demo/noises/narrowband.ipynb +++ b/demo/noises/narrowband.ipynb @@ -1,277 +1,277 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# narrowband" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.noises import narrowband" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10, 10),\n", - " \"ppd\": 40.,\n", - " \"center_frequency\": 3,\n", - " \"bandwidth\": 1,\n", - " \"rms_contrast\": 0.2,\n", - " \"pseudo_noise\": True,\n", - "}\n", - "\n", - "stim = narrowband(**params)\n", - "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=15, min=5, max=25, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=15, min=5, max=25, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=20, max=60, step=2, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "\n", - "w_freq = iw.FloatSlider(value=3., min=0.1, max=10.0, description=\"center frequency\")\n", - "w_band = iw.FloatSlider(value=1., min=0.5, max=2.0, description=\"bandwidth (octaves)\")\n", - "w_stats = iw.HBox([w_freq, w_band])\n", - "\n", - "w_rms = iw.FloatSlider(value=0.5, min=0.1, max=1.0, description=\"rms contrast (std)\")\n", - "ui = iw.VBox([w_size, w_stats, w_rms])\n", - "\n", - "def show_noise(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " center_frequency=None,\n", - " bandwidth=None,\n", - " rms_contrast=None,\n", - "):\n", - "\n", - " stim = narrowband(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " center_frequency=center_frequency,\n", - " bandwidth=bandwidth,\n", - " rms_contrast=rms_contrast\n", - " )\n", - " plot_stim(stim, vmin=-3, vmax=3)\n", - "\n", - "\n", - "out = iw.interactive_output(show_noise,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"center_frequency\": w_freq,\n", - " \"bandwidth\": w_band,\n", - " \"rms_contrast\": w_rms,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Center frequency and bandwidth" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def show_noise_properties(stim):\n", - " # Get amplitude spectrum\n", - " noise_amplitude = np.abs(np.fft.fftshift(np.fft.fft2(stim[\"img\"])))\n", - " fs = np.fft.fftshift(np.fft.fftfreq(stim[\"shape\"][0], d=1./stim[\"ppd\"]))\n", - " \n", - " # Calculate full-width-half-maximum\n", - " fwhm = stim[\"sigma\"] * 2. * np.sqrt(2. * np.log(2))\n", - " \n", - " # Get lower and upper bound at fwhm\n", - " fup, flow = stim[\"center_frequency\"] + fwhm/2., stim[\"center_frequency\"] - fwhm/2.\n", - " \n", - " # Calculate ratio and fractional bandwidth\n", - " ratio_bandwidth = np.log2(fup / flow) # in octaves\n", - " frac_bandwidth = (fup-flow) / stim[\"center_frequency\"]\n", - "\n", - " # Plotting:\n", - " plt.figure(figsize=(18, 4))\n", - " plt.subplot(1, 2, 1), plt.imshow(noise_amplitude, extent=[fs[0], fs[-1],]*2)\n", - " plt.colorbar(), plt.title('Noise spectrum'), plt.xlabel('cpd')\n", - "\n", - " plt.subplot(1, 2, 2)\n", - " plt.plot(fs, noise_amplitude[int(noise_amplitude.shape[0]/2.), :])\n", - " plt.plot([flow, flow], [0., noise_amplitude.max()], label='lower boundary max/2')\n", - " plt.plot([fup, fup], [0., noise_amplitude.max()], label='upper boundary max/2')\n", - " plt.title('Cut through'), plt.xlabel('cpd'), plt.legend()\n", - " plt.show()\n", - "\n", - " print('Center frequency:', np.round(stim[\"center_frequency\"], 3))\n", - " print('Ratio bandwidth:', np.round(ratio_bandwidth, 3))\n", - " print('Fractional bandwidth:', np.round(frac_bandwidth, 3))\n", - " return" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def interact_noise_properties(center_frequency, bandwidth):\n", - " stim = narrowband(visual_size=(10, 10), ppd=40, center_frequency=center_frequency, bandwidth=bandwidth)\n", - " show_noise_properties(stim)\n", - "\n", - "iw.interact(interact_noise_properties, center_frequency=(0.5, 9.5), bandwidth=(0.5, 1.5))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Random noise vs pseudo-random noise" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def interact_randomness(center_frequency, bandwidth):\n", - " stim1 = narrowband(visual_size=(10, 10), ppd=40, center_frequency=center_frequency, pseudo_noise=True)\n", - " stim2 = narrowband(visual_size=(10, 10), ppd=40, center_frequency=center_frequency, pseudo_noise=False)\n", - " show_noise_properties(stim1)\n", - " show_noise_properties(stim2)\n", - "\n", - "iw.interact(interact_randomness, center_frequency=(0.5, 9.5), bandwidth=(0.5, 1.5))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# narrowband" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.noises import narrowband" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10, 10),\n", + " \"ppd\": 40.,\n", + " \"center_frequency\": 3,\n", + " \"bandwidth\": 1,\n", + " \"rms_contrast\": 0.2,\n", + " \"pseudo_noise\": True,\n", + "}\n", + "\n", + "stim = narrowband(**params)\n", + "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=15, min=5, max=25, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=15, min=5, max=25, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=20, max=60, step=2, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "\n", + "w_freq = iw.FloatSlider(value=3., min=0.1, max=10.0, description=\"center frequency\")\n", + "w_band = iw.FloatSlider(value=1., min=0.5, max=2.0, description=\"bandwidth (octaves)\")\n", + "w_stats = iw.HBox([w_freq, w_band])\n", + "\n", + "w_rms = iw.FloatSlider(value=0.5, min=0.1, max=1.0, description=\"rms contrast (std)\")\n", + "ui = iw.VBox([w_size, w_stats, w_rms])\n", + "\n", + "def show_noise(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " center_frequency=None,\n", + " bandwidth=None,\n", + " rms_contrast=None,\n", + "):\n", + "\n", + " stim = narrowband(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " center_frequency=center_frequency,\n", + " bandwidth=bandwidth,\n", + " rms_contrast=rms_contrast\n", + " )\n", + " plot_stim(stim, vmin=-3, vmax=3)\n", + "\n", + "\n", + "out = iw.interactive_output(show_noise,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"center_frequency\": w_freq,\n", + " \"bandwidth\": w_band,\n", + " \"rms_contrast\": w_rms,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Center frequency and bandwidth" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def show_noise_properties(stim):\n", + " # Get amplitude spectrum\n", + " noise_amplitude = np.abs(np.fft.fftshift(np.fft.fft2(stim[\"img\"])))\n", + " fs = np.fft.fftshift(np.fft.fftfreq(stim[\"shape\"][0], d=1./stim[\"ppd\"]))\n", + " \n", + " # Calculate full-width-half-maximum\n", + " fwhm = stim[\"sigma\"] * 2. * np.sqrt(2. * np.log(2))\n", + " \n", + " # Get lower and upper bound at fwhm\n", + " fup, flow = stim[\"center_frequency\"] + fwhm/2., stim[\"center_frequency\"] - fwhm/2.\n", + " \n", + " # Calculate ratio and fractional bandwidth\n", + " ratio_bandwidth = np.log2(fup / flow) # in octaves\n", + " frac_bandwidth = (fup-flow) / stim[\"center_frequency\"]\n", + "\n", + " # Plotting:\n", + " plt.figure(figsize=(18, 4))\n", + " plt.subplot(1, 2, 1), plt.imshow(noise_amplitude, extent=[fs[0], fs[-1],]*2)\n", + " plt.colorbar(), plt.title('Noise spectrum'), plt.xlabel('cpd')\n", + "\n", + " plt.subplot(1, 2, 2)\n", + " plt.plot(fs, noise_amplitude[int(noise_amplitude.shape[0]/2.), :])\n", + " plt.plot([flow, flow], [0., noise_amplitude.max()], label='lower boundary max/2')\n", + " plt.plot([fup, fup], [0., noise_amplitude.max()], label='upper boundary max/2')\n", + " plt.title('Cut through'), plt.xlabel('cpd'), plt.legend()\n", + " plt.show()\n", + "\n", + " print('Center frequency:', np.round(stim[\"center_frequency\"], 3))\n", + " print('Ratio bandwidth:', np.round(ratio_bandwidth, 3))\n", + " print('Fractional bandwidth:', np.round(frac_bandwidth, 3))\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def interact_noise_properties(center_frequency, bandwidth):\n", + " stim = narrowband(visual_size=(10, 10), ppd=40, center_frequency=center_frequency, bandwidth=bandwidth)\n", + " show_noise_properties(stim)\n", + "\n", + "iw.interact(interact_noise_properties, center_frequency=(0.5, 9.5), bandwidth=(0.5, 1.5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Random noise vs pseudo-random noise" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def interact_randomness(center_frequency, bandwidth):\n", + " stim1 = narrowband(visual_size=(10, 10), ppd=40, center_frequency=center_frequency, pseudo_noise=True)\n", + " stim2 = narrowband(visual_size=(10, 10), ppd=40, center_frequency=center_frequency, pseudo_noise=False)\n", + " show_noise_properties(stim1)\n", + " show_noise_properties(stim2)\n", + "\n", + "iw.interact(interact_randomness, center_frequency=(0.5, 9.5), bandwidth=(0.5, 1.5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/noises/one_over_frequency.ipynb b/demo/noises/one_over_frequency.ipynb index 93bdf3fb..78adc189 100644 --- a/demo/noises/one_over_frequency.ipynb +++ b/demo/noises/one_over_frequency.ipynb @@ -1,311 +1,311 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 1 / f**exponent noise" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.noises.one_over_frequency import one_over_f" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10, 10),\n", - " \"ppd\": 40.,\n", - " \"exponent\": 1.5,\n", - " \"rms_contrast\": 0.2,\n", - " \"pseudo_noise\": True,\n", - "}\n", - "\n", - "stim = one_over_f(**params)\n", - "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=15, min=5, max=25, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=15, min=5, max=25, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=20, max=60, step=2, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "\n", - "w_exp = iw.FloatSlider(value=1.5, min=0.5, max=3.0, description=\"exponent\")\n", - "w_rms = iw.FloatSlider(value=0.5, min=0.1, max=1.0, description=\"rms contrast (std)\")\n", - "w_other = iw.HBox([w_exp, w_rms])\n", - "\n", - "ui = iw.VBox([w_size, w_other])\n", - "\n", - "def show_noise(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " exponent=None,\n", - " rms_contrast=None,\n", - "):\n", - "\n", - " stim = one_over_f(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " exponent=exponent,\n", - " rms_contrast=rms_contrast\n", - " )\n", - " plot_stim(stim, vmin=-3, vmax=3)\n", - "\n", - "\n", - "out = iw.interactive_output(show_noise,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"exponent\": w_exp,\n", - " \"rms_contrast\": w_rms,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Random noise vs pseudo-random noise" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def show_randomness(stim):\n", - " # Get amplitude spectrum\n", - " noise_amplitude = np.abs(np.fft.fftshift(np.fft.fft2(stim[\"img\"])))\n", - " fs = np.fft.fftshift(np.fft.fftfreq(stim[\"shape\"][0], d=1./stim[\"ppd\"]))\n", - "\n", - " # Plotting:\n", - " plt.figure(figsize=(18, 4))\n", - " plt.subplot(1, 2, 1), plt.imshow(noise_amplitude, extent=[fs[0], fs[-1],]*2)\n", - " plt.colorbar(), plt.title('Noise spectrum'), plt.xlabel('cpd')\n", - "\n", - " plt.subplot(1, 2, 2)\n", - " plt.plot(fs, noise_amplitude[int(noise_amplitude.shape[0]/2.), :])\n", - " plt.title('Cut through'), plt.xlabel('cpd')\n", - " plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "stim1 = one_over_f(visual_size=(10, 10), ppd=40, exponent=0.5, pseudo_noise=True)\n", - "stim2 = one_over_f(visual_size=(10, 10), ppd=40, exponent=0.5, pseudo_noise=False)\n", - "show_randomness(stim1)\n", - "show_randomness(stim2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# pink noise\n", - "\n", - "Also: `1 / f` noise" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.noises.one_over_frequency import pink" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10, 10),\n", - " \"ppd\": 40.,\n", - " \"rms_contrast\": 0.2,\n", - " \"pseudo_noise\": True,\n", - "}\n", - "\n", - "stim = pink(**params)\n", - "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# brown noise\n", - "\n", - "Also: `1 / f**2` noise" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.noises.one_over_frequency import brown" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10, 10),\n", - " \"ppd\": 40.,\n", - " \"rms_contrast\": 0.2,\n", - " \"pseudo_noise\": True,\n", - "}\n", - "\n", - "stim = brown(**params)\n", - "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1 / f**exponent noise" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.noises.one_over_frequency import one_over_f" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10, 10),\n", + " \"ppd\": 40.,\n", + " \"exponent\": 1.5,\n", + " \"rms_contrast\": 0.2,\n", + " \"pseudo_noise\": True,\n", + "}\n", + "\n", + "stim = one_over_f(**params)\n", + "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=15, min=5, max=25, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=15, min=5, max=25, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=20, max=60, step=2, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "\n", + "w_exp = iw.FloatSlider(value=1.5, min=0.5, max=3.0, description=\"exponent\")\n", + "w_rms = iw.FloatSlider(value=0.5, min=0.1, max=1.0, description=\"rms contrast (std)\")\n", + "w_other = iw.HBox([w_exp, w_rms])\n", + "\n", + "ui = iw.VBox([w_size, w_other])\n", + "\n", + "def show_noise(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " exponent=None,\n", + " rms_contrast=None,\n", + "):\n", + "\n", + " stim = one_over_f(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " exponent=exponent,\n", + " rms_contrast=rms_contrast\n", + " )\n", + " plot_stim(stim, vmin=-3, vmax=3)\n", + "\n", + "\n", + "out = iw.interactive_output(show_noise,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"exponent\": w_exp,\n", + " \"rms_contrast\": w_rms,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Random noise vs pseudo-random noise" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def show_randomness(stim):\n", + " # Get amplitude spectrum\n", + " noise_amplitude = np.abs(np.fft.fftshift(np.fft.fft2(stim[\"img\"])))\n", + " fs = np.fft.fftshift(np.fft.fftfreq(stim[\"shape\"][0], d=1./stim[\"ppd\"]))\n", + "\n", + " # Plotting:\n", + " plt.figure(figsize=(18, 4))\n", + " plt.subplot(1, 2, 1), plt.imshow(noise_amplitude, extent=[fs[0], fs[-1],]*2)\n", + " plt.colorbar(), plt.title('Noise spectrum'), plt.xlabel('cpd')\n", + "\n", + " plt.subplot(1, 2, 2)\n", + " plt.plot(fs, noise_amplitude[int(noise_amplitude.shape[0]/2.), :])\n", + " plt.title('Cut through'), plt.xlabel('cpd')\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stim1 = one_over_f(visual_size=(10, 10), ppd=40, exponent=0.5, pseudo_noise=True)\n", + "stim2 = one_over_f(visual_size=(10, 10), ppd=40, exponent=0.5, pseudo_noise=False)\n", + "show_randomness(stim1)\n", + "show_randomness(stim2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# pink noise\n", + "\n", + "Also: `1 / f` noise" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.noises.one_over_frequency import pink" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10, 10),\n", + " \"ppd\": 40.,\n", + " \"rms_contrast\": 0.2,\n", + " \"pseudo_noise\": True,\n", + "}\n", + "\n", + "stim = pink(**params)\n", + "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# brown noise\n", + "\n", + "Also: `1 / f**2` noise" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.noises.one_over_frequency import brown" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10, 10),\n", + " \"ppd\": 40.,\n", + " \"rms_contrast\": 0.2,\n", + " \"pseudo_noise\": True,\n", + "}\n", + "\n", + "stim = brown(**params)\n", + "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/demo/noises/white.ipynb b/demo/noises/white.ipynb index ba7dbb9d..2e2a7c1d 100644 --- a/demo/noises/white.ipynb +++ b/demo/noises/white.ipynb @@ -1,225 +1,225 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import IPython\n", - "import ipywidgets as iw\n", - "from stimuli.utils import plot_stimuli, plot_stim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# white" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from stimuli.noises import white" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\n", - " \"visual_size\": (10, 10),\n", - " \"ppd\": 40.,\n", - " \"rms_contrast\": 0.5,\n", - " \"pseudo_noise\": True,\n", - "}\n", - "\n", - "stim = white(**params)\n", - "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interactive" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define widgets\n", - "w_height = iw.IntSlider(value=15, min=5, max=25, description=\"height [deg]\")\n", - "w_width = iw.IntSlider(value=15, min=5, max=25, description=\"width [deg]\")\n", - "w_ppd = iw.IntSlider(value=40, min=20, max=60, step=2, description=\"ppd\")\n", - "w_size = iw.HBox([w_height, w_width, w_ppd])\n", - "\n", - "w_rms = iw.FloatSlider(value=0.5, min=0.1, max=1.0, description=\"rms contrast (std)\")\n", - "ui = iw.VBox([w_size, w_rms])\n", - "\n", - "def show_noise(\n", - " height=None,\n", - " width=None,\n", - " ppd=None,\n", - " rms_contrast=None,\n", - "):\n", - "\n", - " stim = white(\n", - " visual_size=(height, width),\n", - " ppd=ppd,\n", - " rms_contrast=rms_contrast\n", - " )\n", - " plot_stim(stim, vmin=-2, vmax=2)\n", - "\n", - "\n", - "out = iw.interactive_output(show_noise,\n", - " {\n", - " \"height\": w_height,\n", - " \"width\": w_width,\n", - " \"ppd\": w_ppd,\n", - " \"rms_contrast\": w_rms,\n", - " })\n", - "\n", - "display(ui, out)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Random noise vs pseudo-random noise" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def show_randomness(stim):\n", - " # Get amplitude spectrum\n", - " noise_amplitude = np.abs(np.fft.fftshift(np.fft.fft2(stim[\"img\"])))\n", - " fs = np.fft.fftshift(np.fft.fftfreq(stim[\"shape\"][0], d=1./stim[\"ppd\"]))\n", - "\n", - " # Plotting:\n", - " plt.figure(figsize=(18, 4))\n", - " plt.subplot(1, 2, 1), plt.imshow(noise_amplitude, extent=[fs[0], fs[-1],]*2)\n", - " plt.colorbar(), plt.title('Noise spectrum'), plt.xlabel('cpd')\n", - "\n", - " plt.subplot(1, 2, 2)\n", - " plt.plot(fs, noise_amplitude[int(noise_amplitude.shape[0]/2.), :])\n", - " plt.title('Cut through'), plt.xlabel('cpd')\n", - " plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "stim1 = white(visual_size=(10, 10), ppd=40, pseudo_noise=True)\n", - "stim2 = white(visual_size=(10, 10), ppd=40, pseudo_noise=False)\n", - "show_randomness(stim1)\n", - "show_randomness(stim2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import IPython\n", + "import ipywidgets as iw\n", + "from stimupy.utils import plot_stimuli, plot_stim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# white" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stimupy.noises import white" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"visual_size\": (10, 10),\n", + " \"ppd\": 40.,\n", + " \"rms_contrast\": 0.5,\n", + " \"pseudo_noise\": True,\n", + "}\n", + "\n", + "stim = white(**params)\n", + "plot_stim(stim, vmin=stim[\"intensity_range\"][0], vmax=stim[\"intensity_range\"][1])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interactive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define widgets\n", + "w_height = iw.IntSlider(value=15, min=5, max=25, description=\"height [deg]\")\n", + "w_width = iw.IntSlider(value=15, min=5, max=25, description=\"width [deg]\")\n", + "w_ppd = iw.IntSlider(value=40, min=20, max=60, step=2, description=\"ppd\")\n", + "w_size = iw.HBox([w_height, w_width, w_ppd])\n", + "\n", + "w_rms = iw.FloatSlider(value=0.5, min=0.1, max=1.0, description=\"rms contrast (std)\")\n", + "ui = iw.VBox([w_size, w_rms])\n", + "\n", + "def show_noise(\n", + " height=None,\n", + " width=None,\n", + " ppd=None,\n", + " rms_contrast=None,\n", + "):\n", + "\n", + " stim = white(\n", + " visual_size=(height, width),\n", + " ppd=ppd,\n", + " rms_contrast=rms_contrast\n", + " )\n", + " plot_stim(stim, vmin=-2, vmax=2)\n", + "\n", + "\n", + "out = iw.interactive_output(show_noise,\n", + " {\n", + " \"height\": w_height,\n", + " \"width\": w_width,\n", + " \"ppd\": w_ppd,\n", + " \"rms_contrast\": w_rms,\n", + " })\n", + "\n", + "display(ui, out)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Random noise vs pseudo-random noise" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def show_randomness(stim):\n", + " # Get amplitude spectrum\n", + " noise_amplitude = np.abs(np.fft.fftshift(np.fft.fft2(stim[\"img\"])))\n", + " fs = np.fft.fftshift(np.fft.fftfreq(stim[\"shape\"][0], d=1./stim[\"ppd\"]))\n", + "\n", + " # Plotting:\n", + " plt.figure(figsize=(18, 4))\n", + " plt.subplot(1, 2, 1), plt.imshow(noise_amplitude, extent=[fs[0], fs[-1],]*2)\n", + " plt.colorbar(), plt.title('Noise spectrum'), plt.xlabel('cpd')\n", + "\n", + " plt.subplot(1, 2, 2)\n", + " plt.plot(fs, noise_amplitude[int(noise_amplitude.shape[0]/2.), :])\n", + " plt.title('Cut through'), plt.xlabel('cpd')\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stim1 = white(visual_size=(10, 10), ppd=40, pseudo_noise=True)\n", + "stim2 = white(visual_size=(10, 10), ppd=40, pseudo_noise=False)\n", + "show_randomness(stim1)\n", + "show_randomness(stim2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" + } + } }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "d79f930315d22092267204fc095ece7b80c939bb23fde6f7397d8c6352112825" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/manuscript/paper.md b/manuscript/paper.md index b41ec4b9..97191f1a 100644 --- a/manuscript/paper.md +++ b/manuscript/paper.md @@ -1,5 +1,5 @@ --- -title: 'PyStim: A Python stimulus creation package for vision science' +title: 'Stimupy: A Python stimulus creation package for vision science' tags: - Python package - vision science @@ -28,26 +28,41 @@ bibliography: bibliography.bib # Summary -[PyStim](change) is a Python package for creating visual stimuli which are commonly used to study the human visual system. -These include basic shapes, a variety of gratings, different noises and other relevant visual phenomena (e.g. checkerboards, Cornsweet stimulus, White stimulus). - -The main purpose of [PyStim](change) is to support vision science by providing a large, openly-available and flexible battery of relevant visual stimuli. -Its key feature is that all stimulus-functions are highly parameterized with parameters that vision scientists care about, such as visual angle, spatial frequency, or target placements to name a few. -[PyStim](change) is designed in a modular fashion, i.e. more complex stimuli are composed of less complex stimuli. -The output of all stimulus functions is a dictionary which contains the stimulus-array but also other potentially useful information such as (target) masks, sizes and other stimulus-specificities. -Using dictionaries is useful because it allows for any additional information (including stimulus specificities or experimental data) to be added. - -Given the flexibility of the stimulus functions, [PyStim](change) can be used to re-create many stimuli that have been used in prior vision research or create entirely new stimuli. +Stimupy is a Python package for creating visual stimuli which are commonly used to study the human visual system. +These include basic shapes, a variety of gratings, different noises and other relevant visual phenomena +(e.g. checkerboards, Cornsweet stimulus, White stimulus). + +The main purpose of Stimupy is to support vision science by providing a large, +openly-available and flexible battery of relevant visual stimuli. +Its key feature is that all stimulus-functions are highly parameterized +with parameters that vision scientists care about, +such as visual angle, spatial frequency, or target placements to name a few. +Stimupy is designed in a modular fashion, +i.e. more complex stimuli are composed of less complex stimuli. +The output of all stimulus functions is a dictionary which contains the stimulus-array +but also other potentially useful information such as (target) masks, sizes and other stimulus-specificities. +Using dictionaries is useful because it allows for any additional information to be added +(including stimulus specificities or experimental data). + +Given the flexibility of the stimulus functions, +Stimupy can be used to re-create many stimuli +that have been used in prior vision research or create entirely new stimuli. This is particularly useful to built stimulus benchmarks which can be easily accessed by the community. -As a second key feature, [PyStim](change) therefore provides a selection of stimulus sets (e.g. ModelFest) as they have been used in the original manuscripts including their experimental findings. -Given the modular nature of the package, any stimulus or stimulus set that is currently not available can be easily added in the future. +As a second key feature, Stimupy therefore provides a selection of stimulus sets +(e.g. ModelFest) +as they have been used in the original manuscripts including their experimental findings. +Given the modular nature of the package, +any stimulus or stimulus set that is currently not available can be easily added in the future. -Finally, [PyStim](change) allows for exploration of parameter spaces underlying the individual stimulus functions. +Finally, Stimupy allows for exploration of parameter spaces underlying the individual stimulus functions. This can support both experimental as well as theoretical work in vision science. -As such, exploring parameter spaces can in itself provide insights into the kind of stimuli we use in vision science, e.g. by revealing how certain stimuli are connected or by exploring their edge cases. -On the other hand, the exploration of certain parameter spaces along a single or multiple dimensions can be directly mapped to research questions that we want to study in our field. +As such, exploring parameter spaces can in itself provide insights +into the kind of stimuli we use in vision science, +e.g. by revealing how certain stimuli are connected or by exploring their edge cases. +On the other hand, the exploration of certain parameter spaces along a single or multiple dimensions +can be directly mapped to research questions that we want to study in our field. -Building blocks of [PyStim](change): +Building blocks of Stimupy: - Stimulus components containing basic shapes, gratings, and Gaussians ([components](link)) - Different kinds of noise which are commonly used for noise-masking experiments ([noise](link)) @@ -62,33 +77,44 @@ Building blocks of [PyStim](change): Visual stimuli are one of the core concepts to elucidate the mechanisms underlying visual processing. Though not complete, there are types of stimuli which are commonly used. -Despite their relevance, there is little software (but see related software) which supports the creation of many stimuli parameterized in a way which is targeted towards vision science. +Despite their relevance, there is little software (but see related software) +which supports the creation of many stimuli parameterized in a way which is targeted towards vision science. Hence, most vision research requires to implement stimuli from scratch. -Depending on the complexity of these stimuli, this endeavor can be time-consuming, prone to error and can make comparison with other research harder. -This is where [PyStim](change) comes into place. +Depending on the complexity of these stimuli, this endeavor can be time-consuming, +prone to error and can make comparison with other research harder. +This is where Stimupy comes into place. -[PyStim](change) is an openly-available Python package for creating a multitude of visual stimuli in a parameterized way and is targeted towards the needs of vision science. +Stimupy is an openly-available Python package for creating a multitude of visual stimuli +in a parameterized way and is targeted towards the needs of vision science. It can be easily downloaded and installed via github and pip. -Using [PyStim](change) can improve the consistency and accessibility of visual stimuli in the field, while helping to avoid bugs. -Given its modular and application-oriented nature, [PyStim](change) supports the use and design of visual stimuli for studying visual perception. +Using Stimupy can improve the consistency and accessibility of visual stimuli in the field, while helping to avoid bugs. +Given its modular and application-oriented nature, Stimupy supports the use and design of visual stimuli for studying visual perception. This is particularly useful because varying stimuli along one or multiple of the provided dimensions can be directly mapped to certain experimental designs. Another challenge in some vision disciplines is the need for benchmark datasets [@carney1999, @murray2021]. -These are particularly relevant to test the validity of vision models, hence support the understanding of visual mechanisms. -The usefulness of benchmarks have been shown in spatial vision with the ModelFest dataset [@carney1999, @carney2000] but also in various applications of computer vision including object recognition [@deng2009] or object segmentation [@martin2001]. -However, visual stimuli from previous vision research are not always accessible and the creation of visual stimuli can be challenging. +These are particularly relevant to test the validity of vision models, +hence support the understanding of visual mechanisms. +The usefulness of benchmarks have been shown +in spatial vision with the ModelFest dataset [@carney1999, @carney2000] +but also in various applications of computer vision +including object recognition [@deng2009] or object segmentation [@martin2001]. +However, visual stimuli from previous vision research are not always accessible +and the creation of visual stimuli can be challenging. Therefore, creating and agreeing on a benchmark dataset is not trivial. -To support this process and facilitate the accessibility of previously used stimuli, [PyStim](change) provides a collection of stimulus sets (including ModelFest) as they have been used in the original manuscripts. +To support this process and facilitate the accessibility of previously used stimuli, +Stimupy provides a collection of stimulus sets (including ModelFest) as they have been used in the original manuscripts. Hence, entire stimulus sets (including experimental findings) can be accessed via a single line of code. -Finally, due to its high degree of parameterizability, [PyStim](change) allows for extensive explorations of stimulus spaces. +Finally, due to its high degree of parameterizability, +Stimupy allows for extensive explorations of stimulus parameter spaces. This can be useful for vision experiments because varying stimuli along one or multiple dimensions can be mapped to certain experimental designs. Moreover, this feature can also guide theoretical work because it allows to find so-called maximally-differentiable stimuli [@wang2008]. The basic idea of maximum differentiation is to analyze model predictions for systematically varied stimuli to find the ones which differentiate best between models. Like this, the number of stimuli tested experimentally can be reduced to the most relevant stimuli. -Since collecting (human) data is resourceful, maximal differentiation is a useful method to reduce theoretically large stimulus parameter spaces to a testable number of stimuli. +Since collecting (human) data is resourceful, +maximal differentiation is a useful method to reduce theoretically large stimulus parameter spaces to a testable number of stimuli. -Key features of [PyStim](change): +Key features of Stimupy: - modular, open-source and unifying software for creating many visual stimuli, - functions tailored towards the needs of vision science, @@ -100,16 +126,24 @@ Key features of [PyStim](change): # Projects Using the Software -As stimulus creation is relevant for many vision science projects, stimulus functions which are part of [PyStim](change) or a pre-release version of the software have been used in almost all of the work of our laboratory within the last two years. -Some of [PyStim](change)'s noise functions have been used to generate the narrowband noise masks of varying center frequency in [@schmittwilken2022b]. -A pre-release version was used in multiple conference contributions in which we ... (joris vss 2021) [@vincent2021], in which we compared existing models of human brightness perception on a large battery of brightness stimuli [@schmittwilken2022a], in which we (joris ecvp 2022) [@vincent2022], and in which we studied human edge processing with Cornsweet stimuli in various kinds of noise (white, pink, brown, several narrowband noises) [@schmittwilken2022c]. -All these stimuli were created with [PyStim](change) or functions that are included in the software. -Moreover, we are using [PyStim](change) in ongoing work in our laboratory and in many student projects. +As stimulus creation is relevant for many vision science projects, +stimulus functions which are part of Stimupy or a pre-release version of the software +have been used in almost all of the work of our laboratory within the last two years. +Some of Stimupy's noise functions have been used to generate the narrowband noise masks of varying center frequency in [@schmittwilken2022b]. +A pre-release version was used in multiple conference contributions +in which we ... (joris vss 2021) [@vincent2021], +in which we compared existing models of human brightness perception on a large battery of brightness stimuli [@schmittwilken2022a], +in which we (joris ecvp 2022) [@vincent2022], +and in which we studied human edge processing with Cornsweet stimuli in various kinds of noise +(white, pink, brown, several narrowband noises) [@schmittwilken2022c]. +All these stimuli were created with Stimupy or functions that are included in the software. +Moreover, we are using Stimupy in ongoing work in our laboratory and in many student projects. # Related Software -Since the creation of visual stimuli is a central topic in vision sciences, there exist some software which fulfills partial or related needs as [PyStim](change). +Since the creation of visual stimuli is a central topic in vision sciences, +there exist some software which fulfills partial or related needs as Stimupy. These are - Psychtoolbox [@brainard1997], @@ -117,35 +151,45 @@ These are - Pyllusion [@makowski2021]. Vision experiments require profound control over the hardware at hand for precise stimulus display. -In their essence, Psychtoolbox and Psychopy support this process by interfacing between the computer hardware and Matlab (Psychtoolbox) and Python (Psychopy). +In their essence, Psychtoolbox and Psychopy support this process +by interfacing between the computer hardware and Matlab (Psychtoolbox) and Python (Psychopy). Hence, Pychtoolbox and Psychopy facilitate hardware control for vision experimentation via Matlab and Python. Though their main focus is on hardware control, Psychtoolbox and Psychopy also provide functions for generating stimuli. These include lines, basic shapes, gratings and Gaussian noise, as well as some dynamic displays and sound. Other recent software which recognized the need for unified and automatized stimulus creation is Pyllusion. -Pyllusion is a framework to generate optical illusions systematically using parameters which are relevant for vision experimentation (e.g. illusion strength). -This parametric approach of Pyllusion is in spirit very similar to [PyStim](change). +Pyllusion is a framework to generate optical illusions systematically using parameters +which are relevant for vision experimentation (e.g. illusion strength). +This parametric approach of Pyllusion is in spirit very similar to Stimupy. Besides, there exist no software to access existing stimulus sets in vision science. -The two most common practices are to re-implement the stimuli in an idiosyncratic fashion or to import static stimulus files (image or data files; see e.g. @carney1999, @murray2020). - -Within [PyStim](change), we combined and extended these software features and practices to unify and support stimulus creation and accessibility. -Compared to existing software, it contains a wide variety of visual stimuli which are all highly parameterizable and tailored towards vision science, and it can be used to access whole stimulus sets from previous vision research within a single line of code. -On top of that, the output dictionaries of the stimulus functions can store useful stimulus information , such as (target) masks and experimental data if applicable. +The two most common practices are to re-implement the stimuli in an idiosyncratic fashion +or to import static stimulus files (image or data files; see e.g. @carney1999, @murray2020). + +Within Stimupy, we combined and extended these software features and practices +to unify and support stimulus creation and accessibility. +Compared to existing software, it contains a wide variety of visual stimuli +which are all highly parameterizable and tailored towards vision science, +and it can be used to access whole stimulus sets from previous vision research within a single line of code. +On top of that, the output dictionaries of the stimulus functions can store useful stimulus information, +such as (target) masks and experimental data if applicable. This is useful for comparing experimental findings and for developing and testing models of human vision. # Future Work In theory, there is an infinite number of stimuli which are or could be interesting in the future of vision research. -Hence, [PyStim](change) will by default remain under active development. -In future versions, we want to add more visual stimuli and more stimulus sets - particularly dynamic stimuli which are currently not included. -Finally, we want to foster the development of stimulus benchmarks in vision science which will be added to [PyStim](change). +Hence, Stimupy will by default remain under active development. +In future versions, we want to add more visual stimuli and more stimulus sets +-- particularly dynamic stimuli which are currently not included. +Finally, we want to foster the development of stimulus benchmarks in vision science which will be added to Stimupy. # Acknowledgements -Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) under Germany's Excellence Strategy -EXC 2002/1 "Science of Intelligence"- project number 390523135 and individual grants MA 5127/4-1 and 5-1 to M. Maertens +Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) +under Germany's Excellence Strategy -EXC 2002/1 "Science of Intelligence"- project number 390523135 +and individual grants MA 5127/4-1 and 5-1 to M. Maertens # References diff --git a/stimuli/__init__.py b/stimuli/__init__.py deleted file mode 100644 index 71927450..00000000 --- a/stimuli/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from stimuli import ( - components, - illusions, - noises, - papers, -) diff --git a/stimuli/illusions/.gitignore b/stimuli/illusions/.gitignore deleted file mode 100644 index aab52d90..00000000 --- a/stimuli/illusions/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.png \ No newline at end of file diff --git a/stimuli/illusions/README.md b/stimuli/illusions/README.md deleted file mode 100644 index 018e8286..00000000 --- a/stimuli/illusions/README.md +++ /dev/null @@ -1,20 +0,0 @@ -## Illusions - -This directory contains modules for creating various brightness illusions. -Each module contains a function to create the general version of the illusion with suitable default parameters. -Beside that, in each module one can find the implementations of functions for generating illusions used in published papers. -These functions are the one being called from the modules in `papers` directory. - -An overview of all illusions can be generated by running the `overview.py` module. -Each functions returns a `Stimulus` object. `img` attribute contains a 2D numpy array -representing the illusion and `target_mask` attribute contains an integer mask specifying -the location of target patches inside the illusion. -### Example usage -```python -from stimuli import illusions, utils -import matplotlib.pyplot as plt - -stimulus = illusions.whites.white() -utils.plot_stim(stimulus, mask=True) -plt.show() -``` \ No newline at end of file diff --git a/stimuli/papers/RHS2007_params.txt b/stimuli/papers/RHS2007_params.txt deleted file mode 100644 index 38d500b8..00000000 --- a/stimuli/papers/RHS2007_params.txt +++ /dev/null @@ -1,473 +0,0 @@ -This txt file contains information about the stimuli used in the Robinson, Hammon and De Sa paper from 2007. -All the information specified in the paper is here and some parameters, which were not specifically specified in the paper -are approximated by the author. This txt file is not connected to any script in this repository, but the information from here -was used to write the functions in RHS2007.py - -{ - #Done - "WE-thick": { - "source": ["Blakeslee and McCourt (1999)", "White (1979)"], - "illusion": "Whites_classic", - "grating": { - "wave": "square", - "orientation": "horizontal", # direction of wave - "n_cycles": 4, # full cycles (white + black) - "phase": 0, # starting phase (bar), 0 = black - "height": 12, # deg, height of grating - "phase_width": 2, # deg, wwidth of single phase (bar) - "width": 16, # deg, phase_width * 2 * n_cycles - }, - "target": [ - { - "shape": (2, 4), # deg, width, height - "phase_idx": 3, # which phase (bar) target is on - "offset": 0, # deg, vertical distance from horizontal meridian - }, - { - "shape": (2, 4), # deg, width, height - "phase_idx": 5, # which phase (bar) target is on - "offset": 0, # deg, vertical distance from horizontal meridian - }, - ], - }, - "WE-thin-wide": { - "source": ["Blakeslee and McCourt (1999)", "White (1979)"], - "illusion": "Whites_classic", - "grating": { - "wave": "square", - "orientation": "horizontal", # direction of wave - "n_cycles": 8, # full cycles (white + black) - "phase": 1, # starting phase (bar), 0 = black - "height": 12, # deg, height of grating - "phase_width": 1, # deg, wwidth of single phase (bar) - "width": 16, # deg, phase_width * 2 * n_cycles - }, - "target": [ - { - "shape": (1, 2), # deg, width, height - "phase_idx": 4, # which phase (bar) target is on - "offset": 0, # deg, vertical distance from horizontal meridian - }, - { - "shape": (1, 2), # deg, width, height - "phase_idx": 13, # which phase (bar) target is on - "offset": 0, # deg, vertical distance from horizontal meridian - }, - ], - }, - "WE-dual": { - "source": "Robinson, Hammon, and De SA (2007)", - "stimuli": [ - { - "grating": { - "wave": "square", - "orientation": "horizontal", # direction of wave - "n_cycles": 4, # full cycles (white + black) - "phase": 0, # starting phase (left bar), 0 = black - "height": 6, # deg, height of grating - "phase_width": 1, # deg, wwidth of single phase (bar) - "width": 8, # deg, phase_width * 2 * n_cycles - }, - "target": [ - { - "shape": (1, 2), # deg, width, height - "phase_idx": 3, # which phase (bar) target is on - "offset": 0, # deg, vertical distance from horizontal meridian - }, - { - "shape": (1, 2), # deg, width, height - "phase_idx": 5, # which phase (bar) target is on - "offset": 0, # deg, vertical distance from horizontal meridian - }, - ], - }, - { - "grating": { - "wave": "square", - "orientation": "vertical", # direction of wave - "n_cycles": 4, # full cycles (white + black) - "phase": 0, # starting phase (top bar), 0 = black - "width": 6, # deg, height of grating - "phase_height": 1, # deg, wwidth of single phase (bar) - "height": 8, # deg, phase_width * 2 * n_cycles - }, - "target": [ - { - "shape": (2, 1), # deg, width, height - "phase_idx": 3, # which phase (bar) target is on - "offset": 0, # deg, horizontal distance from grating meridian - }, - { - "shape": (2, 1), # deg, width, height - "phase_idx": 5, # which phase (bar) target is on - "offset": 0, # deg, horizontal distance from grating meridian - }, - ], - }, - ], - }, - "WE-Anderson": { - "source": ["Blakeslee et al. (2005)", "Anderson (2001)"], - "target_shape": [1, 3], - }, - "WE-Howe": { - "source": ["Blakeslee et al. (2005)", "Howe (2001)"], - "target_shape": [1, 3], - }, - "WE-zigzag": { - "source": ["Based on Clifford and Spehar (2003)"], - "target_shape": [1, 3], - }, - "WE-radial-thick-small": { - "source": "Based on Anstis (2003)", - "grating": { - "wave": "square", - "orientation": "circular", - "radius": 8, # maybe? Looks like twice the height of the target (4deg, according to table) - "phase": 0, # black, first segment from right, counterclockwise - "n_cycles": 7, - "height": 16, # deg, 2 * radius - "width": 16, # deg, 2 * radius - }, - "target": [ - { - "height": 4, # deg, height - "phase_idx": 4, # which phase (segment) target is on - "offset": 2, # maybe? deg, from origin - }, - { - "height": 4, # deg, height - "phase_idx": 11, # which phase (segment) target is on - "offset": 2, # maybe? deg, from origin - }, - ], - }, - "WE-radial-thick": { - "source": "Based on Anstis (2003)", - "grating": { - "wave": "square", - "orientation": "circular", - "radius": 12, # maybe? - "phase": 1, # white, first segment from right, counterclockwise - "n_cycles": 9, - "height": 24, # deg, 2 * radius - "width": 24, # deg, 2 * radius - }, - "target": [ - { - "height": 4, # deg, height - "phase_idx": 5, # which phase (segment) target is on - "offset": 4, # maybe? deg, from origin - }, - { - "height": 4, # deg, height - "phase_idx": 14, # which phase (segment) target is on - "offset": 4, # maybe? deg, from origin - }, - ], - }, - "WE-radial-thin-small": { - "source": "Based on Anstis (2003)", - "grating": { - "wave": "square", - "orientation": "circular", - "radius": 8, # maybe? Looks like twice the height of the target (4deg, according to table) - "phase": 1, # black, first segment from right, counterclockwise - "n_cycles": 13, - "height": 16, # deg, 2 * radius - "width": 16, # deg, 2 * radius - }, - "target": [ - { - "height": 2, # deg, height - "phase_idx": 7, # which phase (segment) target is on - "offset": 3, # maybe? deg, from origin - }, - { - "height": 2, # deg, height - "phase_idx": 20, # which phase (segment) target is on - "offset": 3, # maybe? deg, from origin - }, - ], - }, - "WE-radial-thin": { - "source": "Based on Anstis (2003)", - "grating": { - "wave": "square", - "orientation": "circular", - "radius": 12, # maybe? - "phase": 1, # white, first segment from right, counterclockwise - "n_cycles": 21, - "height": 24, # deg, 2 * radius - "width": 24, # deg, 2 * radius - }, - "target": [ - { - "height": 2, # deg, height - "phase_idx": 11, # which phase (segment) target is on - "offset": 5, # maybe? deg, from origin - }, - { - "height": 2, # deg, height - "phase_idx": 32, # which phase (segment) target is on - "offset": 5, # maybe? deg, from origin - }, - ], - }, - "WE-circular1": { - "source": "Based on Howe (2005)", - "stimuli": [ - { - "grating": { - "orientation": "radial", - "phase": 1, # white, starting phase (center) - "phase_width": 1, # deg, thickness of phase (ring) - "n_cycles": 4, # full cycles, i.e., black+white rings - "radius": 8, # deg, phase_width*2*n_cycles - "width": 16, # deg 2*radius - "height": 16, # deg 2*radius - }, - "target": {"phase_idx": 5}, - }, - { - "grating": { - "orientation": "radial", - "phase": 0, # white, starting phase (center) - "phase_width": 1, # deg, thickness of phase (ring) - "n_cycles": 4, # full cycles, i.e., black+white rings - "radius": 8, # deg, phase_width*2*n_cycles - "width": 16, # deg 2*radius - "height": 16, # deg 2*radius - }, - "target": {"phase_idx": 5}, - }, - ], - }, - "WE-circular.5": { - "source": "Based on Howe (2005)", - "stimuli": [ - { - "grating": { - "orientation": "radial", - "phase": 1, # white, starting phase (center) - "phase_width": 0.5, # deg, thickness of phase (ring) - "n_cycles": 8, # full cycles, i.e., black+white rings - "radius": 8, # deg, phase_width*2*n_cycles - "width": 16, # deg 2*radius - "height": 16, # deg 2*radius - }, - "target": {"phase_idx": 11}, - }, - { - "grating": { - "orientation": "radial", - "phase": 0, # white, starting phase (center) - "phase_width": 0.5, # deg, thickness of phase (ring) - "n_cycles": 8, # full cycles, i.e., black+white rings - "radius": 8, # deg, phase_width*2*n_cycles - "width": 16, # deg 2*radius - "height": 16, # deg 2*radius - }, - "target": {"phase_idx": 11}, - }, - ], - }, - "WE-circular.25": { - "source": "Based on Howe (2005)", - "stimuli": [ - { - "grating": { - "orientation": "radial", - "phase": 1, # white, starting phase (center) - "phase_width": 0.25, # deg, thickness of phase (ring) - "n_cycles": 16, # full cycles, i.e., black+white rings - "radius": 8, # deg, phase_width*2*n_cycles - "width": 16, # deg 2*radius - "height": 16, # deg 2*radius - }, - "target": {"phase_idx": 23}, - }, - { - "grating": { - "orientation": "radial", - "phase": 0, # white, starting phase (center) - "phase_width": 0.25, # deg, thickness of phase (ring) - "n_cycles": 16, # full cycles, i.e., black+white rings - "radius": 8, # deg, phase_width*2*n_cycles - "width": 16, # deg 2*radius - "height": 16, # deg 2*radius - }, - "target": {"phase_idx": 23}, - }, - ], - }, - "Grating_induction": { - "source": ["Blakeslee and McCourt (1999)", "McCourt (1982)"], - "grating": { - "wave": "sine", - "orientation": "horizontal", # direction of wave - "n_cycles": 4, # full cycles (white + black) - "phase": 1, # starting phase (bar), 0 = black - "height": 12, # deg, height of grating - "phase_width": 2, # deg, wwidth of single phase (bar) - "width": 16, # deg, phase_width * 2 * n_cycles - }, - "target": {"height": 1}, # deg, height of neutral bar in center - }, - "SBC-large": { - "source": "Blakeslee and McCourt (1999)", - "illusion": "simultaneous brightness contrast", - "stimuli": [ - { - "background": {"height": 12, "width": 15, "phase": 1}, - "target": {"width": 3, "height": 3}, # deg - }, - { - "background": {"height": 12, "width": 15, "phase": 0}, - "target": {"width": 3, "height": 3}, # deg - }, - ], - }, - "SBC-small": { - "source": "Blakeslee and McCourt (1999)", - "illusion": "simultaneous brightness contrast", - "stimuli": [ - { - "background": {"height": 12, "width": 15, "phase": 1}, - "target": {"width": 1, "height": 1}, # deg - }, - { - "background": {"height": 12, "width": 15, "phase": 0}, - "target": {"width": 1, "height": 1}, # deg - }, - ], - }, - "Todorovic-equal": { - "source": ["Blakeslee and McCourt (1999)", "Pessoa et al. (1998)"], - "stimuli": [ - { - "background": {"height": 12, "width": 15, "phase": 1}, - "target": {"cross_length": 8}, # deg - }, - { - "background": {"height": 12, "width": 15, "phase": 0}, - "target": {"cross_length": 8}, # deg - }, - ], - }, - "Todorovic-in-large": { - "source": ["Blakeslee and McCourt (1999)", "Todorovic (1997)"], - "stimuli": [ - { - "background": {"height": 12, "width": 15, "phase": 1}, - "target": {"cross_length": 5.3}, # deg - }, - { - "background": {"height": 12, "width": 15, "phase": 0}, - "target": {"cross_length": 5.3}, # deg - }, - ], - }, - "Todorovic-in-small": { - "source": ["Blakeslee and McCourt (1999)", "Todorovic (1997)"], - "stimuli": [ - { - "background": {"height": 12, "width": 15, "phase": 1}, - "target": {"cross_length": 3}, # deg - }, - { - "background": {"height": 12, "width": 15, "phase": 0}, - "target": {"cross_length": 3}, # deg - }, - ], - }, - "Todorovic-out": { - "source": ["Blakeslee and McCourt (1999)", "Pessoa et al. (1998)"], - "stimuli": [ - { - "background": {"height": 12, "width": 15, "phase": 1}, - "target": {"cross_length": 8.7}, # deg - }, - { - "background": {"height": 12, "width": 15, "phase": 0}, - "target": {"cross_length": 8.7}, # deg - }, - ], - }, - "Checkerboard-0.156": { - "source": ["Blakeslee and McCourt (2004)", "DeValois and DeValois (1988)"], - "checkerboard": { - "check_height": 0.156, # deg - "check_width": 0.156, # deg - "n_checks": (102, 40), # number of checks, (width, height) - "width": 15.91, # deg - "height": 6.57, # deg - }, - "target": [ - { - "height": 0.156, # deg - "width": 0.156, # deg - "phase_idx": 17, # which check is target on? - }, - { - "height": 0.156, # deg - "width": 0.156, # deg - "phase_idx": 86, # which check is target on? - }, - ], - }, - "Checkerboard-0.938": { - "source": ["Blakeslee and McCourt (2004)", "DeValois and DeValois (1988)"], - "checkerboard": { - "check_height": 0.938, # deg - "check_width": 0.938, # deg - "n_checks": (25, 7), # number of checks, (width, height) - "width": 18.8, # deg - "height": 6.57, # deg - }, - "target": [ - {"height": 0.938, "width": 0.938, "phase_idx": 7}, # deg # deg - {"height": 0.938, "width": 0.938, "phase_idx": 18}, # deg # deg - ], - }, - "Checkerboard-2.09": { - "source": ["Blakeslee and McCourt (2004)", "DeValois and DeValois (1988)"], - "checkerboard": { - "check_height": 2.09, # deg - "check_width": 2.09, # deg - "n_checks": (10, 3), # number of checks, (width, height) - "width": 20.09, # deg - "height": 6.27, # deg - }, - "target": [ - { - "height": 2.09, # deg - "width": 2.09, # deg - "phase_idx": 3, # which check is target on? - }, - { - "height": 2.09, # deg - "width": 2.09, # deg - "phase_idx": 8, # which check is target on? - }, - ], - }, - "Corrugated Mondrian": { - "source": ["Blakeslee and McCourt (2001)", "Adelson (1993)"], - "target_shape": [2, 2], - }, - "Benary cross": { - "source": ["Blakeslee and McCourt (2001)", "Benary (1924)"], - "target_hypothenuse": 3, - }, - "Todorovic Benary 1–2": { - "source": ["Blakeslee and McCourt (2001)", "Todorovic (1997)"], - "target_hypothenuse": 3, - }, - "Todorovic Benary 3–4": { - "source": ["Blakeslee and McCourt (2001)", "Todorovic (1997)"], - "target_hypothenuse": 3, - }, - "Bullseye-thin": {"source": "Bindman and Chubb (2004)", "width": 0.608}, - "Bullseye-thick": {"source": "Bindman and Chubb (2004)", "width": 0.608}, -} diff --git a/stimupy/__init__.py b/stimupy/__init__.py new file mode 100644 index 00000000..9627cd8d --- /dev/null +++ b/stimupy/__init__.py @@ -0,0 +1 @@ +from . import components, illusions, noises, utils diff --git a/stimuli/components/__init__.py b/stimupy/components/__init__.py similarity index 94% rename from stimuli/components/__init__.py rename to stimupy/components/__init__.py index 12baded4..4bee84de 100644 --- a/stimuli/components/__init__.py +++ b/stimupy/components/__init__.py @@ -1,9 +1,10 @@ import itertools import warnings from copy import deepcopy + import numpy as np -from stimuli.utils import int_factorize, resolution +from stimupy.utils import int_factorize, resolution def image_base(visual_size=None, shape=None, ppd=None, rotation=0.0, origin="mean"): @@ -30,14 +31,14 @@ def image_base(visual_size=None, shape=None, ppd=None, rotation=0.0, origin="mea dict with keys: "visual_size", "ppd" : resolved from input arguments, "x", "y" : single axes - "horiztonal", "vertical" : numpy.ndarray of shape, with distance from origin, - in deg. visual angle, at each pixel + "horizontal", "vertical" : numpy.ndarray of shape, with distance from origin, + in deg. visual angle, at each pixel "radial" : numpyn.ndarray of shape, with radius from origin, - in deg. visual angle, at each pixel + in deg. visual angle, at each pixel "angular" : numpy.ndarray of shape, with angle relative to 3 o'clock, - in rad, at each pixel + in rad, at each pixel "cityblock" : numpy.ndarray of shape, with cityblock distance from origin, - in deg. visual angle ,at each pixel + in deg. visual angle ,at each pixel """ # Resolve resolution @@ -74,8 +75,8 @@ def image_base(visual_size=None, shape=None, ppd=None, rotation=0.0, origin="mea # Rotated alpha = [np.cos(np.deg2rad(rotation)), np.sin(np.deg2rad(rotation))] - rotated = alpha[0]*xx + alpha[1]*yy - + rotated = alpha[0] * xx + alpha[1] * yy + if origin == "corner": rotated = rotated - rotated.min() @@ -108,7 +109,7 @@ def mask_elements( Parameters ---------- - orientation : any of keys in stimuli.components.image_base() + orientation : any of keys in stimupy.components.image_base() which dimension to mask over edges : Sequence[Number] upper-limit of each consecutive elements @@ -419,31 +420,31 @@ def round_n_phases(n_phases, length, period="either"): return int(closest) -def create_overview(): - import numpy as np - - import stimuli.components.angulars as angulars - import stimuli.components.checkerboards as checkerboards - import stimuli.components.circulars as circulars - import stimuli.components.edges as edges - import stimuli.components.frames as frames - import stimuli.components.gaussians as gaussians - import stimuli.components.gratings as gratings - import stimuli.components.lines as lines - import stimuli.components.mondrians as mondrians - import stimuli.components.shapes as shapes +from . import ( + angulars, + checkerboards, + circulars, + edges, + frames, + gaussians, + gratings, + lines, + mondrians, + shapes, +) +def create_overview(): p = { "visual_size": 10, "ppd": 20, - } + } p_mondrians = { - "mondrian_positions": ((0,0), (0,5), (1,3), (4,6), (6,1)), + "mondrian_positions": ((0, 0), (0, 5), (1, 3), (4, 6), (6, 1)), "mondrian_sizes": 3, "mondrian_intensities": np.random.rand(5), - } + } # fmt: off stims = { @@ -505,7 +506,7 @@ def create_overview(): def overview(mask=False, save=None): - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = create_overview() diff --git a/stimuli/components/angulars.py b/stimupy/components/angulars.py similarity index 95% rename from stimuli/components/angulars.py rename to stimupy/components/angulars.py index c4d07683..4ef4651b 100644 --- a/stimuli/components/angulars.py +++ b/stimupy/components/angulars.py @@ -1,8 +1,8 @@ import numpy as np -from stimuli.components import draw_regions, mask_elements, resolve_grating_params -from stimuli.components.circulars import ring -from stimuli.utils import resolution +from stimupy.components import draw_regions, mask_elements, resolve_grating_params +from stimupy.components.circulars import ring +from stimupy.utils import resolution __all__ = [ "wedge", @@ -52,7 +52,7 @@ def mask_angle( visual_size=visual_size, ppd=ppd, origin=origin, - ) + ) stim["wedge_mask"] = stim["mask"] del stim["mask"] return stim @@ -185,7 +185,7 @@ def mask_segments( visual_size=visual_size, ppd=ppd, origin=origin, - ) + ) stim["wedge_mask"] = stim["mask"] del stim["mask"] return stim @@ -244,7 +244,9 @@ def angular_segments( # Draw image stim["img"] = draw_regions( - stim["wedge_mask"], intensities=intensity_segments, intensity_background=intensity_background + stim["wedge_mask"], + intensities=intensity_segments, + intensity_background=intensity_background, ) return stim @@ -296,12 +298,14 @@ def grating( """ lst = [visual_size, ppd, shape, frequency, n_segments, segment_width] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'grating()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'n_segments', 'segment_width'") + raise ValueError( + "'grating()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'n_segments', 'segment_width'" + ) # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - + if frequency is not None and frequency > 0.5: raise ValueError("'frequency' in angular grating must be smaller than 0.5") @@ -395,8 +399,10 @@ def pinwheel( """ lst = [visual_size, ppd, shape, frequency, n_segments, segment_width] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'pinwheel()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'n_segments', 'segment_width'") + raise ValueError( + "'pinwheel()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'n_segments', 'segment_width'" + ) if radius is None: raise ValueError("pinwheel() missing argument 'radius' which is not 'None'") diff --git a/stimuli/components/checkerboards.py b/stimupy/components/checkerboards.py similarity index 89% rename from stimuli/components/checkerboards.py rename to stimupy/components/checkerboards.py index eb094918..a8bfbf17 100644 --- a/stimuli/components/checkerboards.py +++ b/stimupy/components/checkerboards.py @@ -1,7 +1,8 @@ -import numpy as np import warnings -from stimuli.components.gratings import square_wave +import numpy as np + +from stimupy.components.gratings import square_wave __all__ = [ "checkerboard", @@ -9,6 +10,7 @@ # TODO: Fix bug that changing rotation, affect check size! + def checkerboard( visual_size=None, ppd=None, @@ -55,8 +57,10 @@ def checkerboard( """ lst = [visual_size, ppd, shape, frequency, board_shape, check_visual_size] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'checkerboard()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'board_shape', 'check_visual_size'") + raise ValueError( + "'checkerboard()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'board_shape', 'check_visual_size'" + ) if isinstance(frequency, (float, int)) or frequency is None: frequency = (frequency, frequency) @@ -64,7 +68,7 @@ def checkerboard( board_shape = (board_shape, board_shape) if isinstance(check_visual_size, (float, int)) or check_visual_size is None: check_visual_size = (check_visual_size, check_visual_size) - + create_twice = visual_size is None and shape is None # Create checkerboard by treating it as a plaid @@ -81,8 +85,8 @@ def checkerboard( intensity_bars=intensity_checks, origin="corner", round_phase_width=round_phase_width, - ) - + ) + sw2 = square_wave( visual_size=visual_size, ppd=ppd, @@ -91,13 +95,13 @@ def checkerboard( n_bars=board_shape[1], bar_width=check_visual_size[1], period=period, - rotation=rotation+90, + rotation=rotation + 90, phase_shift=0, intensity_bars=intensity_checks, origin="corner", round_phase_width=round_phase_width, - ) - + ) + # If neither a visual_size nor a shape was given, each square wave # grating is always a square. An easy solution is to just recreate # both gratings with the resolved parameters @@ -116,8 +120,8 @@ def checkerboard( intensity_bars=intensity_checks, origin="corner", round_phase_width=round_phase_width, - ) - + ) + sw2 = square_wave( visual_size=(sw1["visual_size"][0], sw2["visual_size"][1]), ppd=sw1["ppd"], @@ -126,23 +130,25 @@ def checkerboard( n_bars=board_shape[1], bar_width=check_visual_size[1], period=period, - rotation=rotation+90, + rotation=rotation + 90, phase_shift=0, intensity_bars=intensity_checks, origin="corner", round_phase_width=round_phase_width, - ) + ) warnings.filterwarnings("default") - + # Add the two square-wave gratings into a checkerboard img = sw1["img"] + sw2["img"] - img = np.where(img == intensity_checks[0]+intensity_checks[1], intensity_checks[1], intensity_checks[0]) - + img = np.where( + img == intensity_checks[0] + intensity_checks[1], intensity_checks[1], intensity_checks[0] + ) + # Create a mask with target indices for each check - mask = sw1["grating_mask"] + sw2["grating_mask"]*sw1["grating_mask"].max()*10 + mask = sw1["grating_mask"] + sw2["grating_mask"] * sw1["grating_mask"].max() * 10 unique_vals = np.unique(mask) for v in range(len(unique_vals)): - mask[mask == unique_vals[v]] = v+1 + mask[mask == unique_vals[v]] = v + 1 stim = { "img": img, @@ -157,5 +163,5 @@ def checkerboard( "rotation": rotation, "intensity_checks": intensity_checks, "edges": (sw1["edges"], sw2["edges"]), - } + } return stim diff --git a/stimuli/components/circulars.py b/stimupy/components/circulars.py similarity index 95% rename from stimuli/components/circulars.py rename to stimupy/components/circulars.py index 7deb54c2..59187e1c 100644 --- a/stimuli/components/circulars.py +++ b/stimupy/components/circulars.py @@ -1,20 +1,14 @@ import copy import itertools import warnings + import numpy as np import scipy.special as sp -from stimuli.components import image_base, mask_elements, resolve_grating_params -from stimuli.utils import resolution +from stimupy.components import image_base, mask_elements, resolve_grating_params +from stimupy.utils import resolution -__all__ = [ - "disc_and_rings", - "disc", - "ring", - "annulus", - "grating", - "bessel" -] +__all__ = ["disc_and_rings", "disc", "ring", "annulus", "grating", "bessel"] def resolve_circular_params( @@ -79,7 +73,7 @@ def resolve_circular_params( frequency=frequency, period="ignore", ) - + # Remove too large radii: if visual_angle is not None: if params["edges"][-1] > visual_angle: @@ -215,13 +209,7 @@ def disc_and_rings( visual_size = resolution.validate_visual_size(visual_size) # Get masks for rings - params = mask_rings( - radii=radii, - shape=shape, - visual_size=visual_size, - ppd=ppd, - origin=origin - ) + params = mask_rings(radii=radii, shape=shape, visual_size=visual_size, ppd=ppd, origin=origin) shape = params["shape"] # Draw rings @@ -297,7 +285,7 @@ def disc( shape=shape, origin=origin, ) - stim["ring_mask"] = (stim["ring_mask"]/2).astype(int) + stim["ring_mask"] = (stim["ring_mask"] / 2).astype(int) return stim @@ -417,9 +405,11 @@ def grating( """ lst = [visual_size, ppd, shape, frequency, n_rings, ring_width] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'grating()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'n_rings', 'ring_width'") - + raise ValueError( + "'grating()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'n_rings', 'ring_width'" + ) + # Resolve sizes try: shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) @@ -434,7 +424,7 @@ def grating( frequency=frequency, n_rings=n_rings, ring_width=ring_width, - ) + ) # Clean-up params for passing through stim_params = copy.deepcopy(params) @@ -503,8 +493,8 @@ def bessel( shape=shape, rotation=0, origin=origin, - ) - + ) + img = base["radial"] * frequency * 2 * np.pi img = sp.jv(order, img) img = (img - img.min()) / (img.max() - img.min()) @@ -519,22 +509,22 @@ def bessel( "order": order, "frequency": frequency, "intensity_rings": intensity_rings, - } + } return stim - + if __name__ == "__main__": - from stimuli.utils.plotting import plot_stimuli - + from stimupy.utils.plotting import plot_stimuli + p = { "visual_size": (10, 20), "ppd": 50, - } - + } + stims = { "grating": grating(**p, frequency=2), "disc_and_rings": disc_and_rings(**p, radii=(1, 2, 3)), "ring": ring(**p, radii=(1, 2)), - } - + } + plot_stimuli(stims, mask=False) diff --git a/stimuli/components/edges.py b/stimupy/components/edges.py similarity index 96% rename from stimuli/components/edges.py rename to stimupy/components/edges.py index 7cb0fd0e..d4d92631 100644 --- a/stimuli/components/edges.py +++ b/stimupy/components/edges.py @@ -1,8 +1,7 @@ import numpy as np -from stimuli.components import gaussians -from stimuli.components import image_base -from stimuli.utils import resolution +from stimupy.components import gaussians, image_base +from stimupy.utils import resolution __all__ = [ "step_edge", @@ -15,7 +14,7 @@ def step_edge( visual_size=None, ppd=None, shape=None, - rotation=0., + rotation=0.0, intensity_edges=(0.0, 1.0), ): """Draw a central step edge @@ -47,8 +46,8 @@ def step_edge( shape=shape, rotation=rotation, origin="corner", - ) - + ) + img = np.ones(shape) * intensity_edges[0] img = np.where(base["rotated"] < base["rotated"].mean(), img, intensity_edges[1]) mask = np.ones(shape) @@ -62,8 +61,8 @@ def step_edge( "shape": base["shape"], "rotation": rotation, "intensity_edges": intensity_edges, - } - + } + return stim @@ -72,7 +71,7 @@ def gaussian_edge( ppd=None, shape=None, sigma=None, - rotation=0., + rotation=0.0, intensity_edges=(0.0, 1.0), intensity_background=0.5, ): @@ -111,15 +110,15 @@ def gaussian_edge( shape=shape, rotation=rotation, intensity_edges=(0.0, 1.0), - ) - + ) + window = gaussians.gaussian( visual_size=visual_size, ppd=ppd, shape=shape, sigma=sigma, - ) - + ) + img = stim["img"] - intensity_background img = img * window["img"] + intensity_background stim["img"] = img @@ -184,10 +183,10 @@ def cornsweet_edge( raise ValueError("cornsweet_edge() missing argument 'ramp_width' which is not 'None'") # Resolve resolution - shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) + shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") - if ramp_width > visual_size[1]/2: + if ramp_width > visual_size[1] / 2: raise ValueError("ramp_width is too large") ramp_width_px = int(ramp_width * ppd[1]) diff --git a/stimuli/components/frames.py b/stimupy/components/frames.py similarity index 95% rename from stimuli/components/frames.py rename to stimupy/components/frames.py index d038c661..5755d4fb 100644 --- a/stimuli/components/frames.py +++ b/stimupy/components/frames.py @@ -1,7 +1,7 @@ import numpy as np -from stimuli.components import draw_regions, mask_elements, resolve_grating_params -from stimuli.utils import resolution +from stimupy.components import draw_regions, mask_elements, resolve_grating_params +from stimupy.utils import resolution __all__ = [ "frames", @@ -47,7 +47,7 @@ def mask_frames( visual_size=visual_size, ppd=ppd, origin=origin, - ) + ) stim["frame_mask"] = stim["mask"] del stim["mask"] return stim @@ -162,8 +162,10 @@ def grating( """ lst = [visual_size, ppd, shape, frequency, n_frames, frame_width] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'grating()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'n_frames', 'frame_width'") + raise ValueError( + "'grating()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'n_frames', 'frame_width'" + ) # Try to resolve resolution try: @@ -219,7 +221,7 @@ def grating( if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = { "frames": frames(visual_size=(8, 16), frame_radii=(1, 2, 3), ppd=32), @@ -227,4 +229,3 @@ def grating( } plot_stimuli(stims, mask=False, save=None) - diff --git a/stimuli/components/gaussians.py b/stimupy/components/gaussians.py similarity index 79% rename from stimuli/components/gaussians.py rename to stimupy/components/gaussians.py index 5ee66be6..6640cbd2 100644 --- a/stimuli/components/gaussians.py +++ b/stimupy/components/gaussians.py @@ -1,10 +1,10 @@ import numpy as np -from stimuli.components import image_base -from stimuli.components.shapes import ellipse +from stimupy.components import image_base +from stimupy.components.shapes import ellipse __all__ = [ - "gaussian", + "gaussian", ] @@ -14,10 +14,10 @@ def gaussian( shape=None, sigma=None, rotation=0, - intensity_max=1., + intensity_max=1.0, origin="mean", - ): - """ Create a Gaussian (envelop) +): + """Create a Gaussian (envelop) Parameters ---------- @@ -49,7 +49,7 @@ def gaussian( raise ValueError("gaussian() missing argument 'sigma' which is not 'None'") if isinstance(sigma, (float, int)): sigma = (sigma, sigma) - + # Resolve resolutions and get distances base = image_base( visual_size=visual_size, @@ -57,7 +57,7 @@ def gaussian( shape=shape, rotation=rotation, origin=origin, - ) + ) xx = base["horizontal"] yy = base["vertical"] @@ -65,14 +65,14 @@ def gaussian( theta = np.deg2rad(rotation) # determine a, b, c coefficients - a = (np.cos(theta)**2 / (2*sigma[1]**2)) + (np.sin(theta)**2 / (2*sigma[0]**2)) - b = -(np.sin(2*theta) / (4*sigma[1]**2)) + (np.sin(2*theta) / (4*sigma[0]**2)) - c = (np.sin(theta)**2 / (2*sigma[1]**2)) + (np.cos(theta)**2 / (2*sigma[0]**2)) + a = (np.cos(theta) ** 2 / (2 * sigma[1] ** 2)) + (np.sin(theta) ** 2 / (2 * sigma[0] ** 2)) + b = -(np.sin(2 * theta) / (4 * sigma[1] ** 2)) + (np.sin(2 * theta) / (4 * sigma[0] ** 2)) + c = (np.sin(theta) ** 2 / (2 * sigma[1] ** 2)) + (np.cos(theta) ** 2 / (2 * sigma[0] ** 2)) # create Gaussian - gaussian = np.exp(-(a*xx**2 + 2*b*xx*yy + c*yy**2)) + gaussian = np.exp(-(a * xx**2 + 2 * b * xx * yy + c * yy**2)) gaussian = gaussian / gaussian.max() * intensity_max - + # create mask as ellipse with sigma radius mask = ellipse( visual_size=visual_size, @@ -81,8 +81,8 @@ def gaussian( radius=sigma, rotation=rotation, origin=origin, - )["shape_mask"] - + )["shape_mask"] + stim = { "img": gaussian, "gaussian_mask": mask.astype(int), @@ -91,22 +91,22 @@ def gaussian( "visual_size": base["visual_size"], "shape": base["shape"], "ppd": base["ppd"], - } + } return stim if __name__ == "__main__": - from stimuli.utils.plotting import plot_stimuli - + from stimupy.utils.plotting import plot_stimuli + p = { "visual_size": (10, 8), "ppd": 50, "rotation": 90, - } - + } + stims = { "gaussian1": gaussian(**p, sigma=2), "gaussian2": gaussian(**p, sigma=(3, 2)), - } - - plot_stimuli(stims, mask=True) \ No newline at end of file + } + + plot_stimuli(stims, mask=True) diff --git a/stimuli/components/gratings.py b/stimupy/components/gratings.py similarity index 93% rename from stimuli/components/gratings.py rename to stimupy/components/gratings.py index 59db06d8..7f0f959f 100644 --- a/stimuli/components/gratings.py +++ b/stimupy/components/gratings.py @@ -1,8 +1,8 @@ import numpy as np -from stimuli.components import draw_regions, mask_elements, resolve_grating_params -from stimuli.components.gaussians import gaussian -from stimuli.utils import resolution +from stimupy.components import draw_regions, mask_elements, resolve_grating_params +from stimupy.components.gaussians import gaussian +from stimupy.utils import resolution __all__ = [ "square_wave", @@ -49,7 +49,7 @@ def mask_bars( mask with integer index for each bar (key: "grating_mask"), and additional keys containing stimulus parameters """ - + stim = mask_elements( edges=edges, orientation=orientation, @@ -58,20 +58,20 @@ def mask_bars( shape=shape, visual_size=visual_size, ppd=ppd, - ) + ) stim["grating_mask"] = stim["mask"] del stim["mask"] return stim def shift_edges( - edges, - ppd=None, - phase_shift=None, - phase_width=None, - intensity_bars=None, - origin=None, - ): + edges, + ppd=None, + phase_shift=None, + phase_width=None, + intensity_bars=None, + origin=None, +): """Function to shift edges Parameters @@ -98,32 +98,34 @@ def shift_edges( phase_shift = phase_shift % 360 edges = np.array(edges) - + if phase_shift != 0: if phase_shift > 0 and phase_shift <= 180: intensity_bars = (intensity_bars[1], intensity_bars[0]) - + phase_shift_deg = phase_shift * phase_width / 180 phase_shift_deg = np.round(phase_shift_deg * ppd) / ppd edges = edges + phase_shift_deg - + if phase_shift > 0 and phase_shift <= 180: edges = np.append(phase_shift_deg, edges) elif phase_shift > 180: edges = np.append([phase_shift_deg - phase_width, phase_shift_deg], edges) - + if origin != "corner": nedges = int(len(edges) / 2) phase_width = np.diff(edges).mean() - edges_small = edges[0:nedges+1] + edges_small = edges[0 : nedges + 1] edges_large = edges[nedges::] - edges[nedges] + phase_width - edges[0] # edges_large = -np.round(edges_large[::-1], 8) edges_large = np.ceil(-edges_large[::-1] * 10e7) / 10e7 edges_large = edges_large[edges_large <= 1e-07] edges = list(np.append(edges_large, edges_small)) if nedges % 2: - edges = [edges[0]-phase_width,] + edges + edges = [ + edges[0] - phase_width, + ] + edges return list(edges), intensity_bars @@ -185,8 +187,10 @@ def square_wave( """ lst = [visual_size, ppd, shape, frequency, n_bars, bar_width] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'square_wave()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'") + raise ValueError( + "'square_wave()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'" + ) # Try to resolve resolution try: @@ -230,7 +234,7 @@ def square_wave( # Determine size/shape of whole image if None in shape: - shape = [length*alpha[1], length*alpha[0]] + shape = [length * alpha[1], length * alpha[0]] if np.round(alpha[1], 5) == 0: shape[0] = shape[1] if np.round(alpha[0], 5) == 0: @@ -245,7 +249,7 @@ def square_wave( shape = resolution.validate_shape(shape) visual_size = resolution.validate_visual_size(visual_size) ppd = resolution.validate_ppd(ppd) - + # Phase shift: edges = params["edges"] edges, intensities = shift_edges( @@ -254,8 +258,8 @@ def square_wave( phase_shift=phase_shift, phase_width=params["phase_width"], intensity_bars=intensity_bars, - origin=origin - ) + origin=origin, + ) # Get bars mask stim = mask_bars( @@ -334,8 +338,10 @@ def sine_wave( """ lst = [visual_size, ppd, shape, frequency, n_bars, bar_width] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'sine_wave()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'") + raise ValueError( + "'sine_wave()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'" + ) # Try to resolve resolution try: @@ -379,7 +385,7 @@ def sine_wave( # Determine size/shape of whole image if None in shape: - shape = [length*alpha[1], length*alpha[0]] + shape = [length * alpha[1], length * alpha[0]] if np.round(alpha[1], 5) == 0: shape[0] = shape[1] if np.round(alpha[0], 5) == 0: @@ -394,7 +400,7 @@ def sine_wave( shape = resolution.validate_shape(shape) visual_size = resolution.validate_visual_size(visual_size) ppd = resolution.validate_ppd(ppd) - + # Phase shift: edges = params["edges"] edges, intensities = shift_edges( @@ -403,8 +409,8 @@ def sine_wave( phase_shift=phase_shift, phase_width=params["phase_width"], intensity_bars=intensity_bars, - origin=origin - ) + origin=origin, + ) # Get bars mask stim = mask_bars( @@ -508,7 +514,7 @@ def gabor( origin=origin, ) mean_int = (intensity_bars[0] + intensity_bars[1]) / 2 - stim["img"] = (stim["img"]-mean_int) * gaussian_window["img"] + mean_int + stim["img"] = (stim["img"] - mean_int) * gaussian_window["img"] + mean_int return { **stim, @@ -564,8 +570,10 @@ def staircase( """ lst = [visual_size, ppd, shape, frequency, n_bars, bar_width] if len([x for x in lst if x is not None]) < 3: - raise ValueError("'staircase()' needs 3 non-None arguments for resolving from 'visual_size', " - "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'") + raise ValueError( + "'staircase()' needs 3 non-None arguments for resolving from 'visual_size', " + "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'" + ) # Try to resolve resolution try: @@ -608,7 +616,7 @@ def staircase( # Determine size/shape of whole image if None in shape: - shape = [length*alpha[1], length*alpha[0]] + shape = [length * alpha[1], length * alpha[0]] if np.round(alpha[1], 5) == 0: shape[0] = shape[1] if np.round(alpha[0], 5) == 0: @@ -623,7 +631,7 @@ def staircase( shape = resolution.validate_shape(shape) visual_size = resolution.validate_visual_size(visual_size) ppd = resolution.validate_ppd(ppd) - + # Phase shift: edges = params["edges"] edges, intensities = shift_edges( @@ -632,8 +640,8 @@ def staircase( phase_shift=0, phase_width=params["phase_width"], intensity_bars=intensity_bars, - origin="corner" - ) + origin="corner", + ) # Get bars mask stim = mask_bars( @@ -661,12 +669,12 @@ def staircase( def plaid( - grating_parameters1, - grating_parameters2, - weight1=1, - weight2=1, - sigma=None, - ): + grating_parameters1, + grating_parameters2, + weight1=1, + weight2=1, + sigma=None, +): """Create plaid consisting of two sine-wave gratings Parameters @@ -697,7 +705,7 @@ def plaid( grating_parameters2["origin"] = "center" grating1 = sine_wave(**grating_parameters1) grating2 = sine_wave(**grating_parameters2) - + if grating1["shape"] != grating2["shape"]: raise ValueError("Gratings must have the same shape") if grating1["ppd"] != grating2["ppd"]: @@ -711,10 +719,10 @@ def plaid( sigma=sigma, origin=grating1["origin"], ) - - img = (weight1*grating1["img"] + weight2*grating2["img"]) * window["img"] + + img = (weight1 * grating1["img"] + weight2 * grating2["img"]) * window["img"] img = img / (weight1 + weight2) - + # Update parameters grating1["img"] = img grating1["sigma"] = sigma @@ -727,7 +735,7 @@ def plaid( if __name__ == "__main__": - from stimuli.utils.plotting import plot_stimuli + from stimupy.utils.plotting import plot_stimuli rotation = 0 origin = "center" @@ -763,7 +771,7 @@ def plaid( "bar_width": 3.5, "period": "ignore", "origin": origin, - "rotation": rotation+90, + "rotation": rotation + 90, } p5 = { @@ -772,12 +780,12 @@ def plaid( "bar_width": 4, "period": "ignore", } - + p6 = { "visual_size": 4.0, "ppd": 25, "bar_width": 0.08, - } + } stims = { "n_bars": square_wave(**p1, rotation=rotation, origin=origin), @@ -795,6 +803,6 @@ def plaid( "gabor_odd": gabor(**p3, sigma=5), "gabor_ignore": gabor(**p4, sigma=3), "staircase": staircase(**p5, rotation=rotation), - "plaid": plaid(p3, p4, sigma=4.) + "plaid": plaid(p3, p4, sigma=4.0), } plot_stimuli(stims, mask=False) diff --git a/stimuli/components/lines.py b/stimupy/components/lines.py similarity index 84% rename from stimuli/components/lines.py rename to stimupy/components/lines.py index e1430eaf..5739d940 100644 --- a/stimuli/components/lines.py +++ b/stimupy/components/lines.py @@ -1,10 +1,11 @@ -import numpy as np -from PIL import Image, ImageDraw import copy import warnings -from stimuli.utils import resolution -from stimuli.components.shapes import ring +import numpy as np +from PIL import Image, ImageDraw + +from stimupy.components.shapes import ring +from stimupy.utils import resolution __all__ = [ "line", @@ -24,7 +25,7 @@ def line( intensity_line=1, intensity_background=0, origin="corner", - ): +): """Draw a line given the input parameters Parameters @@ -62,53 +63,58 @@ def line( """ if line_length is None: raise ValueError("line() missing argument 'line_length' which is not 'None'") - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) alpha = [np.cos(np.deg2rad(rotation)), np.sin(np.deg2rad(rotation))] - + if isinstance(line_position, (float, int)) and line_position is not None: line_position = (line_position, line_position) if line_position is None: origin = "center" - line_position = (-line_length*alpha[0] / 2, - -line_length*alpha[1] / 2) - + line_position = (-line_length * alpha[0] / 2, -line_length * alpha[1] / 2) + if origin == "corner": - position = (line_position[0]*ppd[0], line_position[1]*ppd[1]) + position = (line_position[0] * ppd[0], line_position[1] * ppd[1]) elif origin == "center" or "mean": - position = (int(np.round(line_position[0]*ppd[0]+shape[0]/2)), - int(np.round(line_position[1]*ppd[1]+shape[1]/2))) + position = ( + int(np.round(line_position[0] * ppd[0] + shape[0] / 2)), + int(np.round(line_position[1] * ppd[1] + shape[1] / 2)), + ) else: raise ValueError("origin must be corner, center or mean") - + line_width_old = copy.deepcopy(line_width) - line_width = np.round(line_width*ppd[0]) / ppd[0] + line_width = np.round(line_width * ppd[0]) / ppd[0] if line_width != line_width: warnings.warn(f"Rounding line_width; {line_width_old} -> {line_width}") if line_width == 0: warnings.warn("line_width == 0 -> using line_width of 1px") - + # Create Pillow Image object img = Image.new("RGB", (shape.width, shape.height)) - + # Calculate line coordinates - coords = (position[::-1], (int(np.round(position[1]+line_length*alpha[1]*ppd[1])), - int(np.round(position[0]+line_length*alpha[0]*ppd[0])))) - - if (any(num < 0 for num in coords[0]) or - any(num < 0 for num in coords[1])): + coords = ( + position[::-1], + ( + int(np.round(position[1] + line_length * alpha[1] * ppd[1])), + int(np.round(position[0] + line_length * alpha[0] * ppd[0])), + ), + ) + + if any(num < 0 for num in coords[0]) or any(num < 0 for num in coords[1]): raise ValueError("Line does not fully fit into image") - + # Create line image - ImageDraw.Draw(img).line(coords, width=int(line_width*ppd[0])) - + ImageDraw.Draw(img).line(coords, width=int(line_width * ppd[0])) + # Convert to numpy array, create mask and adapt intensities - img = np.array(img)[:,:,0] / 255 + img = np.array(img)[:, :, 0] / 255 mask = copy.deepcopy(img) img = img * (intensity_line - intensity_background) + intensity_background - + stim = { "img": img, "line_mask": mask.astype(int), @@ -122,7 +128,7 @@ def line( "intensity_line": intensity_line, "intensity_background": intensity_background, "origin": origin, - } + } return stim @@ -135,7 +141,7 @@ def dipole( line_gap=None, rotation=0, intensity_lines=(0, 1), - ): +): """Draw a two centered parallel lines given the input parameters Parameters @@ -172,16 +178,20 @@ def dipole( raise ValueError("dipole() missing argument 'line_gap' which is not 'None'") if line_gap == 0: raise ValueError("line_gap should be larger than 0") - + intensity_background = (intensity_lines[0] + intensity_lines[1]) / 2 alpha1 = [np.cos(np.deg2rad(rotation)), np.sin(np.deg2rad(rotation))] - alpha2 = [np.cos(np.deg2rad(rotation+90)), np.sin(np.deg2rad(rotation+90))] + alpha2 = [np.cos(np.deg2rad(rotation + 90)), np.sin(np.deg2rad(rotation + 90))] - line_position1 = (-line_length*alpha1[0]/2 + line_gap/2*alpha2[0], - -line_length*alpha1[1]/2 + line_gap/2*alpha2[1]) - - line_position2 = (-line_length*alpha1[0]/2 - line_gap/2*alpha2[0], - -line_length*alpha1[1]/2 - line_gap/2*alpha2[1]) + line_position1 = ( + -line_length * alpha1[0] / 2 + line_gap / 2 * alpha2[0], + -line_length * alpha1[1] / 2 + line_gap / 2 * alpha2[1], + ) + + line_position2 = ( + -line_length * alpha1[0] / 2 - line_gap / 2 * alpha2[0], + -line_length * alpha1[1] / 2 - line_gap / 2 * alpha2[1], + ) stim1 = line( visual_size=visual_size, @@ -194,7 +204,7 @@ def dipole( intensity_line=intensity_lines[0], intensity_background=intensity_background, origin="center", - ) + ) stim2 = line( visual_size=visual_size, @@ -204,19 +214,19 @@ def dipole( line_length=line_length, line_width=line_width, rotation=rotation, - intensity_line=intensity_lines[1]-intensity_background, + intensity_line=intensity_lines[1] - intensity_background, intensity_background=0, origin="center", - ) - + ) + stim1["img"] = stim1["img"] + stim2["img"] - stim1["line_mask"] = (stim1["line_mask"] + stim2["line_mask"]*2).astype(int) - + stim1["line_mask"] = (stim1["line_mask"] + stim2["line_mask"] * 2).astype(int) + if line_width == 0: line_width = 1 / np.unique(stim1["ppd"]) if line_width >= line_gap: raise ValueError("line_width should not be larger than line_gap") - + return stim1 @@ -228,7 +238,7 @@ def circle( line_width=0, intensity_line=1, intensity_background=0, - ): +): """Draw a circle given the input parameters Parameters @@ -258,14 +268,14 @@ def circle( """ if radius is None: raise ValueError("circle() missing argument 'radius' which is not 'None'") - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - if line_width*ppd[0] == 0: - line_width = 1/ppd[0] + if line_width * ppd[0] == 0: + line_width = 1 / ppd[0] stim = ring( - radii=(radius, radius+line_width), + radii=(radius, radius + line_width), intensity_rings=intensity_line, visual_size=visual_size, ppd=ppd, @@ -280,7 +290,7 @@ def circle( if __name__ == "__main__": - from stimuli.utils.plotting import plot_stimuli + from stimupy.utils.plotting import plot_stimuli p1 = { "visual_size": (10, 5), @@ -295,6 +305,6 @@ def circle( stims = { "line": line(**p1), "dipole": dipole(**p1, line_gap=1), - "circle": circle(visual_size=10, ppd=10, radius=3) + "circle": circle(visual_size=10, ppd=10, radius=3), } plot_stimuli(stims, mask=False) diff --git a/stimuli/components/mondrians.py b/stimupy/components/mondrians.py similarity index 74% rename from stimuli/components/mondrians.py rename to stimupy/components/mondrians.py index a211b724..1ac5a40a 100644 --- a/stimuli/components/mondrians.py +++ b/stimupy/components/mondrians.py @@ -1,7 +1,7 @@ import numpy as np -from stimuli.components.shapes import parallelogram -from stimuli.utils import resolution, degrees_to_pixels +from stimupy.components.shapes import parallelogram +from stimupy.utils import degrees_to_pixels, resolution __all__ = [ "mondrians", @@ -16,7 +16,7 @@ def mondrians( mondrian_sizes=None, mondrian_intensities=None, intensity_background=0.5, - ): +): """ Draw Mondrians of given size and intensity at given position @@ -57,21 +57,26 @@ def mondrians( shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") - + img = np.ones(shape) * intensity_background mask = np.zeros(shape) - + n_mondrians = len(mondrian_positions) if isinstance(mondrian_intensities, (float, int)): mondrian_intensities = (mondrian_intensities,) * n_mondrians - + if isinstance(mondrian_sizes, (float, int)): - mondrian_sizes = ((mondrian_sizes, mondrian_sizes), ) * n_mondrians + mondrian_sizes = ((mondrian_sizes, mondrian_sizes),) * n_mondrians - if any(len(lst) != n_mondrians for lst in [mondrian_positions, mondrian_sizes, mondrian_intensities]): - raise Exception("There need to be as many mondrian_positions as there are " - "mondrian_sizes and mondrian_intensities.") + if any( + len(lst) != n_mondrians + for lst in [mondrian_positions, mondrian_sizes, mondrian_intensities] + ): + raise Exception( + "There need to be as many mondrian_positions as there are " + "mondrian_sizes and mondrian_intensities." + ) mondrian_positions_px = [] mondrian_shapes = [] @@ -89,42 +94,48 @@ def mondrians( try: if len(individual_shapes) == 2: depth = 0 - individual_shapes = individual_shapes + [depth,] + individual_shapes = individual_shapes + [ + depth, + ] elif len(individual_shapes) == 3: depth = mondrian_sizes[m][2] else: - raise ValueError("Mondrian size tuples should be (height, width) for " - "rectangles or (height, width, depth) for parallelograms") + raise ValueError( + "Mondrian size tuples should be (height, width) for " + "rectangles or (height, width, depth) for parallelograms" + ) except Exception: - raise ValueError("Mondrian size tuples should be (height, width) for" - "rectangles or (height, width, depth) for parallelograms") - + raise ValueError( + "Mondrian size tuples should be (height, width) for" + "rectangles or (height, width, depth) for parallelograms" + ) + if depth < 0: - xpos += int(depth*ppd[0]) + xpos += int(depth * ppd[0]) mondrian_positions_px.append(tuple([ypos, xpos])) mondrian_shapes.append(tuple(individual_shapes)) # Create parallelogram patch = parallelogram( - visual_size=(mondrian_sizes[m][0], mondrian_sizes[m][1]+np.abs(depth)), + visual_size=(mondrian_sizes[m][0], mondrian_sizes[m][1] + np.abs(depth)), ppd=ppd, parallelogram_size=(mondrian_sizes[m][0], mondrian_sizes[m][1], depth), intensity_background=intensity_background, intensity_parallelogram=mondrian_intensities[m], ) - + # Place it into Mondrian mosaic yshape, xshape = patch["img"].shape if ypos < 0 or xpos < 0: raise ValueError("There are no negative position coordinates") - if (ypos+yshape > shape[0]) or (xpos+xshape > shape[1]): + if (ypos + yshape > shape[0]) or (xpos + xshape > shape[1]): raise ValueError("Not all Mondrians fit into the stimulus") mask_large = np.zeros(shape) - mask_large[ypos:ypos+yshape, xpos:xpos+xshape] = patch["shape_mask"] + mask_large[ypos : ypos + yshape, xpos : xpos + xshape] = patch["shape_mask"] img[mask_large == 1] = mondrian_intensities[m] - mask[mask_large == 1] = m+1 - + mask[mask_large == 1] = m + 1 + stim = { "img": img, "mondrian_mask": mask.astype(int), @@ -142,37 +153,37 @@ def mondrians( if __name__ == "__main__": - from stimuli.utils.plotting import plot_stimuli - + from stimupy.utils.plotting import plot_stimuli + p1 = { - "mondrian_positions": ((0,0), (0,4), (1,3), (4,4), (5,1)), + "mondrian_positions": ((0, 0), (0, 4), (1, 3), (4, 4), (5, 1)), "mondrian_sizes": 3, "mondrian_intensities": np.random.rand(5), - } - + } + p2 = { - "mondrian_positions": ((0,0), (8,4), (1,6), (4,4), (5,1)), - "mondrian_sizes": ((3,4,1), (2,2,0), (5,4,-1), (3,4,1), (5,2,0)), + "mondrian_positions": ((0, 0), (8, 4), (1, 6), (4, 4), (5, 1)), + "mondrian_sizes": ((3, 4, 1), (2, 2, 0), (5, 4, -1), (3, 4, 1), (5, 2, 0)), "mondrian_intensities": np.random.rand(5), - } - + } + p3 = { - "mondrian_positions": ((0,0), (0, 2)), - "mondrian_sizes": ((2,2,0), (2,2,0)), + "mondrian_positions": ((0, 0), (0, 2)), + "mondrian_sizes": ((2, 2, 0), (2, 2, 0)), "mondrian_intensities": (0.2, 0.8), - } - + } + p4 = { - "mondrian_positions": ((0,0), (0, 2)), - "mondrian_sizes": ((2,2,1), (2,2,1)), + "mondrian_positions": ((0, 0), (0, 2)), + "mondrian_sizes": ((2, 2, 1), (2, 2, 1)), "mondrian_intensities": (0.2, 0.8), - } - + } + stims = { "mondrians1": mondrians(visual_size=8, ppd=10, **p1), "mondrians2": mondrians(visual_size=10, ppd=10, **p2), "mondrians3": mondrians(visual_size=(2, 6), ppd=10, **p3), "mondrians4": mondrians(visual_size=(2, 6), ppd=10, **p4), - } - + } + plot_stimuli(stims, mask=False) diff --git a/stimuli/components/shapes.py b/stimupy/components/shapes.py similarity index 84% rename from stimuli/components/shapes.py rename to stimupy/components/shapes.py index dd172625..24722d17 100644 --- a/stimuli/components/shapes.py +++ b/stimupy/components/shapes.py @@ -1,9 +1,9 @@ import numpy as np -from stimuli.components import image_base -from stimuli.components.angulars import wedge -from stimuli.components.circulars import annulus, disc, ring -from stimuli.utils import resolution +from stimupy.components import image_base +from stimupy.components.angulars import wedge +from stimupy.components.circulars import annulus, disc, ring +from stimupy.utils import resolution __all__ = [ "rectangle", @@ -59,7 +59,7 @@ def rectangle( """ if rectangle_size is None: raise ValueError("rectangle() missing argument 'rectangle_size' which is not 'None'") - + # Resolve resolutions and get distances base = image_base( visual_size=visual_size, @@ -67,61 +67,61 @@ def rectangle( shape=shape, rotation=rotation, origin="center", - ) + ) xx = base["horizontal"] yy = base["vertical"] theta = np.deg2rad(rotation) rectangle_size = resolution.validate_visual_size(visual_size=rectangle_size) - + # Determine center position rect_posy = (base["visual_size"].height / 2) - (rectangle_size.height / 2) rect_posx = (base["visual_size"].width / 2) - (rectangle_size.width / 2) center_pos = (rect_posy, rect_posx) - + if rectangle_position is None: # If no position is given, place rectangle centrally rectangle_position = center_pos if isinstance(rectangle_position, (float, int)): rectangle_position = (rectangle_position, rectangle_position) - + # Determine shift rect_pos = (np.array(rectangle_position) * base["ppd"]).astype(int) center_pos = np.round(np.array(center_pos) * base["ppd"]) rect_shift = (rect_pos - center_pos).astype(int) - + # Rotate coordinate systems x = np.round(np.cos(theta) * xx - np.sin(theta) * yy, 8) y = np.round(np.sin(theta) * xx + np.cos(theta) * yy, 8) - + # Rounding for more robust behavior: - x = np.round(x * (base["ppd"][0]*2)) / (base["ppd"][0]*2) - y = np.round(y * (base["ppd"][0]*2)) / (base["ppd"][0]*2) - + x = np.round(x * (base["ppd"][0] * 2)) / (base["ppd"][0] * 2) + y = np.round(y * (base["ppd"][0] * 2)) / (base["ppd"][0] * 2) + # Draw rectangle - img1 = np.where(x < rectangle_size.width/2, 1, 0) - img2 = np.where(x >= -rectangle_size.width/2, 1, 0) - img3 = np.where(y < rectangle_size.height/2, 1, 0) - img4 = np.where(y >= -rectangle_size.height/2, 1, 0) + img1 = np.where(x < rectangle_size.width / 2, 1, 0) + img2 = np.where(x >= -rectangle_size.width / 2, 1, 0) + img3 = np.where(y < rectangle_size.height / 2, 1, 0) + img4 = np.where(y >= -rectangle_size.height / 2, 1, 0) img = img1 * img2 * img3 * img4 - + # Shift rectangle img = np.roll(img, (rect_shift[0], rect_shift[1]), axis=(0, 1)) # Does the rectangle fit? - x1 = rectangle_size[1]/2 * np.cos(theta) - x2 = rectangle_size[1]/2 * np.sin(theta) - y1 = rectangle_size[0]/2 * np.cos(theta) - y2 = rectangle_size[0]/2 * np.sin(theta) - cy = x2+y1 + np.abs(rect_shift[0] / base["ppd"][0]) + x1 = rectangle_size[1] / 2 * np.cos(theta) + x2 = rectangle_size[1] / 2 * np.sin(theta) + y1 = rectangle_size[0] / 2 * np.cos(theta) + y2 = rectangle_size[0] / 2 * np.sin(theta) + cy = x2 + y1 + np.abs(rect_shift[0] / base["ppd"][0]) cy = np.floor(cy * base["ppd"][0]) / base["ppd"][0] - cx = x1+y2 + np.abs(rect_shift[1] / base["ppd"][1]) + cx = x1 + y2 + np.abs(rect_shift[1] / base["ppd"][1]) cx = np.floor(cx * base["ppd"][1]) / base["ppd"][1] - if (cy > base["visual_size"][0]/2) or (cx > base["visual_size"][1]/2): + if (cy > base["visual_size"][0] / 2) or (cx > base["visual_size"][1] / 2): raise ValueError("stimulus does not fully fit into requested size") return { - "img": img*(intensity_rectangle-intensity_background) + intensity_background, + "img": img * (intensity_rectangle - intensity_background) + intensity_background, "shape_mask": img.astype(int), "visual_size": base["visual_size"], "ppd": base["ppd"], @@ -131,7 +131,7 @@ def rectangle( "intensity_background": intensity_background, "intensity_rectangle": intensity_rectangle, "rotation": rotation, - } + } def triangle( @@ -172,7 +172,7 @@ def triangle( """ if triangle_size is None: raise ValueError("triangle() missing argument 'triangle_size' which is not 'None'") - + # Resolve resolutions and get distances base = image_base( visual_size=visual_size, @@ -180,24 +180,24 @@ def triangle( shape=shape, rotation=rotation, origin="center", - ) + ) xx = base["horizontal"] yy = base["vertical"] triangle_size = resolution.validate_visual_size(visual_size=triangle_size) - - angle_diagonal = np.arctan(triangle_size[1]/triangle_size[0]) + + angle_diagonal = np.arctan(triangle_size[1] / triangle_size[0]) angle_diagonal = np.rad2deg(angle_diagonal) - theta = np.deg2rad(rotation+angle_diagonal) + theta = np.deg2rad(rotation + angle_diagonal) x = np.round(np.cos(theta) * xx - np.sin(theta) * yy, 8) - + # Split image in two parts following the diagonal if include_corners: img = np.where(x <= 0, 1, 0) else: - fac = base["ppd"][0] / 2 + fac = base["ppd"][0] / 2 x = np.round(x * fac) / fac img = np.where(x < 0, 1, 0) - + # Create rectangular mask rect = rectangle( visual_size=visual_size, @@ -205,11 +205,11 @@ def triangle( shape=shape, rectangle_size=triangle_size, rotation=rotation, - ) + ) img = img * rect["shape_mask"] return { - "img": img*(intensity_triangle-intensity_background) + intensity_background, + "img": img * (intensity_triangle - intensity_background) + intensity_background, "shape_mask": img.astype(int), "visual_size": base["visual_size"], "ppd": base["ppd"], @@ -218,7 +218,7 @@ def triangle( "intensity_triangle": intensity_triangle, "rotation": rotation, "include_corners": include_corners, - } + } def cross( @@ -266,7 +266,7 @@ def cross( raise ValueError("cross() missing argument 'cross_size' which is not 'None'") if cross_thickness is None: raise ValueError("cross() missing argument 'cross_thickness' which is not 'None'") - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) cross_size = resolution.validate_visual_size(cross_size) @@ -274,12 +274,12 @@ def cross( if isinstance(cross_arm_ratios, (float, int)): cross_arm_ratios = (cross_arm_ratios, cross_arm_ratios) - + # Determine coordinate center - cy = (visual_size.height / 2) - cx = (visual_size.width / 2) + cy = visual_size.height / 2 + cx = visual_size.width / 2 theta = np.deg2rad(rotation) - + # Calculate cross placement based on ratios of cross legs updown = cross_size.height - cross_thickness[0] down = updown / (cross_arm_ratios[0] + 1) @@ -288,11 +288,11 @@ def cross( right = leftright / (cross_arm_ratios[1] + 1) left = leftright - right - posy1 = cy - cross_size[0]/2 + (down-up) * np.cos(theta) / 2 - posx1 = cx - cross_thickness[0]/2 + (down-up) * np.sin(theta) / 2 + posy1 = cy - cross_size[0] / 2 + (down - up) * np.cos(theta) / 2 + posx1 = cx - cross_thickness[0] / 2 + (down - up) * np.sin(theta) / 2 - posy2 = cy - cross_thickness[1]/2 + (right-left) * np.sin(-theta) / 2 - posx2 = cx - cross_size[1]/2 + (right-left) * np.cos(-theta) / 2 + posy2 = cy - cross_thickness[1] / 2 + (right - left) * np.sin(-theta) / 2 + posx2 = cx - cross_size[1] / 2 + (right - left) * np.cos(-theta) / 2 # Create cross as two rectangles rect1 = rectangle( @@ -301,7 +301,7 @@ def cross( rectangle_size=(cross_size[0], cross_thickness[0]), rectangle_position=(posy1, posx1), rotation=rotation, - ) + ) rect2 = rectangle( visual_size=visual_size, @@ -309,13 +309,13 @@ def cross( rectangle_size=(cross_thickness[1], cross_size[1]), rectangle_position=(posy2, posx2), rotation=rotation, - ) - + ) + img = rect1["img"] + rect2["img"] img[img > 1] = 1 return { - "img": img*(intensity_cross-intensity_background) + intensity_background, + "img": img * (intensity_cross - intensity_background) + intensity_background, "shape_mask": img.astype(int), "shape": shape, "visual_size": visual_size, @@ -365,11 +365,18 @@ def parallelogram( and additional keys containing stimulus parameters """ if parallelogram_size is None: - raise ValueError("parallelogram() missing argument 'parallelogram_size' which is not 'None'") + raise ValueError( + "parallelogram() missing argument 'parallelogram_size' which is not 'None'" + ) if isinstance(parallelogram_size, (float, int)): parallelogram_size = (parallelogram_size, parallelogram_size, 0) if len(parallelogram_size) == 2: - parallelogram_size = tuple(list(parallelogram_size) + [0,]) + parallelogram_size = tuple( + list(parallelogram_size) + + [ + 0, + ] + ) # Resolve resolutions and get distances base = image_base( @@ -378,19 +385,19 @@ def parallelogram( shape=shape, rotation=rotation, origin="center", - ) + ) xx = base["horizontal"] yy = base["vertical"] - + # Create rectangule - rectangle_size = (parallelogram_size[0], parallelogram_size[1]+np.abs(parallelogram_size[2])) + rectangle_size = (parallelogram_size[0], parallelogram_size[1] + np.abs(parallelogram_size[2])) rect = rectangle( visual_size=visual_size, ppd=ppd, shape=shape, rectangle_size=rectangle_size, rotation=rotation, - ) + ) img = rect["img"] if parallelogram_size[2] != 0: @@ -401,28 +408,28 @@ def parallelogram( triangle_size = (np.abs(parallelogram_size[2]), parallelogram_size[0]) rot1 = rotation - 90 - angle_diagonal = np.arctan(triangle_size[1]/triangle_size[0]) + angle_diagonal = np.arctan(triangle_size[1] / triangle_size[0]) angle_diagonal = np.rad2deg(angle_diagonal) - theta = np.deg2rad(rot1+angle_diagonal) + theta = np.deg2rad(rot1 + angle_diagonal) x = np.round(np.cos(theta) * xx - np.sin(theta) * yy, 8) - + # Shift diagonals so that resulting triangles cover corners of rectangle theta = np.deg2rad(rotation) - pwidth = parallelogram_size[1]/2 * base["ppd"][0] + pwidth = parallelogram_size[1] / 2 * base["ppd"][0] shift1 = int(np.round(pwidth) * np.sin(theta)) shift2 = int(np.floor(pwidth) * np.cos(theta)) - + # Split image in two parts following the diagonal - tri1 = np.where(np.roll(x, (shift1, -shift2), axis=(0,1)) < 0, 0, 1) + tri1 = np.where(np.roll(x, (shift1, -shift2), axis=(0, 1)) < 0, 0, 1) tri1 = np.where(x < 0, tri1, 0) - tri2 = np.where(np.roll(x, (-shift1, shift2), axis=(0,1)) > 0, 0, 1) + tri2 = np.where(np.roll(x, (-shift1, shift2), axis=(0, 1)) > 0, 0, 1) tri2 = np.where(x >= 0, tri2, 0) - + # Combine everything img = tri1 * img + tri2 * img return { - "img": img*(intensity_parallelogram-intensity_background) + intensity_background, + "img": img * (intensity_parallelogram - intensity_background) + intensity_background, "shape_mask": img.astype(int), "shape": shape, "visual_size": visual_size, @@ -442,7 +449,7 @@ def ellipse( intensity_ellipse=1.0, intensity_background=0.0, rotation=0, - origin="mean" + origin="mean", ): """Draw an ellipse @@ -485,7 +492,7 @@ def ellipse( shape=shape, rotation=rotation, origin=origin, - ) + ) xx = base["horizontal"] yy = base["vertical"] @@ -496,22 +503,22 @@ def ellipse( y = np.round(np.sin(theta) * yy + np.cos(theta) * xx, 8) # Draw ellipse - arr = np.sqrt(x**2 + (y*radius[0]/radius[1])**2) + arr = np.sqrt(x**2 + (y * radius[0] / radius[1]) ** 2) img = np.where(arr <= radius[0], 1, 0) - + # Does ellipse fit? x1 = radius[1] * np.cos(theta) x2 = radius[1] * np.sin(theta) y1 = radius[0] * np.cos(theta) y2 = radius[0] * np.sin(theta) - cy = np.floor((x2+y1) * base["ppd"][0]) / base["ppd"][0] - cx = np.floor((x1+y2) * base["ppd"][1]) / base["ppd"][1] + cy = np.floor((x2 + y1) * base["ppd"][0]) / base["ppd"][0] + cx = np.floor((x1 + y2) * base["ppd"][1]) / base["ppd"][1] - if (cy > base["visual_size"][0]/2) or (cx > base["visual_size"][1]/2): + if (cy > base["visual_size"][0] / 2) or (cx > base["visual_size"][1] / 2): raise ValueError("stimulus does not fully fit into requested size") - + return { - "img": img*(intensity_ellipse-intensity_background) + intensity_background, + "img": img * (intensity_ellipse - intensity_background) + intensity_background, "shape_mask": img.astype(int), "shape": shape, "visual_size": visual_size, @@ -524,21 +531,21 @@ def ellipse( if __name__ == "__main__": - from stimuli.utils.plotting import plot_stimuli - + from stimupy.utils.plotting import plot_stimuli + p = { "visual_size": (10, 8), "ppd": 50, "rotation": 90, - } - + } + stims = { "rectangle": rectangle(**p, rectangle_size=(4, 2.5)), "triangle": triangle(**p, triangle_size=(4, 2.5)), "cross": cross(**p, cross_size=(4, 2.5), cross_thickness=1, cross_arm_ratios=(1, 1)), "parallelogram": parallelogram(**p, parallelogram_size=(5.2, 3.1, 0.9)), "parallelogram2": parallelogram(shape=(100, 100), ppd=10, parallelogram_size=(10, 9, -1)), - "ellipse": ellipse(**p, radius=(4, 3)) - } - + "ellipse": ellipse(**p, radius=(4, 3)), + } + plot_stimuli(stims, mask=False) diff --git a/stimuli/illusions/__init__.py b/stimupy/illusions/__init__.py similarity index 79% rename from stimuli/illusions/__init__.py rename to stimupy/illusions/__init__.py index 5bc0d768..a616031e 100644 --- a/stimuli/illusions/__init__.py +++ b/stimupy/illusions/__init__.py @@ -1,42 +1,26 @@ -from .angulars import * -from .benarys import * -from .checkerboards import * -from .circulars import * -from .cornsweets import * -from .cubes import * -from .delboeufs import * -from .dungeons import * -from .frames import * -from .gratings import * -from .hermanns import * -from .mondrians import * -from .mueller_lyers import * -from .ponzos import * -from .sbcs import * -from .todorovics import * -from .wedding_cakes import * -from .whites import * +from . import ( + angulars, + benarys, + checkerboards, + circulars, + cornsweets, + cubes, + delboeufs, + dungeons, + frames, + gratings, + hermanns, + mondrians, + mueller_lyers, + ponzos, + sbcs, + todorovics, + wedding_cakes, + whites, +) def create_overview(): - import stimuli.illusions.angulars as angulars - import stimuli.illusions.benarys as benarys - import stimuli.illusions.checkerboards as checkerboards - import stimuli.illusions.circulars as circulars - from stimuli.illusions.cornsweets import cornsweet - import stimuli.illusions.cubes as cubes - import stimuli.illusions.delboeufs as delboeufs - from stimuli.illusions.dungeons import dungeon - import stimuli.illusions.gratings as gratings - from stimuli.illusions.hermanns import grid as hermann_grid - from stimuli.illusions.mondrians import corrugated_mondrians - import stimuli.illusions.mueller_lyers as mueller_lyers - from stimuli.illusions.ponzos import ponzo - import stimuli.illusions.sbcs as sbcs - import stimuli.illusions.todorovics as todorovics - from stimuli.illusions.wedding_cakes import wedding_cake - import stimuli.illusions.whites as whites - import stimuli.illusions.frames as frames p = { "visual_size": (10, 10), "ppd": 20, @@ -53,12 +37,11 @@ def create_overview(): ), } - p_small_grating = { "ppd": 20, "frequency": 1, "intensity_bars": (1, 0), - } + } # fmt: off stims = { @@ -80,7 +63,7 @@ def create_overview(): "circular_bullseye": circulars.bullseye(**p, frequency=1.0), "circular_bullseye_two_sided": circulars.two_sided_bullseye(**p, frequency=1.0), # Cornsweet - "cornsweet": cornsweet(**p, ramp_width=3), + "cornsweet": cornsweets.cornsweet(**p, ramp_width=3), # Cube "cube_variable": cubes.varying_cells(ppd=20, cell_heights=(1, 1.5, 1), cell_widths=(1.5, 2, 1.5), cell_spacing=0.5, targets=1), "cube": cubes.cube(**p, n_cells=5, targets=(1, 2), cell_thickness=1, cell_spacing=0.5), @@ -88,7 +71,7 @@ def create_overview(): "delboeuf": delboeufs.delboeuf(**p, outer_radius=4, target_radius=1), "2sided_delboeuf": delboeufs.two_sided(**p, outer_radii=(2, 1.1), target_radius=1), # Dungeon - "dungeon": dungeon(**p, n_cells=5), + "dungeon": dungeons.dungeon(**p, n_cells=5), # Frames "frames": frames.rings(**p, frequency=0.5, target_indices=3), "frames_general": frames.rings_generalized(**p, frame_radii=(1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5), target_indices=3), @@ -112,14 +95,14 @@ def create_overview(): "grating_induction": gratings.induction(**p, frequency=0.5, target_width=0.5), "grating_induction_blur": gratings.induction_blur(**p, frequency=0.5, target_width=0.5, target_blur=2), # Hermann´ - "hermann": hermann_grid(**p, element_size=(1.5, 1.5, 0.2)), + "hermann": hermanns.grid(**p, element_size=(1.5, 1.5, 0.2)), # Mondrians - "mondrians": corrugated_mondrians(**p, **p_mondrians), + "mondrians": mondrians.corrugated_mondrians(**p, **p_mondrians), # Mueller-Lyer "mueller-lyer": mueller_lyers.mueller_lyer(**p, outer_lines_length=1.5, outer_lines_angle=45, target_length=6, line_width=0.1), "2sided_mueller-lyer": mueller_lyers.two_sided(**p, outer_lines_length=1.5, outer_lines_angle=45, target_length=2.5, line_width=0.1), # Ponzo - "ponzo": ponzo(**p, outer_lines_length=8, outer_lines_width=0.1, target_lines_length=3, target_lines_width=0.1, target_distance=3), + "ponzo": ponzos.ponzo(**p, outer_lines_length=8, outer_lines_width=0.1, target_lines_length=3, target_lines_width=0.1, target_distance=3), # SBC "sbc_generalized": sbcs.generalized(**p, target_size=3, target_position=(0, 2)), "sbc_basic": sbcs.basic(**p, target_size=3), @@ -138,7 +121,7 @@ def create_overview(): "2sided_todorovic_cross": todorovics.two_sided_cross(**p, cross_size=3, cross_thickness=1.5, covers_size=1.5), "2sided_todorovic_equal": todorovics.two_sided_equal(**p, cross_size=3, cross_thickness=1.5), # Wedding cake - "wedding_cake": wedding_cake(**p, L_size=(3, 3, 1), target_height=1, target_indices1=((1, 1), (2, 1)),), + "wedding_cake": wedding_cakes.wedding_cake(**p, L_size=(3, 3, 1), target_height=1, target_indices1=((1, 1), (2, 1)),), # White "white_general": whites.generalized(**p, frequency=0.5, target_indices=(1, 3, 5), target_center_offsets=(-1, -3, -1), target_heights=(2, 3, 2)), "white_basic": whites.white(**p, frequency=0.5, target_indices=(2, -3), target_height=2), @@ -153,7 +136,7 @@ def create_overview(): def overview(mask=False, save=None): - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = create_overview() diff --git a/stimuli/illusions/angulars.py b/stimupy/illusions/angulars.py similarity index 95% rename from stimuli/illusions/angulars.py rename to stimupy/illusions/angulars.py index 5753ff0a..527b2af5 100644 --- a/stimuli/illusions/angulars.py +++ b/stimupy/illusions/angulars.py @@ -1,8 +1,9 @@ import itertools + import numpy as np -from stimuli.components.angulars import pinwheel as pinwheel_shape -from stimuli.components.shapes import ring as ring_shape +from stimupy.components.angulars import pinwheel as pinwheel_shape +from stimupy.components.shapes import ring as ring_shape __all__ = [ "pinwheel", @@ -135,8 +136,8 @@ def pinwheel( ppd=stim["ppd"], shape=stim["shape"], ) - condition1 = (stim["wedge_mask"] == segment_idx) - condition2 = (ring_stim["ring_mask"] == 2) + condition1 = stim["wedge_mask"] == segment_idx + condition2 = ring_stim["ring_mask"] == 2 target_mask = np.where(condition1 & condition2, target_idx + 1, target_mask) stim["img"] = np.where(target_mask == (target_idx + 1), intensity, stim["img"]) stim["target_mask"] = target_mask @@ -145,7 +146,7 @@ def pinwheel( if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = { "pinwheel": pinwheel(visual_size=(8, 8), ppd=32, n_segments=8), diff --git a/stimuli/illusions/benarys.py b/stimupy/illusions/benarys.py similarity index 97% rename from stimuli/illusions/benarys.py rename to stimupy/illusions/benarys.py index 1546a984..9110aaa2 100644 --- a/stimuli/illusions/benarys.py +++ b/stimupy/illusions/benarys.py @@ -1,9 +1,10 @@ import warnings + import numpy as np from scipy.ndimage import rotate -from stimuli.components.shapes import cross, triangle -from stimuli.utils import degrees_to_pixels, resolution +from stimupy.components.shapes import cross, triangle +from stimupy.utils import degrees_to_pixels, resolution __all__ = [ "cross_generalized", @@ -72,7 +73,9 @@ def cross_generalized( Psychologische Forschung, 5, 131–142. https://doi.org/10.1007/BF00402398 """ if cross_thickness is None: - raise ValueError("cross_generalized() missing argument 'cross_thickness' which is not 'None'") + raise ValueError( + "cross_generalized() missing argument 'cross_thickness' which is not 'None'" + ) # Create cross cross_stim = cross( @@ -147,7 +150,9 @@ def cross_rectangles( Psychologische Forschung, 5, 131–142. https://doi.org/10.1007/BF00402398 """ if cross_thickness is None: - raise ValueError("cross_rectangles() missing argument 'cross_thickness' which is not 'None'") + raise ValueError( + "cross_rectangles() missing argument 'cross_thickness' which is not 'None'" + ) # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) @@ -164,7 +169,7 @@ def cross_rectangles( # Calculate target placement for classical Benarys cross target_x = ( (visual_size[1] - cross_thickness) / 2.0 - target_size[1], - shape[1]/ppd[1] - np.round(target_size[1]*ppd[1])/ppd[1], + shape[1] / ppd[1] - np.round(target_size[1] * ppd[1]) / ppd[1], ) target_y = ( @@ -234,7 +239,9 @@ def cross_triangles( Psychologische Forschung, 5, 131–142. https://doi.org/10.1007/BF00402398 """ if cross_thickness is None: - raise ValueError("cross_triangles() missing argument 'cross_thickness' which is not 'None'") + raise ValueError( + "cross_triangles() missing argument 'cross_thickness' which is not 'None'" + ) # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) @@ -685,7 +692,7 @@ def add_targets( if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli params_benary = { "visual_size": 10, diff --git a/stimuli/illusions/checkerboards.py b/stimupy/illusions/checkerboards.py similarity index 96% rename from stimuli/illusions/checkerboards.py rename to stimupy/illusions/checkerboards.py index 4673922d..73dfffe1 100644 --- a/stimuli/illusions/checkerboards.py +++ b/stimupy/illusions/checkerboards.py @@ -1,8 +1,8 @@ import numpy as np -from stimuli.components.checkerboards import checkerboard as board -from stimuli.utils.contrast_conversions import transparency -from stimuli.utils import resolution +from stimupy.components.checkerboards import checkerboard as board +from stimupy.utils import resolution +from stimupy.utils.contrast_conversions import transparency __all__ = [ "checkerboard", @@ -33,7 +33,7 @@ def mask_from_idx(checkerboard_stim, check_idc): See also -------- - stimuli.components.checkerboard + stimupy.components.checkerboard """ board_shape = checkerboard_stim["board_shape"] @@ -103,7 +103,7 @@ def add_targets(checkerboard_stim, target_indices, extend_targets=False, intensi See also -------- - stimuli.components.checkerboard + stimupy.components.checkerboard """ mask = np.zeros(checkerboard_stim["shape"]) for i, target in enumerate(target_indices): @@ -178,7 +178,7 @@ def checkerboard( See also -------- - stimuli.components.checkerboard + stimupy.components.checkerboard References ---------- @@ -314,17 +314,19 @@ def contrast_contrast( if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + p = { "ppd": 32, "board_shape": (8, 8), "check_visual_size": (2, 2), - } + } stims = { - "checkerboard": checkerboard(**p, target_indices=[(3, 2), (5, 5)],), + "checkerboard": checkerboard( + **p, + target_indices=[(3, 2), (5, 5)], + ), "contrast_contrast": contrast_contrast(**p, target_shape=(4, 4)), - } + } plot_stimuli(stims, mask=True, save=None) - diff --git a/stimuli/illusions/circulars.py b/stimupy/illusions/circulars.py similarity index 96% rename from stimuli/illusions/circulars.py rename to stimupy/illusions/circulars.py index 5122bc98..b17f2717 100644 --- a/stimuli/illusions/circulars.py +++ b/stimupy/illusions/circulars.py @@ -1,17 +1,12 @@ import itertools from copy import deepcopy -import numpy as np -from stimuli.components.circulars import grating -from stimuli.utils import resolution, stack_dicts +import numpy as np +from stimupy.components.circulars import grating +from stimupy.utils import resolution, stack_dicts -__all__ = [ - "rings", - "two_sided_rings", - "bullseye", - "two_sided_bullseye" -] +__all__ = ["rings", "two_sided_rings", "bullseye", "two_sided_bullseye"] def rings( @@ -126,7 +121,7 @@ def rings( stim["rings"] = deepcopy(stim["ring_mask"]) mask = np.zeros(stim["shape"]) for i, ring_idx in enumerate(target_indices): - mask = np.where(stim["ring_mask"] == ring_idx+1, i+1, 0) + mask = np.where(stim["ring_mask"] == ring_idx + 1, i + 1, 0) stim["target_mask"] = mask.astype(int) stim["target_indices"] = target_indices stim["intensity_target"] = intensity_target @@ -209,7 +204,7 @@ def two_sided_rings( shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = rings( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_rings=n_rings, @@ -219,10 +214,10 @@ def two_sided_rings( intensity_rings=intensity_rings, intensity_background=intensity_background, origin=origin, - ) - + ) + stim2 = rings( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_rings=n_rings, @@ -232,8 +227,8 @@ def two_sided_rings( intensity_rings=intensity_rings[::-1], intensity_background=intensity_background, origin=origin, - ) - + ) + stim = stack_dicts(stim1, stim2) stim["shape"] = shape stim["visual_size"] = visual_size @@ -405,7 +400,7 @@ def two_sided_bullseye( shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = bullseye( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_rings=n_rings, @@ -414,10 +409,10 @@ def two_sided_bullseye( intensity_rings=intensity_rings, intensity_background=intensity_background, origin=origin, - ) - + ) + stim2 = bullseye( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_rings=n_rings, @@ -426,8 +421,8 @@ def two_sided_bullseye( intensity_rings=intensity_rings[::-1], intensity_background=intensity_background, origin=origin, - ) - + ) + stim = stack_dicts(stim1, stim2) stim["shape"] = shape stim["visual_size"] = visual_size @@ -435,7 +430,7 @@ def two_sided_bullseye( if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = { "rings": rings(visual_size=(8, 8), ppd=32, frequency=1.0), diff --git a/stimuli/illusions/cornsweets.py b/stimupy/illusions/cornsweets.py similarity index 96% rename from stimuli/illusions/cornsweets.py rename to stimupy/illusions/cornsweets.py index 988c7eaa..d7433f2c 100644 --- a/stimuli/illusions/cornsweets.py +++ b/stimupy/illusions/cornsweets.py @@ -1,11 +1,12 @@ import numpy as np -from stimuli.components.edges import cornsweet_edge +from stimupy.components.edges import cornsweet_edge __all__ = [ "cornsweet", ] + def cornsweet( visual_size=None, ppd=None, @@ -67,7 +68,7 @@ def cornsweet( intensity_edges=intensity_edges, intensity_plateau=intensity_plateau, exponent=exponent, - ) + ) shape = stim["shape"] ramp_width_px = stim["ramp_width"] * stim["ppd"][0] @@ -80,7 +81,7 @@ def cornsweet( if __name__ == "__main__": - from stimuli.utils import plot_stim + from stimupy.utils import plot_stim stim = cornsweet(visual_size=10, ppd=10, ramp_width=3) plot_stim(stim, stim_name="cornsweet", mask=True, save=None) diff --git a/stimuli/illusions/cubes.py b/stimupy/illusions/cubes.py similarity index 83% rename from stimuli/illusions/cubes.py rename to stimupy/illusions/cubes.py index 5799edd8..f6d3b7f8 100644 --- a/stimuli/illusions/cubes.py +++ b/stimupy/illusions/cubes.py @@ -1,6 +1,6 @@ import numpy as np -from stimuli.utils import degrees_to_pixels, resolution +from stimupy.utils import degrees_to_pixels, resolution __all__ = [ "varying_cells", @@ -67,10 +67,10 @@ def varying_cells( targets = () if isinstance(targets, (float, int)): targets = (targets,) - + n_cells = np.maximum(len(cell_heights), len(cell_widths)) - n_cells = np.maximum(n_cells, len(cell_spacing)+1) - + n_cells = np.maximum(n_cells, len(cell_spacing) + 1) + if len(cell_heights) == 1: cell_heights = cell_heights * n_cells if len(cell_widths) == 1: @@ -78,7 +78,7 @@ def varying_cells( if len(cell_spacing) == 1: cell_spacing = cell_spacing * n_cells cell_spacing = list(cell_spacing) - cell_spacing[n_cells-1] = 0 + cell_spacing[n_cells - 1] = 0 cheights = degrees_to_pixels(cell_heights, ppd) cwidths = degrees_to_pixels(cell_widths, ppd) @@ -89,7 +89,7 @@ def varying_cells( # Initiate image img = np.ones([height, width]) * intensity_background mask = np.zeros([height, width]) - + # Add cells: top and bottom xs = 0 for i in range(n_cells): @@ -99,10 +99,10 @@ def varying_cells( else: fill_img = intensity_grid fill_mask = 0 - img[0:cheights[i], xs:xs+cwidths[i]] = fill_img - img[height-cheights[i]::, xs:xs+cwidths[i]] = fill_img - mask[0:cheights[i], xs:xs+cwidths[i]] = fill_mask - mask[height-cheights[i]::, xs:xs+cwidths[i]] = fill_mask + img[0 : cheights[i], xs : xs + cwidths[i]] = fill_img + img[height - cheights[i] : :, xs : xs + cwidths[i]] = fill_img + mask[0 : cheights[i], xs : xs + cwidths[i]] = fill_mask + mask[height - cheights[i] : :, xs : xs + cwidths[i]] = fill_mask xs += cwidths[i] + cspaces[i] # Add cells: left and right @@ -114,10 +114,10 @@ def varying_cells( else: fill_img = intensity_grid fill_mask = 0 - img[xs:xs+cwidths[i], 0:cheights[i]] = fill_img - img[xs:xs+cwidths[i], width-cheights[i]::] = fill_img - mask[xs:xs+cwidths[i], 0:cheights[i]] = fill_mask - mask[xs:xs+cwidths[i], width-cheights[i]::] = fill_mask + img[xs : xs + cwidths[i], 0 : cheights[i]] = fill_img + img[xs : xs + cwidths[i], width - cheights[i] : :] = fill_img + mask[xs : xs + cwidths[i], 0 : cheights[i]] = fill_mask + mask[xs : xs + cwidths[i], width - cheights[i] : :] = fill_mask xs += cwidths[i] + cspaces[i] stim = { @@ -133,7 +133,7 @@ def varying_cells( "intensity_background": intensity_background, "intensity_grid": intensity_grid, "intensity_target": intensity_target, - } + } return stim @@ -192,7 +192,7 @@ def cube( facilitation in brightness perception. Frontiers in Human Neuroscience, 9, 93. https://doi.org/10.3389/fnhum.2015.00093 """ - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: @@ -220,18 +220,18 @@ def cube( mask = np.zeros([height, width]) # Calculate cell widths and heights - cell_height = int((height - cell_space[0]*(n_cells[0]-1)) / n_cells[0]) - cell_width = int((width - cell_space[1]*(n_cells[1]-1)) / n_cells[1]) - + cell_height = int((height - cell_space[0] * (n_cells[0] - 1)) / n_cells[0]) + cell_width = int((width - cell_space[1] * (n_cells[1] - 1)) / n_cells[1]) + if (cell_thick > cell_height) or (cell_thick > cell_width): raise ValueError("cell_thickness is too large") # Calculate cell placements: - xs = np.arange(0, width-1, cell_width+cell_space[1]) + xs = np.arange(0, width - 1, cell_width + cell_space[1]) rxs = xs[::-1] - ys = np.arange(0, height-1, cell_height+cell_space[0]) + ys = np.arange(0, height - 1, cell_height + cell_space[0]) rys = ys[::-1] - + # Add cells: top and bottom for i in range(n_cells[1]): if i in targets: @@ -240,10 +240,10 @@ def cube( else: fill_img = intensity_grid fill_mask = 0 - img[0:cell_thick, xs[i]:xs[i]+cell_width] = fill_img - img[height-cell_thick::, rxs[i]:rxs[i]+cell_width] = fill_img - mask[0:cell_thick, xs[i]:xs[i]+cell_width] = fill_mask - mask[height-cell_thick::, rxs[i]:rxs[i]+cell_width] = fill_mask + img[0:cell_thick, xs[i] : xs[i] + cell_width] = fill_img + img[height - cell_thick : :, rxs[i] : rxs[i] + cell_width] = fill_img + mask[0:cell_thick, xs[i] : xs[i] + cell_width] = fill_mask + mask[height - cell_thick : :, rxs[i] : rxs[i] + cell_width] = fill_mask # Add cells: left and right for i in range(n_cells[0]): @@ -253,10 +253,10 @@ def cube( else: fill_img = intensity_grid fill_mask = 0 - img[rys[i]:rys[i]+cell_height, 0:cell_thick] = fill_img - img[ys[i]:ys[i]+cell_height, height-cell_thick::] = fill_img - mask[rys[i]:rys[i]+cell_height, 0:cell_thick] = fill_mask - mask[ys[i]:ys[i]+cell_height, height-cell_thick::] = fill_mask + img[rys[i] : rys[i] + cell_height, 0:cell_thick] = fill_img + img[ys[i] : ys[i] + cell_height, height - cell_thick : :] = fill_img + mask[rys[i] : rys[i] + cell_height, 0:cell_thick] = fill_mask + mask[ys[i] : ys[i] + cell_height, height - cell_thick : :] = fill_mask stim = { "img": img, @@ -271,22 +271,21 @@ def cube( "intensity_background": intensity_background, "intensity_grid": intensity_grid, "intensity_target": intensity_target, - } + } return stim - if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + p1 = { "ppd": 10, "cell_heights": (1, 2, 1), "cell_widths": (1.5, 2, 1.5), "cell_spacing": 0.5, "targets": 1, - } - + } + p2 = { "visual_size": 10, "ppd": 10, @@ -294,10 +293,10 @@ def cube( "targets": (1, 2), "cell_thickness": 1, "cell_spacing": 0.5, - } + } stims = { "varying cells": varying_cells(**p1), "cube": cube(**p2), - } + } plot_stimuli(stims, mask=True, save=None) diff --git a/stimuli/illusions/delboeufs.py b/stimupy/illusions/delboeufs.py similarity index 91% rename from stimuli/illusions/delboeufs.py rename to stimupy/illusions/delboeufs.py index 56dd9913..5fb22ed4 100644 --- a/stimuli/illusions/delboeufs.py +++ b/stimupy/illusions/delboeufs.py @@ -1,12 +1,9 @@ -from stimuli.components import lines -from stimuli.components.shapes import disc -from stimuli.utils import resolution, stack_dicts +from stimupy.components import lines +from stimupy.components.shapes import disc +from stimupy.utils import resolution, stack_dicts +__all__ = ["delboeuf", "two_sided"] -__all__ = [ - "delboeuf", - "two_sided" -] def delboeuf( visual_size=None, @@ -50,7 +47,7 @@ def delboeuf( dict with the stimulus (key: "img"), mask with integer index for the target (key: "target_mask"), and additional keys containing stimulus parameters - + References ---------- Delboeuf, F. J. (1865). Note sur certaines illusions d’optique: Essai d'une @@ -69,25 +66,25 @@ def delboeuf( shape=shape, radius=outer_radius, line_width=outer_line_width, - intensity_line=intensity_outer_line-intensity_background, + intensity_line=intensity_outer_line - intensity_background, intensity_background=0, - ) - + ) + inner = disc( radius=target_radius, - intensity_discs=intensity_target-intensity_background, + intensity_discs=intensity_target - intensity_background, visual_size=visual_size, ppd=ppd, shape=shape, intensity_background=0, - ) + ) inner["img"] = outer["img"] + inner["img"] + intensity_background inner["line_mask"] = outer["line_mask"] inner["outer_radius"] = outer_radius inner["target_radius"] = target_radius return inner - + def two_sided( visual_size=None, @@ -131,7 +128,7 @@ def two_sided( dict with the stimulus (key: "img"), mask with integer index for the target (key: "target_mask"), and additional keys containing stimulus parameters - + References ---------- Delboeuf, F. J. (1865). Note sur certaines illusions d’optique: Essai d'une @@ -148,7 +145,7 @@ def two_sided( shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = delboeuf( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, outer_radius=outer_radii[0], outer_line_width=outer_line_width, @@ -156,10 +153,10 @@ def two_sided( intensity_outer_line=intensity_outer_line, intensity_target=intensity_target, intensity_background=intensity_background, - ) - + ) + stim2 = delboeuf( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, outer_radius=outer_radii[1], outer_line_width=outer_line_width, @@ -167,8 +164,8 @@ def two_sided( intensity_outer_line=intensity_outer_line, intensity_target=intensity_target, intensity_background=intensity_background, - ) - + ) + stim = stack_dicts(stim1, stim2) stim["shape"] = shape stim["visual_size"] = visual_size @@ -178,15 +175,16 @@ def two_sided( if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli + p1 = { "visual_size": (10, 20), "ppd": 30, "target_radius": 2, - } + } stims = { "single": delboeuf(**p1, outer_radius=4), "two_sided": two_sided(**p1, outer_radii=(4, 2.5)), - } + } plot_stimuli(stims, mask=False, save=None) diff --git a/stimuli/illusions/dungeons.py b/stimupy/illusions/dungeons.py similarity index 87% rename from stimuli/illusions/dungeons.py rename to stimupy/illusions/dungeons.py index 94afec6d..e653345b 100644 --- a/stimuli/illusions/dungeons.py +++ b/stimupy/illusions/dungeons.py @@ -1,6 +1,6 @@ import numpy as np -from stimuli.utils import resolution, pad_to_shape +from stimupy.utils import pad_to_shape, resolution __all__ = [ "dungeon", @@ -66,7 +66,7 @@ def dungeon( n_cells = (n_cells, n_cells) params = resolve_dungeon_params(None, visual_size, ppd, n_cells, cell_size) - n_cells = (int(params["n_cells"][0]), int(params["n_cells"][1])) + n_cells = (int(params["n_cells"][0]), int(params["n_cells"][1])) height, width = params["shape"].height, params["shape"].width cheight = height / (n_cells[0] * 2 - 1) @@ -97,12 +97,12 @@ def dungeon( img = arr.repeat(cheight, axis=0).repeat(cwidth, axis=1) mask = mask_arr.repeat(cheight, axis=0).repeat(cwidth, axis=1) - + # Make sure that stimulus size is as requested if (img.shape[0] != height) or (img.shape[1] != width): img = pad_to_shape(img, (height, width), intensity_background) mask = pad_to_shape(mask, (height, width), 0) - + stim = { "img": img, "target_mask": mask.astype(int), @@ -111,7 +111,7 @@ def dungeon( "intensity_grid": intensity_grid, "intensity_target": intensity_target, **params, - } + } return stim @@ -131,25 +131,24 @@ def resolve_dungeon_params( shape = resolution.validate_shape(shape) visual_size = resolution.validate_visual_size(visual_size) - n_cells1, visual_angle1, cell_size1 = resolve_cells_1d(visual_size[0], - n_cells[0]-0.5, - cell_size[0]) - n_cells2, visual_angle2, cell_size2 = resolve_cells_1d(visual_size[1], - n_cells[1]-0.5, - cell_size[1]) + n_cells1, visual_angle1, cell_size1 = resolve_cells_1d( + visual_size[0], n_cells[0] - 0.5, cell_size[0] + ) + n_cells2, visual_angle2, cell_size2 = resolve_cells_1d( + visual_size[1], n_cells[1] - 0.5, cell_size[1] + ) - # Now resolve resolution shape, visual_size, ppd = resolution.resolve( - shape=shape, visual_size=(visual_angle1/2, visual_angle2/2), ppd=ppd + shape=shape, visual_size=(visual_angle1 / 2, visual_angle2 / 2), ppd=ppd ) return { "visual_size": visual_size, "ppd": ppd, "shape": shape, - "n_cells": (n_cells1+1, n_cells2+1), - "cell_size": (cell_size1/2, cell_size2/2), + "n_cells": (n_cells1 + 1, n_cells2 + 1), + "cell_size": (cell_size1 / 2, cell_size2 / 2), } @@ -177,9 +176,8 @@ def resolve_cells_1d( return n_cells, visual_angle, cell_size - if __name__ == "__main__": - from stimuli.utils import plot_stim + from stimupy.utils import plot_stim stim = dungeon(visual_size=10, ppd=10, n_cells=5) plot_stim(stim, stim_name="dungeon", mask=True, save=None) diff --git a/stimuli/illusions/frames.py b/stimupy/illusions/frames.py similarity index 96% rename from stimuli/illusions/frames.py rename to stimupy/illusions/frames.py index 15fc270f..c1667527 100644 --- a/stimuli/illusions/frames.py +++ b/stimupy/illusions/frames.py @@ -1,8 +1,9 @@ import itertools + import numpy as np -from stimuli.components import frames as frames_component -from stimuli.utils import resolution, stack_dicts +from stimupy.components import frames as frames_component +from stimupy.utils import resolution, stack_dicts __all__ = [ "rings", @@ -164,7 +165,7 @@ def two_sided_rings( shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = rings( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_frames=n_frames, @@ -172,10 +173,10 @@ def two_sided_rings( intensity_target=intensity_target, intensity_frames=intensity_frames, target_indices=target_indices, - ) - + ) + stim2 = rings( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_frames=n_frames, @@ -183,8 +184,8 @@ def two_sided_rings( intensity_target=intensity_target, intensity_frames=intensity_frames[::-1], target_indices=target_indices, - ) - + ) + stim = stack_dicts(stim1, stim2) stim["shape"] = shape stim["visual_size"] = visual_size @@ -396,7 +397,7 @@ def bullseye_generalized( target_indices=1, intensity_target=intensity_target, origin=origin, - ) + ) return stim @@ -459,25 +460,25 @@ def two_sided_bullseye( shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = bullseye( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_frames=n_frames, frame_width=frame_width, intensity_target=intensity_target, intensity_frames=intensity_frames, - ) - + ) + stim2 = bullseye( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, frequency=frequency, n_frames=n_frames, frame_width=frame_width, intensity_target=intensity_target, intensity_frames=intensity_frames[::-1], - ) - + ) + stim = stack_dicts(stim1, stim2) stim["shape"] = shape stim["visual_size"] = visual_size @@ -485,20 +486,22 @@ def two_sided_bullseye( if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + p1 = { "frame_radii": (1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5), "visual_size": 10, "ppd": 10, - } + } stims = { "rings": rings(visual_size=10, ppd=10, frequency=0.5, target_indices=(1, 3)), "rings_generalized": rings_generalized(**p1, target_indices=(1, 3)), - "two_sided_rings": two_sided_rings(visual_size=10, ppd=10, frequency=1, target_indices=(1, 3)), + "two_sided_rings": two_sided_rings( + visual_size=10, ppd=10, frequency=1, target_indices=(1, 3) + ), "bullseye": bullseye(visual_size=10, ppd=10, frequency=0.5), "bullseye_generalized": bullseye_generalized(**p1), - "two_sided_bullseye": two_sided_bullseye(visual_size=10, ppd=10, frequency=1) + "two_sided_bullseye": two_sided_bullseye(visual_size=10, ppd=10, frequency=1), } plot_stimuli(stims, mask=True, save=None) diff --git a/stimuli/illusions/gratings.py b/stimupy/illusions/gratings.py similarity index 92% rename from stimuli/illusions/gratings.py rename to stimupy/illusions/gratings.py index f451b58d..6b4d9917 100644 --- a/stimuli/illusions/gratings.py +++ b/stimupy/illusions/gratings.py @@ -1,12 +1,13 @@ import itertools +import warnings + import numpy as np from scipy.ndimage import gaussian_filter -import warnings -from stimuli.components.gratings import square_wave as square_wave_component -from stimuli.components.gratings import sine_wave -from stimuli.components.shapes import parallelogram, rectangle -from stimuli.utils import pad_dict_to_visual_size, pad_dict_to_shape, resolution +from stimupy.components.gratings import sine_wave +from stimupy.components.gratings import square_wave as square_wave_component +from stimupy.components.shapes import parallelogram, rectangle +from stimupy.utils import pad_dict_to_shape, pad_dict_to_visual_size, resolution __all__ = [ "square_wave", @@ -213,7 +214,9 @@ def uniform( ) # Padding - stim = pad_dict_to_visual_size(stim, visual_size=visual_size, ppd=ppd, pad_value=intensity_background) + stim = pad_dict_to_visual_size( + stim, visual_size=visual_size, ppd=ppd, pad_value=intensity_background + ) # Repack stim.update( @@ -259,14 +262,14 @@ def grating_masked( lightness of grey bars within square-wave test gratings. Perception, 10(2), 215–230. https://doi.org/10.1068/p100215 """ - + # Create gratings small_grating = square_wave(**small_grating_params) large_grating = square_wave(**large_grating_params) - + if small_grating["ppd"] != large_grating["ppd"]: raise ValueError("Gratings must have same ppd") - + if mask_size is None: mask_size = small_grating["visual_size"] if mask_rotation is None: @@ -277,8 +280,8 @@ def grating_masked( shape=large_grating["shape"], parallelogram_size=mask_size, rotation=mask_rotation, - )["img"] - + )["img"] + small_grating = pad_dict_to_shape(small_grating, large_grating["shape"]) img = np.where(window, small_grating["img"], large_grating["img"]) mask = np.where(window, small_grating["target_mask"], 0) @@ -322,7 +325,7 @@ def grating( stim = grating_masked( small_grating_params, large_grating_params, - ) + ) return stim @@ -343,14 +346,16 @@ def counterphase_induction( origin="corner", ): if target_size is None: - raise ValueError("counterphase_induction() missing argument 'target_size' which is not 'None'") + raise ValueError( + "counterphase_induction() missing argument 'target_size' which is not 'None'" + ) if orientation == "horizontal": rotation = 0 elif orientation == "vertical": rotation = 90 else: raise ValueError("orientation must be horizontal or vertical") - + # Spatial square-wave grating stim = square_wave_component( visual_size=visual_size, @@ -366,7 +371,7 @@ def counterphase_induction( origin=origin, round_phase_width=True, ) - + stim_target = square_wave_component( visual_size=target_size, ppd=stim["ppd"], @@ -384,11 +389,11 @@ def counterphase_induction( # Translate phase information into pixels target_phasea = np.abs(target_phase_shift) target_phasea = target_phasea % 360 - target_amount = target_phasea / 360. + target_amount = target_phasea / 360.0 target_shift = target_amount * cycle_px target_shifti = int(np.round(target_shift)) target_phasei = target_shifti / cycle_px * 360 - + if target_shift != int(target_shift): s = np.sign(target_phase_shift) warnings.warn(f"Rounding phase; {target_phase_shift} -> {s*target_phasei}") @@ -397,24 +402,32 @@ def counterphase_induction( cy, cx = stim["shape"] if target_phase_shift < 0: if orientation == "horizontal": - stim_target["img"][:, 0:cx-target_shifti] = stim_target["img"][:, target_shifti::] - stim_target["grating_mask"][:, 0:cx-target_shifti] = stim_target["grating_mask"][:, target_shifti::] + stim_target["img"][:, 0 : cx - target_shifti] = stim_target["img"][:, target_shifti::] + stim_target["grating_mask"][:, 0 : cx - target_shifti] = stim_target["grating_mask"][ + :, target_shifti:: + ] elif orientation == "vertical": - stim_target["img"][0:cx-target_shifti, :] = stim_target["img"][target_shifti::, :] - stim_target["grating_mask"][0:cx-target_shifti, :] = stim_target["grating_mask"][target_shifti::, :] + stim_target["img"][0 : cx - target_shifti, :] = stim_target["img"][target_shifti::, :] + stim_target["grating_mask"][0 : cx - target_shifti, :] = stim_target["grating_mask"][ + target_shifti::, : + ] else: if orientation == "horizontal": - stim_target["img"][:, target_shifti::] = stim_target["img"][:, 0:cx-target_shifti] - stim_target["grating_mask"][:, target_shifti::] = stim_target["grating_mask"][:, 0:cx-target_shifti] + stim_target["img"][:, target_shifti::] = stim_target["img"][:, 0 : cx - target_shifti] + stim_target["grating_mask"][:, target_shifti::] = stim_target["grating_mask"][ + :, 0 : cx - target_shifti + ] elif orientation == "vertical": - stim_target["img"][target_shifti::, :] = stim_target["img"][0:cx-target_shifti, :] - stim_target["grating_mask"][target_shifti::, :] = stim_target["grating_mask"][0:cx-target_shifti, :] + stim_target["img"][target_shifti::, :] = stim_target["img"][0 : cx - target_shifti, :] + stim_target["grating_mask"][target_shifti::, :] = stim_target["grating_mask"][ + 0 : cx - target_shifti, : + ] # Add targets on grating mask_temp = np.ones(stim["shape"]) mask_temp[stim_target["img"] == intensity_target] = 0 img = stim["img"] * mask_temp + stim_target["img"] - + # Create target mask mask = np.where(stim_target["img"] == intensity_target, stim_target["grating_mask"], 0) unique_vals = np.unique(mask) @@ -638,36 +651,40 @@ def induction_blur( if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + params = { "ppd": 40, "n_bars": 8, "bar_width": 1.0, - } - + } + small_grating = { "ppd": 40, "bar_width": 1.0, "n_bars": 7, "intensity_bars": (0.2, 0.8), "target_indices": (0, 1, 3, 5, 7), - } - + } + large_grating = { "ppd": 40, "bar_width": 1.0, "n_bars": 21, - } + } stims = { "square_wave": square_wave(**params, target_indices=(4, 6)), "uniform": uniform(**params, visual_size=20, grating_size=5, target_indices=3), "grating": grating(large_grating_params=large_grating, small_grating_params=small_grating), - "grating_masked": grating_masked(large_grating_params=large_grating, - small_grating_params={**small_grating, "rotation": 90}, - mask_size=(5, 5, 2)), - "counterphase_induction": counterphase_induction(**params, target_size=4, target_phase_shift=90), + "grating_masked": grating_masked( + large_grating_params=large_grating, + small_grating_params={**small_grating, "rotation": 90}, + mask_size=(5, 5, 2), + ), + "counterphase_induction": counterphase_induction( + **params, target_size=4, target_phase_shift=90 + ), "induction": induction(**params, target_width=0.5), "induction_blur": induction_blur(**params, target_width=0.5, target_blur=5), } diff --git a/stimuli/illusions/hermanns.py b/stimupy/illusions/hermanns.py similarity index 95% rename from stimuli/illusions/hermanns.py rename to stimupy/illusions/hermanns.py index 7ba014a3..cf8b666a 100644 --- a/stimuli/illusions/hermanns.py +++ b/stimupy/illusions/hermanns.py @@ -1,11 +1,12 @@ import numpy as np -from stimuli.utils import degrees_to_pixels, resolution +from stimupy.utils import degrees_to_pixels, resolution __all__ = [ "grid", ] + def grid( visual_size=None, ppd=None, @@ -38,7 +39,7 @@ def grid( dict with the stimulus (key: "img"), empty mask (key: "target_mask"), and additional keys containing stimulus parameters - + References ---------- Hermann L (1870). Eine Erscheinung simultanen Contrastes". Pflügers Archiv @@ -48,7 +49,7 @@ def grid( raise ValueError("grid() missing argument 'element_size' which is not 'None'") # Resolve resolution - shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) + shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") @@ -80,7 +81,7 @@ def grid( if __name__ == "__main__": - from stimuli.utils import plot_stim + from stimupy.utils import plot_stim stim = grid(visual_size=10, ppd=10, element_size=(1.5, 1.5, 0.2)) plot_stim(stim, stim_name="hermann_grid", mask=True, save=None) diff --git a/stimuli/illusions/mondrians.py b/stimupy/illusions/mondrians.py similarity index 83% rename from stimuli/illusions/mondrians.py rename to stimupy/illusions/mondrians.py index 6c123fee..36d9e748 100644 --- a/stimuli/illusions/mondrians.py +++ b/stimupy/illusions/mondrians.py @@ -1,7 +1,7 @@ import numpy as np -from stimuli.components.mondrians import mondrians -from stimuli.utils import resolution, degrees_to_pixels +from stimupy.components.mondrians import mondrians +from stimupy.utils import degrees_to_pixels, resolution __all__ = [ "corrugated_mondrians", @@ -51,9 +51,13 @@ def corrugated_mondrians( Science, 262(5142), 2042–2044. https://doi.org/10.1126/science.8266102 """ if mondrian_depths is None: - raise ValueError("corrugated_mondrians() missing argument 'mondrian_depths' which is not 'None'") + raise ValueError( + "corrugated_mondrians() missing argument 'mondrian_depths' which is not 'None'" + ) if mondrian_intensities is None: - raise ValueError("corrugated_mondrians() missing argument 'mondrian_intensities' which is not 'None'") + raise ValueError( + "corrugated_mondrians() missing argument 'mondrian_intensities' which is not 'None'" + ) # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) @@ -61,21 +65,23 @@ def corrugated_mondrians( raise ValueError("ppd should be equal in x and y direction") nrows = len(mondrian_intensities) ncols = len(mondrian_intensities[0]) - + if isinstance(mondrian_depths, (float, int)): mondrian_depths = (mondrian_depths,) * nrows - + if len(mondrian_depths) != nrows: - raise ValueError("Unclear number of Mondrians in y-direction, check elements " - "in mondrian_intensities and mondrian_depths") - + raise ValueError( + "Unclear number of Mondrians in y-direction, check elements " + "in mondrian_intensities and mondrian_depths" + ) + height, width = visual_size mdepths_px = degrees_to_pixels(mondrian_depths, ppd[0]) max_depth = np.abs(np.array(mdepths_px)).max() sum_depth = np.array(mdepths_px).sum() red_depth = np.maximum(max_depth, sum_depth) - mheight_px, mwidth_px = int(shape[0] / nrows), int((shape[1]-red_depth) / ncols) - + mheight_px, mwidth_px = int(shape[0] / nrows), int((shape[1] - red_depth) / ncols) + # Initial y coordinates yst = 0 @@ -85,23 +91,23 @@ def corrugated_mondrians( temp[temp > 0] = 0.0 xstarts += temp xstarts += np.abs(xstarts.min()) - + sizes = [] poses = [] ints = [] tlist = [] target_counter = 1 - + for r in range(nrows): xst = xstarts[r] if mondrian_depths[r] < 0: - xst -= int(mondrian_depths[r]*ppd[0]) + xst -= int(mondrian_depths[r] * ppd[0]) for c in range(ncols): - msize = (mheight_px/ppd[0], mwidth_px/ppd[1], mondrian_depths[r]) - mpos = (yst/ppd[0], xst/ppd[1]) + msize = (mheight_px / ppd[0], mwidth_px / ppd[1], mondrian_depths[r]) + mpos = (yst / ppd[0], xst / ppd[1]) mint = mondrian_intensities[r][c] - + sizes.append(msize) poses.append(mpos) ints.append(mint) @@ -121,21 +127,21 @@ def corrugated_mondrians( mondrian_sizes=sizes, mondrian_intensities=ints, intensity_background=intensity_background, - ) + ) target_mask = np.zeros(shape) for t in range(len(tlist)): - target_mask[stim["mondrian_mask"] == tlist[t]] = t+1 + target_mask[stim["mondrian_mask"] == tlist[t]] = t + 1 stim["target_mask"] = target_mask.astype(int) stim["target_indices"] = target_indices - + if len(np.unique(stim["img"][target_mask != 0])) > 1: raise Exception("targets are not equiluminant.") return stim if __name__ == "__main__": - from stimuli.utils import plot_stim - + from stimupy.utils import plot_stim + p = { "visual_size": 10, "ppd": 30, @@ -147,7 +153,7 @@ def corrugated_mondrians( (0.0, 0.4, 0.0, 0.4), ), "target_indices": ((1, 1), (3, 1)), - } + } stim = corrugated_mondrians(**p) plot_stim(stim, stim_name="corrugated_mondrians", mask=True, save=None) diff --git a/stimuli/illusions/mueller_lyers.py b/stimupy/illusions/mueller_lyers.py similarity index 84% rename from stimuli/illusions/mueller_lyers.py rename to stimupy/illusions/mueller_lyers.py index 71524252..386a3b1c 100644 --- a/stimuli/illusions/mueller_lyers.py +++ b/stimupy/illusions/mueller_lyers.py @@ -1,14 +1,16 @@ -import numpy as np import copy -from stimuli.components import lines -from stimuli.utils import resolution, stack_dicts +import numpy as np + +from stimupy.components import lines +from stimupy.utils import resolution, stack_dicts __all__ = [ "mueller_lyer", "two_sided", ] + def mueller_lyer( visual_size=None, ppd=None, @@ -38,7 +40,7 @@ def mueller_lyer( angle of outer lines in degrees. Must be between -180 and 180 degrees. target_length : Number length of target line in degrees visual angle - line_width : + line_width : line width in degrees visual angle; if 0 (default), line width is 1 px intensity_outer_lines : Number intensity value of outer lines @@ -53,7 +55,7 @@ def mueller_lyer( dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters - + References ---------- Mueller-Lyer, F. (1896). Zur Lehre von den optischen Taeuschungen. Ueber @@ -61,7 +63,9 @@ def mueller_lyer( der Sinnesorgane, IX, 1-16. """ if outer_lines_length is None: - raise ValueError("mueller_lyer() missing argument 'outer_lines_length' which is not 'None'") + raise ValueError( + "mueller_lyer() missing argument 'outer_lines_length' which is not 'None'" + ) if target_length is None: raise ValueError("mueller_lyer() missing argument 'target_length' which is not 'None'") @@ -75,7 +79,7 @@ def mueller_lyer( angle2 = -angle1 - 180 angle4 = copy.deepcopy(outer_lines_angle) - 90 angle3 = -angle4 - 180 - + target_line = lines.line( visual_size=visual_size, ppd=ppd, @@ -84,76 +88,78 @@ def mueller_lyer( line_length=target_length, line_width=line_width, rotation=90, - intensity_line=intensity_target-intensity_background, + intensity_line=intensity_target - intensity_background, intensity_background=0, origin="center", - ) - + ) + oline1 = lines.line( visual_size=visual_size, ppd=ppd, shape=shape, - line_position=(0, -target_length/2), + line_position=(0, -target_length / 2), line_length=outer_lines_length, line_width=line_width, rotation=angle1, - intensity_line=intensity_outer_lines-intensity_background, + intensity_line=intensity_outer_lines - intensity_background, intensity_background=0, origin="center", - ) + ) oline2 = lines.line( visual_size=visual_size, ppd=ppd, shape=shape, - line_position=(0, -target_length/2), + line_position=(0, -target_length / 2), line_length=outer_lines_length, line_width=line_width, rotation=angle2, - intensity_line=intensity_outer_lines-intensity_background, + intensity_line=intensity_outer_lines - intensity_background, intensity_background=0, origin="center", - ) - + ) + oline3 = lines.line( visual_size=visual_size, ppd=ppd, shape=shape, - line_position=(0, target_length/2), + line_position=(0, target_length / 2), line_length=outer_lines_length, line_width=line_width, rotation=angle3, - intensity_line=intensity_outer_lines-intensity_background, + intensity_line=intensity_outer_lines - intensity_background, intensity_background=0, origin="center", - ) - + ) + oline4 = lines.line( visual_size=visual_size, ppd=ppd, shape=shape, - line_position=(0, target_length/2), + line_position=(0, target_length / 2), line_length=outer_lines_length, line_width=line_width, rotation=angle4, - intensity_line=intensity_outer_lines-intensity_background, + intensity_line=intensity_outer_lines - intensity_background, intensity_background=0, origin="center", - ) + ) # Add outer lines together olines = oline1["img"] + oline2["img"] + oline3["img"] + oline4["img"] - omasks1 = oline1["line_mask"]*2 + oline2["line_mask"]*3 - omasks2 = oline3["line_mask"]*4 + oline4["line_mask"]*5 + omasks1 = oline1["line_mask"] * 2 + oline2["line_mask"] * 3 + omasks2 = oline3["line_mask"] * 4 + oline4["line_mask"] * 5 target_line["img"] += olines - target_line["img"] = np.where(target_line["img"] > intensity_outer_lines, intensity_outer_lines, target_line["img"]) + target_line["img"] = np.where( + target_line["img"] > intensity_outer_lines, intensity_outer_lines, target_line["img"] + ) target_line["line_mask"] += omasks1 target_line["line_mask"] = np.where(target_line["line_mask"] > 3, 3, target_line["line_mask"]) target_line["line_mask"] += omasks2 target_line["line_mask"] = np.where(target_line["line_mask"] > 5, 5, target_line["line_mask"]) - + target_line["target_mask"] = np.where(target_line["line_mask"] == 1, 1, 0).astype(int) return target_line @@ -187,7 +193,7 @@ def two_sided( angle of outer lines in degrees. Must be between -180 and 180 degrees. target_length : Number length of target line in degrees visual angle - line_width : + line_width : line width in degrees visual angle; if 0 (default), line width is 1 px intensity_outer_lines : Number intensity value of outer lines @@ -202,7 +208,7 @@ def two_sided( dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters - + References ---------- Mueller-Lyer, F. (1896). Zur Lehre von den optischen Taeuschungen. Ueber @@ -213,7 +219,7 @@ def two_sided( shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = mueller_lyer( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, outer_lines_length=outer_lines_length, outer_lines_angle=outer_lines_angle, @@ -222,40 +228,40 @@ def two_sided( intensity_outer_lines=intensity_outer_lines, intensity_target=intensity_target, intensity_background=intensity_background, - ) - + ) + stim2 = mueller_lyer( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, outer_lines_length=outer_lines_length, - outer_lines_angle=outer_lines_angle+90, + outer_lines_angle=outer_lines_angle + 90, target_length=target_length, line_width=line_width, intensity_outer_lines=intensity_outer_lines, intensity_target=intensity_target, intensity_background=intensity_background, - ) - + ) + stim = stack_dicts(stim1, stim2) stim["shape"] = shape stim["visual_size"] = visual_size return stim - if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli + p1 = { "visual_size": 10, "ppd": 20, "outer_lines_length": 1, - "outer_lines_angle" : 45, + "outer_lines_angle": 45, "target_length": 3, "line_width": 0.1, - } + } stims = { "single": mueller_lyer(**p1), "two_sided": two_sided(**p1), - } + } plot_stimuli(stims, mask=True, save=None) diff --git a/stimuli/illusions/ponzos.py b/stimupy/illusions/ponzos.py similarity index 84% rename from stimuli/illusions/ponzos.py rename to stimupy/illusions/ponzos.py index 02c05d04..9d1f85bc 100644 --- a/stimuli/illusions/ponzos.py +++ b/stimupy/illusions/ponzos.py @@ -1,11 +1,11 @@ -from stimuli.utils import resolution, stack_dicts -from stimuli.components import lines - +from stimupy.components import lines +from stimupy.utils import resolution, stack_dicts __all__ = [ "ponzo", ] + def ponzo( visual_size=None, ppd=None, @@ -40,7 +40,7 @@ def ponzo( angle of outer lines in degrees. Must be between -45 and 45 degrees. target_lines_length : Number length of target lines in degrees visual angle - target_lines_width : + target_lines_width : line width of target lines in degrees visual angle if 0 (default), set line width to 1 px target_distance : Number @@ -58,7 +58,7 @@ def ponzo( dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters - + References ---------- Ponzo, M. (1910). Intorno ad alcune illusioni nel campo delle sensazioni @@ -74,40 +74,40 @@ def ponzo( # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - + if outer_lines_angle < -45 or outer_lines_angle > 45: raise ValueError("outer_lines_angle should be between -45 and 45 deg") if isinstance(intensity_outer_lines, (float, int)): intensity_outer_lines = (intensity_outer_lines, intensity_outer_lines) if isinstance(intensity_target_lines, (float, int)): intensity_target_lines = (intensity_target_lines, intensity_target_lines) - + line1 = lines.line( ppd=ppd, - shape=(shape[0], shape[1]/2), + shape=(shape[0], shape[1] / 2), line_position=None, line_length=outer_lines_length, line_width=outer_lines_width, rotation=-outer_lines_angle, - intensity_line=intensity_outer_lines[0]-intensity_background, + intensity_line=intensity_outer_lines[0] - intensity_background, intensity_background=0, origin="center", - ) + ) line2 = lines.line( ppd=ppd, - shape=(shape[0], shape[1]/2), + shape=(shape[0], shape[1] / 2), line_position=None, line_length=outer_lines_length, line_width=outer_lines_width, rotation=outer_lines_angle, - intensity_line=intensity_outer_lines[1]-intensity_background, + intensity_line=intensity_outer_lines[1] - intensity_background, intensity_background=0, origin="center", - ) - - line_position1 = (-target_distance/2, -target_lines_length/2) - line_position2 = (target_distance/2, -target_lines_length/2) + ) + + line_position1 = (-target_distance / 2, -target_lines_length / 2) + line_position2 = (target_distance / 2, -target_lines_length / 2) line3 = lines.line( ppd=ppd, @@ -116,11 +116,11 @@ def ponzo( line_length=target_lines_length, line_width=target_lines_width, rotation=90, - intensity_line=intensity_target_lines[0]-intensity_background, + intensity_line=intensity_target_lines[0] - intensity_background, intensity_background=0, origin="center", - ) - + ) + line4 = lines.line( ppd=ppd, shape=shape, @@ -128,29 +128,29 @@ def ponzo( line_length=target_lines_length, line_width=target_lines_width, rotation=90, - intensity_line=intensity_target_lines[1]-intensity_background, + intensity_line=intensity_target_lines[1] - intensity_background, intensity_background=0, origin="center", - ) - + ) + line1 = stack_dicts(line1, line2) line1["img"] += line3["img"] + line4["img"] + intensity_background - line1["line_mask"] += line3["line_mask"]*3 + line4["line_mask"]*4 - line1["target_mask"] = line3["line_mask"] + line4["line_mask"]*2 + line1["line_mask"] += line3["line_mask"] * 3 + line4["line_mask"] * 4 + line1["target_mask"] = line3["line_mask"] + line4["line_mask"] * 2 return line1 - if __name__ == "__main__": - from stimuli.utils import plot_stim + from stimupy.utils import plot_stim + p1 = { "visual_size": 10, "ppd": 20, "outer_lines_length": 8, - "outer_lines_angle" : 10, + "outer_lines_angle": 10, "target_lines_length": 3, "target_distance": 5, - } + } stim = ponzo(**p1) plot_stim(stim, stim_name="ponzo", mask=True, save=None) diff --git a/stimuli/illusions/sbcs.py b/stimupy/illusions/sbcs.py similarity index 94% rename from stimuli/illusions/sbcs.py rename to stimupy/illusions/sbcs.py index a4b37cb0..d37151b6 100644 --- a/stimuli/illusions/sbcs.py +++ b/stimupy/illusions/sbcs.py @@ -1,7 +1,7 @@ import numpy as np -from stimuli.components.shapes import disc, rectangle -from stimuli.utils import pad_by_visual_size, pad_to_shape, resolution, stack_dicts +from stimupy.components.shapes import disc, rectangle +from stimupy.utils import pad_by_visual_size, pad_to_shape, resolution, stack_dicts __all__ = [ "generalized", @@ -67,12 +67,17 @@ def generalized( intensity_background=intensity_background, intensity_rectangle=intensity_target, ) - + stim["target_mask"] = stim["shape_mask"] stim["target_size"] = stim["rectangle_size"] stim["target_position"] = stim["rectangle_position"] stim["intensity_target"] = stim["intensity_rectangle"] - del (stim["shape_mask"], stim["rectangle_size"], stim["rectangle_position"], stim["intensity_rectangle"]) + del ( + stim["shape_mask"], + stim["rectangle_size"], + stim["rectangle_position"], + stim["intensity_rectangle"], + ) return stim @@ -166,26 +171,26 @@ def two_sided( """ if target_size is None: raise ValueError("two_sided() missing argument 'target_size' which is not 'None'") - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = basic( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, target_size=target_size, intensity_background=intensity_backgrounds[0], intensity_target=intensity_target, ) - + stim2 = basic( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, target_size=target_size, intensity_background=intensity_backgrounds[1], intensity_target=intensity_target, ) - + stim = stack_dicts(stim1, stim2) del stim["intensity_background"] del stim["target_position"] @@ -257,11 +262,13 @@ def with_dots( # n_dots = number of dots vertical, horizontal, analogous to degrees n_dots = resolution.validate_visual_size(n_dots) - + if shape is None and visual_size is None: - visual_size = (n_dots[0]*dot_radius*2 + n_dots[0]*distance, - n_dots[1]*dot_radius*2 + n_dots[1]*distance) - + visual_size = ( + n_dots[0] * dot_radius * 2 + n_dots[0] * distance, + n_dots[1] * dot_radius * 2 + n_dots[1] * distance, + ) + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: @@ -310,8 +317,7 @@ def with_dots( img = pad_to_shape(img, shape, intensity_background) mask = pad_to_shape(mask, shape, 0) except Exception: - raise ValueError("visual_size or shape_argument are too small. " - "Advice: set to None") + raise ValueError("visual_size or shape_argument are too small. Advice: set to None") stim = { "img": img, @@ -392,11 +398,13 @@ def dotted( # n_dots = number of dots vertical, horizontal, analogous to degrees n_dots = resolution.validate_visual_size(n_dots) - + if shape is None and visual_size is None: - visual_size = (n_dots[0]*dot_radius*2 + n_dots[0]*distance, - n_dots[1]*dot_radius*2 + n_dots[1]*distance) - + visual_size = ( + n_dots[0] * dot_radius * 2 + n_dots[0] * distance, + n_dots[1] * dot_radius * 2 + n_dots[1] * distance, + ) + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: @@ -439,13 +447,12 @@ def dotted( img = np.where(patch, intensity_dots, intensity_background) img = np.where(patch + sbc["shape_mask"] == 2, intensity_target, img) mask = np.where(patch + sbc["shape_mask"] == 2, 1, 0) - + try: img = pad_to_shape(img, shape, intensity_background) mask = pad_to_shape(mask, shape, 0) except Exception: - raise ValueError("visual_size or shape_argument are too small. " - "Advice: set to None") + raise ValueError("visual_size or shape_argument are too small. Advice: set to None") stim = { "img": img, @@ -474,7 +481,7 @@ def two_sided_with_dots( distance=None, target_shape=None, intensity_backgrounds=(0.0, 1.0), - intensity_dots=(1.0, 0.), + intensity_dots=(1.0, 0.0), intensity_target=0.5, ): """ @@ -515,11 +522,11 @@ def two_sided_with_dots( Bressan, P., & Kramer, P. (2008). Gating of remote effects on lightness. Journal of Vision, 8(2), 16–16. https://doi.org/10.1167/8.2.16 """ - + # Resolve resolution try: shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - visual_size_ = (visual_size[0], visual_size[1]/2) + visual_size_ = (visual_size[0], visual_size[1] / 2) except: visual_size_ = None @@ -534,7 +541,7 @@ def two_sided_with_dots( intensity_dots=intensity_dots[0], intensity_target=intensity_target, ) - + stim2 = with_dots( visual_size=visual_size_, ppd=ppd, @@ -546,7 +553,7 @@ def two_sided_with_dots( intensity_dots=intensity_dots[1], intensity_target=intensity_target, ) - + stim = stack_dicts(stim1, stim2) del stim["intensity_background"] stim["intensity_backgrounds"] = intensity_backgrounds @@ -606,11 +613,11 @@ def two_sided_dotted( Bressan, P., & Kramer, P. (2008). Gating of remote effects on lightness. Journal of Vision, 8(2), 16–16. https://doi.org/10.1167/8.2.16 """ - + # Resolve resolution try: shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - visual_size_ = (visual_size[0], visual_size[1]/2) + visual_size_ = (visual_size[0], visual_size[1] / 2) except: visual_size_ = None @@ -625,7 +632,7 @@ def two_sided_dotted( intensity_dots=intensity_dots[0], intensity_target=intensity_target, ) - + stim2 = dotted( visual_size=visual_size_, ppd=ppd, @@ -637,7 +644,7 @@ def two_sided_dotted( intensity_dots=intensity_dots[1], intensity_target=intensity_target, ) - + stim = stack_dicts(stim1, stim2) del stim["intensity_background"] stim["intensity_backgrounds"] = intensity_backgrounds @@ -648,11 +655,11 @@ def two_sided_dotted( if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + p = { "ppd": 20, - } + } stims = { "generalized": generalized(**p, visual_size=10, target_size=3, target_position=(0, 2)), @@ -660,7 +667,11 @@ def two_sided_dotted( "two_sided": two_sided(**p, visual_size=10, target_size=2), "with_dots": with_dots(**p, n_dots=5, dot_radius=2, distance=0.5, target_shape=3), "dotted": dotted(**p, n_dots=5, dot_radius=2, distance=0.5, target_shape=3), - "2sided_with_dots": two_sided_with_dots(**p, n_dots=5, dot_radius=2, distance=0.5, target_shape=3), - "2sided_dotted": two_sided_dotted(**p, n_dots=5, dot_radius=2, distance=0.5, target_shape=3), + "2sided_with_dots": two_sided_with_dots( + **p, n_dots=5, dot_radius=2, distance=0.5, target_shape=3 + ), + "2sided_dotted": two_sided_dotted( + **p, n_dots=5, dot_radius=2, distance=0.5, target_shape=3 + ), } plot_stimuli(stims, mask=True, save=None) diff --git a/stimuli/illusions/todorovics.py b/stimupy/illusions/todorovics.py similarity index 95% rename from stimuli/illusions/todorovics.py rename to stimupy/illusions/todorovics.py index aa08bec4..9a5305d0 100644 --- a/stimuli/illusions/todorovics.py +++ b/stimupy/illusions/todorovics.py @@ -1,8 +1,8 @@ import numpy as np -from stimuli.components.shapes import cross as cross_shape -from stimuli.components.shapes import rectangle as rectangle_shape -from stimuli.utils import degrees_to_pixels, pad_dict_to_shape, resolution, stack_dicts +from stimupy.components.shapes import cross as cross_shape +from stimupy.components.shapes import rectangle as rectangle_shape +from stimupy.utils import degrees_to_pixels, pad_dict_to_shape, resolution, stack_dicts __all__ = [ "rectangle_generalized", @@ -76,14 +76,18 @@ def rectangle_generalized( Todorovic, D. (1997). Lightness and junctions. Perception, 26, 379–395. """ if target_size is None: - raise ValueError("rectangle_generalized() missing argument 'target_size' which is not 'None'") + raise ValueError( + "rectangle_generalized() missing argument 'target_size' which is not 'None'" + ) if covers_size is None: - raise ValueError("rectangle_generalized() missing argument 'covers_size' which is not 'None'") + raise ValueError( + "rectangle_generalized() missing argument 'covers_size' which is not 'None'" + ) if covers_x is None: raise ValueError("rectangle_generalized() missing argument 'covers_x' which is not 'None'") if covers_y is None: raise ValueError("rectangle_generalized() missing argument 'covers_y' which is not 'None'") - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: @@ -295,7 +299,9 @@ def cross_generalized( if cross_size is None: raise ValueError("cross_generalized() missing argument 'cross_size' which is not 'None'") if cross_thickness is None: - raise ValueError("cross_generalized() missing argument 'cross_thickness' which is not 'None'") + raise ValueError( + "cross_generalized() missing argument 'cross_thickness' which is not 'None'" + ) if covers_size is None: raise ValueError("cross_generalized() missing argument 'covers_size' which is not 'None'") if covers_x is None: @@ -432,8 +438,8 @@ def cross( ct = (ct + (ct % 2)) / ppd ct_half = ct / 2 - y1 = cy - ct_half - covers_size[0] + (cy*ppd%2)/ppd - x1 = cx - ct_half - covers_size[1] + (cx*ppd%2)/ppd + y1 = cy - ct_half - covers_size[0] + (cy * ppd % 2) / ppd + x1 = cx - ct_half - covers_size[1] + (cx * ppd % 2) / ppd y2 = cy + ct_half x2 = cx + ct_half @@ -580,12 +586,12 @@ def two_sided_rectangle( (Supplement), 39, S159. Todorovic, D. (1997). Lightness and junctions. Perception, 26, 379–395. """ - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = rectangle( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, target_size=target_size, covers_size=covers_size, @@ -594,9 +600,9 @@ def two_sided_rectangle( intensity_target=intensity_target, intensity_covers=intensity_covers[0], ) - + stim2 = rectangle( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, target_size=target_size, covers_size=covers_size, @@ -605,7 +611,7 @@ def two_sided_rectangle( intensity_target=intensity_target, intensity_covers=intensity_covers[1], ) - + stim = stack_dicts(stim1, stim2) del stim["intensity_background"] del stim["target_position"] @@ -670,12 +676,12 @@ def two_sided_cross( (Supplement), 39, S159. Todorovic, D. (1997). Lightness and junctions. Perception, 26, 379–395. """ - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = cross( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, cross_size=cross_size, cross_thickness=cross_thickness, @@ -684,9 +690,9 @@ def two_sided_cross( intensity_target=intensity_target, intensity_covers=intensity_covers[0], ) - + stim2 = cross( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, cross_size=cross_size, cross_thickness=cross_thickness, @@ -695,7 +701,7 @@ def two_sided_cross( intensity_target=intensity_target, intensity_covers=intensity_covers[1], ) - + stim = stack_dicts(stim1, stim2) del stim["intensity_background"] stim["intensity_backgrounds"] = intensity_backgrounds @@ -755,12 +761,12 @@ def two_sided_equal( (Supplement), 39, S159. Todorovic, D. (1997). Lightness and junctions. Perception, 26, 379–395. """ - + # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) stim1 = equal( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, cross_size=cross_size, cross_thickness=cross_thickness, @@ -768,9 +774,9 @@ def two_sided_equal( intensity_target=intensity_target, intensity_covers=intensity_covers[0], ) - + stim2 = equal( - visual_size=(visual_size[0], visual_size[1]/2), + visual_size=(visual_size[0], visual_size[1] / 2), ppd=ppd, cross_size=cross_size, cross_thickness=cross_thickness, @@ -778,7 +784,7 @@ def two_sided_equal( intensity_target=intensity_target, intensity_covers=intensity_covers[1], ) - + stim = stack_dicts(stim1, stim2) del stim["intensity_background"] stim["intensity_backgrounds"] = intensity_backgrounds @@ -788,9 +794,8 @@ def two_sided_equal( return stim - if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli p1 = { "visual_size": 10, @@ -798,7 +803,7 @@ def two_sided_equal( "target_size": 3, "covers_size": 1.5, } - + p2 = { "visual_size": 10, "ppd": 10, @@ -808,7 +813,9 @@ def two_sided_equal( stims = { "rectangle": rectangle(**p1, covers_offset=1.5), - "rectangle_general": rectangle_generalized(**p1, target_position=3.5, covers_x=(2, 6), covers_y=(2, 6)), + "rectangle_general": rectangle_generalized( + **p1, target_position=3.5, covers_x=(2, 6), covers_y=(2, 6) + ), "cross": cross(**p2, covers_size=2), "cross_general": cross_generalized(**p2, covers_size=2, covers_x=(2, 6), covers_y=(2, 6)), "equal": equal(**p2), diff --git a/stimuli/illusions/wedding_cakes.py b/stimupy/illusions/wedding_cakes.py similarity index 75% rename from stimuli/illusions/wedding_cakes.py rename to stimupy/illusions/wedding_cakes.py index 211701ae..aa606bc2 100644 --- a/stimuli/illusions/wedding_cakes.py +++ b/stimupy/illusions/wedding_cakes.py @@ -1,7 +1,7 @@ import numpy as np from scipy.signal import fftconvolve -from stimuli.utils import degrees_to_pixels, resolution +from stimupy.utils import degrees_to_pixels, resolution __all__ = [ "wedding_cake", @@ -18,7 +18,7 @@ def wedding_cake( target_height=None, target_indices1=None, target_indices2=None, - intensity_grating=(1., 0.), + intensity_grating=(1.0, 0.0), intensity_target=0.5, ): """ @@ -64,34 +64,34 @@ def wedding_cake( raise ValueError("wedding_cake() missing argument 'L_size' which is not 'None'") # Resolve resolution - shape, visual_size, ppd_ = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) + shape, visual_size, ppd_ = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") nY, nX = shape Ly, Lx, Lw = degrees_to_pixels(L_size, np.unique(ppd)) - Lyh, Lxh = int(Ly / 2)+1, int(Lx / 2)+1 - + Lyh, Lxh = int(Ly / 2) + 1, int(Lx / 2) + 1 + # Create L-shaped patch L_patch = np.zeros([Ly, Lx]) L_patch[0:Lw, 0:Lx] = 1 L_patch[0:Ly, Lx - Lw : :] = 1 - + # We initially create a larger image array to avoid boundary problems - nY, nX = nY*2, nX*2 + nY, nX = nY * 2, nX * 2 # Create grid-like array to create wedding cake pattern array1 = np.zeros([nY, nX]) - ys, xs = np.arange(0, nY, Ly-Lw), np.arange(0, nX, Lx) + ys, xs = np.arange(0, nY, Ly - Lw), np.arange(0, nX, Lx) n = np.minimum(len(ys), len(xs)) - array1[ys[0:n], xs[0:n]] = np.arange(1, n+1) + array1[ys[0:n], xs[0:n]] = np.arange(1, n + 1) array2 = np.zeros([nY, nX]) - ys, xs = np.arange(0, nY, Lw*2), np.arange(0, nX, Lw*2) + ys, xs = np.arange(0, nY, Lw * 2), np.arange(0, nX, Lw * 2) n = np.minimum(len(ys), len(xs)) - array2[ys[0:n], xs[0:n]] = np.arange(1, n+1) + array2[ys[0:n], xs[0:n]] = np.arange(1, n + 1) array2 = np.rot90(array2) - amax = int( (array2.max()+1) / 2) + amax = int((array2.max() + 1) / 2) array3 = fftconvolve(array1, array2, "same") @@ -99,45 +99,47 @@ def wedding_cake( # Create target patch2 theight = degrees_to_pixels(target_height, np.unique(ppd)) tpatch1 = np.zeros(L_patch.shape) - tpatch1[int(Ly/2 - theight/2):int(Ly/2+theight/2), - Lx-Lw::] = -intensity_grating[1] + intensity_target + tpatch1[int(Ly / 2 - theight / 2) : int(Ly / 2 + theight / 2), Lx - Lw : :] = ( + -intensity_grating[1] + intensity_target + ) array_t1 = np.zeros(array3.shape) for (ty, tx) in target_indices1: arr1 = np.copy(array1) arr2 = np.copy(array2) - arr1[arr1!=ty+2] = 0 - arr2[arr2!=tx+amax] = 0 + arr1[arr1 != ty + 2] = 0 + arr2[arr2 != tx + amax] = 0 array_t1 += fftconvolve(arr1, arr2, "same") - array_t1[array_t1<1] = 0 - array_t1[array_t1>1] = 1 + array_t1[array_t1 < 1] = 0 + array_t1[array_t1 > 1] = 1 t1 = np.round(fftconvolve(array_t1, tpatch1, "same"), 5) - t1 = t1[Lyh:Lyh+int(nY/2), Lxh:Lxh+int(nX/2)] + t1 = t1[Lyh : Lyh + int(nY / 2), Lxh : Lxh + int(nX / 2)] else: - t1 = np.zeros([int(nY/2), int(nX/2)]) - + t1 = np.zeros([int(nY / 2), int(nX / 2)]) + if target_indices2 is not None and target_height is not None: # Create target patch2 theight = degrees_to_pixels(target_height, np.unique(ppd)) tpatch2 = np.zeros(L_patch.shape) - tpatch2[int(Ly/2 - theight/2):int(Ly/2+theight/2), - Lx-Lw::] = -intensity_grating[0] + intensity_target + tpatch2[int(Ly / 2 - theight / 2) : int(Ly / 2 + theight / 2), Lx - Lw : :] = ( + -intensity_grating[0] + intensity_target + ) array_t2 = np.zeros(array3.shape) for (ty, tx) in target_indices2: arr1 = np.copy(array1) arr2 = np.copy(array2) - arr1[arr1!=ty+2] = 0 - arr2[arr2!=tx+amax] = 0 + arr1[arr1 != ty + 2] = 0 + arr2[arr2 != tx + amax] = 0 array_t2 += fftconvolve(arr1, arr2, "same") - array_t2[array_t2<1] = 0 - array_t2[array_t2>1] = 1 + array_t2[array_t2 < 1] = 0 + array_t2[array_t2 > 1] = 1 t2 = np.round(fftconvolve(array_t2, tpatch2, "same"), 5) - t2 = t2[Lyh-Lw:Lyh+int(nY/2)-Lw, Lxh+Lw:Lxh+int(nX/2)+Lw] + t2 = t2[Lyh - Lw : Lyh + int(nY / 2) - Lw, Lxh + Lw : Lxh + int(nX / 2) + Lw] else: - t2 = np.zeros([int(nY/2), int(nX/2)]) + t2 = np.zeros([int(nY / 2), int(nX / 2)]) # Create wedding cake pattern imgt = fftconvolve(array3, L_patch, "same") @@ -145,16 +147,16 @@ def wedding_cake( img = np.copy(imgt) img[imgt > 1] = intensity_grating[1] img[imgt < 1] = intensity_grating[0] - img = img[Lyh:Lyh+int(nY/2), Lxh:Lxh+int(nX/2)] - + img = img[Lyh : Lyh + int(nY / 2), Lxh : Lxh + int(nX / 2)] + # Create target mask - mask = np.abs(t1 * 1./intensity_target) + np.abs(t2 * 2./intensity_target) + mask = np.abs(t1 * 1.0 / intensity_target) + np.abs(t2 * 2.0 / intensity_target) mask = np.round(mask).astype(int) - + # Add targets img = img + t1 img = img + t2 - + stim = { "img": img, "target_mask": mask.astype(int), @@ -168,15 +170,13 @@ def wedding_cake( "target_indices1": target_indices1, "target_indices2": target_indices2, } - - return stim - + return stim if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + params = { "visual_size": 10, "ppd": 10, @@ -184,10 +184,10 @@ def wedding_cake( "target_height": 1, "target_indices1": None, "target_indices2": ((0, 1), (1, 1)), - } + } stims = { "wedding_cake": wedding_cake(**params), - } + } plot_stimuli(stims, mask=False, save=None) diff --git a/stimuli/illusions/whites.py b/stimupy/illusions/whites.py similarity index 94% rename from stimuli/illusions/whites.py rename to stimupy/illusions/whites.py index 6be49549..bc9352be 100644 --- a/stimuli/illusions/whites.py +++ b/stimupy/illusions/whites.py @@ -1,13 +1,14 @@ -import numpy as np import itertools import warnings -from stimuli.components import image_base -from stimuli.components.gratings import square_wave -from stimuli.utils import degrees_to_pixels -from stimuli.illusions.angulars import pinwheel as radial -from stimuli.illusions.circulars import rings as circular -from stimuli.illusions.wedding_cakes import wedding_cake +import numpy as np + +from stimupy.components import image_base +from stimupy.components.gratings import square_wave +from stimupy.illusions.angulars import pinwheel as radial +from stimupy.illusions.circulars import rings as circular +from stimupy.illusions.wedding_cakes import wedding_cake +from stimupy.utils import degrees_to_pixels __all__ = [ "generalized", @@ -96,7 +97,7 @@ def generalized( """ if target_heights is None: raise ValueError("generalized() missing argument 'target_heights' which is not 'None'") - + # Spatial square-wave grating stim = square_wave( visual_size=visual_size, @@ -141,22 +142,22 @@ def generalized( shape=shape, rotation=rotation, origin="center", - ) + ) xx = base["horizontal"] yy = base["vertical"] theta = np.deg2rad(rotation) x = np.round(np.cos(theta) * yy - np.sin(theta) * xx, 8) - + target_zip = zip(target_indices, intensity_target, target_heights, target_center_offsets) # Place target(s) targets_mask = np.zeros_like(stim["grating_mask"]) for target_idx, (bar_idx, intensity, height, offset) in enumerate(target_zip): - mask1 = np.where(x >= offset + height/2, 0, 1) - mask2 = np.where(x < offset - height/2, 0, 1) + mask1 = np.where(x >= offset + height / 2, 0, 1) + mask2 = np.where(x < offset - height / 2, 0, 1) if bar_idx < 0: bar_idx = int(stim["n_bars"]) + bar_idx - mask3 = np.where(stim["grating_mask"] == bar_idx+1, target_idx + 1, 0) + mask3 = np.where(stim["grating_mask"] == bar_idx + 1, target_idx + 1, 0) targets_mask += mask1 * mask2 * mask3 stim["img"] = np.where(targets_mask == target_idx + 1, intensity, stim["img"]) @@ -485,7 +486,9 @@ def anderson( ) if stripe_center_offset * ppd % 1 != 0: offsets_new = stripe_center_offset_px / ppd - warnings.warn(f"Stripe offsets rounded because of ppd; {stripe_center_offset} -> {offsets_new}") + warnings.warn( + f"Stripe offsets rounded because of ppd; {stripe_center_offset} -> {offsets_new}" + ) # Add stripe at top ystart = height // 2 - stripe_center_offset_px - stripe_size_px // 2 @@ -598,7 +601,7 @@ def howe( intensity_stripes=intensity_stripes, stripe_center_offset=target_center_offset, stripe_height=target_height, - ) + ) def yazdanbakhsh( @@ -744,21 +747,50 @@ def yazdanbakhsh( if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + params = { "visual_size": 10, "ppd": 10, "frequency": 0.5, - } + } stims = { - "generalized": generalized(**params, target_indices=(1, 3), target_center_offsets=(-1, -3), target_heights=(2, 3)), + "generalized": generalized( + **params, target_indices=(1, 3), target_center_offsets=(-1, -3), target_heights=(2, 3) + ), "white": white(**params, target_indices=(2, -3), target_height=2), - "white_two_rows": white_two_rows(**params, target_indices_top=(2,4), target_indices_bottom=(-2, -4), target_height=1, target_center_offset=2), - "anderson": anderson(**params, target_indices_top=3, target_indices_bottom=-2, target_center_offset=2, target_height=2, stripe_center_offset=1.5, stripe_height=2), - "howe": howe(**params, target_indices_top=3, target_indices_bottom=-2, target_center_offset=2, target_height=2), - "yazdanbakhsh": yazdanbakhsh(**params, target_indices_top=3, target_indices_bottom=-2, target_center_offset=2, target_height=2, gap_size=0.5), + "white_two_rows": white_two_rows( + **params, + target_indices_top=(2, 4), + target_indices_bottom=(-2, -4), + target_height=1, + target_center_offset=2, + ), + "anderson": anderson( + **params, + target_indices_top=3, + target_indices_bottom=-2, + target_center_offset=2, + target_height=2, + stripe_center_offset=1.5, + stripe_height=2, + ), + "howe": howe( + **params, + target_indices_top=3, + target_indices_bottom=-2, + target_center_offset=2, + target_height=2, + ), + "yazdanbakhsh": yazdanbakhsh( + **params, + target_indices_top=3, + target_indices_bottom=-2, + target_center_offset=2, + target_height=2, + gap_size=0.5, + ), } plot_stimuli(stims, mask=True, save=None) diff --git a/stimuli/noises/__init__.py b/stimupy/noises/__init__.py similarity index 65% rename from stimuli/noises/__init__.py rename to stimupy/noises/__init__.py index 509b011f..ca7a656d 100644 --- a/stimuli/noises/__init__.py +++ b/stimupy/noises/__init__.py @@ -1,13 +1,11 @@ -from .whites import white as white -from .binaries import binary as binary +from .binaries import * +from .narrowbands import * from .naturals import * -from .narrowbands import narrowband as narrowband from .utils import * +from .whites import * def create_overview(): - import stimuli.noises.naturals as naturals - params = { "visual_size": 10, "ppd": 10, @@ -21,9 +19,9 @@ def create_overview(): # White "white_noise": white(**params), # One over frequency - "one_over_f": naturals.one_over_f(**params, exponent=0.5), - "pink_noise": naturals.pink(**params), - "brown_noise": naturals.brown(**params), + "one_over_f": one_over_f(**params, exponent=0.5), + "pink_noise": pink(**params), + "brown_noise": brown(**params), # Narrowband "narrowband_1cpd": narrowband(**params, bandwidth=1, center_frequency=1.0), "narrowband_3cpd": narrowband(**params, bandwidth=1, center_frequency=3.0), @@ -34,7 +32,7 @@ def create_overview(): def overview(mask=False, save=None): - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = create_overview() diff --git a/stimuli/noises/binaries.py b/stimupy/noises/binaries.py similarity index 85% rename from stimuli/noises/binaries.py rename to stimupy/noises/binaries.py index 563eb410..cb12765a 100644 --- a/stimuli/noises/binaries.py +++ b/stimupy/noises/binaries.py @@ -3,14 +3,15 @@ """ import numpy as np -from stimuli.utils import resolution -from stimuli.utils.contrast_conversions import adapt_intensity_range +from stimupy.utils import resolution +from stimupy.utils.contrast_conversions import adapt_intensity_range __all__ = [ "binary", ] + def binary( visual_size=None, ppd=None, @@ -39,14 +40,16 @@ def binary( """ # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - + if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") binary_noise = np.random.randint(0, 2, size=shape) - 0.5 # Adjust intensity range: - binary_noise = adapt_intensity_range({"img": binary_noise}, intensity_range[0], intensity_range[1])["img"] + binary_noise = adapt_intensity_range( + {"img": binary_noise}, intensity_range[0], intensity_range[1] + )["img"] stim = { "img": binary_noise, @@ -60,12 +63,12 @@ def binary( if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + params = { "visual_size": 10, "ppd": 10, - } + } stims = { "Binary noise": binary(**params), diff --git a/stimuli/noises/narrowbands.py b/stimupy/noises/narrowbands.py similarity index 90% rename from stimuli/noises/narrowbands.py rename to stimupy/noises/narrowbands.py index fa8d1416..4b76e5bf 100644 --- a/stimuli/noises/narrowbands.py +++ b/stimupy/noises/narrowbands.py @@ -3,15 +3,16 @@ """ import numpy as np -from stimuli.utils import bandpass_filter, resolution -from stimuli.noises.utils import pseudo_white_spectrum -from stimuli.utils.contrast_conversions import adapt_intensity_range +from stimupy.noises.utils import pseudo_white_spectrum +from stimupy.utils import bandpass_filter, resolution +from stimupy.utils.contrast_conversions import adapt_intensity_range __all__ = [ "narrowband", ] + def narrowband( visual_size=None, ppd=None, @@ -54,7 +55,7 @@ def narrowband( # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - + if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") @@ -87,7 +88,9 @@ def narrowband( narrow_noise = np.real(narrow_noise) # Adjust intensity range: - narrow_noise = adapt_intensity_range({"img": narrow_noise}, intensity_range[0], intensity_range[1])["img"] + narrow_noise = adapt_intensity_range( + {"img": narrow_noise}, intensity_range[0], intensity_range[1] + )["img"] stim = { "img": narrow_noise, @@ -104,12 +107,13 @@ def narrowband( if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli + params = { "visual_size": 10, "ppd": 20, "pseudo_noise": True, - } + } stims = { "Narrowband noise - 3cpd": narrowband(**params, bandwidth=1, center_frequency=3.0), diff --git a/stimuli/noises/naturals.py b/stimupy/noises/naturals.py similarity index 94% rename from stimuli/noises/naturals.py rename to stimupy/noises/naturals.py index e93a7a96..6b76865e 100644 --- a/stimuli/noises/naturals.py +++ b/stimupy/noises/naturals.py @@ -3,10 +3,10 @@ """ import numpy as np -from stimuli.utils import resolution -from stimuli.noises.utils import pseudo_white_spectrum -from stimuli.utils.contrast_conversions import adapt_intensity_range +from stimupy.noises.utils import pseudo_white_spectrum +from stimupy.utils import resolution +from stimupy.utils.contrast_conversions import adapt_intensity_range __all__ = [ "one_over_f", @@ -14,6 +14,7 @@ "brown", ] + def one_over_f( visual_size=None, ppd=None, @@ -50,7 +51,7 @@ def one_over_f( raise ValueError("one_over_f() missing argument 'exponent' which is not 'None'") # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - + if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") @@ -122,13 +123,14 @@ def pink( ------- A stimulus dictionary with the noise array ['img'] """ - + stim = one_over_f( visual_size=visual_size, ppd=ppd, exponent=1.0, intensity_range=intensity_range, - pseudo_noise=pseudo_noise) + pseudo_noise=pseudo_noise, + ) return stim @@ -159,23 +161,25 @@ def brown( ------- A stimulus dictionary with the noise array ['img'] """ - + stim = one_over_f( visual_size=visual_size, ppd=ppd, exponent=2.0, intensity_range=intensity_range, - pseudo_noise=pseudo_noise) + pseudo_noise=pseudo_noise, + ) return stim if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli + params = { "visual_size": 10, "ppd": 20, "pseudo_noise": True, - } + } stims = { "One over f": one_over_f(**params, exponent=0.5), diff --git a/stimuli/noises/utils.py b/stimupy/noises/utils.py similarity index 99% rename from stimuli/noises/utils.py rename to stimupy/noises/utils.py index 3ea4728c..f56e311d 100644 --- a/stimuli/noises/utils.py +++ b/stimupy/noises/utils.py @@ -3,7 +3,8 @@ """ import numpy as np -from stimuli.utils import oriented_filter + +from stimupy.utils import oriented_filter # TODO: what to do with orient_noise? diff --git a/stimuli/noises/whites.py b/stimupy/noises/whites.py similarity index 86% rename from stimuli/noises/whites.py rename to stimupy/noises/whites.py index aa7fb7e2..2a12090b 100644 --- a/stimuli/noises/whites.py +++ b/stimupy/noises/whites.py @@ -3,15 +3,16 @@ """ import numpy as np -from stimuli.utils import resolution -from stimuli.noises.utils import pseudo_white_spectrum -from stimuli.utils.contrast_conversions import adapt_intensity_range +from stimupy.noises.utils import pseudo_white_spectrum +from stimupy.utils import resolution +from stimupy.utils.contrast_conversions import adapt_intensity_range __all__ = [ "white", ] + def white( visual_size=None, ppd=None, @@ -41,7 +42,7 @@ def white( """ # Resolve resolution shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd) - + if len(np.unique(ppd)) > 1: raise ValueError("ppd should be equal in x and y direction") @@ -57,7 +58,9 @@ def white( white_noise = np.random.rand(*shape) * 2.0 - 1.0 # Adjust intensity range: - white_noise = adapt_intensity_range({"img": white_noise}, intensity_range[0], intensity_range[1])["img"] + white_noise = adapt_intensity_range( + {"img": white_noise}, intensity_range[0], intensity_range[1] + )["img"] stim = { "img": white_noise, @@ -72,13 +75,13 @@ def white( if __name__ == "__main__": - from stimuli.utils import plot_stimuli - + from stimupy.utils import plot_stimuli + params = { "visual_size": 10, "ppd": 20, "pseudo_noise": True, - } + } stims = { "White noise": white(**params), diff --git a/stimuli/papers/RHS2007.py b/stimupy/papers/RHS2007.py similarity index 98% rename from stimuli/papers/RHS2007.py rename to stimupy/papers/RHS2007.py index 0ad7d013..c68d4be3 100644 --- a/stimuli/papers/RHS2007.py +++ b/stimupy/papers/RHS2007.py @@ -6,7 +6,7 @@ in that paper. Each stimulus is provided by a separate function, -a full list can be found as stimuli.papers.RHS2007.__all__ +a full list can be found as stimupy.papers.RHS2007.__all__ The output of each of these functions is a stimulus dictionary. @@ -19,7 +19,7 @@ ---------- __all__ (list of str): list of all stimulus-functions that are exported by this module when executing - >>> from stimuli.papers.RHS2007 import * + >>> from stimupy.papers.RHS2007 import * References ----------- @@ -30,15 +30,15 @@ import numpy as np -from stimuli import illusions -from stimuli.utils import ( +from stimupy import illusions +from stimupy.utils import ( + flip_dict, pad_dict_by_visual_size, pad_dict_to_shape, pad_dict_to_visual_size, - stack_dicts, - rotate_dict, - flip_dict, resolution, + rotate_dict, + stack_dicts, ) __all__ = [ @@ -146,10 +146,10 @@ def WE_thick(ppd=PPD, pad=True): if pad: stim = pad_dict_to_visual_size(stim, VISEXTENT, ppd, pad_value=v2) - + experimental_data = { "effect_strength": 4.18, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -208,7 +208,7 @@ def WE_thin_wide(ppd=PPD, pad=True): experimental_data = { "effect_strength": 4.6, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -333,7 +333,7 @@ def WE_anderson(ppd=PPD, pad=True): experimental_data = { "effect_strength": 6.43, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -397,7 +397,7 @@ def WE_howe(ppd=PPD, pad=True): experimental_data = { "effect_strength": 0.0, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -877,7 +877,7 @@ def grating_induction(ppd=PPD, pad=True): **params, ) mask1 = np.where(stim["target_mask"] % 2, 1, 2) - mask2 = np.where(stim["target_mask"]==0, 0, 1) + mask2 = np.where(stim["target_mask"] == 0, 0, 1) stim["target_mask"] = (mask1 * mask2).astype(int) if pad: @@ -885,7 +885,7 @@ def grating_induction(ppd=PPD, pad=True): experimental_data = { "effect_strength": 6.23, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -944,7 +944,7 @@ def sbc_large(ppd=PPD, pad=True): experimental_data = { "effect_strength": 11.35, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1003,7 +1003,7 @@ def sbc_small(ppd=PPD, pad=True): experimental_data = { "effect_strength": 19.78, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1068,7 +1068,7 @@ def todorovic_equal(ppd=PPD, pad=True): experimental_data = { "effect_strength": 2.2, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1131,7 +1131,7 @@ def todorovic_in_large(ppd=PPD, pad=True): experimental_data = { "effect_strength": 2.4, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1194,7 +1194,7 @@ def todorovic_in_small(ppd=PPD, pad=True): experimental_data = { "effect_strength": 4.4, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1260,7 +1260,7 @@ def todorovic_out(ppd=PPD, pad=True): experimental_data = { "effect_strength": 1.53, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1315,7 +1315,7 @@ def checkerboard_016(ppd=PPD, pad=True): experimental_data = { "effect_strength": 7.46, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1370,7 +1370,7 @@ def checkerboard_094(ppd=PPD, pad=True): experimental_data = { "effect_strength": 2.84, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1425,7 +1425,7 @@ def checkerboard_21(ppd=PPD, pad=True): experimental_data = { "effect_strength": 5.67, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1469,7 +1469,7 @@ def corrugated_mondrian(ppd=PPD, pad=True): (v3, v2, v3, v2, v3), ) params = { - "visual_size": (5*2, 5*2+1), + "visual_size": (5 * 2, 5 * 2 + 1), "ppd": ppd, "mondrian_depths": (0.0, -1.0, 0.0, 1.0, 0.0), "mondrian_intensities": values, @@ -1484,7 +1484,7 @@ def corrugated_mondrian(ppd=PPD, pad=True): experimental_data = { "effect_strength": 10.85, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1537,7 +1537,7 @@ def benary_cross(ppd=PPD, pad=True): # Switch target indices mask1 = np.where(stim["target_mask"] == 1, 1, 0) mask2 = np.where(stim["target_mask"] == 2, 1, 0) - stim["target_mask"] = (mask1*2 + mask2).astype(int) + stim["target_mask"] = (mask1 * 2 + mask2).astype(int) if pad: stim = pad_dict_by_visual_size(stim, ((0.0, 0.0), (4.0, 4.0)), ppd, pad_value=v3) @@ -1545,7 +1545,7 @@ def benary_cross(ppd=PPD, pad=True): experimental_data = { "effect_strength": 9.2, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1603,7 +1603,7 @@ def todorovic_benary1_2(ppd=PPD, pad=True): experimental_data = { "effect_strength": 11.96, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1661,7 +1661,7 @@ def todorovic_benary3_4(ppd=PPD, pad=True): experimental_data = { "effect_strength": 9.55, - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -1752,7 +1752,7 @@ def bullseye_thin(ppd=PPD, pad=True): frame_radii = np.array([0.304, 0.426, 0.548, 0.670, 0.792]) params = { - "visual_size": 0.792*2, + "visual_size": 0.792 * 2, "ppd": ppd, "frame_radii": frame_radii, "intensity_target": v2, @@ -1810,7 +1810,7 @@ def bullseye_thick(ppd=PPD, pad=True): frame_radii = np.array([0.304, 0.547, 0.790, 1.033, 1.276]) params = { - "visual_size": 1.276*2, + "visual_size": 1.276 * 2, "ppd": ppd, "frame_radii": frame_radii, "intensity_target": v2, @@ -1843,7 +1843,7 @@ def bullseye_thick(ppd=PPD, pad=True): if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = gen_all(pad=True, skip=True) plot_stimuli(stims, mask=True) diff --git a/stimuli/papers/__init__.py b/stimupy/papers/__init__.py similarity index 95% rename from stimuli/papers/__init__.py rename to stimupy/papers/__init__.py index 01a5867f..984383b0 100644 --- a/stimuli/papers/__init__.py +++ b/stimupy/papers/__init__.py @@ -5,4 +5,4 @@ "RHS2007", "white1981", "white1985", - ] +] diff --git a/stimuli/papers/carney1999.py b/stimupy/papers/carney1999.py similarity index 89% rename from stimuli/papers/carney1999.py rename to stimupy/papers/carney1999.py index d659c78a..1aafc7be 100644 --- a/stimuli/papers/carney1999.py +++ b/stimupy/papers/carney1999.py @@ -7,7 +7,7 @@ https://www.visionscience.com/data/modelfest/ Each stimulus is provided by a separate function, -a full list can be found as stimuli.papers.carney1999.__all__ +a full list can be found as stimupy.papers.carney1999.__all__ The output of each of these functions is a stimulus dictionary. @@ -20,7 +20,7 @@ ---------- __all__ (list of str): list of all stimulus-functions that are exported by this module when executing - >>> from stimuli.papers.carney1999 import * + >>> from stimupy.papers.carney1999 import * References ----------- @@ -30,15 +30,16 @@ 542-551. https://doi.org/10.1117/12.348473 """ +from pathlib import Path + import numpy as np import pandas as pd -from pathlib import Path -from stimuli.components import gratings, gaussians, shapes, checkerboards, lines -from stimuli.components.edges import gaussian_edge -from stimuli.components.circulars import bessel -from stimuli.noises.binaries import binary as binary_noise -from stimuli.utils import stack_dicts, pad_dict_to_shape, resize_dict, roll_dict +from stimupy.components import checkerboards, gaussians, gratings, lines, shapes +from stimupy.components.circulars import bessel +from stimupy.components.edges import gaussian_edge +from stimupy.noises.binaries import binary as binary_noise +from stimupy.utils import pad_dict_to_shape, resize_dict, roll_dict, stack_dicts __all__ = [ "GaborPatch1", @@ -153,15 +154,15 @@ def GaborPatch1(ppd=PPD): } stim = gratings.gabor(**params) - + v = 1 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -205,10 +206,10 @@ def GaborPatch2(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -252,10 +253,10 @@ def GaborPatch3(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -299,10 +300,10 @@ def GaborPatch4(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -346,10 +347,10 @@ def GaborPatch5(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -393,10 +394,10 @@ def GaborPatch6(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -440,10 +441,10 @@ def GaborPatch7(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -487,10 +488,10 @@ def GaborPatch8(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -534,10 +535,10 @@ def GaborPatch9(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -581,10 +582,10 @@ def GaborPatch10(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -628,10 +629,10 @@ def GaborPatch11(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -675,10 +676,10 @@ def GaborPatch12(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -722,10 +723,10 @@ def GaborPatch13(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -769,10 +770,10 @@ def GaborPatch14(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -816,10 +817,10 @@ def ElongatedGabor15(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -863,10 +864,10 @@ def ElongatedGabor16(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -910,10 +911,10 @@ def ElongatedGabor17(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -957,10 +958,10 @@ def Baguette18(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1004,10 +1005,10 @@ def Baguette19(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1051,10 +1052,10 @@ def Baguette20(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1098,10 +1099,10 @@ def Baguette21(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1139,9 +1140,9 @@ def Subthreshold22(ppd=PPD): } stim1 = gratings.gabor(**params, frequency=2) - stim2 = gratings.gabor(**params, frequency=2*np.sqrt(2)) - - stim1["img"] = stim1["img"]/2 + stim2["img"]/2 + stim2 = gratings.gabor(**params, frequency=2 * np.sqrt(2)) + + stim1["img"] = stim1["img"] / 2 + stim2["img"] / 2 stim1["grating_mask2"] = stim2["grating_mask"] stim1["edges2"] = stim2["edges"] stim1["frequency2"] = stim2["frequency"] @@ -1152,10 +1153,10 @@ def Subthreshold22(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim1, "experimental_data": experimental_data} @@ -1194,8 +1195,8 @@ def Subthreshold23(ppd=PPD): stim1 = gratings.gabor(**params, frequency=2) stim2 = gratings.gabor(**params, frequency=4) - - stim1["img"] = stim1["img"]/2 + stim2["img"]/2 + + stim1["img"] = stim1["img"] / 2 + stim2["img"] / 2 stim1["grating_mask2"] = stim2["grating_mask"] stim1["edges2"] = stim2["edges"] stim1["frequency2"] = stim2["frequency"] @@ -1206,10 +1207,10 @@ def Subthreshold23(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim1, "experimental_data": experimental_data} @@ -1247,9 +1248,9 @@ def Subthreshold24(ppd=PPD): } stim1 = gratings.gabor(**params, frequency=4) - stim2 = gratings.gabor(**params, frequency=4*np.sqrt(2)) - - stim1["img"] = stim1["img"]/2 + stim2["img"]/2 + stim2 = gratings.gabor(**params, frequency=4 * np.sqrt(2)) + + stim1["img"] = stim1["img"] / 2 + stim2["img"] / 2 stim1["grating_mask2"] = stim2["grating_mask"] stim1["edges2"] = stim2["edges"] stim1["frequency2"] = stim2["frequency"] @@ -1260,10 +1261,10 @@ def Subthreshold24(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim1, "experimental_data": experimental_data} @@ -1302,8 +1303,8 @@ def Subthreshold25(ppd=PPD): stim1 = gratings.gabor(**params, frequency=4) stim2 = gratings.gabor(**params, frequency=8) - - stim1["img"] = stim1["img"]/2 + stim2["img"]/2 + + stim1["img"] = stim1["img"] / 2 + stim2["img"] / 2 stim1["grating_mask2"] = stim2["grating_mask"] stim1["edges2"] = stim2["edges"] stim1["frequency2"] = stim2["frequency"] @@ -1314,10 +1315,10 @@ def Subthreshold25(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim1, "experimental_data": experimental_data} @@ -1352,16 +1353,16 @@ def Gaussians26(ppd=PPD): } stim = gaussians.gaussian(**params) - stim["img"] = stim["img"]/ 2 + 0.5 + stim["img"] = stim["img"] / 2 + 0.5 v = 101 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1396,16 +1397,16 @@ def Gaussians27(ppd=PPD): } stim = gaussians.gaussian(**params) - stim["img"] = stim["img"]/ 2 + 0.5 + stim["img"] = stim["img"] / 2 + 0.5 v = 105 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1440,16 +1441,16 @@ def Gaussians28(ppd=PPD): } stim = gaussians.gaussian(**params) - stim["img"] = stim["img"]/ 2 + 0.5 + stim["img"] = stim["img"] / 2 + 0.5 v = 109 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1484,16 +1485,16 @@ def Gaussians29(ppd=PPD): } stim = gaussians.gaussian(**params) - stim["img"] = stim["img"]/ 2 + 0.5 + stim["img"] = stim["img"] / 2 + 0.5 v = 113 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1519,17 +1520,17 @@ def Edge30(ppd=PPD): for designing and testing human vision models. Proceedings of SPIE, 3644, 542-551. https://doi.org/10.1117/12.348473 """ - - stim = gaussian_edge(visual_size=256/PPD, ppd=ppd, rotation=90, sigma=0.5) + + stim = gaussian_edge(visual_size=256 / PPD, ppd=ppd, rotation=90, sigma=0.5) v = 117 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1556,17 +1557,17 @@ def Line31(ppd=PPD): for designing and testing human vision models. Proceedings of SPIE, 3644, 542-551. https://doi.org/10.1117/12.348473 """ - + stim = lines.line( - visual_size=256/PPD, + visual_size=256 / PPD, ppd=ppd, - line_length=256/PPD, - line_width=0.5/60, + line_length=256 / PPD, + line_width=0.5 / 60, rotation=90, - intensity_background=0.5 - ) - window = gaussians.gaussian(visual_size=256/PPD, ppd=ppd, sigma=0.5) - + intensity_background=0.5, + ) + window = gaussians.gaussian(visual_size=256 / PPD, ppd=ppd, sigma=0.5) + img = (stim["img"] - 0.5) * window["img"] + 0.5 stim["img"] = img stim["sigma"] = window["sigma"] @@ -1575,17 +1576,17 @@ def Line31(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} def Dipole32(ppd=PPD): """Dipole32: lines x Gaussian, Carney et al (1999) - Line width: 3 px = - Seperation: 1 px = + Line width: 3 px = + Seperation: 1 px = Gaussian window: sy=sx=0.5 deg Parameters @@ -1606,18 +1607,18 @@ def Dipole32(ppd=PPD): for designing and testing human vision models. Proceedings of SPIE, 3644, 542-551. https://doi.org/10.1117/12.348473 """ - + stim = lines.dipole( - visual_size=256/PPD, + visual_size=256 / PPD, ppd=ppd, - line_length=256/PPD, - line_width=1/PPD, - line_gap=2/PPD, + line_length=256 / PPD, + line_width=1 / PPD, + line_gap=2 / PPD, rotation=270, - ) + ) stim = roll_dict(stim, -1, axes=0) - window = gaussians.gaussian(visual_size=256/PPD, ppd=ppd, sigma=0.5) - + window = gaussians.gaussian(visual_size=256 / PPD, ppd=ppd, sigma=0.5) + img = (stim["img"] - 0.5) * window["img"] + 0.5 stim["img"] = img stim["sigma"] = window["sigma"] @@ -1626,10 +1627,10 @@ def Dipole32(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1657,7 +1658,7 @@ def GaborString33(ppd=PPD): for designing and testing human vision models. Proceedings of SPIE, 3644, 542-551. https://doi.org/10.1117/12.348473 """ - + params = { "visual_size": 256 / PPD / 6, "ppd": ppd, @@ -1669,13 +1670,13 @@ def GaborString33(ppd=PPD): } stim = gratings.gabor(**params) - + # Stack collinear Gabors horizontally and pad stimc = stack_dicts(stim, stim) stimc = stack_dicts(stimc, stimc) stimc = stack_dicts(stimc, stim) stimc = pad_dict_to_shape(stimc, (256, 256), pad_value=0.5) - + stim["img"] = stimc["img"] stim["grating_mask"] = stimc["grating_mask"] stim = roll_dict(stim, (-1, -1), axes=(0, 1)) @@ -1684,10 +1685,10 @@ def GaborString33(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1715,7 +1716,7 @@ def GaborString34(ppd=PPD): for designing and testing human vision models. Proceedings of SPIE, 3644, 542-551. https://doi.org/10.1117/12.348473 """ - + params = { "visual_size": 256 / PPD / 6, "ppd": ppd, @@ -1727,14 +1728,14 @@ def GaborString34(ppd=PPD): stim1 = gratings.gabor(**params, phase_shift=-90) stim2 = gratings.gabor(**params, phase_shift=90) - + # Stack collinear Gabors horizontally and pad stimc = stack_dicts(stim2, stim1) stimc = stack_dicts(stimc, stim2) stimc = stack_dicts(stimc, stim1) stimc = stack_dicts(stimc, stim2) stimc = pad_dict_to_shape(stimc, (256, 256), pad_value=0.5) - + stim1["img"] = stimc["img"] stim1["grating_mask"] = stimc["grating_mask"] stim1["phase_shift2"] = stim2["phase_shift"] @@ -1745,10 +1746,10 @@ def GaborString34(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim1, "experimental_data": experimental_data} @@ -1775,12 +1776,12 @@ def Noise35_random(ppd=PPD): 542-551. https://doi.org/10.1117/12.348473 """ - stim = binary_noise(visual_size=256/PPD, ppd=ppd/2, rms_contrast=1) + stim = binary_noise(visual_size=256 / PPD, ppd=ppd / 2, rms_contrast=1) stim = resize_dict(stim, (2, 2)) - window = gaussians.gaussian(visual_size=256/PPD, ppd=ppd, sigma=0.5) - + window = gaussians.gaussian(visual_size=256 / PPD, ppd=ppd, sigma=0.5) + img = stim["img"] * window["img"] - stim["img"] = img/2 + 0.5 + stim["img"] = img / 2 + 0.5 stim["mask"] = np.zeros(img.shape).astype(int) return stim @@ -1810,23 +1811,23 @@ def Noise35(ppd=PPD): # Read natural image from Modelfest img = read_tif(Path(__file__).parents[0] / "carney1999_noise.tif") img = img / img.max() - + stim = { "img": img, - "visual_size": (256/PPD, 256/PPD), + "visual_size": (256 / PPD, 256 / PPD), "shape": img.shape, "ppd": PPD, "intensity_range": (img.min(), img.max()), - } - + } + v = 169 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } mask = np.zeros(img.shape).astype(int) return {**stim, "mask": mask, "experimental_data": experimental_data} @@ -1872,10 +1873,10 @@ def Orientation36(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1920,10 +1921,10 @@ def Orientation37(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -1963,8 +1964,8 @@ def Plaids38(ppd=PPD): stim = gratings.gabor(**params, rotation=0) stim2 = gratings.gabor(**params, rotation=90) - - stim["img"] = stim["img"]/2 + stim2["img"]/2 + + stim["img"] = stim["img"] / 2 + stim2["img"] / 2 stim["rotation"] = stim2["rotation"] stim["grating_mask2"] = stim2["grating_mask"] stim["distances2"] = stim2["distances"] @@ -1973,10 +1974,10 @@ def Plaids38(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -2016,8 +2017,8 @@ def Plaids39(ppd=PPD): stim = gratings.gabor(**params, rotation=45) stim2 = gratings.gabor(**params, rotation=90) - - stim["img"] = stim["img"]/2 + stim2["img"]/2 + + stim["img"] = stim["img"] / 2 + stim2["img"] / 2 stim["rotation"] = stim2["rotation"] stim["grating_mask2"] = stim2["grating_mask"] stim["distances2"] = stim2["distances"] @@ -2026,10 +2027,10 @@ def Plaids39(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -2056,17 +2057,17 @@ def Disk40(ppd=PPD): 542-551. https://doi.org/10.1117/12.348473 """ - stim = shapes.disc(visual_size=256/PPD, ppd=ppd, radius=0.125, origin="center") + stim = shapes.disc(visual_size=256 / PPD, ppd=ppd, radius=0.125, origin="center") stim = roll_dict(stim, (-2, -2), axes=(0, 1)) v = 157 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -2094,24 +2095,24 @@ def Bessel41(ppd=PPD): 542-551. https://doi.org/10.1117/12.348473 """ - stim = bessel(visual_size=256/PPD, ppd=ppd, frequency=4, origin="center") - window = gaussians.gaussian(visual_size=256/PPD, ppd=ppd, sigma=0.5) - + stim = bessel(visual_size=256 / PPD, ppd=ppd, frequency=4, origin="center") + window = gaussians.gaussian(visual_size=256 / PPD, ppd=ppd, sigma=0.5) + mean_int = stim["img"].mean() img = (stim["img"] - mean_int) * window["img"] stim["img"] = img + mean_int stim["img"] = stim["img"] + 0.41 - stim["img"] = stim["img"] / stim["img"].max() # TODO: adjust range + stim["img"] = stim["img"] / stim["img"].max() # TODO: adjust range stim["sigma"] = window["sigma"] v = 161 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -2147,8 +2148,8 @@ def Checkerboard42(ppd=PPD): } stim = checkerboards.checkerboard(**params) - window = gaussians.gaussian(visual_size=256/PPD, ppd=ppd, sigma=0.5) - + window = gaussians.gaussian(visual_size=256 / PPD, ppd=ppd, sigma=0.5) + img = (stim["img"] - 0.5) * window["img"] + 0.5 stim["img"] = img stim["sigma"] = window["sigma"] @@ -2157,10 +2158,10 @@ def Checkerboard42(ppd=PPD): experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } return {**stim, "experimental_data": experimental_data} @@ -2186,46 +2187,50 @@ def NaturalScene43(ppd=PPD): for designing and testing human vision models. Proceedings of SPIE, 3644, 542-551. https://doi.org/10.1117/12.348473 """ - + # Read natural image from Modelfest img = read_tif(Path(__file__).parents[0] / "carney1999_natural_scene.tif") img = img / img.max() stim = { "img": img, - "visual_size": (256/PPD, 256/PPD), + "visual_size": (256 / PPD, 256 / PPD), "shape": img.shape, "ppd": PPD, "intensity_range": (img.min(), img.max()), - } + } v = 169 experimental_data = { "participants": participants, "thresholds1": df[v], - "thresholds2": df[v+1], - "thresholds3": df[v+2], - "thresholds4": df[v+3], - } + "thresholds2": df[v + 1], + "thresholds3": df[v + 2], + "thresholds4": df[v + 3], + } mask = np.zeros(img.shape).astype(int) return {**stim, "mask": mask, "experimental_data": experimental_data} def read_tif(filename): from PIL import Image + img = np.array(Image.open(filename)).astype(float) return img def compare(o1, s1, filename): import matplotlib.pyplot as plt + o1 = o1 / o1.mean() s1 = s1 / s1.mean() vmin, vmax = o1.min(), o1.max() plt.figure(figsize=(20, 6)) plt.subplot(141), plt.imshow(o1, vmin=vmin, vmax=vmax), plt.title("Original") plt.subplot(142), plt.imshow(s1, vmin=vmin, vmax=vmax), plt.title("Our implementation") - plt.subplot(143), plt.imshow(o1 - s1, vmin=-vmax, vmax=vmax), plt.title("Difference"), plt.colorbar() + plt.subplot(143), plt.imshow(o1 - s1, vmin=-vmax, vmax=vmax), plt.title( + "Difference" + ), plt.colorbar() plt.subplot(144), plt.plot(o1[:, 128], label="Original") plt.plot(s1[:, 128], label="Our implementation"), plt.legend() plt.savefig("./comparisons/" + filename) @@ -2241,7 +2246,7 @@ def compare_all(): if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = gen_all(skip=True) plot_stimuli(stims, mask=False) diff --git a/stimuli/papers/carney1999_data.csv b/stimupy/papers/carney1999_data.csv similarity index 100% rename from stimuli/papers/carney1999_data.csv rename to stimupy/papers/carney1999_data.csv diff --git a/stimuli/papers/carney1999_natural_scene.tif b/stimupy/papers/carney1999_natural_scene.tif similarity index 100% rename from stimuli/papers/carney1999_natural_scene.tif rename to stimupy/papers/carney1999_natural_scene.tif diff --git a/stimuli/papers/carney1999_noise.tif b/stimupy/papers/carney1999_noise.tif similarity index 100% rename from stimuli/papers/carney1999_noise.tif rename to stimupy/papers/carney1999_noise.tif diff --git a/stimuli/papers/domijan2015.py b/stimupy/papers/domijan2015.py similarity index 98% rename from stimuli/papers/domijan2015.py rename to stimupy/papers/domijan2015.py index e1db5e38..8cfe6973 100644 --- a/stimuli/papers/domijan2015.py +++ b/stimupy/papers/domijan2015.py @@ -12,7 +12,7 @@ (in pixels), a visual_size (in degrees) and/or a resolution (in ppd). Each stimulus is provided by a separate function, -a full list can be found as stimuli.papers.domijan2015.__all__ +a full list can be found as stimupy.papers.domijan2015.__all__ The output of each of these functions is a stimulus dictionary. @@ -25,7 +25,7 @@ ---------- __all__ (list of str): list of all stimulus-functions that are exported by this module when executing - >>> from stimuli.papers.domijan2015 import * + >>> from stimupy.papers.domijan2015 import * References ----------- @@ -36,8 +36,13 @@ import numpy as np -from stimuli import illusions -from stimuli.utils import pad_dict_by_visual_size, pad_dict_to_shape, resolution, stack_dicts +from stimupy import illusions +from stimupy.utils import ( + pad_dict_by_visual_size, + pad_dict_to_shape, + resolution, + stack_dicts, +) # TODO: Add warning when stimulus shape or visual_size is different from what requested! @@ -236,7 +241,7 @@ def dungeon(visual_size=VSIZES["dungeon"], ppd=PPD, shape=SHAPES["dungeon"]): stim1 = pad_dict_by_visual_size(stim1, padding, ppd, v1) stim2 = pad_dict_by_visual_size(stim2, padding, ppd, v3) stim = stack_dicts(stim1, stim2) - + params.update( original_shape=SHAPES["dungeon"], original_ppd=PPD, @@ -245,7 +250,7 @@ def dungeon(visual_size=VSIZES["dungeon"], ppd=PPD, shape=SHAPES["dungeon"]): intensity_range=(v1, v3), visual_size=resolution.visual_size_from_shape_ppd(stim["img"].shape, ppd), shape=stim["img"].shape, - ) + ) return {**stim, **params} @@ -304,7 +309,7 @@ def cube(visual_size=VSIZES["cube"], ppd=PPD, shape=SHAPES["cube"]): stim1 = pad_dict_by_visual_size(stim1, padding, ppd, v1) stim2 = pad_dict_by_visual_size(stim2, padding, ppd, v3) stim = stack_dicts(stim1, stim2) - + params.update( original_shape=SHAPES["cube"], original_ppd=PPD, @@ -313,7 +318,7 @@ def cube(visual_size=VSIZES["cube"], ppd=PPD, shape=SHAPES["cube"]): intensity_range=(v1, v3), visual_size=resolution.visual_size_from_shape_ppd(stim["img"].shape, ppd), shape=stim["img"].shape, - ) + ) return {**stim, **params} @@ -374,7 +379,7 @@ def grating(visual_size=VSIZES["grating"], ppd=PPD, shape=SHAPES["grating"]): stim1 = pad_dict_by_visual_size(stim1, padding, ppd, v1) stim2 = pad_dict_by_visual_size(stim2, padding, ppd, v3) stim = stack_dicts(stim1, stim2) - + params.update( original_shape=SHAPES["grating"], original_ppd=PPD, @@ -383,7 +388,7 @@ def grating(visual_size=VSIZES["grating"], ppd=PPD, shape=SHAPES["grating"]): intensity_range=(v1, v3), visual_size=resolution.visual_size_from_shape_ppd(stim["img"].shape, ppd), shape=stim["img"].shape, - ) + ) return {**stim, **params} @@ -417,7 +422,7 @@ def rings(visual_size=VSIZES["rings"], ppd=PPD, shape=SHAPES["rings"]): frame_radii = np.array((0.55, 1.05, 1.55, 2.05, 2.55, 3.05, 3.55, 4.05)) * visual_resize params = { - "visual_size": 4.05*2, + "visual_size": 4.05 * 2, "ppd": ppd, "frame_radii": frame_radii, } @@ -448,7 +453,7 @@ def rings(visual_size=VSIZES["rings"], ppd=PPD, shape=SHAPES["rings"]): intensity_range=(v1, v3), visual_size=resolution.visual_size_from_shape_ppd(stim["img"].shape, ppd), shape=stim["img"].shape, - ) + ) return {**stim, **params} @@ -482,7 +487,7 @@ def bullseye(visual_size=VSIZES["bullseye"], ppd=PPD, shape=SHAPES["bullseye"]): frame_radii = np.array((0.55, 1.05, 1.55, 2.05, 2.55, 3.05, 3.55, 4.05)) * visual_resize params = { - "visual_size": 4.05*2, + "visual_size": 4.05 * 2, "ppd": ppd, "frame_radii": frame_radii, } @@ -511,7 +516,7 @@ def bullseye(visual_size=VSIZES["bullseye"], ppd=PPD, shape=SHAPES["bullseye"]): intensity_range=(v1, v3), visual_size=resolution.visual_size_from_shape_ppd(stim["img"].shape, ppd), shape=stim["img"].shape, - ) + ) return {**stim, **params} @@ -579,7 +584,7 @@ def simultaneous_brightness_contrast( intensity_range=(v1, v3), visual_size=resolution.visual_size_from_shape_ppd(stim["img"].shape, ppd), shape=stim["img"].shape, - ) + ) return {**stim, **params} @@ -772,7 +777,7 @@ def todorovic(visual_size=VSIZES["todorovic"], ppd=PPD, shape=SHAPES["todorovic" intensity_range=(v1, v3), visual_size=resolution.visual_size_from_shape_ppd(stim["img"].shape, ppd), shape=stim["img"].shape, - ) + ) return {**stim, **params} @@ -1245,7 +1250,7 @@ def white_howe(visual_size=VSIZES["white_howe"], ppd=PPD, shape=SHAPES["white_ho if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = gen_all(skip=True) plot_stimuli(stims, mask=False) diff --git a/stimuli/papers/murray2020.mat b/stimupy/papers/murray2020.mat similarity index 100% rename from stimuli/papers/murray2020.mat rename to stimupy/papers/murray2020.mat diff --git a/stimuli/papers/murray2020.py b/stimupy/papers/murray2020.py similarity index 98% rename from stimuli/papers/murray2020.py rename to stimupy/papers/murray2020.py index bc033eae..f3ec1d57 100644 --- a/stimuli/papers/murray2020.py +++ b/stimupy/papers/murray2020.py @@ -7,7 +7,7 @@ NOTE that the Haze illusion (Fig 1m) is not provided. Each stimulus is provided by a separate function, -a full list can be found as stimuli.papers.murray2020.__all__ +a full list can be found as stimupy.papers.murray2020.__all__ The output of each of these functions is a stimulus dictionary. @@ -20,7 +20,7 @@ ---------- __all__ (list of str): list of all stimulus-functions that are exported by this module when executing - >>> from stimuli.papers.murray2020 import * + >>> from stimupy.papers.murray2020 import * References ----------- @@ -34,8 +34,8 @@ import numpy as np import scipy.io -from stimuli import illusions -from stimuli.utils import pad_dict_by_visual_size, rotate_dict +from stimupy import illusions +from stimupy.utils import pad_dict_by_visual_size, rotate_dict __all__ = [ "argyle", @@ -137,11 +137,11 @@ def argyle(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.4875, "CI95_proportion_expected": (-0.154903, 0.154903), - } + } params = { "visual_size": np.array(normed_img.shape) / ppd, @@ -187,11 +187,11 @@ def argyle_control(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.35, "CI95_proportion_expected": (-0.147814, 0.147814), - } + } stim = { "img": normed_img, @@ -239,11 +239,11 @@ def argyle_long(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.6, "CI95_proportion_expected": (-0.151821, 0.151821), - } + } stim = { "img": normed_img, @@ -291,11 +291,11 @@ def snake(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.9625, "CI95_proportion_expected": (-0.0588765, 0.0588765), - } + } stim = { "img": normed_img, @@ -343,11 +343,11 @@ def snake_control(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.8, "CI95_proportion_expected": (-0.123961, 0.123961), - } + } stim = { "img": normed_img, @@ -395,11 +395,11 @@ def koffka_adelson(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.8375, "CI95_proportion_expected": (-0.114326, 0.114326), - } + } stim = { "img": normed_img, @@ -447,11 +447,11 @@ def koffka_broken(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.8, "CI95_proportion_expected": (-0.123961, 0.123961), - } + } stim = { "img": normed_img, @@ -499,11 +499,11 @@ def koffka_connected(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.6, "CI95_proportion_expected": (-0.151821, 0.151821), - } + } stim = { "img": normed_img, @@ -560,11 +560,11 @@ def checkassim(ppd=PPD, pad=PAD): # Normalize intensity values to [0, 1] original_range = (stim["img"].min(), stim["img"].max()) stim["img"] = (stim["img"] - stim["img"].min()) / (stim["img"].max() - stim["img"].min()) - + experimental_data = { "mean_proportion_expected": 0.8625, "CI95_proportion_expected": (-0.106723, 0.106723), - } + } params.update( visual_size=np.array(stim["img"].shape) / ppd, @@ -609,11 +609,11 @@ def simcon(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.925, "CI95_proportion_expected": (-0.0816258, 0.0816258), - } + } stim = { "img": normed_img, @@ -661,11 +661,11 @@ def simcon_articulated(ppd=PPD): # Normalize intensity values to [0, 1] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 1.0, "CI95_proportion_expected": (-0.0, 0.0), - } + } stim = { "img": normed_img, @@ -723,11 +723,11 @@ def white(ppd=PPD): img = stim["img"] original_range = (img.min(), img.max()) normed_img = (img - img.min()) / (img.max() - img.min()) - + experimental_data = { "mean_proportion_expected": 0.95, "CI95_proportion_expected": (-0.0675418, 0.0675418), - } + } params.update( visual_size=np.array(normed_img.shape) / ppd, @@ -740,7 +740,7 @@ def white(ppd=PPD): if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli # Generate all stimuli exported in __all__ stims = gen_all(skip=True) diff --git a/stimuli/papers/white1981.py b/stimupy/papers/white1981.py similarity index 92% rename from stimuli/papers/white1981.py rename to stimupy/papers/white1981.py index 3a1c61e4..cd7bc0e0 100644 --- a/stimuli/papers/white1981.py +++ b/stimupy/papers/white1981.py @@ -5,7 +5,7 @@ described in that paper. Each stimulus is provided by a separate function, -a full list can be found as stimuli.papers.white1981.__all__ +a full list can be found as stimupy.papers.white1981.__all__ The output of each of these functions is a stimulus dictionary. @@ -18,7 +18,7 @@ ---------- __all__ (list of str): list of all stimulus-functions that are exported by this module when executing - >>> from stimuli.papers.white1981 import * + >>> from stimupy.papers.white1981 import * References ----------- @@ -27,11 +27,12 @@ 215–230. https://doi.org/10.1068/p100215 """ -import numpy as np import copy import warnings -from stimuli import illusions +import numpy as np + +from stimupy import illusions __all__ = [ "square_white", @@ -48,9 +49,9 @@ "grating_black_orthogonal", ] -VISUAL_SIZE = 4.0 # originally 4.3592 deg -TARGET_SIZE = 0.72 # originally 0.7696 deg -BAR_WIDTH = 0.08 # originally 0.0855 deg +VISUAL_SIZE = 4.0 # originally 4.3592 deg +TARGET_SIZE = 0.72 # originally 0.7696 deg +BAR_WIDTH = 0.08 # originally 0.0855 deg PPD = 50 ORIENTATION = 90 @@ -82,10 +83,12 @@ def gen_all(ppd=PPD, skip=False): def resolve_bar_width(bar_width=BAR_WIDTH, ppd=PPD): bar_width_old = copy.deepcopy(bar_width) bar_width = np.round(bar_width * ppd) / ppd - + if bar_width_old != bar_width: - warnings.warn(f"Rounding bar_width because of ppd; {bar_width_old} -> {bar_width}. " - "This will also effect the stimulus size.") + warnings.warn( + f"Rounding bar_width because of ppd; {bar_width_old} -> {bar_width}. " + "This will also effect the stimulus size." + ) return bar_width @@ -228,7 +231,7 @@ def grating_white_black(ppd=PPD): params = { "visual_size": VISUAL_SIZE, "ppd": ppd, - "grating_size": bar_width*9, + "grating_size": bar_width * 9, "bar_width": bar_width, "rotation": ORIENTATION, "intensity_background": v1, @@ -269,7 +272,7 @@ def grating_black_white(ppd=PPD): params = { "visual_size": VISUAL_SIZE, "ppd": ppd, - "grating_size": bar_width*9, + "grating_size": bar_width * 9, "bar_width": bar_width, "rotation": ORIENTATION, "intensity_background": v3, @@ -310,7 +313,7 @@ def grating_black_black(ppd=PPD): params = { "visual_size": VISUAL_SIZE, "ppd": ppd, - "grating_size": bar_width*9, + "grating_size": bar_width * 9, "bar_width": bar_width, "rotation": ORIENTATION, "intensity_background": v1, @@ -347,7 +350,7 @@ def grating_white_in(ppd=PPD): 215–230. """ bar_width = resolve_bar_width(BAR_WIDTH, ppd) - + small_params = { "bar_width": bar_width, "n_bars": 9, @@ -359,7 +362,7 @@ def grating_white_in(ppd=PPD): } large_params = { "bar_width": bar_width, - "visual_size": 51*bar_width, + "visual_size": 51 * bar_width, "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v1, v3), @@ -367,8 +370,8 @@ def grating_white_in(ppd=PPD): stim = illusions.gratings.grating( small_grating_params=small_params, large_grating_params=large_params, - ) - + ) + stim["target_mask"] = np.where(stim["target_mask"] != 0, 1, 0) return stim @@ -397,7 +400,7 @@ def grating_black_in(ppd=PPD): 215–230. """ bar_width = resolve_bar_width(BAR_WIDTH, ppd) - + small_params = { "bar_width": bar_width, "n_bars": 9, @@ -409,7 +412,7 @@ def grating_black_in(ppd=PPD): } large_params = { "bar_width": bar_width, - "visual_size": 51*bar_width, + "visual_size": 51 * bar_width, "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v3, v1), @@ -417,8 +420,8 @@ def grating_black_in(ppd=PPD): stim = illusions.gratings.grating( small_grating_params=small_params, large_grating_params=large_params, - ) - + ) + stim["target_mask"] = np.where(stim["target_mask"] != 0, 1, 0) return stim @@ -450,7 +453,7 @@ def grating_white_out(ppd=PPD): small_params = { "bar_width": bar_width, - "visual_size": (50*bar_width, 9*bar_width), + "visual_size": (50 * bar_width, 9 * bar_width), "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v3, v1), @@ -459,7 +462,7 @@ def grating_white_out(ppd=PPD): } large_params = { "bar_width": bar_width, - "visual_size": 50*bar_width, + "visual_size": 50 * bar_width, "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v1, v3), @@ -467,8 +470,8 @@ def grating_white_out(ppd=PPD): stim = illusions.gratings.grating( small_grating_params=small_params, large_grating_params=large_params, - ) - + ) + stim["target_mask"] = np.where(stim["target_mask"] != 0, 1, 0) return stim @@ -500,7 +503,7 @@ def grating_black_out(ppd=PPD): small_params = { "bar_width": bar_width, - "visual_size": (50*bar_width, 9*bar_width), + "visual_size": (50 * bar_width, 9 * bar_width), "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v1, v3), @@ -509,7 +512,7 @@ def grating_black_out(ppd=PPD): } large_params = { "bar_width": bar_width, - "visual_size": 50*bar_width, + "visual_size": 50 * bar_width, "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v3, v1), @@ -517,8 +520,8 @@ def grating_black_out(ppd=PPD): stim = illusions.gratings.grating( small_grating_params=small_params, large_grating_params=large_params, - ) - + ) + stim["target_mask"] = np.where(stim["target_mask"] != 0, 1, 0) return stim @@ -549,17 +552,17 @@ def grating_white_orthogonal(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) small_params = { - "visual_size": (17*bar_width, 9*bar_width), + "visual_size": (17 * bar_width, 9 * bar_width), "bar_width": bar_width, "ppd": ppd, - "rotation": ORIENTATION+90, + "rotation": ORIENTATION + 90, "intensity_bars": (v3, v1), "intensity_target": v2, "target_indices": (2, 4, 6, 8), } large_params = { "bar_width": bar_width, - "visual_size": 51*bar_width, + "visual_size": 51 * bar_width, "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v1, v3), @@ -567,10 +570,10 @@ def grating_white_orthogonal(ppd=PPD): stim = illusions.gratings.grating_masked( small_grating_params=small_params, large_grating_params=large_params, - mask_size=(9*bar_width, 9*bar_width, 8*bar_width), + mask_size=(9 * bar_width, 9 * bar_width, 8 * bar_width), mask_rotation=ORIENTATION, - ) - + ) + stim["target_mask"] = np.where(stim["target_mask"] != 0, 1, 0) return stim @@ -601,17 +604,17 @@ def grating_black_orthogonal(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) small_params = { - "visual_size": (17*bar_width, 9*bar_width), + "visual_size": (17 * bar_width, 9 * bar_width), "bar_width": bar_width, "ppd": ppd, - "rotation": ORIENTATION+90, + "rotation": ORIENTATION + 90, "intensity_bars": (v1, v3), "intensity_target": v2, "target_indices": (2, 4, 6, 8), } large_params = { "bar_width": bar_width, - "visual_size": 51*bar_width, + "visual_size": 51 * bar_width, "ppd": ppd, "rotation": ORIENTATION, "intensity_bars": (v3, v1), @@ -619,16 +622,16 @@ def grating_black_orthogonal(ppd=PPD): stim = illusions.gratings.grating_masked( small_grating_params=small_params, large_grating_params=large_params, - mask_size=(9*bar_width, 9*bar_width, 8*bar_width), + mask_size=(9 * bar_width, 9 * bar_width, 8 * bar_width), mask_rotation=ORIENTATION, - ) - + ) + stim["target_mask"] = np.where(stim["target_mask"] != 0, 1, 0) return stim if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = gen_all(skip=True) plot_stimuli(stims, mask=False) diff --git a/stimuli/papers/white1985.py b/stimupy/papers/white1985.py similarity index 91% rename from stimuli/papers/white1985.py rename to stimupy/papers/white1985.py index bd8eec53..4119b385 100644 --- a/stimuli/papers/white1985.py +++ b/stimupy/papers/white1985.py @@ -5,7 +5,7 @@ they were described in that paper. Each stimulus is provided by a separate function, -a full list can be found as stimuli.papers.white1985.__all__ +a full list can be found as stimupy.papers.white1985.__all__ The output of each of these functions is a stimulus dictionary. @@ -18,7 +18,7 @@ ---------- __all__ (list of str): list of all stimulus-functions that are exported by this module when executing - >>> from stimuli.papers.white1985 import * + >>> from stimupy.papers.white1985 import * References ----------- @@ -26,10 +26,12 @@ research, 25 (9), 1331-1335. https://doi.org/10.1016/0042-6989(85)90049-5 """ -import numpy as np import copy import warnings -from stimuli.illusions import gratings as grating + +import numpy as np + +from stimupy.illusions import gratings as grating __all__ = [ "wide_0phase", @@ -78,10 +80,12 @@ def gen_all(ppd=PPD, skip=False): def resolve_bar_width(bar_width=BAR_WIDTH, ppd=PPD): bar_width_old = copy.deepcopy(bar_width) bar_width = np.round(bar_width * ppd) / ppd - + if bar_width_old != bar_width: - warnings.warn(f"Rounding bar_width because of ppd; {bar_width_old} -> {bar_width}. " - "This will also effect the stimulus size.") + warnings.warn( + f"Rounding bar_width because of ppd; {bar_width_old} -> {bar_width}. " + "This will also effect the stimulus size." + ) return bar_width @@ -112,10 +116,10 @@ def wide_0phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, 0.75), + "target_size": (bar_width * 7, 0.75), "target_phase_shift": START_PHASE1 + 0, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -153,10 +157,10 @@ def wide_36phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, 0.75), + "target_size": (bar_width * 7, 0.75), "target_phase_shift": START_PHASE1 + 36, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -195,10 +199,10 @@ def wide_72phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, 0.75), + "target_size": (bar_width * 7, 0.75), "target_phase_shift": START_PHASE1 + 72, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -237,10 +241,10 @@ def wide_108phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, 0.75), + "target_size": (bar_width * 7, 0.75), "target_phase_shift": START_PHASE1 + 108, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -279,10 +283,10 @@ def wide_144phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, 0.75), + "target_size": (bar_width * 7, 0.75), "target_phase_shift": START_PHASE1 + 144, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -321,10 +325,10 @@ def wide_180phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, 0.75), + "target_size": (bar_width * 7, 0.75), "target_phase_shift": START_PHASE1 + 180, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -363,10 +367,10 @@ def square_0phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, bar_width), + "target_size": (bar_width * 7, bar_width), "target_phase_shift": START_PHASE2 + 0, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -405,10 +409,10 @@ def square_36phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, bar_width), + "target_size": (bar_width * 7, bar_width), "target_phase_shift": START_PHASE2 + 36, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -447,10 +451,10 @@ def square_72phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, bar_width), + "target_size": (bar_width * 7, bar_width), "target_phase_shift": START_PHASE2 + 72, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -489,10 +493,10 @@ def square_108phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, bar_width), + "target_size": (bar_width * 7, bar_width), "target_phase_shift": START_PHASE2 + 108, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -531,10 +535,10 @@ def square_144phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, bar_width), + "target_size": (bar_width * 7, bar_width), "target_phase_shift": START_PHASE2 + 144, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -573,10 +577,10 @@ def square_180phase(ppd=PPD): bar_width = resolve_bar_width(BAR_WIDTH, ppd) params = { - "visual_size": 25*bar_width, + "visual_size": 25 * bar_width, "ppd": ppd, "bar_width": bar_width, - "target_size": (bar_width*7, bar_width), + "target_size": (bar_width * 7, bar_width), "target_phase_shift": START_PHASE2 + 180, "intensity_bars": (v1, v3), "intensity_target": v2, @@ -589,7 +593,7 @@ def square_180phase(ppd=PPD): if __name__ == "__main__": - from stimuli.utils import plot_stimuli + from stimupy.utils import plot_stimuli stims = gen_all(skip=True) plot_stimuli(stims, mask=False) diff --git a/stimuli/utils/README.md b/stimupy/utils/README.md similarity index 100% rename from stimuli/utils/README.md rename to stimupy/utils/README.md diff --git a/stimuli/utils/__init__.py b/stimupy/utils/__init__.py similarity index 100% rename from stimuli/utils/__init__.py rename to stimupy/utils/__init__.py diff --git a/stimuli/utils/color_conversions.py b/stimupy/utils/color_conversions.py similarity index 100% rename from stimuli/utils/color_conversions.py rename to stimupy/utils/color_conversions.py diff --git a/stimuli/utils/contrast_conversions.py b/stimupy/utils/contrast_conversions.py similarity index 94% rename from stimuli/utils/contrast_conversions.py rename to stimupy/utils/contrast_conversions.py index a1b06e57..cd5f8e4d 100644 --- a/stimuli/utils/contrast_conversions.py +++ b/stimupy/utils/contrast_conversions.py @@ -49,9 +49,9 @@ def adapt_michelson_contrast(stim, michelson_contrast, mean_luminance=None): # Adapt Michelson contrast img = (stim["img"] - stim["img"].min()) / (stim["img"].max() - stim["img"].min()) - img = (img * michelson_contrast * 2.0 * mean_luminance) + img = img * michelson_contrast * 2.0 * mean_luminance img += mean_luminance - michelson_contrast * mean_luminance - + stim["img"] = img stim["michelson_contrast"] = michelson_contrast stim["mean_luminance"] = mean_luminance @@ -110,7 +110,7 @@ def adapt_normalized_rms_contrast(stim, rms_contrast, mean_luminance=None): mean_luminance = stim["img"].mean() img = stim["img"] - stim["img"].mean() - img = img / img.std() * rms_contrast*mean_luminance + mean_luminance + img = img / img.std() * rms_contrast * mean_luminance + mean_luminance stim["img"] = img stim["rms_contrast"] = rms_contrast @@ -118,7 +118,7 @@ def adapt_normalized_rms_contrast(stim, rms_contrast, mean_luminance=None): return stim -def adapt_intensity_range(stim, intensity_min=0., intensity_max=1.): +def adapt_intensity_range(stim, intensity_min=0.0, intensity_max=1.0): """ Adapt intensity range of image @@ -136,7 +136,7 @@ def adapt_intensity_range(stim, intensity_min=0., intensity_max=1.): Updated stimulus dict with keys "img", "intensity_min" and "intensity_max" """ - + img = (stim["img"] - stim["img"].min()) / (stim["img"].max() - stim["img"].min()) img = img * (intensity_max - intensity_min) + intensity_min @@ -144,4 +144,3 @@ def adapt_intensity_range(stim, intensity_min=0., intensity_max=1.): stim["intensity_min"] = intensity_min stim["intensity_max"] = intensity_max return stim - diff --git a/stimuli/utils/export.py b/stimupy/utils/export.py similarity index 100% rename from stimuli/utils/export.py rename to stimupy/utils/export.py diff --git a/stimuli/utils/filter.py b/stimupy/utils/filter.py similarity index 100% rename from stimuli/utils/filter.py rename to stimupy/utils/filter.py diff --git a/stimuli/utils/masks.py b/stimupy/utils/masks.py similarity index 100% rename from stimuli/utils/masks.py rename to stimupy/utils/masks.py diff --git a/stimuli/utils/pad.py b/stimupy/utils/pad.py similarity index 63% rename from stimuli/utils/pad.py rename to stimupy/utils/pad.py index d3d861b5..804dc167 100644 --- a/stimuli/utils/pad.py +++ b/stimupy/utils/pad.py @@ -1,6 +1,7 @@ -import numpy as np import copy +import numpy as np + from . import resolution @@ -29,7 +30,7 @@ def pad_by_visual_size(img, padding, ppd, pad_value=0.0): See also --------- - stimuli.utils.resolution + stimupy.utils.resolution """ # Broadcast to ((before_1, after_1),...(before_N, after_N)) @@ -72,7 +73,7 @@ def pad_to_visual_size(img, visual_size, ppd, pad_value=0): See also --------- - stimuli.utils.resolution + stimupy.utils.resolution """ # visual_size to shape @@ -197,30 +198,32 @@ def pad_dict_by_visual_size(dct, padding, ppd, pad_value=0.0, keys=("img", "*mas See also --------- - stimuli.utils.resolution + stimupy.utils.resolution """ # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct) - + if isinstance(keys, str): keys = (keys,) # Find relevant keys - keys = [dkey for key in keys for dkey in dct.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - + keys = [ + dkey + for key in keys + for dkey in dct.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + for key in dct.keys(): - if key in keys: - img = dct[key] - if isinstance(img, np.ndarray): - if key.endswith("mask"): - img = pad_by_visual_size(img, padding, ppd, 0) - img = img.astype(int) - else: - img = pad_by_visual_size(img, padding, ppd, pad_value) - new_dict[key] = img + if key in keys: + img = dct[key] + if isinstance(img, np.ndarray): + if key.endswith("mask"): + img = pad_by_visual_size(img, padding, ppd, 0) + img = img.astype(int) + else: + img = pad_by_visual_size(img, padding, ppd, pad_value) + new_dict[key] = img return new_dict @@ -246,7 +249,7 @@ def pad_dict_to_visual_size(dct, visual_size, ppd, pad_value=0, keys=("img", "*m See also --------- - stimuli.utils.resolution + stimupy.utils.resolution """ # visual_size to shape @@ -280,29 +283,31 @@ def pad_dict_by_shape(dct, padding, pad_value=0, keys=("img", "*mask")): """ # Ensure padding is in integers padding = np.array(padding, dtype=np.int32) - + # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct) - + if isinstance(keys, str): keys = (keys,) # Find relevant keys - keys = [dkey for key in keys for dkey in dct.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - + keys = [ + dkey + for key in keys + for dkey in dct.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + for key in dct.keys(): - if key in keys: - img = dct[key] - if isinstance(img, np.ndarray): - if key.endswith("mask"): - img = np.pad(img, padding, mode="constant", constant_values=0) - img = img.astype(int) - else: - img = np.pad(img, padding, mode="constant", constant_values=pad_value) - new_dict[key] = img + if key in keys: + img = dct[key] + if isinstance(img, np.ndarray): + if key.endswith("mask"): + img = np.pad(img, padding, mode="constant", constant_values=0) + img = img.astype(int) + else: + img = np.pad(img, padding, mode="constant", constant_values=pad_value) + new_dict[key] = img return new_dict @@ -331,34 +336,36 @@ def pad_dict_to_shape(dct, shape, pad_value=0, keys=("img", "*mask")): """ # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct) - + if isinstance(keys, str): keys = (keys,) # Find relevant keys - keys = [dkey for key in keys for dkey in dct.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - + keys = [ + dkey + for key in keys + for dkey in dct.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + for key in dct.keys(): - if key in keys: - img = dct[key] - if isinstance(img, np.ndarray): - if np.any(img.shape > shape): - raise ValueError("img is bigger than size after padding") - - padding_per_axis = np.array(shape) - np.array(img.shape) - padding_before = padding_per_axis // 2 - padding_after = padding_per_axis - padding_before - padding = np.stack([padding_before, padding_after]).T - - if key.endswith("mask"): - img = pad_by_shape(img, padding=padding, pad_value=0) - img = img.astype(int) - else: - img = pad_by_shape(img, padding=padding, pad_value=pad_value) - new_dict[key] = img + if key in keys: + img = dct[key] + if isinstance(img, np.ndarray): + if np.any(img.shape > shape): + raise ValueError("img is bigger than size after padding") + + padding_per_axis = np.array(shape) - np.array(img.shape) + padding_before = padding_per_axis // 2 + padding_after = padding_per_axis - padding_before + padding = np.stack([padding_before, padding_after]).T + + if key.endswith("mask"): + img = pad_by_shape(img, padding=padding, pad_value=0) + img = img.astype(int) + else: + img = pad_by_shape(img, padding=padding, pad_value=pad_value) + new_dict[key] = img return new_dict @@ -382,28 +389,32 @@ def resize_dict(dct, factor, keys=("img", "*mask")): """ # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct) - + if isinstance(keys, str): keys = (keys,) # Find relevant keys - keys = [dkey for key in keys for dkey in dct.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - + keys = [ + dkey + for key in keys + for dkey in dct.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + for key in dct.keys(): - if key in keys: - img = dct[key] - if isinstance(img, np.ndarray): - img = np.repeat(np.repeat(img, factor[0], axis=0), factor[1], axis=1) - if key.endswith("mask"): - img = img.astype(int) - new_dict[key] = img + if key in keys: + img = dct[key] + if isinstance(img, np.ndarray): + img = np.repeat(np.repeat(img, factor[0], axis=0), factor[1], axis=1) + if key.endswith("mask"): + img = img.astype(int) + new_dict[key] = img return new_dict -def stack_dicts(dct1, dct2, direction="horizontal", keys=("img", "*mask"), keep_mask_indices=False): +def stack_dicts( + dct1, dct2, direction="horizontal", keys=("img", "*mask"), keep_mask_indices=False +): """ Return a dict with resized key-arrays by the given factor. Every value is repeated factor[d] times along dimension d. @@ -423,44 +434,48 @@ def stack_dicts(dct1, dct2, direction="horizontal", keys=("img", "*mask"), keep_ ------- dict with keys with stacked arrays """ - + # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct1) - + if isinstance(keys, str): keys = (keys,) # Find relevant keys - keys1 = [dkey for key in keys for dkey in dct1.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - keys2 = [dkey for key in keys for dkey in dct2.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] + keys1 = [ + dkey + for key in keys + for dkey in dct1.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + keys2 = [ + dkey + for key in keys + for dkey in dct2.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] if not keys1 == keys2: raise ValueError("The requested keys do not exist in both dicts") - + for key in dct1.keys(): - if key in keys1: - img1 = dct1[key] - img2 = dct2[key] - if isinstance(img1, np.ndarray) and isinstance(img2, np.ndarray): - if key.endswith("mask") and not keep_mask_indices: - img2 = np.where(img2 != 0, img2+img1.max(), 0) - - if direction == "horizontal": - img = np.hstack([img1, img2]) - elif direction == "vertical": - img = np.vstack([img1, img2]) - else: - raise ValueError("direction must be horizontal or vertical") - - if key.endswith("mask"): - img = img.astype(int) - new_dict[key] = img + if key in keys1: + img1 = dct1[key] + img2 = dct2[key] + if isinstance(img1, np.ndarray) and isinstance(img2, np.ndarray): + if key.endswith("mask") and not keep_mask_indices: + img2 = np.where(img2 != 0, img2 + img1.max(), 0) + + if direction == "horizontal": + img = np.hstack([img1, img2]) + elif direction == "vertical": + img = np.vstack([img1, img2]) + else: + raise ValueError("direction must be horizontal or vertical") + + if key.endswith("mask"): + img = img.astype(int) + new_dict[key] = img return new_dict @@ -481,28 +496,30 @@ def rotate_dict(dct, nrots=1, keys=("img", "*mask")): ------- dict with keys with rotated arrays """ - + # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct) # Find relevant keys - keys = [dkey for key in keys for dkey in dct.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - + keys = [ + dkey + for key in keys + for dkey in dct.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + for key in dct.keys(): - if key in keys: - img = dct[key] - if isinstance(img, np.ndarray): - if isinstance(nrots, (int, float)): - img = np.rot90(img, nrots) - else: - raise ValueError("nrots must be a number") - - if key.endswith("mask"): - img = img.astype(int) - new_dict[key] = img + if key in keys: + img = dct[key] + if isinstance(img, np.ndarray): + if isinstance(nrots, (int, float)): + img = np.rot90(img, nrots) + else: + raise ValueError("nrots must be a number") + + if key.endswith("mask"): + img = img.astype(int) + new_dict[key] = img return new_dict @@ -523,30 +540,32 @@ def flip_dict(dct, direction="lr", keys=("img", "*mask")): ------- dict with keys with rotated arrays """ - + # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct) # Find relevant keys - keys = [dkey for key in keys for dkey in dct.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - + keys = [ + dkey + for key in keys + for dkey in dct.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + for key in dct.keys(): - if key in keys: - img = dct[key] - if isinstance(img, np.ndarray): - if direction == "lr": - img = np.fliplr(img) - elif direction == "ud": - img = np.flipud(img) - else: - raise ValueError("direction must be lr or ud") - - if key.endswith("mask"): - img = img.astype(int) - new_dict[key] = img + if key in keys: + img = dct[key] + if isinstance(img, np.ndarray): + if direction == "lr": + img = np.fliplr(img) + elif direction == "ud": + img = np.flipud(img) + else: + raise ValueError("direction must be lr or ud") + + if key.endswith("mask"): + img = img.astype(int) + new_dict[key] = img return new_dict @@ -569,24 +588,26 @@ def roll_dict(dct, shift, axes, keys=("img", "*mask")): ------- dict with keys with rolled arrays """ - + # Create deepcopy to not override existing dict new_dict = copy.deepcopy(dct) shift = np.array(shift).astype(int) # Find relevant keys - keys = [dkey for key in keys for dkey in dct.keys() if ((dkey == key) or - ((dkey.endswith(key[1::])) and - (key.startswith("*"))) - )] - + keys = [ + dkey + for key in keys + for dkey in dct.keys() + if ((dkey == key) or ((dkey.endswith(key[1::])) and (key.startswith("*")))) + ] + for key in dct.keys(): - if key in keys: - img = dct[key] - if isinstance(img, np.ndarray): - img = np.roll(img, shift=shift, axis=axes) - - if key.endswith("mask"): - img = img.astype(int) - new_dict[key] = img + if key in keys: + img = dct[key] + if isinstance(img, np.ndarray): + img = np.roll(img, shift=shift, axis=axes) + + if key.endswith("mask"): + img = img.astype(int) + new_dict[key] = img return new_dict diff --git a/stimuli/utils/plotting.py b/stimupy/utils/plotting.py similarity index 99% rename from stimuli/utils/plotting.py rename to stimupy/utils/plotting.py index a145bb9f..5f563b9d 100644 --- a/stimuli/utils/plotting.py +++ b/stimupy/utils/plotting.py @@ -53,7 +53,7 @@ def plot_stim( mask = np.dstack([mask, mask, mask]) if np.unique(mask).size >= 20: - colormap = plt.cm.colors.ListedColormap(np.random.rand(mask.max()+1,3)) + colormap = plt.cm.colors.ListedColormap(np.random.rand(mask.max() + 1, 3)) elif np.unique(mask).size > 10 and np.unique(mask).size < 20: colormap = plt.cm.tab20 else: diff --git a/stimuli/utils/resolution.py b/stimupy/utils/resolution.py similarity index 98% rename from stimuli/utils/resolution.py rename to stimupy/utils/resolution.py index 42b3d3c1..e79648bb 100644 --- a/stimuli/utils/resolution.py +++ b/stimupy/utils/resolution.py @@ -113,7 +113,10 @@ def resolve_1D(length=None, visual_angle=None, ppd=None, round=True): visual_angle_old = np.round(copy.deepcopy(visual_angle), 10) visual_angle = np.floor(np.round(visual_angle * ppd, 10)) / ppd if visual_angle_old != visual_angle: - warnings.warn(f"Rounding visual angle because of ppd; {visual_angle_old} -> {visual_angle}") + warnings.warn( + f"Rounding visual angle because of ppd; {visual_angle_old} ->" + f" {visual_angle}" + ) length = pix_from_visual_angle_ppd_1D(visual_angle=visual_angle, ppd=ppd, round=round) elif visual_angle is None: visual_angle = visual_angle_from_length_ppd_1D(length=length, ppd=ppd) diff --git a/stimuli/utils/utils.py b/stimupy/utils/utils.py similarity index 100% rename from stimuli/utils/utils.py rename to stimupy/utils/utils.py diff --git a/tests/papers/gen_ground_truth.py b/tests/papers/gen_ground_truth.py index ff4f9b8d..f224c7d2 100644 --- a/tests/papers/gen_ground_truth.py +++ b/tests/papers/gen_ground_truth.py @@ -1,8 +1,8 @@ from os.path import abspath, dirname -from stimuli.papers import * -from stimuli.papers import __all__ as papers -from stimuli.utils import export +from stimupy.papers import * +from stimupy.papers import __all__ as papers +from stimupy.utils import export d = dirname(abspath(__file__)) diff --git a/tests/papers/test_RHS2007.py b/tests/papers/test_RHS2007.py index 405c853d..fca7b606 100644 --- a/tests/papers/test_RHS2007.py +++ b/tests/papers/test_RHS2007.py @@ -3,9 +3,9 @@ import pytest -import stimuli.papers.RHS2007 -from stimuli.papers.RHS2007 import __all__ as stimlist -from stimuli.utils import export +import stimupy.papers.RHS2007 +from stimupy.papers.RHS2007 import __all__ as stimlist +from stimupy.utils import export data_dir = os.path.dirname(__file__) jsonfile = os.path.join(data_dir, "RHS2007.json") @@ -14,7 +14,7 @@ @pytest.mark.parametrize("stim_name", stimlist) def test_stim(stim_name): - func = getattr(stimuli.papers.RHS2007, stim_name) + func = getattr(stimupy.papers.RHS2007, stim_name) stim = export.arrs_to_checksum(func(), keys=["img", "target_mask"]) assert stim["img"] == loaded[stim_name]["img"], "imgs are different" assert stim["target_mask"] == loaded[stim_name]["mask"], "masks are different" diff --git a/tests/papers/test_carney1999.py b/tests/papers/test_carney1999.py index dd34472c..3210d4cd 100644 --- a/tests/papers/test_carney1999.py +++ b/tests/papers/test_carney1999.py @@ -3,9 +3,9 @@ import pytest -import stimuli.papers.carney1999 -from stimuli.papers.carney1999 import __all__ as stimlist -from stimuli.utils import export +import stimupy.papers.carney1999 +from stimupy.papers.carney1999 import __all__ as stimlist +from stimupy.utils import export data_dir = os.path.dirname(__file__) jsonfile = os.path.join(data_dir, "carney1999.json") @@ -14,6 +14,6 @@ @pytest.mark.parametrize("stim_name", stimlist) def test_stim(stim_name): - func = getattr(stimuli.papers.carney1999, stim_name) + func = getattr(stimupy.papers.carney1999, stim_name) stim = export.arrs_to_checksum(func(), keys=["img"]) assert stim["img"] == loaded[stim_name]["img"], "imgs are different" diff --git a/tests/papers/test_domijan2015.py b/tests/papers/test_domijan2015.py index 8c9c01f8..1913b9bd 100644 --- a/tests/papers/test_domijan2015.py +++ b/tests/papers/test_domijan2015.py @@ -5,9 +5,9 @@ import numpy as np import pytest -import stimuli.papers.domijan2015 -from stimuli.papers.domijan2015 import __all__ as stimlist -from stimuli.utils import export +import stimupy.papers.domijan2015 +from stimupy.papers.domijan2015 import __all__ as stimlist +from stimupy.utils import export data_dir = os.path.dirname(__file__) jsonfile = os.path.join(data_dir, "domijan2015.json") @@ -16,7 +16,7 @@ @pytest.mark.parametrize("stim_name", stimlist) def test_stim(stim_name): - func = getattr(stimuli.papers.domijan2015, stim_name) + func = getattr(stimupy.papers.domijan2015, stim_name) stim = export.arrs_to_checksum(func(), keys=["img", "target_mask"]) assert stim["img"] == loaded[stim_name]["img"], "imgs are different" assert stim["target_mask"] == loaded[stim_name]["mask"], "masks are different" @@ -24,9 +24,9 @@ def test_stim(stim_name): @pytest.mark.parametrize("stim_name, ppd", product(stimlist, (8, 10, 12, 13, 15, 20))) def test_ppd(stim_name, ppd): - func = getattr(stimuli.papers.domijan2015, stim_name) + func = getattr(stimupy.papers.domijan2015, stim_name) stim = func(ppd=ppd, shape=None) - reshape = ppd / stimuli.papers.domijan2015.PPD + reshape = ppd / stimupy.papers.domijan2015.PPD target_shape = np.array(stim["original_shape"]) * reshape assert np.allclose(target_shape, stim["img"].shape, rtol=1, atol=(2 * (reshape % 1))) diff --git a/tests/papers/test_murray2020.py b/tests/papers/test_murray2020.py index 5c84e23b..e71b32f8 100644 --- a/tests/papers/test_murray2020.py +++ b/tests/papers/test_murray2020.py @@ -2,9 +2,10 @@ import os.path import pytest -import stimuli.papers.murray2020 -from stimuli.papers.murray2020 import __all__ as stimlist -from stimuli.utils import export + +import stimupy.papers.murray2020 +from stimupy.papers.murray2020 import __all__ as stimlist +from stimupy.utils import export data_dir = os.path.dirname(__file__) jsonfile = os.path.join(data_dir, "murray2020.json") @@ -13,7 +14,7 @@ @pytest.mark.parametrize("stim_name", stimlist) def test_checksum(stim_name): - func = getattr(stimuli.papers.murray2020, stim_name) + func = getattr(stimupy.papers.murray2020, stim_name) stim = export.arrs_to_checksum(func(), keys=["img", "target_mask"]) assert stim["img"] == loaded[stim_name]["img"], "imgs are different" assert stim["target_mask"] == loaded[stim_name]["mask"], "masks are different" @@ -21,7 +22,7 @@ def test_checksum(stim_name): @pytest.mark.parametrize("stim_name", stimlist) def test_normalization(stim_name): - func = getattr(stimuli.papers.murray2020, stim_name) + func = getattr(stimupy.papers.murray2020, stim_name) stim = func() assert stim["img"].min() == 0, "img minimum is not 0" assert stim["img"].max() == 1, "img max is not 1" diff --git a/tests/papers/test_white1981.py b/tests/papers/test_white1981.py index c2d75eb0..e5b585e8 100644 --- a/tests/papers/test_white1981.py +++ b/tests/papers/test_white1981.py @@ -3,9 +3,9 @@ import pytest -import stimuli.papers.white1981 -from stimuli.papers.white1981 import __all__ as stimlist -from stimuli.utils import export +import stimupy.papers.white1981 +from stimupy.papers.white1981 import __all__ as stimlist +from stimupy.utils import export data_dir = os.path.dirname(__file__) jsonfile = os.path.join(data_dir, "white1981.json") @@ -14,7 +14,7 @@ @pytest.mark.parametrize("stim_name", stimlist) def test_stim(stim_name): - func = getattr(stimuli.papers.white1981, stim_name) + func = getattr(stimupy.papers.white1981, stim_name) stim = export.arrs_to_checksum(func(), keys=["img", "target_mask"]) assert stim["img"] == loaded[stim_name]["img"], "imgs are different" assert stim["target_mask"] == loaded[stim_name]["mask"], "masks are different" diff --git a/tests/papers/test_white1985.py b/tests/papers/test_white1985.py index 4d985eed..9892cb0c 100644 --- a/tests/papers/test_white1985.py +++ b/tests/papers/test_white1985.py @@ -3,9 +3,9 @@ import pytest -import stimuli.papers.white1985 -from stimuli.papers.white1985 import __all__ as stimlist -from stimuli.utils import export +import stimupy.papers.white1985 +from stimupy.papers.white1985 import __all__ as stimlist +from stimupy.utils import export data_dir = os.path.dirname(__file__) jsonfile = os.path.join(data_dir, "white1985.json") @@ -14,7 +14,7 @@ @pytest.mark.parametrize("stim_name", stimlist) def test_stim(stim_name): - func = getattr(stimuli.papers.white1985, stim_name) + func = getattr(stimupy.papers.white1985, stim_name) stim = export.arrs_to_checksum(func(), keys=["img", "target_mask"]) assert stim["img"] == loaded[stim_name]["img"], "imgs are different" assert stim["target_mask"] == loaded[stim_name]["mask"], "masks are different" diff --git a/tests/test_circular.py b/tests/test_circular.py index ab67c23b..b7851bfd 100644 --- a/tests/test_circular.py +++ b/tests/test_circular.py @@ -1,6 +1,6 @@ import pytest -from stimuli.components.circulars import resolve_circular_params +from stimupy.components.circulars import resolve_circular_params @pytest.mark.parametrize( diff --git a/tests/test_gratings.py b/tests/test_gratings.py index d5a43ea7..1c3be937 100644 --- a/tests/test_gratings.py +++ b/tests/test_gratings.py @@ -1,6 +1,6 @@ import pytest -from stimuli.components.gratings import resolve_grating_params, square_wave +from stimupy.components.gratings import resolve_grating_params, square_wave @pytest.mark.parametrize( diff --git a/tests/test_overviews.py b/tests/test_overviews.py index 9caae13a..4c164145 100644 --- a/tests/test_overviews.py +++ b/tests/test_overviews.py @@ -1,4 +1,4 @@ -from stimuli import components, illusions, noises +from stimupy import components, illusions, noises def test_components(): diff --git a/tests/test_pad.py b/tests/test_pad.py index a962cc61..a487a0cb 100644 --- a/tests/test_pad.py +++ b/tests/test_pad.py @@ -1,7 +1,7 @@ import numpy as np import pytest -from stimuli.utils import pad, resolution +from stimupy.utils import pad, resolution @pytest.mark.parametrize( diff --git a/tests/test_resolve_resolutions.py b/tests/test_resolve_resolutions.py index d690cac4..f918f609 100644 --- a/tests/test_resolve_resolutions.py +++ b/tests/test_resolve_resolutions.py @@ -25,7 +25,8 @@ If the function under testing raises this specified exception, the test passes. """ import pytest -from stimuli.utils import resolution + +from stimupy.utils import resolution ############################# diff --git a/tests/test_valid_resolution.py b/tests/test_valid_resolution.py index 7746eed2..4428176b 100644 --- a/tests/test_valid_resolution.py +++ b/tests/test_valid_resolution.py @@ -16,7 +16,8 @@ and the function should raise a ResolutionError for each of these. """ import pytest -from stimuli.utils import resolution + +from stimupy.utils import resolution @pytest.mark.parametrize( diff --git a/tests/test_validate_resolution_components.py b/tests/test_validate_resolution_components.py index f411d54b..4712f5b5 100644 --- a/tests/test_validate_resolution_components.py +++ b/tests/test_validate_resolution_components.py @@ -24,7 +24,7 @@ import pytest -from stimuli.utils import resolution +from stimupy.utils import resolution #############################