{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Generating a drifting sinusoidal grating or drifting bar stimulus\n\n*This example shows how to use drifting psychophysics-based stimuli for a retinal implant.*\n\nAlong with images, videos, and oter built-in stimuli, pulse2percept supports\ngenerating :py:class:`~pulse2percept.stimuli.GratingStimulus` and :py:class:`~pulse2percept.stimuli.BarStimulus` as stimuli\nthat can be passed as percepts to implants.\n\n## Creating a Stimulus\n\nFirst, create the stimuli:\n\n\nShape (`(height, width)` in pixels) is the only required parameter for creating these stimuli.\n\nA drifting sinusoidal grating is represented by :py:class:`~pulse2percept.stimuli.GratingStimulus`.\nThe following illustrates one frame of a grating stimulus.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\nfrom pulse2percept.stimuli import GratingStimulus\nstim = GratingStimulus((50, 50), spatial_freq=0.1, temporal_freq=0.1)\nplt.imshow(stim.data[:, 0].reshape(50, 50), cmap='gray')\nplt.title(\"Grating Stimulus\")\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "You can view the entire stimulus over time using `stim.play()`\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "stim.play()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Here, the spatial frequency of the grating (i.e., the inverse of how many pixels it\ntakes to represent one cycle of the sinusoid) is given as 0.1 cycles/pixel, whereas\nthe temporal frequency (i.e., the inverse of how many frames it takes to represent\none cycle of the sinusoid) is given as 0.1 cycles/frame.\nBy default, the drift direction of the grating will be to the right (0 degrees).\n\nA drifting bar is represented by :py:class:`~pulse2percept.stimuli.BarStimulus`.\nA visual example of a basic sinusoidal grating can be generated as such:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pulse2percept.stimuli.psychophysics import BarStimulus\nstim = BarStimulus((50, 50), speed=1)\nstim.play()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Here, the drift speed of the bar is given as 1 pixel/frame and, by default, the\nbar will drift to the right (0 degrees).\n\n## Customizing the Stimulus\n\nFor both :py:class:`~pulse2percept.stimuli.BarStimulus` and \n:py:class:`~pulse2percept.stimuli.GratingStimulus`,\nthe only argument you must pass to the constructor is the shape `(height, width)` \nin pixels.\nThere are many optional arguments that can be passed to change various attributes \nof the stimulus.\nIn the examples above, we changed the speed at which the GratingStimulus changed \nwith temporal_freq *(scalar, cycles/frame)* \nand the speed at which the BarStimulus moved with speed *(scalar, pixels/frame)* \nin order to make the effect easier to visualize.\nIf, for example, we wanted a longer stimulus, we cold set the time parameter \n(in units of milliseconds)\nto change the duration of the stimulus:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "stim = BarStimulus((50, 50), speed=1, time=1500)\nstim.play()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can also change the direction *(scalar in [0, 360) degrees)*, where 0 degrees\nrepresents rightward motion, 90 degrees represents upward motion, 180 degrees\nrepresents leftward motion, and 270 degrees represents downward motion.\n\n.. code:: python\n\n    BarStimulus((height, width), direction=direction)\n\nOr the contrast *(scalar in [0,1])*\n\n.. code:: python\n\n    BarStimulus((height, width), contrast=contrast)\n\nFor exact info on all of the arguments, please refer to\n:py:class:`~pulse2percept.stimuli.BarStimulus` and \n:py:class:`~pulse2percept.stimuli.GratingStimulus`.\n\n\n## Passing to an Implant\n\nPsychophsyics stimuli can be passed to an implant and combined with a model.\nTo demonstrate, we will pass a ``GratingStimululus`` to an\n:py:class:`~pulse2percept.implants.ArgusII` implant and use the\n:py:class:`~pulse2percept.models.AxonMapModel` [Beyeler2019]_ to interpret it:\n\n.. important ::\n\n  Don't forget to build the model before using ``predict_percept``\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pulse2percept.implants import ArgusII\nfrom pulse2percept.models import AxonMapModel\n\nmodel = AxonMapModel()\nmodel.build()\n\nimplant = ArgusII()\nimplant.stim = GratingStimulus((25,25), temporal_freq=0.1)\n\npercept = model.predict_percept(implant)\npercept.play()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "As you can see in the above code segment, the stimulus passed to the implant does\nnot necessarily have to have the same dimensions as the electrode grid.\nThis is functionality built in to the implant code: The implant will automatically\nrescale the stimulus to the appropriate size.\nIn the case of Argus II, the stimulus would thus be downscaled to a 6x10 image.\n\n## Pre-Processing Stimuli\n\nSince both :py:class:`~pulse2percept.stimuli.BarStimulus` and \n:py:class:`~pulse2percept.stimuli.GratingStimulus`\ninherit form :py:class:`~pulse2percept.stimuli.VideoStimulus`, we can apply \nany video processing methods provided by ``VideoStimulus``.\n\nIn the following example, we will invert the stimulus before passing it to the\nimplant:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "model = AxonMapModel()\nmodel.build()\n\nimplant = ArgusII()\nimplant.stim = GratingStimulus((25,25), temporal_freq=0.1).invert()\n\npercept = model.predict_percept(implant)\npercept.play()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "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.9"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}