Python 子进程:将图像 blob 通过管道传输到 imagemagick shell 命令

Python subprocess: pipe an image blob to imagemagick shell command

我在内存中有一个图像,我希望使用 Python 的 subprocess 执行 imagemagick 的 convert 方法。虽然这条线在 Ubuntu 的终端上运行良好:

cat image.png | convert - new_image.jpg

这段代码无法使用 Python:

jpgfile = Image.open('image.png');
proc = Popen(['convert', '-', 'new_image.jpg'], stdin=PIPE, shell=True)
print proc.communicate(jpgfile.tostring())

我也试过在不使用 PIL 的情况下将图像作为常规文件读取,我试过在 subprocess 方法和写入标准输入的不同方法之间切换。

最棒的是,什么也没有发生,但我没有收到真正的错误。打印标准输出时,我可以在终端上看到 imagemagick 帮助,然后是以下内容:

By default, the image format of `file' is determined by its magic number. To specify a particular image format, precede the filename with an image format name and a colon (i.e. ps:image) or specify the image type as the filename suffix (i.e. image.ps). Specify 'file' as '-' for standard input or output. (None, None)

也许这里有我没有得到的提示。 请指出正确的方向,我是 Python 的新手,但根据我对 PHP 的经验,这应该是一项非常容易的任务,我希望如此。

编辑:

这是我最终在不保存临时文件的情况下处理PIL图像对象的解决方案。希望它能帮助别人。 (在示例中我正在从本地驱动器读取文件,但我的想法是从远程位置读取图像)

out = StringIO()
jpgfile = Image.open('image.png')
jpgfile.save(out, 'png', quality=100);
out.seek(0);
proc = Popen(['convert', '-', 'image_new.jpg'], stdin=PIPE)
proc.communicate(out.read())

导致任何问题的不是子进程,而是您传递给 imagemagick 的内容不正确,tostring() 确实传递给了 imagemagick。如果您真的想复制 linux 命令,您可以从一个进程通过管道传输到另一个进程:

from subprocess import Popen,PIPE
proc = Popen(['cat', 'image.jpg'], stdout=PIPE)

p2 = Popen(['convert', '-', 'new_image.jpg'],stdin=proc.stdout)

proc.stdout.close()
out,err = proc.communicate()

print(out)

当您传递一个参数列表时,您不需要 shell=True,如果您想使用 shell=True,您将传递一个字符串:

from subprocess import check_call
check_call('cat image.jpg | convert - new_image.jpg',shell=True)

通常我会避免 shell=Trueanswer 概括了 shell=True 的作用。

您还可以将文件对象传递给标准输入:

with open('image.jpg') as jpgfile:
    proc = Popen(['convert', "-", 'new_image.jpg'], stdin=jpgfile)

out, err = proc.communicate()

print(out)

但是由于代码成功运行时没有输出,您可以使用 check_call 如果存在非零退出状态,您可以捕获并采取适当的操作,这将引发 CalledProcessError:

from subprocess import check_call, CalledProcessError


with open('image.jpg') as jpgfile:
    try:
        check_call(['convert', "-", 'new_image.jpg'], stdin=jpgfile)
    except CalledProcessError as e:
        print(e.message)

如果您想使用 communicate 写入标准输入,您也可以使用 .read 传递文件内容:

with  open('image.jpg') as jpgfile:
     proc = Popen(['convert', '-', 'new_image.jpg'], stdin=PIPE)
     proc.communicate(jpgfile.read())

如果您不想将图像存储在磁盘上,请使用 tempfile:

import tempfile
import requests

r = requests.get("http://www.reptileknowledge.com/images/ball-python.jpg")
out = tempfile.TemporaryFile()
out.write(r.content)
out.seek(0)

from subprocess import check_call
check_call(['convert',"-", 'new_image.jpg'], stdin=out)

使用 CStringIo.StringIO 对象:

import requests

r = requests.get("http://www.reptileknowledge.com/images/ball-python.jpg")
out = cStringIO.StringIO(r.content)

from subprocess import check_call,Popen,PIPE
p = Popen(['convert',"-", 'new_image.jpg'], stdin=PIPE)

p.communicate(out.read())