Почему мой код с многопроцессорностью tkinter и PIL не работает?

Я попробовал это реализовать, но все равно не работает Он просит меня обратиться к безопасному импорту основного модуля с помощью: if name == "main": Но даже то, что я пробовал, все равно не работало, и я продолжал получать ошибки.

Вот новый код с реализацией ответа (не уверен, что сделал это правильно):

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

ошибка:

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

Спасибо за помощь и желаю вам хорошего дня!

Я не вижу знака if __name__ == "__main__" в вашем обновленном коде.

AKX 30.05.2024 10:14
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ошибка типа: невозможно выбрать объект «_tkinter.tkapp»

Пиклинг — метод сериализации Python; все объекты, отправляемые в подпроцессы multiprocessing, должны быть разборчивыми.

Вы отправляете light_calculations, который включает в себя tkinter Canvas и tkinter PhotoImage — их нельзя выбрать (для упрощения, поскольку они относятся к объектам tkinter, которые действительны только в одном процессе).

Чтобы это исправить, вам придется изменить ситуацию, чтобы вы не отправляли в эти дочерние процессы ничего, связанного с Tkinter. (Более того, попытка putpixel над объектом в дочернем процессе в любом случае не изменит его в родительском процессе; дочерние элементы получают копии объектов.)

Если вы знаете, что подпроцессам не нужно все изображение для выполнения вычислений, я бы предложил отправлять фрагменты, например. 64х64 пикселей и получение то же самое. Это позволяет очень легко составить окончательное изображение в родительском процессе. Это можно легко сделать с помощью multiprocessing.Pool() и Map() (или imaping или imap_unordereding!) по плиткам, например.

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)

Обновлено: В общем, чтобы адаптировать ваш отредактированный вопрос:

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)

Здравствуйте, большое спасибо за помощь, но это не работает (вероятно, ошибка с моей стороны), и я не могу понять почему: (я обновил пост)

Isidore BILLARD 28.05.2024 22:53

а что такое переменная job? Мы не связываем ее с работой?

Isidore BILLARD 29.05.2024 17:01

спасибо большое, благодаря вам наконец-то получилось! Я искренне желаю тебе хорошего дня и ты самый лучший! Спасибо

Isidore BILLARD 30.05.2024 21:32

Другие вопросы по теме