You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数内调用PIL ImageDraw.Draw()绘制不生效问题求解

问题原因

你遇到的问题本质是多进程的内存空间隔离特性导致的:

  • 你在主进程初始化的im图像对象和draw绘图对象,在子进程启动时会被复制一份到子进程的独立内存空间中
  • 你在box_renderer函数里调用draw.point修改的只是子进程本地的图像副本,主进程里的原始im对象没有发生任何修改
  • 把im.save放到函数内部时,保存的是子进程本地已经完成绘制的图像副本,所以能得到有内容的图片,但多进程场景下每个子进程保存的图只会包含自己负责的分块内容。
可行解决方案

方案1:子进程返回像素数据,主进程统一拼接(最稳妥,无并发冲突)

这是最推荐的实现方式,完全规避跨进程共享可变对象的问题:

  • 调整box_renderer逻辑,不直接操作全局绘图对象,计算完当前分块的所有像素颜色后,返回分块坐标和对应的像素列表
  • 主进程收集所有子进程返回的分块数据,统一写入全局图像对象后再保存

示例代码:

from PIL import Image, ImageDraw
import multiprocessing

HOR_RES = 1920
VERT_RES = 1080
ITERATIONS = 100

def mandelbrot(x, y):
    # 你的Mandelbrot计算逻辑
    c = complex(x/300 - 2.5, y/200 - 1.5)
    z = 0j
    for i in range(ITERATIONS):
        if abs(z) > 2:
            return i
        z = z*z + c
    return ITERATIONS

def box_renderer(args):
    x_start, x_end, y_start, y_end = args
    block_pixels = []
    for y in range(y_start, y_end):
        row = []
        for x in range(x_start, x_end):
            iter_count = mandelbrot(x, y)
            colour = 255 - int(255*iter_count/ITERATIONS)
            row.append((0, 0, colour))
        block_pixels.append(row)
    # 返回分块坐标和像素数据
    return (x_start, x_end, y_start, y_end, block_pixels)

if __name__ == "__main__":
    im = Image.new("RGB", (HOR_RES, VERT_RES), (0, 0, 0))
    draw = ImageDraw.Draw(im)
    
    # 拆分渲染分块,示例拆成4个横向块
    blocks = [
        (0, HOR_RES, 0, VERT_RES//4),
        (0, HOR_RES, VERT_RES//4, VERT_RES//2),
        (0, HOR_RES, VERT_RES//2, VERT_RES*3//4),
        (0, HOR_RES, VERT_RES*3//4, VERT_RES)
    ]
    
    # 多进程渲染
    with multiprocessing.Pool(processes=4) as pool:
        results = pool.map(box_renderer, blocks)
    
    # 主进程拼接所有分块
    for x_start, x_end, y_start, y_end, block_pixels in results:
        for y_offset, row in enumerate(block_pixels):
            y = y_start + y_offset
            for x_offset, color in enumerate(row):
                x = x_start + x_offset
                draw.point([x, y], color)
    
    im.save("mandelbrot.png", "PNG")

方案2:使用共享内存存储图像数据(性能最优)

如果追求渲染效率,不想额外做数据复制和拼接,可以将图像的底层像素缓冲区放到多进程共享内存中,所有子进程直接操作同一块内存:

  • 用multiprocessing.RawArray创建共享内存缓冲区,对应图像的所有像素存储
  • 所有进程操作共享内存中的像素数据,因为各进程负责的分块无重叠,不需要加锁也不会出现并发冲突
  • 主进程最后将共享内存中的数据转换为PIL图像保存即可。

方案3:子进程输出临时分块图,主进程拼接(改动最小)

如果不想大幅调整现有代码逻辑,可以让每个子进程渲染完自己的分块后,保存为临时图片文件,主进程等所有子进程执行完成后,读取所有临时分块图拼接到全局大图中,最后删除临时文件即可,适合小规模测试使用。

内容的提问来源于stack exchange,提问作者Joost Visser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 19:06:03