Display Image Later Based on Previous Choices

Hello,

I am assisting a student with their final year project, but am still a novice at coding. This is quite a big ask, so please bear with me.

I am using some help I previously requested, which can be found here: Saving responses to call images up again later

Essentially, the study consists of 3 blocks. There are 60 images which need to be shown at random. For the first block, they make a rating (1-5) on each image. In the second block, they make a different 1-5 rating, but on the same 60 images. These images are randomised, so will be shown at different points in each block. The student has set this bit up fine, but it’s the final block we’re stuck on.

Each image where they rate it 3 or higher in both blocks, needs to be saved and shown again in this final block. In the final block they make a choice between the saved image and the other 59 images. This block also needs to be randomised, so if they had 10 images saved it wouldn’t just work through image 1 vs others, and then image 2 vs others etc.

Currently I have a begin experiment routine that has 3 lists
savedImages1, savedImages2 and choiceImages

My thought is that as the images are randomised in the trials I will need to populated the first list with every image that is rated 3 or higher, the same with the second list, and then some code before the third block to check if an image is in both lists, add that image to choiceImages

I tried to use the code:
if trials1.response() >= 3:
savedImages1.append(images)

But I was met with the error that “response” is not an attribute, is this because they are using a slider? Is it because responses are string values and therefore the >= 3 wouldn’t work?

Once I am able to save based on responses, how would I go about doing the check and save for the third list? I believe I would need something similar to what I’ve written below.

for x in savedImages1:
for y in savedImages2:
if x == y:
choiceImages.append(x)

Then finally (hopefully) they are shown the final block. In this block they will be presented with the images saved to choiceImages on the left (one by one and at random) and then the remaining 59 images on the right. This is the part I am struggling most with in terms of visualising the code.

Should I have an excel sheet with 60 columns of 59 images (1 column for each image, then the 59 images being the 60 - that image) and some code to call the correct one? Or is there an easier method that I am not aware of?

I understand this is a big project and so close to the holidays. Any help would be fantastic.

Thank you

Jake

Hi Jdigg,

I believe there are three questions so I will answer each of them in turn:

1. How to get the response of a rating scale/slider scale:
Both the visual.RatingScale and the visual.Slider have a .getRating() method. So if trials1 is a rating scale/slider object, you get the response by writing trials1.getRating()

2. Getting the common images from block 1 and block two into choice images
The procedure that you mention is indeed correct. Python also contains a set data structure, which is similar to a list but can only contain unique elements and allows you to perform set operations. Your problem could also be expressed as finding the intersection between two sets, and you could solve this as follows:

choiceImages = list(set(savedImages1).intersection(savedImages2))

The only tricky thing here is that you are doing some back and forth transformations between the set and list data structures, but otherwise the results should be the same

3. Showing the final block
From what I understand each image in choiceImages is presented in turn, together with the remaining images on the right? I am assuming that this means that the remaining 59 images will need to be laid out in a grid? This means you need two things: the location where each image is going to be presented and the actual image you are going to present at teach location.

# This will create a list of ImageStim objects layed out in a 10 * 6 grid
# You can adjust the values in the range for setting the locations
# Since you only need 59 images this will include one unneeded one
image_stim_placeholders = []
for x in range(0, 100, 10):
   for y in range(110, 0, -20):
      im = visual.ImageStim(win, pos = (x, y))
      image_stim_placeholders.append(im)

To know which image you need to present on the right, you could again use these set operation:

right_images = list(set(all_images).difference(choiceImages[currentTrialIndex]))

This assumes that you have a list ‘all_images’ with each of the 60 images, so you can calculate the difference with the current choiceImages. Alternatively you could do this using list operations:

right_images = list(all_images)
right_images.remove(choiceImages[currentTrialIndex])

Finally, you can present the images by enumerating over this list and assigning the images to the image stim:

for i, image in right_images:
   image_stim_placeholders[i].im = image

Note that I am assuming that all_images and choiceImages contain strings of the filenames of the images that you want to present.

Let me know if you need any further assistance!

Good morning,

Firstly, thank you for taking the time to help. Your information about the getRating really helped me on my way to sorting the rest of the problem. Though the “set” data looked promising, I noticed it didn’t appear in the JS panel of the code box, and as this is going to be uploaded with Pavlovia at some point I wanted to avoid that mess and instead used the iteration.

For part 3, this is poor wording on my part. They will be shown the chosen images vs the other images but as a pair. So image 1 vs image 2, then image 2 vs image 4 etc. Hopefully that makes a bit more sense, as I have a very inelegant solution, which does create the needed lists, I just can’t get it to display the images as I get the message
" raise AttributeError(msg)
AttributeError: Couldn’t make sense of requested image."

