在Django中裁剪图像会导致大量内存增加

时间:2012-02-28 00:00:18

标签: django python-imaging-library webfaction

我最近遇到了我的Django项目和WebFaction上的内存使用问题。

以下是在webfaction上为此项目在内存中运行的两个进程:

30396  4-20:20:00 13486 
30404  4-20:20:00 13487 

运行视图后,其中一个进程将大幅增加:

69720  4-20:20:22 13486 
30404  4-20:20:22 13487 

正如您所看到的,第一个进程的内存使用量增加了一倍以上!由于这个功能经常被使用,我需要弄清楚发生了什么。我相信我已将其缩小到以下视图(这是上传图像,添加细节,裁剪缩略图的3个步骤。)

以下是视图。它获取一个照片对象,从文件加载图像,获取用户提交的框坐标,然后创建一个200,200大小的图像。这个新创建的图像将以文件名中的.thumbnail写回磁盘,并保存照片对象。

@login_required
def upload3(request, photo_pk):
    photo = get_object_or_404(Photo, pk=photo_pk, user=request.user)
    if request.method == "POST":
        form = upload3Form(request.POST)
        if form.is_valid():
            im = Image.open(photo.image.path)
            try:
                box =(form.cleaned_data['x1'],form.cleaned_data['y1'],form.cleaned_data['x2'],form.cleaned_data['y2'])
            except:
                box = ('0','0','1000','1000')
            cropped = im.crop(box)
            cropped.thumbnail((200,200),Image.ANTIALIAS)
            result = os.path.splitext(photo.image.path)
            cropped.save(result[0] + '.thumbnail' + result[1])
            photo.status = 3
            photo.save()

任何关于我可能做错的想法都将不胜感激。

Update 1:用于测试的图片均为Jpeg,尺寸约为3600 x 2700,每张图片约为2 MB。

2 个答案:

答案 0 :(得分:2)

2M图是针对压缩的JPEG图像,但是未压缩的3600 x 2700真彩色将是大约38M(每像素4B的9,720,000像素),接近您正在经历的内存使用量增加。

这是PIL的已知问题,我可以通过向您发送40000x40000像素的黑色图片作为png来制作像素炸弹。在加载之前始终检查分辨率(或使用处理OutOfMemory的try / except块保护代码)。查看是否使用im.tile属性按块处理图像块可以减少内存占用。

值得一试:

一些替代方案据说可以在处理更大的图像时更好地处理内存:

  • GDAL(地理空间数据抽象图书馆)
  • OIIO(OpenImageIO)
  • Mahotas(NumPy)

[更新]

  

你知道在PIL中是否有办法从内存中释放对象?因为理论上这对于这个视图是最好的,因为我需要它像它一样工作,但只是更好地处理图像。

  • 为了避免内存峰值,您可以检测到巨大的图像并尝试使用im.tile而不是im.crop(不幸地在较低级别操作)以块的形式处理它们。
  • 您可以尽快删除中间图像对象以获得更短的峰值(使用gc module可以强制清理垃圾收集器)。

答案 1 :(得分:0)

经过大量挖掘和死胡同之后,我尝试了一些在任何地方都没有建议的东西,并且它有效。

在包含PIL中使用的图像对象的每个对象上,我必须在完成它后删除该对象。例如:

im = Image.open(photo.image.path)
try:
    box  =(form.cleaned_data['x1'],form.cleaned_data['y1'],form.cleaned_data['x2'],form.cleaned_data['y2'])
except:
    box = ('0','0','1000','1000')
cropped = im.crop(box)
newimage = cropped.resize((form.cleaned_data['dw'],form.cleaned_data['dh']),Image.ANTIALIAS)
del im
del cropped 

所以一旦我完成了对象,我就会在该项目上调用del。它似乎解决了这个问题。我不再有记忆增加,我也不会更快乐。