I tried implementing this but it still doesn't work
It asks me refer myself to the safe importing of main module, with:
if name == "main":
But even with what I tried it still wouldn't work and I kept getting errors
Here is the new code with the implementation of the answere (not sure i did it right) :
import multiprocessing
import tkinter as tk
from PIL import Image, ImageTk
import math, time
def process_tile(_x, _y, tile):
# (do processing on the tile)
pastTile = tile
def non_lerp(a: float, b: float, t: float) -> float:
"""Interpolation non linéaire entre a et b en fonction du temps t"""
return ((1 - t) * a)/1.5 + (t * b)/10
global lightposes
for x in range(_x): # on parcourt les pixels en colonne
for y in range(_y): # on parcourt les pixels en ligne
r = tile.getpixel((x, y))[0]
v = tile.getpixel((x, y))[1]
b = tile.getpixel((x, y))[2]
g = int((tile.getpixel((x,y))[0]+tile.getpixel((x,y))[1]+tile.getpixel((x,y))[2])/3)
total_light_intensites = []
if not(r == 0 and b == 0 and v == 0):
for i in range(len(lightposes)):
distance = math.sqrt((lightposes[i][0]-x)**2+(lightposes[i][1]-y)**2)
if distance <= lightposes[i][2]:
r+=lightposes[i][4][0]
v+=lightposes[i][4][1]
b+=lightposes[i][4][2]
pointlight_intensite = 0.0001
if((distance/lightposes[i][2])+(lightposes[i][3])<=1):
pointlight_intensite = non_lerp(1,0.1,(distance/lightposes[i][2])+(lightposes[i][3]))
total_light_intensites.append(pointlight_intensite)
red_color,green_color,blue_color = 0,0,0
for i in range(len(total_light_intensites)):
red_color += int(total_light_intensites[i]*(r*g)/200)
green_color += int(total_light_intensites[i]*(v*g)/200)
blue_color += int(total_light_intensites[i]*(b*g)/200)
final_pixel_color = (red_color,green_color,blue_color)
tile.putpixel((x,y),final_pixel_color)
if pastTile != tile:
print(pastTile, tile)
return (_x, _y, tile)
fenetre=tk.Tk() # on créé la fenêtre
largeur=1000 # on définit les dimensions de la fenêtre
hauteur=600
toile = tk.Canvas(fenetre, width=largeur, height=hauteur) # on crée une "toile" dans la fen^tre dans laquelle on pourra dessiner
bouton = tk.Button(fenetre, text='Quitter', command = fenetre.destroy) # on crée un bouton pour quitter le jeu
toile.pack() # on place la toile dans la fenêtre
bouton.pack() # on place le bouton dans la fenêtre
fenetre.geometry('1000x700')
fenetre.configure(background='#7ace54')
fond1 = Image.open("img/fond.png") # on ouvre l'image à modifier
fond1 = fond1.resize((largeur, hauteur)) # on redimensionne l'image par rapport à la taille de la fenêtre
fond2 = fond1.copy() # on créé une copie de cette image
fond3 = ImageTk.PhotoImage(fond2, master = toile)
# on les ajoutes à la toile
main_image = toile.create_image(largeur/2,hauteur/2, image = fond3)
light_calculations= [main_image, fond3, fond2, fond1]
#--------------light calculations-----------------
lightposes=[] #1st = xpose, 2nd = ypose, 3rd = size, 4th = intensity (inversed), 5th = colour
#illumination globale
lightposes.append([500, 300, 1000, 0.85, [2,20,10]])
lightposes.append([1400, -200, 2050, 0.42, [0,170,60]])
lightposes.append([200, -1200, 1900, 0.01, [0,12,40]])
startTime = time.time()
with multiprocessing.Pool(4) as p:
jobs = []
for x in range(0, largeur, 64):
for y in range(0, hauteur, 64):
job = [x, y, fond2.crop((x, y, x + 64, y + 64))]
jobs.append(job)
for x, y, result in p.map(process_tile, jobs):
# (paste the result back in the image at x/y
light_calculations[2].paste(result, (x, y))
# (update the photoimage with the current result)
light_calculations[1] = ImageTk.PhotoImage(light_calculations[2], master = toile)
toile.itemconfigure(light_calculations[0], image = light_calculations[1])
print(time.time()-startTime)
fenetre.mainloop() # permet à la fenêtre "d'écouter" les évènements en boucle
error:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\spawn.py", line 122, in spawn_main
exitcode = _main(fd, parent_sentinel)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\spawn.py", line 131, in _main
prepare(preparation_data)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\spawn.py", line 246, in prepare
_fixup_main_from_path(data['init_main_from_path'])
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\spawn.py", line 297, in _fixup_main_from_p
ath
main_content = runpy.run_path(main_path,
^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen runpy>", line 291, in run_path
File "<frozen runpy>", line 98, in _run_module_code
File "<frozen runpy>", line 88, in _run_code
File "d:\NSI\Cupidon\test multiprocess.py", line 77, in <module>
with multiprocessing.Pool(4) as p:
^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\context.py", line 119, in Pool
return Pool(processes, initializer, initargs, maxtasksperchild,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\pool.py", line 215, in __init__
self._repopulate_pool()
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\pool.py", line 306, in _repopulate_pool
return self._repopulate_pool_static(self._ctx, self.Process,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\pool.py", line 329, in _repopulate_pool_st
atic
w.start()
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\process.py", line 121, in start
self._popen = self._Popen(self)
^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\context.py", line 336, in _Popen return Popen(process_obj)
^^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\popen_spawn_win32.py", line 46, in __init_
_
prep_data = spawn.get_preparation_data(process_obj._name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\spawn.py", line 164, in get_preparation_da
ta
_check_not_importing_main()
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\multiprocessing\spawn.py", line 140, in _check_not_importi
ng_main
raise RuntimeError('''
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
To fix this issue, refer to the "Safe importing of main module"
section in https://docs.python.org/3/library/multiprocessing.html
Thanks for the help and I wish you a great day!
TypeError: cannot pickle '_tkinter.tkapp' object
Pickling is the Python serialization method; all objects that are sent to multiprocessing
subprocesses need to be pickleable.
You're sending over light_calculations
, which includes a tkinter
canvas and a tkinter
PhotoImage – those aren't pickleable (to simplify, since they refer to tkinter objects that are only valid in a single process).
To fix this, you'll have to change things up so you're not sending anything Tkinter-related to those child processes. (Furthermore, attempting to putpixel
on an object in the child process would not change it in the parent process, anyway; the children get copies of the objects.)
If you know the subprocesses don't need the entire image to do their calculations, I would suggest sending tiles of e.g. 64x64 pixels in and receiving the same. That makes it very easy to compose the final image in your parent process. This can be done easily with a multiprocessing.Pool()
and map()
ing (or imap
ing or imap_unordered
ing!) over tiles, e.g.
def process_tile(x, y, tile):
... # (do processing on the tile)
return (x, y, tile)
with multiprocessing.Pool(4) as p:
jobs = []
for x in range(0, width, 64):
for y in range(0, height, 64):
job = [x, y, fond2.crop((x, y, x + 64, y + 64)]
for x, y, result in p.map(process_tile, jobs):
... # (paste the result back in the image at x/y)
... # (update the photoimage with the current result)
EDIT: All in all, to adapt your edited question:
def process_tile(job):
x_offset, y_offset, tile, lightposes = job
width, height = tile.size
for tile_x in range(width): # on parcourt les pixels en colonne
for tile_y in range(height): # on parcourt les pixels en ligne
x = x_offset + tile_x
y = y_offset + tile_y
r, v, b = tile.getpixel((tile_x, tile_y))
g = (r + v + b) / 3
total_light_intensites = []
for light_x, light_y, max_distance, intensity, light_color in lightposes:
distance = math.sqrt((light_x - x) ** 2 + (light_y - y) ** 2)
if distance <= max_distance:
r += light_color[0]
v += light_color[1]
b += light_color[2]
pointlight_intensite = 0.0001
if (distance / max_distance) + (intensity) <= 1:
pointlight_intensite = non_lerp(1, 0.1, (distance / max_distance) + (intensity))
total_light_intensites.append(pointlight_intensite)
red_color, green_color, blue_color = 0, 0, 0
for intensity in total_light_intensites:
red_color += int(intensity * (r * g) / 200)
green_color += int(intensity * (v * g) / 200)
blue_color += int(intensity * (b * g) / 200)
final_pixel_color = (red_color, green_color, blue_color)
tile.putpixel((tile_x, tile_y), final_pixel_color)
return (x_offset, y_offset, tile)
# ...
with multiprocessing.Pool(4) as p:
jobs = []
for x in range(0, largeur, 64):
for y in range(0, hauteur, 64):
job = [x, y, fond2.crop((x, y, x + 64, y + 64)), lightposes]
jobs.append(job)