This would be the easiest way to do so if using the vanilla PsychoPy API (and likely to get the fewest complaints from a reviewer since the blur algorithm is well defined somewhere). However, it might not be fast enough for real-time (per-frame) rendering without doing something clever, like caching images for each frame in video memory ahead of time. This is something that can be done from the “coder” side of things with some OpenGL calls, you might be able to get “builder” to run this code somehow. The example below shows generally how to do this. However, it’s upto you to make it work within your experiment.
Before we draw anything, we want to load our image, blur it using some library and then upload it to video memory.
from PIL import Image
import ctypes
import numpy as np
import pyglet.gl as GL
# load image
imageFile = "/path/to/image/file.png"
im = Image.open(imageFile)
im = im.transpose(Image.FLIP_TOP_BOTTOM)
im = im.convert("RGBA")
pixelData = np.array(im) # convert to numpy array
imageCache = [] # blurred versions of a single image
blurLevels = [0.0, 1.0, ... ] # blur levels or whatever
# image stimulus
stim = visual.ImageStim(win, .....)
for blurLevel in blurLevels :
# run whatever blur function here on a copy of the image array
imageBlurred = blurFunction(pixelData.copy(), blurLevel)
# load the image to video memory for fast access, this is from
# https://discourse.psychopy.org/t/imagestim-texture-handle-swapping/906
thisID =GL.GLuint()
GL.glGenTextures(1, ctypes.byref(thisID)) # just creates the id
#create the actual texture
stim._createTexture(
imageBlurred, # or imageBlurred.ctypes?
stim=stim,
pixFormat=GL.GL_RGBA,
dataType=GL.GL_FLOAT,
forcePOW2=False)
imageCache.append(thisID)
Within the main loop of your experiment, we tell ImageStim to reference the image data stored in video memory. We stored these references in the imageCache
array. So each frame we select the next image ID imageToUseThisFrame
which points to an image with a different amount of blur.
# to change the image displayed by the ImageStim, this is done very quickly
texId = imageCache[imageToUseThisFrame]
stim._texID = texId
stim. _needUpdate = True # so the stimulus is updated during draw
stim.draw()
When done, call this to free video memory. You might need to call this during your experiment if you are running out of video memory. This will require you to reload and blur images between trials. All the IDs in imageCache
are invalided after this call.
for i in imageCache:
GL.glDeleteTextures(1, i)
# clear the array since the IDs are now invalid and will cause errors if used
imageCache = []
Keep in mind video memory can quickly run out if you have lots of images.