Is there a faster way to load an ImageTK Object while also taking less memory?

Viewed 33

So, I'm developing a Tkinter application using Python 3.9.7 in the newest version of Spyder.

TL;DR -> Jump to the end of this question and read the "My questions are:", although in order to properly understand them, it is necessary to understand how I'm currently opening my images and displaying them as ImageTk objects.

from tkinter import *
from PIL import ImageTk, Image
import pickle

In order to display images on my tkinter canvases, I make use of the following method:

  1. All of my images are stored as _io.BytesIO Archives inside several pickle DAT archives.
  2. Those _io.BytesIO objects are opened using Image.open(b_obj) # b_obj is the _io.BytesIO Object.
  3. After the conversion into PIL images through the 2nd step, all images are stored in a single python dictionary, called "anidict". The keys of anidict are named in a standardized faction, accordingly to the screen that the images are supposed to be disposed.
    > Example: anidict['PIL_mainscreen_background'] leads to a list gathering the PIL images related to the background animation of the mainscreen.
  4. Finally, all PIL images are converted into Tkinter Images using the ImageTk.PhotoImage method, then stored inside a temporary dictionary, and finally stored inside anidict, now with keys that are standardized with "ImageTK" prefix.
    > anidict['PIL_mainscreen_background'] will still exist, but now anidict['ImageTK_mainscreen_background'] will also be a key of anidict, leading to the collection of ImageTKs related to the background of the mainscreen.

In order for the animations to show properly, I have a single thread that is always active until the finalization of the program. This thread is composed of a function with several if-elif statements, each checking global boolean variables that change accordingly to which screen the user is seeing by the moment.

def image_update_thread():
    if is_seeing_mainscreen:
        # Update of a single frame of the mainscreen background animation
        canvas_mainscreen.itemconfigure(ms_image, image= anidict['ImageTK_mainscreen_background'][next_image_index]) #ms_image is the image object of this particular canvas, which is constantly itemconfigured. It has been defined previously as ms_image = canvas_mainscreen.create_image(x_position, y_position, image= anidict['ImageTK_mainscreen_background'][0], anchor='nw')
    elif is_seeing_screen2:
        # Update a single frame from another animation
    ...
    ...
    time.sleep(0.1) 
    image_update_thread() # Calls this function again and again, until termination.

The problem I'm facing is: as the application grows in size, the amount of images that I'm using also increases, and is currently at ~800 individual images being opened and converted to ImageTks. This is causing the memory usage to go wildly up (as far as taking ~2.200MB of RAM), while also increasing the time needed for the application to initiate (note: I've set the application so it only opens after all images load).

My questions are: is there a faster way to do these same processes? How can I deal with the increased memory usage?

Note: I've considered using spritesheets to reduce the amount of individual images being loaded, but eventually, as the application grows, the amount of images tends to reach the same point again, so I'm seeking for a way to improve the coding, and not how the artwork is delivered.

0 Answers
Related