psychopy.org | Reference | Downloads | Github

Blurred mask for RDK

Hi, all,

i have a RDK stimulus, which I would like to present with blurred edges, so that dots disappear gradually instead of abruptly when they move beyond the dot field’s area.

Drawing a grating with a gaussian mask (as suggested here ) does not solve it because I need the central region of the dot field to have maximum contrast, blurring only the edges.

I have also tried to draw an elementarrrayStim on top of the RDK, with opacity decreasing as a function of distance from the center, keeping opacity at zero in the central region. However, this quickly becomes very heavy to draw. Is there any way to do it inside Psychopy?

Thanks,

Alex.

Something like this might work, if you set the mask to raisedCos rather than gauss.

Hi,

thanks for the suggestion and sorry for the long time to respond. It took me a while to work this out.

I tried following the suggestion in the link, but the result was actually the opposite of what I was looking for: a mask with opacity decreasing with distance fro m the center. I am not sure why this happened. Below is my code, with pieces of the suggestion in the link:

from psychopy import visual
import numpy as np
import pyglet.gl

bg_col = -1.0

Screen = 0
FullScreen = False

# Monitor settings
Monitor = 'S22C150'
if Monitor == 'S22C150':
    screenRes = [1920,1080]
    screenX = 51.18
    screenY = 31.08
    pixDeg = screenRes[0]/screenX

#Setup window
win = visual.Window(screenRes, color = [bg_col] * 3, units='pix', allowGUI=False, fullscr=FullScreen, screen = Screen, monitor = Monitor)


# Create RDK and mask
dots = visual.DotStim(win, nDots = 500, coherence = 1, fieldPos=(0.0, 0.0), fieldSize= 14.7 * pixDeg, 
    fieldShape = 'circle', dotSize = 0.1 * pixDeg, dotLife = 5, dir=180, speed = 13.0/60 * pixDeg, color = 'white', 
    units = 'pix', opacity = 1.0, contrast = 1.0, depth = 0, signalDots = 'same', noiseDots = 'walk')

dotsMask = visual.GratingStim(win,mask= "raisedCos", tex = np.ones([256,256]) * bg_col, units = "pix", size= [256,256])


# Show masked stimulus
f = 0
while f < 240:
    dots.draw()
    
    pyglet.gl.glBlendFunc(
    pyglet.gl.GL_ONE_MINUS_SRC_ALPHA,
    pyglet.gl.GL_SRC_ALPHA
)

    dotsMask.draw()
    pyglet.gl.glBlendFunc(
    pyglet.gl.GL_SRC_ALPHA,
    pyglet.gl.GL_ONE_MINUS_SRC_ALPHA
)

    win.flip()
    f += 1

So I did some further search, and found an alternative solution in the old forum, which consists of creating a mask using visual.filters.makeMask, using raisedCosine instead of gaussian, multiplying it by -1 to invert it, and applying to a gratingStim to be drawn on top of the RDK. This worked well:

from psychopy import visual
import numpy as np

Screen = 0
FullScreen = False

# Monitor settings
Monitor = 'S22C150'
if Monitor == 'S22C150':
    screenRes = [1920,1080]
    screenX = 51.18
    screenY = 31.08
    pixDeg = screenRes[0]/screenX

#Setup window
win = visual.Window(screenRes, color="grey", units='pix', allowGUI=False, fullscr=FullScreen, screen = Screen, monitor = Monitor)

# Create RDK and mask
dots = visual.DotStim(win, nDots = 500, coherence = 1, fieldPos=(0.0, 0.0), fieldSize= 14.7 * pixDeg, 
    fieldShape = 'circle', dotSize = 0.1 * pixDeg, dotLife = 5, dir=180, speed = 13.0/60 * pixDeg, color = 'white', 
    units = 'pix', opacity = 1.0, contrast = 1.0, depth = 0, signalDots = 'same', noiseDots = 'walk')
    
#maskParams
visibleArea = (14.7/2) * pixDeg
zeroOpacArea = (4.9 + 0.5) * pixDeg
shadedArea = (visibleArea - zeroOpacArea)/visibleArea

# Create a raisedCosine mask array and assign it to a Grating stimulus (grey outside, transparent inside)
raisedCosTexture = visual.filters.makeMask(14.7 * pixDeg, shape= 'raisedCosine', fringeWidth= shadedArea, radius = [1.0, 1.0])
invRaisedCosTexture = -raisedCosTexture # inverts mask to blur edges instead of center
dotsMask = visual.GratingStim(win, mask = invRaisedCosTexture, tex=None, contrast= 1.0, size=(15.2 * pixDeg, 15.2 * pixDeg), color = 'grey')


f = 0
while f < 240:
    dots.draw()
    dotsMask.draw()
    win.flip()
    f += 1

However, I would still like to clarify why the solution suggested in the previous responses did not work, if possible.

Thanks for the help!

1 Like

That’s weird, it doesn’t work for me either - but works with an older version.

This is with 1.90.dev3:

blend_func_1_90_dev3

This is with 1.85.3:

blend_func_1_85_3.

The change seems to happen in between 1.85.3 and 1.85.4.

I am currently using version 1.90.0, so that makes sense. Should I mark my own answer as final solution?

Sure.

I’ve opened an issue for the problem with the blending approach.