【问题标题】:Is it possible to resize an image by its bytes rather than width and height?是否可以通过字节而不是宽度和高度来调整图像的大小?
【发布时间】:2018-10-23 02:36:36
【问题描述】:

我想将图像缩小到更小的尺寸,以便于共享和更快地上传。

但我意识到,如果我只是通过它的硬件来减小大小,它并不能真正起到作用,因为大图像文件可能有更小的硬件,而小图像文件可能有很大的硬件,所以减少图像大小减少它的高度和重量可能并不总是按照我想要的方式缩小尺寸。

所以现在我用这个得到了字节大小:

import os
os.stat('myImage.jpg').st_size

是否可以通过减小字节来减小图像大小?并保持它的比例?

【问题讨论】:

  • 一个有用的 Google 搜索短语可能是 jpg 压缩
  • 您总是可以使用固定宽度,然后通过将目标宽度乘以原始图像的纵横比来计算相应的高度。
  • @Jeronimo,感谢您的建议,但我不想使用宽度来减小尺寸,因为宽度和高度与实际图像尺寸并不相关。否则,我可以只使用:.resize((500, int(500/float(w) * h)), Image.ANTIALIAS)
  • @usr2564301,所以基本上,我不想通过操纵它的高度和重量来减小图像大小,我只想通过减小它的大小(字节)来减小图像大小。

标签: python-3.x reduce image-size


【解决方案1】:

这是我用 PIL 编写的一个函数。它对图像进行一些迭代调整大小和 jpeg 压缩,然后查看生成的文件大小并将其与目标值进行比较,根据大小偏差比猜测下一个最佳宽度/高度组合(基本上是某种 P 控制器)。

它使用 io.BytesIO 来完成所有内存中的大小调整,因此对磁盘上的文件实际上只有一次读取和一次写入访问。此外,使用这种蛮力方法,您可以将目标文件格式更改为 PNG,它可以开箱即用。

from PIL import Image
import os
import io

def limit_img_size(img_filename, img_target_filename, target_filesize, tolerance=5):
    img = img_orig = Image.open(img_filename)
    aspect = img.size[0] / img.size[1]

    while True:
        with io.BytesIO() as buffer:
            img.save(buffer, format="JPEG")
            data = buffer.getvalue()
        filesize = len(data)    
        size_deviation = filesize / target_filesize
        print("size: {}; factor: {:.3f}".format(filesize, size_deviation))

        if size_deviation <= (100 + tolerance) / 100:
            # filesize fits
            with open(img_target_filename, "wb") as f:
                f.write(data)
            break
        else:
            # filesize not good enough => adapt width and height
            # use sqrt of deviation since applied both in width and height
            new_width = img.size[0] / size_deviation**0.5    
            new_height = new_width / aspect
            # resize from img_orig to not lose quality
            img = img_orig.resize((int(new_width), int(new_height)))


limit_img_size(
    "test.jpg",   #  input file
    "test_with_limited_size.jpg",     #  target file
    50000,   # bytes    
    tolerance = 5    # percent of what the file may be bigger than target_filesize
)

编辑:

“内存中”是指当saves 循环中的imgbuffer 时,它会将其保存到BytesIO 对象中,该对象不是磁盘上的文件而是内存中的文件.然后,我可以从该对象确定生成的文件大小(这只是该数据缓冲区的长度),而无需实际将其保存到文件中。最后,也许这正是您期望它工作的方式,但是由于缺乏对 Python 的io.BytesIO 的了解,我看到太多代码浪费了将文件保存在磁盘上的性能。

只有最终结果会被保存到一个文件中——这就是你想要的。尝试为img_target_filename 使用绝对文件名。

【讨论】:

  • 我花了几天时间试图找到一种将图像大小减小给定字节的方法,而您的代码正是我所需要的并且它有效。让我花点时间来吸收这段代码!谢谢老兄。
  • 很高兴它有帮助 - 如果你能将它标记为正确答案,那就太好了。 ;)
  • 有了你的代码,我什至可以保存成不同的格式(png到jpg),太棒了!由于我是 Python 新手,您能否解释一下 > 调整“内存中”的大小是什么意思?这是否意味着我无法将更改后的图像保存到另一个目录?
  • 在编辑中回答。
  • 我还有一个问题,有些图片无法正确缩小尺寸,原因是使用BytesIO时,有些图片的尺寸明显不同,而使用@987654329 @ 将始终返回实际大小。那么,使用这种方法,有些图像无法缩小到它需要的样子,这是为什么呢?
【解决方案2】:

碰巧我的一个朋友需要调整个人图像的大小/重新缩放,并且要求图像最大为 40KB。考虑到磁盘上的大小,我编写了以下代码来缩小/重新缩放图像。我假设 40KB = 40000Bytes 这并不准确,但它可以派上用场

from skimage.io import imread, imshow
from skimage.transform import rescale, resize, downscale_local_mean
from skimage.io import imsave
import matplotlib.pyplot as plt
import os
import numpy as np

img = imread('original.jpg')
target_size = 40000
size = os.path.getsize('original.jpg')
factor = 0.9
while(size>=40000):
    image_rescaled = rescale(img, factor, anti_aliasing=False)
    imsave('new.jpg', image_rescaled)
    print('factor {} image of size {}'.format(factor,size))
    factor = factor - 0.05
    size = os.path.getsize('new.jpg')

end_size = os.path.getsize('new.jpg')
print(end_size)

希望对你有帮助!!!! 你可以在 GitHub 上以 LeninGF 的身份关注我

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-28
    相关资源
    最近更新 更多