Pattern reversal dartboard stimulus

Is it possible to design a circular dartboard (or checkerboard) stimulus like this:

I know there is RadialStim. But how do I define sections within this stimulus, such that each section reverse at a different rate (count by the number of frames), where reverse means turn from black to white and vice versa.

For this example, there are 12 sections across the angularCycles, and 4 across the radialPhase. A total of 48 sections, reversing at a different rate.

Can this be done on PsychoPy?

You could get something similar by using the mask and ori parameters to create a RadialStim for each of the 48 sections. The temporal contrast profile of each section can then be controlled independently.

A rough demo:

import numpy as np

import psychopy.visual
import psychopy.event


stim_diameter_pix = 512
stim_radius_pix = stim_diameter_pix // 2

n_angular_sections = 12
n_radial_sections = 4

angular_size_deg = 360 / n_angular_sections

n_angular_cycles_per_section = 2
n_angular_cycles = n_angular_cycles_per_section * n_angular_sections
angular_phase = 0.25

n_radial_cycles_per_section = 2
n_radial_cycles = n_radial_cycles_per_section * n_radial_sections

base_radial_mask = np.zeros(stim_radius_pix)
base_radial_mask[:(stim_radius_pix // n_radial_sections) - 2] = 1

empty_centre_radius_pix = 10

with psychopy.visual.Window(
    size=(600, 600), color=(-1,) * 3, units="pix", fullscr=False
) as win:

    sections = []

    for i_radial_section in range(n_radial_sections):

        radial_mask = np.zeros(stim_radius_pix)

        # shift the mask to the correct radial section
        radial_mask = np.roll(
            base_radial_mask,
            i_radial_section * stim_radius_pix // n_radial_sections,
        )

        # hide centre region
        radial_mask[:empty_centre_radius_pix] = 0.0

        for i_angular_section in range(n_angular_sections):

            sections.append(
                psychopy.visual.RadialStim(
                    win=win,
                    size=(stim_diameter_pix,) * 2,
                    units="pix",
                    visibleWedge=(0, angular_size_deg),
                    ori=i_angular_section * angular_size_deg,
                    angularCycles=n_angular_cycles,
                    angularPhase=angular_phase,
                    radialCycles=n_radial_cycles,
                    mask=radial_mask,
                )
            )

    for section in sections:

        # set as required for this section
        section.contrast = np.random.choice([-1, 1])

        section.draw()

    win.flip()

    psychopy.event.waitKeys()

1 Like

Thanks @djmannion for your codes, it shows that we can define the black and white squares within RadialStim.

I see that we can use mask parameter, and in your code is defined by base_radial_mask. For this demo stimulus, the values in base_radial_mask looks like this:

[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

There are 256 elements in this array (base_radial_mask), what does each element associates to actually? Since I have to reverse each section at a different rate, where reverse means turning black squares to white. Understanding how mask and base_radial_mask works might be the way to perform reversal.

You can do the reversal via section.contrast, rather than changing the mask. In the demo code, I am randomly reversing the contrast of each section. You would instead need to reverse the contrast of a given section according to its required rate.

Each element corresponds to a radial distance from the centre and specifies its visibility. The base_radial_mask defines the innermost section, and is then moved ‘outwards’ for each subsequent radial section.

1 Like