As for my code:

In the begin routine I have:

savedImages1 = []
savedImages2 = []
choiceImages = []
labelsWhole = [this list contains all the image filenames]
imgL = []
imgR = []

Then I have the code previously stated to append the images to savedImages 1 and 2

During a break block I then have a fair bit of code. It is inelegant, but mostly works:

Firstly:

from itertools import cycle
import random

for x in savedImages1:
    for y in savedImages2:
        if x == y:
            choiceImages.append(x)

This creates my choice images for displaying on the left hand side. I then do:

for z in choiceImages:
    while choiceImages.count(z) < 60:
        choiceImages.append(z)

This is because of there being 60 images in total to compare against (including itself).

Next:

combinedList = list(zip(choiceImages, cycle(labelsWhole)))
combinedList = [list (elem) for elem in combinedList]
random.shuffle(combinedList)

So this is where the list of all the images comes in. This creates a list of image pairs [img1,img2][img1,…img60][img3,img1][1mg3…img60]. Turns them from tuples to a list, and then shuffled them around (so it doesn’t double up any pairings).

Finally:

for a,b in combinedList:
    if a !=b:
        imgL.append(a)
        imgR.append(b)

This remove the doubled instances e.g [img10,img10], then splits the shuffled pairs into the lists imgL and imgR. (This is giving a syntax error in the JS code box which I also need to fix).

Printing them shows the lists exist, but it isn’t showing the images in my choice block as per the above error. I have 2 image components, with the image set to $imgL and $imgR respectively, as well as “set every repeat”.

There is a loop around the choice block so it shows ever image, is this needed? Or will it run through the lists fully before ending?

Finally, will I need to add some code to end the routine, should the choiceImages remain empty?

Best wishes

Jake

Ahh! I see where I was going wrong. I had missed the last step to make them show up.

In my choice block, in begin routine I now have the code:

placeholdl = [image_left]
placeholdr = [image_right]

for i, img in enumerate(imgL):
    placeholdl[i].image = img

for i, img in enumerate(imgR):
    placeholdr[i].image = img

I am now getting the error:
IndexError: list index out of range

I have attached an image of my builder view

I feel like I’m super close but just can’t get these last steps to work.

placeholdl only has length 1.

If imgL has more than 1 item then placeholdl[i] is going to fail when it tries to address the second element.

I have added in

for p in placeholdl:
   while len(placeholdl) < len(imgL):
        placeholdl.append(p)

Which has finally got the images to show on the choice block. However, I think it’s loading every single image on top of each other, which causes a huge spike in lag, but then only shows 1 image pair. Responding also causes lag, which after waiting out, shows the same image pair, so I am guessing that it is loading all of the images at once, rather than as a loop would. My loop is set to a really high number, and I was going to escape it using code once I have this issues fixed.

I have 2 smaller issues to sort as well, should I add them here or create their own topics?

The first is that auto-JS isn’t translating the for loops.

The second is that the second rating scale isn’t saving any responses past the first, but is also not listing the images they responded to.

It’s more useful to create new threads for new issues.

If the auto translate isn’t working then you have an issue in your Python (either a syntax error or a format that doesn’t work online)

Thank you,

I’ve done just that for the saving issue.

I will leave the auto-translate for now, until I can get the experiment working.

Best wishes

Hi Jake,

I hope that you managed to find the solution you needed for your experiment! (if you did do be sure to mark the “solution” for future users).

Since this topic relates to branched experiments I am leaving this youtube video here that might be helpful to others following this topic in future.

Hope this is helpful,
Becca

Hello,

I think the part for trialReps is going to be helpful.

Still waiting on a solution. It just seems to take forever to load the images and I’m sure that it’s because instead of loading 1 of the images to the left and 1 to the right it’s loading all 59 to the left and all 59 to the right. Is there some debugging code I could apply to see how many it’s trying to load?

Hi There,

Ah my apologies I thought you had a solution! Here is a demo that should help with what you need. Images are presented and if an image is rated higher than 3 in the first phase it is presented later. conditionalImSlider [PsychoPy]

If this looks like what you need please do take the files from here ! Rebecca Hirst / conditional_images_demo · GitLab

Hope this helps,
Becca

Thank you.

That’s exactly what I needed. Using the $image_list[loopname.thisN] code in the stimuli display fixed the issue and we now have a working experiment!

Fabulous! so pleased to hear! happy experimenting!

Becca