My suggestion is to draw the stimulus, change the blend mode, draw the filter, and then reset the blend mode. If you change the blend mode to GL_DST_COLOR, GL_ZERO
, then the filter will be multiplied with the stimulus to produce the output (see this great blend mode helper site).
Here is an example:
from __future__ import division
from psychopy import core, visual, event
import numpy as np
import pyglet.gl
CS = 'rgb' # ColorSpace
WHITE = [1, 1, 1]
LIGHT_GREY = [0.5, 0.5, 0.5]
GREY = [0, 0, 0]
BLACK = [-1, -1, -1]
RED = [1, -1, -1]
BLUE = [-1, -1, 1]
GREEN = [-1, 1, -1]
def color_filter(img, rgb_filter):
""" Filter a greyscale image through an RGB color filter
img: a 2D numpy array
filter: a 3-number RGB sequence (-1)-(1)
"""
img_rgb = np.empty([img.shape[0], img.shape[1], 3])
rgb_filter = rescale_zero_min(rgb_filter)
img = rescale_zero_min(img)
for n_channel in range(3): # For each color channel
img_rgb[:,:,n_channel] = img * rgb_filter[n_channel]
return img_rgb
def rescale_zero_min(x):
""" Rescale a color from (-1)-(1) to 0-1.
"""
return (np.array(x) + 1) / 2
img = np.array([[-1, 0], [0, 1]]) # Image bitmap
win = visual.Window([400, 400], monitor='testMonitor',
color=LIGHT_GREY, colorSpace=CS,
useFBO=True,
units='pix')
stimulus = visual.ImageStim(win=win, image=img,
colorSpace=CS,
pos=(0, -100),
size=(100, 100),
units='pix')
new_stimulus = visual.ImageStim(win=win, image=img,
colorSpace=CS,
pos=(0, 100),
size=(100, 100),
units='pix')
img_filter = visual.ImageStim(win=win, image=np.ones((100, 100)),
colorSpace=CS,
pos=(0, 100),
size=(100, 100),
units='pix')
# Show the example stimuli
for color in (WHITE, LIGHT_GREY, GREY, BLACK, RED, BLUE, GREEN):
filtered_image = color_filter(img, color)
stimulus.setImage(filtered_image) ## <---- This is too slow
stimulus.draw()
new_stimulus.draw()
pyglet.gl.glBlendFunc(pyglet.gl.GL_DST_COLOR, pyglet.gl.GL_ZERO)
img_filter.color = color
img_filter.draw()
# reset the blend mode
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
win.flip()
frame = np.array(win.getMovieFrame())
assert np.all(frame[:200, :] == frame[200:, :])
event.waitKeys()