Python:优化内存中的图像(StringIO& POpen with jpegoptim)

时间:2015-02-25 03:59:52

标签: python popen stringio jpegoptim

我正在尝试使用STDIN版本的各种库(本例中为jpegoptim)来压缩图像而不触摸磁盘。

此代码不返回优化(jpegoptim压缩)图像。

有人可以帮助或解释为什么Popen()与StringIO.StringIO()对象的使用不会返回图像的优化版本?如果我将文件保存到磁盘,它可以正常工作。

import sys
import urllib2 as urllib
import StringIO

from subprocess import Popen, PIPE, STDOUT
fp = urllib.urlopen('http://www.path.to/unoptimized.jpg')
out_im2 = StringIO.StringIO(fp.read()) # StringIO Image
print "Image Size: %s" % format(sys.getsizeof(out_im2.getvalue()))
subp = Popen(["/usr/bin/jpegoptim", "-"], shell=True, stdout=PIPE, stdin=PIPE, stderr=STDOUT)
image_str = subp.communicate(input=out_im2.getvalue())[0]
out_im2.write(image_str)

##This should be a different size if it worked! It's not
print "Compressed JPG: %s" % format(sys.getsizeof(out_im2.getvalue()))

2 个答案:

答案 0 :(得分:2)

这是因为您正在写入相同的输入缓冲区。创建一个新的StringIO()。

StringIO缓冲区最初扩展到第一个未压缩的jpeg的大小。然后使用新的较短字符串缓冲区从0位置开始写入缓冲区,但它不会自动截断缓冲区或任何内容。 StringIO缓冲区的大小仍然相同,实际上所有尾随数据都将保留在原始图像的垃圾中。

In [1]: import StringIO

In [2]: out = StringIO.StringIO("abcdefg")

In [3]: out.getvalue()
Out[3]: 'abcdefg'

In [4]: out.write("123")

In [5]: out.getvalue()
Out[5]: '123defg'

答案 1 :(得分:1)

有几个问题:

  1. 错误覆盖StringIO()缓冲区pointed out by @doog abides
  2. 的问题
  3. 使用len代替sys.getsizeof()。后者返回内存中内部表示的大小,该大小不等于bytestring中的字节数

  4. Don't use a list argument and shell=True together

  5. 您可以将套接字作为stdin传递给某些系统上的子进程:

    import socket
    from urllib2 import urlopen
    from subprocess import check_output
    
    saved = socket._fileobject.default_bufsize
    socket._fileobject.default_bufsize = 0  # hack to disable buffering
    try:
        fp = urlopen('http://www.path.to/unoptimized.jpg')
    finally:
        socket._fileobject.default_bufsize = saved # restore back
    
    # urlopen() has read http headers; subprocess can read the body now
    image_bytes = check_output(["/usr/bin/jpegoptim", "-"], stdin=fp) 
    fp.close()
    
    # use `image_bytes` bytestring here..
    

    stderr未设置为避免隐藏错误。