I have a short program that generates images of 1, 2, 3, and 4 random coloured circles to train a neural network on. My issue is, just doing 4000 images takes about 20-30 mins, and I need about 50000 images. My current method is to create the image, screenshot it, and then delete the tkinter object and restart. The issue is, windows 11 has a little fade in/slide animation when a new window is created, only about 200 ms, but it adds up quite a bit, because I need to wait for the animation to finish to take the screenshot.
So my question is, Is there another way to save a tkinter canvas other than screenshot?
I want to point out that I am putting these images into a numpy array, so putting it directly into an array is an option, but I need some way to save that as a file, so I dont need to generate the images every time.
My current code (only showing how I make 4 circles)
from PIL import ImageGrab from tkinter import * from random import choice, randint colors = ["red", "blue", "green", "yellow", "orange", "purple", "black"] s = 1 #scale 1 = 50x50 px canvas, 20x20 px circles def four(i): def ss(): x, y = screen.winfo_rootx(), screen.winfo_rooty() w, h = screen.winfo_width(), screen.winfo_height() img = ImageGrab.grab((x, y, x + w, y + h)) img.save(f"4MC{i}.jpg") def des(): root.destroy() root = Tk() screen = Canvas(root, width = 50 * s, height = 50 * s, bg="white") screen.pack() colors = ["red", "blue", "green", "yellow", "orange", "purple", "black"] x = randint(1 * s, 19 * s) y = randint(1 * s, 19 * s) screen.create_oval(x, y, x + 10 * s, y + 10 * s, fill=choice(colors), outline="") screen.create_oval(x, y + 20 * s, x + 10 * s, y + 30 * s, fill=choice(colors), outline="") screen.create_oval(x + 20 * s, y, x + 30 * s, y + 10 * s, fill=choice(colors), outline="") screen.create_oval(x + 20 * s, y + 20 * s, x + 30 * s, y + 30 * s, fill=choice(colors), outline="") root.after(200, ss) root.after(300, des) root.mainloop() for i in range(1000): four(i)
Advertisement
Answer
I think your approach of destroying and creating a new window over each iteration is tedious way of doing it. Instead, you can clear the canvas each time and keep creating this random circles and then click a picture of it.
from PIL import ImageGrab from tkinter import * from random import choice, randint root = Tk() colors = ["red", "blue", "green", "yellow", "orange", "purple", "black"] s = 1 #scale 1 = 50x50 px canvas, 20x20 px circles i = 1 # Variable for creating numbers in file-name: 1,2,3,... LIMIT = 10 # Variable to keep limit of how many iterations/image def create(i): if i <= LIMIT: screen.delete('all') x = randint(1 * s, 19 * s) y = randint(1 * s, 19 * s) screen.create_oval(x , y , x + 10 * s, y + 10 * s, fill=choice(colors), outline="") screen.create_oval(x , y + 20 * s, x + 10 * s, y + 30 * s, fill=choice(colors), outline="") screen.create_oval(x + 20 * s, y , x + 30 * s, y + 10 * s, fill=choice(colors), outline="") screen.create_oval(x + 20 * s, y + 20 * s, x + 30 * s, y + 30 * s, fill=choice(colors), outline="") if i == 1: # If it is first iteration, then the event loop hasnt been entered, so give a delay root.after(200, capture, screen, f'4MC{i}') else: # Give a general delay of 100ms before capturing the image root.after(100, capture, screen, f'4MC{i}') i += 1 root.after(300, create, i) # Give a delay of 300ms before creating the circle def capture(wid, file_name='img',file_format='png'): """Take screenshot of the passed widget""" x0 = wid.winfo_rootx() y0 = wid.winfo_rooty() x1 = x0 + wid.winfo_width() y1 = y0 + wid.winfo_height() im = ImageGrab.grab(bbox=(x0, y0, x1, y1)) # bbox means boundingbox, which is shown in the image below im.save(f'{file_name}.{file_format}') # Can also say im.show() to display it screen = Canvas(root, width = 50 * s, height = 50 * s, bg="white") screen.pack() create(i) root.mainloop()
I replaced your ss
with my capture
which is taken from another answer of mine, the logic is same but adds more flexibility, you can use your function with necessary changes. I suggest you first run this with a LIMIT = 10
and check if the delay are okay, if not you can adjust it and then move on to produce your 50000 image-set.
If you furthermore want to convert/load your image as a numpy
array, then check the first reference link below.
Extra references: