Fade an image to black instead of inverting colors

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()
1 Like