equ*_*mer 3 python numpy bytesio fastapi
我发现如何使用 FastAPI 将 numpy 数组作为图像返回?然而,我仍然在努力展示图像,它看起来只是一个白色的方块。
io.BytesIO我像这样读入一个数组:
def iterarray(array):
output = io.BytesIO()
np.savez(output, array)
yield output.get_value()
Run Code Online (Sandbox Code Playgroud)
在我的端点中,我的回报是StreamingResponse(iterarray(), media_type='application/octet-stream')
当我留空media_type以推断时,会下载一个 zip 文件。
如何将数组显示为图像?
下面的示例展示了如何将从磁盘加载的图像或内存中的图像(以 numpy 数组的形式)转换为字节(使用 或PIL库OpenCV)并使用自定义Response. 出于本演示的目的,以下代码用于创建基于此答案的内存中示例图像(numpy 数组) 。
# Function to create a sample RGB image\ndef create_img():\n w, h = 512, 512\n arr = np.zeros((h, w, 3), dtype=np.uint8)\n arr[0:256, 0:256] = [255, 0, 0] # red patch in upper left\n return arr\nRun Code Online (Sandbox Code Playgroud)\n您可以使用从磁盘加载图像Image.open,或使用Image.fromarray加载内存中的图像(注意:出于演示目的,当案例从磁盘加载图像时,下面演示了路由内的操作。但是,如果相同的图像将被多次提供,人们只能加载一次图像startup并将其存储在app实例上,如本答案中所述)。接下来,将图像写入缓冲流,即 ,BytesIO并使用该getvalue()方法获取缓冲区的全部内容。尽管缓冲流在超出范围时会被垃圾回收,但通常最好调用close()或使用with语句,如下所示。
from fastapi import Response\nfrom PIL import Image\nimport numpy as np\nimport io\n\n@app.get(\'/image\', response_class=Response)\ndef get_image():\n # loading image from disk\n # im = Image.open(\'test.png\')\n \n # using an in-memory image\n arr = create_img()\n im = Image.fromarray(arr)\n \n # save image to an in-memory bytes buffer\n with io.BytesIO() as buf:\n im.save(buf, format=\'PNG\')\n im_bytes = buf.getvalue()\n \n headers = {\'Content-Disposition\': \'inline; filename="test.png"\'}\n return Response(im_bytes, headers=headers, media_type=\'image/png\')\nRun Code Online (Sandbox Code Playgroud)\n下面演示了如何使用 Python requests 模块向上述端点发送请求,并将接收到的字节写入文件,或将字节转换回 PIL Image,如此处所述。
import requests\nfrom PIL import Image\n\nurl = \'http://127.0.0.1:8000/image\'\nr = requests.get(url=url)\n\n# write raw bytes to file\nwith open(\'test.png\', \'wb\') as f:\n f.write(r.content)\n\n# or, convert back to PIL Image\n# im = Image.open(io.BytesIO(r.content))\n# im.save(\'test.png\') \nRun Code Online (Sandbox Code Playgroud)\n您可以使用函数从磁盘加载图像cv2.imread(),或使用内存中的图像,其中\xe2\x80\x94(如果按顺序排列)RGB,如下例所示\xe2\x80\x94需要转换,因为OpenCV使用BGR它作为其图像的默认颜色顺序。接下来,使用cv2.imencode()函数压缩图像数据(基于您传递的定义输出格式的文件扩展名,即.png、.jpg等)并将其存储在内存缓冲区中,该缓冲区用于通过网络传输数据。
import cv2\n\n@app.get(\'/image\', response_class=Response)\ndef get_image():\n # loading image from disk\n # arr = cv2.imread(\'test.png\', cv2.IMREAD_UNCHANGED)\n \n # using an in-memory image\n arr = create_img()\n arr = cv2.cvtColor(arr, cv2.COLOR_RGB2BGR)\n # arr = cv2.cvtColor(arr, cv2.COLOR_RGBA2BGRA) # if dealing with 4-channel RGBA (transparent) image\n\n success, im = cv2.imencode(\'.png\', arr)\n headers = {\'Content-Disposition\': \'inline; filename="test.png"\'}\n return Response(im.tobytes() , headers=headers, media_type=\'image/png\')\nRun Code Online (Sandbox Code Playgroud)\n在客户端,您可以将原始字节写入文件,或使用函数numpy.frombuffer()和cv2.imdecode()函数将缓冲区解压缩为图像格式(与此类似)\xe2\x80\x94cv2.imdecode()不需要文件扩展名,因为正确的编解码器将从缓冲区中压缩图像的第一个字节推导出来。
url = \'http://127.0.0.1:8000/image\'\nr = requests.get(url=url) \n\n# write raw bytes to file\nwith open(\'test.png\', \'wb\') as f:\n f.write(r.content)\n\n# or, convert back to image format \n# arr = np.frombuffer(r.content, np.uint8)\n# img_np = cv2.imdecode(arr, cv2.IMREAD_UNCHANGED)\n# cv2.imwrite(\'test.png\', img_np)\nRun Code Online (Sandbox Code Playgroud)\n由于您注意到您希望显示的图像类似于 a FileResponse,因此使用自定义Response返回字节应该是执行此操作的方法,而不是使用StreamingResponse(如您的问题所示)。为了指示应在浏览器中查看图像,HTTP响应应包含以下标头,如此处所述和上面的示例所示(如果包含特殊字符filename,则需要将 引起来):filename
headers = {\'Content-Disposition\': \'inline; filename="test.png"\'}\nRun Code Online (Sandbox Code Playgroud)\n然而,要下载图像而不是查看图像(attachment改为使用):
headers = {\'Content-Disposition\': \'attachment; filename="test.png"\'}\nRun Code Online (Sandbox Code Playgroud)\n如果您想使用 JavaScript 接口(例如 Fetch API 或 Axios)显示(或下载)图像,请查看此处和此处的答案。
\n至于StreamingResponse,如果numpy数组从一开始就完全加载到内存中,StreamingResponse则完全没有必要。StreamingResponse通过迭代函数提供的块来流iter()(如果Content-Length未在 headers\xe2\x80\x94 中设置StreamingResponse,则其他Response类会为您设置该标头,以便浏览器知道数据在哪里结束)。正如这个答案中所描述的:
\n\n当您提前不知道输出的大小,并且您不想等到收集它才开始将其发送到客户端之前才知道,分块传输编码就有意义。这可以适用于提供慢速数据库查询结果等内容,但通常不适用于提供图像。
\n
即使您想流式传输保存在磁盘上的图像文件(您不应该这样做,除非它是一个相当大的文件,无法放入内存。相反,您应该使用 use FileResponse),类似文件的对象,例如由 所创建的open(),是普通迭代器;因此,您可以直接在 a 中返回它们,如文档StreamingResponse中所述并如下所示(如果您发现使用时相当慢,请查看此答案以获取解决方案):yield from fStreamingResponse
headers = {\'Content-Disposition\': \'inline; filename="test.png"\'}\nRun Code Online (Sandbox Code Playgroud)\n或者,如果图像被加载到内存中,然后保存到BytesIO缓冲流中以返回字节,则BytesIO它是一个类似文件的对象(就像io 模块的所有具体类一样),这意味着您可以直接返回它在一个StreamingResponse:
headers = {\'Content-Disposition\': \'attachment; filename="test.png"\'}\nRun Code Online (Sandbox Code Playgroud)\n因此,对于您的案例场景,最好返回Response与您的自定义content和media_type,以及设置Content-Disposition标题,如上所述,以便在浏览器中查看图像。
下面的内容不应该用于在浏览器中显示图像,但为了完整起见,在此处添加了它,展示了如何将图像转换为 numpy 数组(最好使用asarray()function),然后以 JSON 格式返回数据,最后,在客户端将数据转换回图像,如本答案和本答案中所述。有关标准 Pythonjson库的更快替代方案,请参阅此答案。
@app.get(\'/image\')\ndef get_image():\n def iterfile(): \n with open(\'test.png\', mode=\'rb\') as f: \n yield from f \n \n return StreamingResponse(iterfile(), media_type=\'image/png\')\nRun Code Online (Sandbox Code Playgroud)\nfrom fastapi import BackgroundTasks\n\n@app.get(\'/image\')\ndef get_image(background_tasks: BackgroundTasks):\n arr = create_img()\n im = Image.fromarray(arr)\n buf = BytesIO()\n im.save(buf, format=\'PNG\')\n buf.seek(0)\n background_tasks.add_task(buf.close)\n return StreamingResponse(buf, media_type=\'image/png\')\nRun Code Online (Sandbox Code Playgroud)\nfrom PIL import Image\nimport numpy as np\nimport json\n\n@app.get(\'/image\')\ndef get_image():\n im = Image.open(\'test.png\')\n # im = Image.open(\'test.png\').convert(\'RGBA\') # if dealing with 4-channel RGBA (transparent) image \n arr = np.asarray(im)\n return json.dumps(arr.tolist())\nRun Code Online (Sandbox Code Playgroud)\nimport requests\nfrom PIL import Image\nimport numpy as np\nimport json\n\nurl = \'http://127.0.0.1:8000/image\'\nr = requests.get(url=url) \narr = np.asarray(json.loads(r.json())).astype(np.uint8)\nim = Image.fromarray(arr)\nim.save(\'test_received.png\')\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
7176 次 |
| 最近记录: |