Python,我该如何异步保存 PIL 图像?

4

对于异步文件保存,我可以使用库。

要使用库,我需要像这样做:

async with aiofiles.open(path, "wb") as file:
   await file.write(data)

我怎样才能异步地保存PIL图像?即使我使用Image.tobytes函数并用file.write(data)保存它,保存的图像也不正确。那么我该怎样异步保存PIL图像?

2
我对asyncio一无所知,但您可以将图像编码为JPEG或PNG格式,并将该缓冲区(在链接的示例中称为JPEG)传递给您在问题中提到的file.write()。请参见https://dev59.com/SW0NtIcB2Jgan1znExGf#70275120。 - Mark Setchell
很棒 - 很高兴它能正常工作。如果你进行一些计时并发现性能有所提升,请写下答案让其他人受益...并接受你自己的答案并获得积分。 - Mark Setchell
3个回答

8

感谢@MarkSetchell发表的评论,我成功找到了解决方案。

async def save_image(path: str, image: memoryview) -> None:
    async with aiofiles.open(path, "wb") as file:
        await file.write(image)


image = Image.open(...)
buffer = BytesIO()
image.save(buffer, format="JPEG")

await save_image('./some/path', buffer.getbuffer())

我不知道可以获得多少速度增益,但在我的情况下,我能够同时运行一些数据处理代码、数据下载代码和图像保存代码,这给了我提速。

0

@CrazyChucky,感谢指出异步同步问题。我又重新处理了一下

from fastapi import File,UploadFile
from PIL import Image,ImageFont, ImageDraw
from io import BytesIO

@router.post("/users/update_user_profile")
async def upload_file(self, image_file : UploadFile = File(None)):         
   out_image_name = 'image_name'+pathlib.Path(image_file.filename).suffix    
   out_image_path = f"images/user/{out_image_name}"
   request_object_content = await image_file.read()
   photo = Image.open(BytesIO(request_object_content)) 
   # make the image editable
   drawing = ImageDraw.Draw(photo)
   black = (3, 8, 12)   
   drawing.text((0,0), 'Your_text', fill=black)#, font=font)
  
   # Create a buffer to hold the bytes
   buf = BytesIO()

   # Save the image as jpeg to the buffer
   photo.save(buf, format='JPEG')#    

   async with aiofiles.open(out_image_path, 'wb') as out_file:       
       outContent = await out_file.write(buf.getbuffer())  # async write    

  return out_image_name 

我参考了 @Karol 的建议。 现在检查一下,这是关于 fastapi 异步方法的。


-1
在我的情况下,以下方法有效(使用 Python FastAPI)。
from fastapi import File,UploadFile
from PIL import Image,ImageFont, ImageDraw
from io import BytesIO

async def upload_file(image_file : UploadFile = File(None)):       
    out_image_name = str(123)+pathlib.Path(image_file.filename).suffix 
    out_image_path = f"images/user/{out_image_name}"
    request_object_content = await image_file.read()
    photo = Image.open(BytesIO(request_object_content)) 
    # make the image editable
    drawing = ImageDraw.Draw(photo)
    black = (3, 8, 12)
    # font = ImageFont.truetype("Pillow/Tests/fonts/FreeMono.ttf", 40)
    drawing.text((0,0), 'text_to_water_mark', fill=black)#, font=font)
    photo.save(out_image_path)

有趣...这是异步打开文件,但同步保存,对吗? - CrazyChucky
它会保存,photo.save(out_image_path)将执行文件保存工作。 - Coco Developer
1
是的,我明白。但它是阻塞式的,不是异步的。那部分没有使用fastapi、aiofiles、缓冲区或任何花哨的东西,只是直接保存到文件路径。 - CrazyChucky
好的,我的错, 我只发布了在fastapi路由器中调用的函数。 我的要求是使用pillow保存带有水印文本的图像。 - Coco Developer

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