ElementArrayStim - updating and passing lists of parameters / stimuli?

Hello all, quite new to Python / Psychopy and coming from MATLAB and PTB. I’ve been using “ElementArrayStim” from “psychopy.visual”to draw multiple instances of the same stimulus to the display. Here’s an example of the initial assignment of stimuli called “disp”.

disp = visual.ElementArrayStim(
    win=win,
    units="pix",
    nElements = s_size,       # i.e., s_size = 12
    elementTex = T1,          # T1 is a 256 * 256 * 3 numpy array                             
    elementMask="None",
    xys  = disp_xys,          # disp_xys is a s_size * 2 numpy array              
    oris = disp_ori,          # disp_ori is a s_size * 1 numpy array 
    sizes=100
) 

In this example, “disp.draw()” blits T1 at 12 xys with 12 oris values.

I can update certain elements of “disp”on a trial-by-trial basis using “.value” syntax (e.g., .elementTex, .xys and .oris). However, if I try to change .nElements (and the corresponding .xys and .oris values), drawing crashes. I can get around this by re-initialising “disp” in the trial loop, but this is awkward and seems to defeat the intended functionality.

Does anyone know if there’s a way to update the number elements on a trial-by-trial basis?

Ideally, I’d also like to pass a list of arrays to .elementTex. This would be useful because I could specify displays with and without a target on each trial prior to drawing, using something like:

            disp.elementTex = [distractor] * set_size 
            disp.elementTex = [target] + [distractor] * set_size – 1

Using “[distractor] * set_size” produces a list of numpy arrays, but passing the list to .elementTex produces the error, "Couldn’t make sense of requested image.”

Thanks in advance,
Doug.

I’ve checked the source code and there is no way to change nElements once it has been set, but you shouldn’t need to. Instead you can set nElements to the maximum number of stimuli that will be shown in any trial and then hide the unwanted ones on each trial, by setting their opacities to zero. The disp.opacities method is able to set the opacity values of individual stimuli if it supplied it with an Nx1 array, where N = nElements.

You can’t manipulate the textures of individual stimuli within the array, they must all be the same. However, you can manipulate the colours of individual stimuli using the disp.colors method, again by supplying it with an Nx1 array of RGB values. This will allow you to have your target one colour and your distractors another.

It is important to note that, in the ElementArrayStim component, textures and colours interact with one another, since both the texture variable and the colour variable specify a set of RGB values. I don’t think they are supposed to interact, since ‘blendmode = avg’ is said to be the default and this should mean that colours with an opacity of 1 should not mix (https://www.psychopy.org/general/rendering.html#blendmode), but trial and error shows that they do.

This interaction is complicated. But since your stimuli have a distinct foreground area and background area (ImageStim with a numpy array having unexpected results), the situation becomes straightforward, provided that you set the background RGB values for your texture to mid-grey (128, 128, 128) and the foreground RGB values for your texture to white (255,255,255). This is because a texture value of 128 interacts with any colour value to yield 128, while a texture value of 255 interacts with any colour value to yield the original colour value. So, when your texture is a white shape on a mid-grey background and you set the colour of a particular stimulus to blue, you’ll get a blue shape on a mid-grey background. And if you set the colour of another stimulus to red, you’ll get a red shape on a mid-grey background.

If you want a different background colour (say, black), then things get more complicated. It becomes necessary to tailor your background texture value to suit your colour value, or vice versa. For example, to give a blue stimulus a black background, the background texture would have to be set to yellow. Since PsychoPy’s ElementArrayStim component cannot set the texture to be different for different stimuli within an array, this means that different stimuli CANNOT have different foreground colours and a uniform background colour unless they are on a mid-grey background.

To sum up, you can have a fixed number of stimuli; and a single texture value, with a white foreground on a mid-grey background, which does not need to change. Each trial, you can set the opacity values of all your stimuli to 1 or 0 depending on whether you want them to be visible; and set the colour values of all your stimuli to the colours you want them to be. For example, if you had 4 stimuli:

disp.colors = [(1.0, -1.0, -1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, 1.0), (1.0, 1.0, -1.0)]
disp.opacities = [1.0, 1.0, 0.0, 0.0]

Note that these are setter methods and they require values to be set for all stimuli, i.e. you must supply an Nx1 array. The way they are named, it looks as if you are addressing the opacity and colour attributes directly and could therefore change a subset of elements in the array, but this is not the case.

Hi Richard,

Thanks for the response. I hadn’t thought of using opacity that way but I’ve implemented the code and it works. It’s also useful to know I can set BG and FG to 0 and 1 respectively, and then use RGB to change individual stimulus colours. ImageStim colours seem to work differently to those in ElementArrayStim and gratingStim, and I get weird values for mid-grey and white using the former.

opacityList = np.zeros(n_obj)                   # set all to 0
opacityList[poss_loc[0:s_size]] = 1.0       # 1:s_size = 1
disp.opacities = opacityList                      # set attribute

Doug.