psychopy.org | Reference | Downloads | Github

Fade an image to black instead of inverting colors

screen
color

#1

Hi everyone,

I’m trying to dynamically change the color of an image on the screen. To accomplish this, I’m using visual.ImageStim and changing the colors with stimulus.color = [r, g, b]. This works great for any color brighter than neutral grey: light colors fade out to grey. However, it does not work for any colors darker than neutral grey. When I try to make the whole image black, for example (stimulus.color = [-1, -1, -1]), the colors in the image are inverted instead of displaying as black.

The math of the color maps makes sense here: Multiply by a negative number to invert the color. However, the same outcome occurs regardless of whether I use the rgb or the rgb255 colormap.

Do you have any suggestions for how to dynamically recolor an image to a dark color?

I’m including a minimal working example here.

Thanks!
geoff

from psychopy import visual, core
import numpy as np

CS = 'rgb'  # ColorSpace
WHITE = [1, 1, 1]
LIGHT_GREY = [0.5, 0.5, 0.5]
GREY = [0, 0, 0]
BLACK = [-1, -1, -1]

## ---- Comment this section in to try a different colorspace
# CS = 'rgb255'  # ColorSpace
# WHITE = [255, 255, 255]
# LIGHT_GREY = [200, 200, 200]
# GREY = [128, 128, 128]
# BLACK = [0, 0, 0]

win = visual.Window([800, 800], monitor='testMonitor',
                    color=LIGHT_GREY, colorSpace=CS,
                    units='pix')

img = np.array([[-1, 0], [0, 1]]) # Image bitmap

stimulus = visual.ImageStim(win=win, image=img,
                     colorSpace=CS,
                     size=(100, 100),
                     units='pix')

# Show the normal stimulus
stimulus.color = WHITE
stimulus.draw()
win.flip()
core.wait(1.0)

# I want to show the stimulus faded to black.
# Instead, this inverts the stimulus, showing
# black areas as white and vice versa, leaving
# neutral grey areas unchanged.
stimulus.color = BLACK
stimulus.draw()
win.flip()
core.wait(1.0)

# This strategy does work for fading to neutral grey, however.
stimulus.color = GREY
stimulus.draw()
win.flip()
core.wait(1.0)

#2

Just realized I forgot to include my system info!

I’m running Psychopy 1.90.2 on Windows 10.

thanks,
geoff


#3

Hi, a quick hack would be to fade your image by performing operations on your numpy array, using np.clip to ensure all values are within range (i…e, -1, 1), and setting your image on every frame. There is probably a less computationally expensive way than setting image on every refresh.

Adding the following to your code…

for i in range(200):
    img = img-.01
    np.clip(img, -1, 1, img)
    stimulus.setImage(img)
    stimulus.draw()
    win.flip()

#4

Hi there,

Thanks for the suggestion – it sounds like that could work in this case! However, I’m hoping for a generalizable solution that will work for any kind of stimulus – Cirlce or TextStim in addition to ImageStim.

The ideal solution would be the ability to do exacly what the current code does, but in a way that’s mathematically consistent with a 0–255 color map instead of a -1–1 colormap. Is it possible to change the colormap at a lower level? It’s perplexing that the color computations act as if they’re using a -1–1 colormap even when you specify 0–255.

Thanks again!


#5

You could draw a black object behind and then dynamically update the foreground object’s opacity. For example:

from psychopy import visual, core
import numpy as np

CS = 'rgb'  # ColorSpace
WHITE = [1, 1, 1]
LIGHT_GREY = [0.5, 0.5, 0.5]
GREY = [0, 0, 0]
BLACK = [-1, -1, -1]

## ---- Comment this section in to try a different colorspace
# CS = 'rgb255'  # ColorSpace
# WHITE = [255, 255, 255]
# LIGHT_GREY = [200, 200, 200]
# GREY = [128, 128, 128]
# BLACK = [0, 0, 0]

win = visual.Window([800, 800], monitor='testMonitor',
                    color=LIGHT_GREY, colorSpace=CS,
                    units='pix')

img = np.array([[-1, 0], [0, 1]]) # Image bitmap

stimulus = visual.ImageStim(win=win, image=img,
                     colorSpace=CS,
                     size=(100, 100),
                     units='pix')

bg = visual.ImageStim(win=win, image=np.ones(img.shape) * -1.0,
                     colorSpace=CS,
                     size=(100, 100),
                     units='pix')

# Show the normal stimulus
stimulus.color = WHITE
stimulus.draw()
win.flip()
core.wait(1.0)

for fade_factor in np.linspace(0.0, 1.0, 60):
    bg.draw()
    stimulus.opacity = 1.0 - fade_factor
    stimulus.draw()
    win.flip()

core.wait(1.0)