Preloading files#

It is generally a good idea to load your external stimuli (images, movies, sounds) into memory prior to presenting them. This helps ensure greater precision in display times. (One exception is if you are loading many 1000s of images in which case you may want to preload them in batches – this won’t be an issue for us).

One effective approach is to have all your stimuli in one directory (e.g., stimuli) and then just load all the files in that directory into memory at the start of the experiment. The trick is to load them in a way that allows you easy access.

import os
import glob

First, a few useful functions for managing directories and file paths. For more info, see help(os.path)

import os

print(os.getcwd()) #what's my current directory?
try:
    os.mkdir('data') #make a new subdirectory called data. If it already exists, raise a `FileExistsError`` 
except FileExistsError:
    print('already exists')
os.path.exists('data') #check if a path (a directory or filename) exists
print(os.path.join(os.getcwd(), 'stimuli/visual/')) #creating a longer path
os.path.basename('/stimuli/visual/lamp.jpg') # get just the filename part of a path
/Users/glupyan/gitRepos/psych750_resources/starter_code/activity_debugging_experiments
already exists
/Users/glupyan/gitRepos/psych750_resources/starter_code/activity_debugging_experiments/stimuli/visual/
'lamp.jpg'

Grabbing (globbing) all the files in a directory#

With these preliminaries out of the way, we can now combine them with os.glob() First, please clone this repository.

Now cd into it. Inside will be a copy of this notebook. Open it inside VSCode. This will ensure that the current working directory is the new repository.

The code below grabs all the files that meet your criteria Confused by the ‘*’ syntax? Those are wildcards – a key part of navigating around a file system – see more here if you need a refresher.

import os
import glob

all_pics = glob.glob('images/*png')
print(all_pics[0:10]) # printing just the first 10 for brevity
['images/duck.png', 'images/dog.png', 'images/spoon.png', 'images/horse.png', 'images/mushroom.png', 'images/doorknob.png', 'images/watch.png', 'images/nut.png', 'images/sled.png', 'images/rabbit.png']
pics_beginning_with_a = glob.glob('images/a*png')
print(pics_beginning_with_a)
['images/ashtray.png', 'images/arm.png', 'images/accordion.png', 'images/artichoke.png', 'images/arrow.png', 'images/ant.png', 'images/asparagus.png', 'images/apple.png', 'images/airplane.png', 'images/axe.png', 'images/anchor.png', 'images/alligator.png']

Now let’s get just the filename part of the path

just_names = [os.path.basename(file) for file in all_pics]
print(just_names)
['003mT_90_60.jpg', '004wF_90_60.jpg', '003mA_90_60.jpg', '001mW_90_60.jpg', '005mU_90_60.jpg', '003wN_90_60.jpg', '002mE_90_60.jpg', '005wW_90_60.jpg', '001wU_90_60.jpg', '002wT_90_60.jpg', '004wU_90_60.jpg', '002mN_90_60.jpg', '002wA_90_60.jpg', '005mF_90_60.jpg', '003wE_90_60.jpg', '004mW_90_60.jpg', '001wF_90_60.jpg', '005mA_90_60.jpg', '002wF_90_60.jpg', '003mU_90_60.jpg', '005wN_90_60.jpg', '005mT_90_60.jpg', '003wW_90_60.jpg', '001wA_90_60.jpg', '001mN_90_60.jpg', '004mE_90_60.jpg', '001wT_90_60.jpg', '002wU_90_60.jpg', '004mN_90_60.jpg', '003mF_90_60.jpg', '004wA_90_60.jpg', '004wT_90_60.jpg', '001mE_90_60.jpg', '002mW_90_60.jpg', '005wE_90_60.jpg', '001mU_90_60.jpg', '005mW_90_60.jpg', '002wE_90_60.jpg', '003mN_90_60.jpg', '005wU_90_60.jpg', '004mF_90_60.jpg', '001wW_90_60.jpg', '003wA_90_60.jpg', '003wT_90_60.jpg', '004wW_90_60.jpg', '001mF_90_60.jpg', '003mE_90_60.jpg', '002mA_90_60.jpg', '005wF_90_60.jpg', '002wN_90_60.jpg', '004mU_90_60.jpg', '002mT_90_60.jpg', '004wE_90_60.jpg', '001mT_90_60.jpg', '001wN_90_60.jpg', '001mA_90_60.jpg', '003mW_90_60.jpg', '005wT_90_60.jpg', '005mN_90_60.jpg', '003wU_90_60.jpg', '005wA_90_60.jpg', '002mF_90_60.jpg', '005mE_90_60.jpg', '002wW_90_60.jpg', '004mT_90_60.jpg', '001wE_90_60.jpg', '003wF_90_60.jpg', '004mA_90_60.jpg', '004wN_90_60.jpg', '002mU_90_60.jpg']

Preloading images into memory#

Now that we’ve seen how to grab a list of files, let’s use this to preload them into memory and then access the psychopy objects we’ve created. Below is a basic version of a useful helper function load_files() that does just that. A more flexible version with some bells and whistles (e.g., it works for both images and audio files) is available here.

import os
import glob
from psychopy import visual

def basic_load_files(directory,extension,win='',restriction='*'):
    """ Loads all the pictures (or narrowed by the restriction argumnt) in the provided directory.
    Need to pass in the Psychopy window (win) object so that it can be used for loading them in.
    Returns a dictionary with references to the loaded images
    """
    file_list = glob.glob(os.path.join(directory,restriction+extension))
    images = {} #initialize fileMatrix  as a dict because it'll be accessed by file names (picture names, sound names)
    for cur_file in file_list:
        stim_filename = os.path.splitext(os.path.basename(cur_file))[0] #just the filename without the extension
        stim = visual.ImageStim(win, image=cur_file,mask=None,interpolate=True)
        images[stim_filename] = stim
 
    return images

win = visual.Window([200,200],color="black", units='pix')
images = basic_load_files('images/','png',win=win)
2022-10-04 22:48:45.112 python[22343:1846768] Warning: Expected min height of view: (<NSPopoverTouchBarItemButton: 0x7f98320b2990>) to be less than or equal to 30 but got a height of 32.000000. This error will be logged once per view in violation.
2022-10-04 22:48:45.113 python[22343:1846768] Warning: Expected min height of view: (<NSButton: 0x7f98320e8ed0>) to be less than or equal to 30 but got a height of 32.000000. This error will be logged once per view in violation.
2022-10-04 22:48:45.116 python[22343:1846768] Warning: Expected min height of view: (<NSPopoverTouchBarItemButton: 0x7f98326681b0>) to be less than or equal to 30 but got a height of 32.000000. This error will be logged once per view in violation.
2022-10-04 22:48:45.118 python[22343:1846768] Warning: Expected min height of view: (<NSPopoverTouchBarItemButton: 0x7f9832681600>) to be less than or equal to 30 but got a height of 32.000000. This error will be logged once per view in violation.

Now to show an image, we can simply use the returned dictionary!

print(type(images['duck'])) # notice the type
images['duck'].draw()
<class 'psychopy.visual.image.ImageStim'>