R中的reticulate,如何清除Python对象在内存中的占用?

11

我使用reticulate包中的一些Python功能创建了一个函数,具体来说是使用PIL打开图像:

image <- "~/Desktop/image.jpg"
pil.image <- reticulate::import( "PIL.Image", convert = FALSE )
img <- pil.image$open( image )

我随后对图像进行了一些处理(我正在提取多个裁剪图像),这很正常。下面是我正在做的示例(outputs 是我需要的裁剪图像的数据框,因此 crop.grid 只是一个包含4个数字的向量。

crop.grid <- c( outputs$x.start[x],
                outputs$y.start[x],
                outputs$x.stop[x],
                outputs$y.stop[x] )
crop.grid <- as.integer( crop.grid )
crop.grid <- reticulate::r_to_py( crop.grid )
output.array <- img$crop( box = crop.grid )
output.array$save( output.filename )

接下来,我希望将图像从内存中清除(我打开的图像非常大,所以需要大量内存)。我尝试在Python中关闭图像:

img$close()

同样也适用于R语言:

rm( img )
gc()

我将对象替换为我知道非常小的东西。

img <- reticulate::r_to_py( 1L )

所有这些东西都可以正常运行,但我的 RAM 仍然显示为非常满。我尝试将它们与我创建的每个 python 对象一起使用,但唯一有效地清除 RAM 的方法是重新启动 R 会话。

我知道在 python 中最好使用 with 打开图像,以便在进程结束时清除它,但我不确定如何使用reticulate来实现它。


--- 使用类比的 python 版本更新:

如果我直接在 python 中执行以上操作:

from PIL import Image
img = Image.open( "/home/user/Desktop/image.jpg" )
output = img.crop( [0,0,100,100] )

然后关闭事物:

output.close()
img.close()

内存被清除。然而从R内部执行同样的操作却不起作用。例如:

output.array$close()
img$close()
gc() # for good measure

无法清除内存。


你尝试过将所有内容包装到 with 中吗?类似这样的代码:with(pil.image$open( image ) %as% img, { img$crop(r_to_py( c(0,0,100,100)))$save("myfile.jpg") }) - lukeA
@lukeA 我之前没有试过,但现在我试了。它可以正常运行,但是仍然存在RAM满的问题。有趣的是,output.array<-img$crop(box=crop.grid)这一步骤会导致内存占用增加,但我在函数中重复运行该步骤,每次迭代似乎都不会重新读取图像(只有第一次迭代需要较长时间)。关闭output.arrayrmgc似乎仍然无法清除RAM。 - rosscova
也许也可以用with包装一下?例如 with(pil.image$open( image ) %as% img, { with(img$crop(r_to_py( c(0,0,100,100))) %as% output.array, { output.array$save("myfile.jpg") }) }) - 只是试错。 - lukeA
公平的调用,但仍然没有结果。我认为这里是在R中操作with,因此只有R对象被删除。Python对象(我假设它坐落在后台环境中)仍然存在。 - rosscova
@antoine-sac 感谢您的回复。您会在我的问题中看到,我尝试在 R 中使用 gc(),但没有效果。不幸的是,由于(我认为)reticulate 启动了一个外部的 python 会话,该会话可能是内存分配问题所在的地方,因此在 R 会话中运行 gc() 没有任何区别。重新启动 R 明确地关闭了 python 会话,因此一个 reticulate 函数可以在不重新启动 R 的情况下完成这个操作,但是一个能够正确清除对象而不关闭该会话的函数将更好。 - rosscova
显示剩余2条评论
1个回答

4

您需要做三件事:

  1. Explicitly create the object in Python:

    py_env <- py_run_string(
        paste(
            "from PIL import Image",
            "img = Image.open('~/Desktop/image.jpg')",
            sep = "\n"
        ),
        convert = FALSE
    )
    img <- py_env$img
    
  2. When you're done with the image, first delete the Python object.

    py_run_string("del img")
    
  3. Then run the Python garbage collector.

    py_gc <- import("gc")
    py_gc$collect()
    
步骤2和3是最重要的。步骤1只是为了有一个要删除的名称而已。如果有一种方法可以删除“隐式”Python对象(想不到更好的术语),那么就可以节省一些样板文件。

你好Nathan,非常感谢回复。正如我所说,我希望使用reticulate的更多R端功能来实现这个问题,而不是使用py_run_string。 我当然可以使用py_run_string来解决,由于这里没有答案建议直接解决问题,我认为这可能是我需要走的路。我打算授予你奖励,因为你确实提供了一个解决方案,即使它不是我所希望的。只是注意一下,我发现在python中运行img.close()似乎会触发垃圾清理,而不需要显式调用。 - rosscova

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接