Python 获取图像大小而无需将图像加载到内存中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15800704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get Image size WITHOUT loading image into memory
提问by Sami A. Haija
I understand that you can get the image size using PIL in the following fashion
我知道您可以通过以下方式使用 PIL 获取图像大小
from PIL import Image
im = Image.open(image_filename)
width, height = im.size
However, I would like to get the image width and height withouthaving to load the image in memory. Is that possible? I am only doing statistics on image sizes and dont care for the image contents. I just want to make my processing faster.
但是,我想获得图像的宽度和高度,而不必将图像加载到内存中。那可能吗?我只做图像大小的统计,不关心图像内容。我只是想让我的处理速度更快。
采纳答案by Hooked
As the comments allude, PIL does not load the image into memory when calling .open. Looking at the docs of PIL 1.1.7, the docstring for .opensays:
正如评论所暗示的那样,PIL 在调用.open. 查看 的文档PIL 1.1.7,文档字符串 for.open说:
def open(fp, mode="r"):
"Open an image file, without loading the raster data"
There are a few file operations in the source like:
源中有一些文件操作,例如:
...
prefix = fp.read(16)
...
fp.seek(0)
...
but these hardly constitute reading the whole file. In fact .opensimply returns a file object and the filename on success. In addition the docssay:
但这些几乎不构成阅读整个文件。事实上,.open只要成功就返回一个文件对象和文件名。此外,文档说:
open(file, mode=”r”)
Opens and identifies the given image file.
This is a lazy operation; this function identifies the file, but the actual image data is not read from the file until you try to process the data (or call the loadmethod).
打开(文件,模式=“r”)
打开并标识给定的图像文件。
这是一个懒惰的操作;此函数标识文件,但在您尝试处理数据(或调用加载方法)之前,不会从文件中读取实际图像数据。
Digging deeper, we see that .opencalls _openwhich is a image-format specific overload. Each of the implementations to _opencan be found in a new file, eg. .jpeg files are in JpegImagePlugin.py. Let's look at that one in depth.
深入挖掘,我们看到.open调用_open是特定于图像格式的重载。每个实现_open都可以在一个新文件中找到,例如。.jpeg 文件位于JpegImagePlugin.py. 让我们深入了解一下。
Here things seem to get a bit tricky, in it there is an infinite loop that gets broken out of when the jpeg marker is found:
这里的事情似乎有点棘手,其中有一个无限循环,当找到 jpeg 标记时会中断:
while True:
s = s + self.fp.read(1)
i = i16(s)
if i in MARKER:
name, description, handler = MARKER[i]
# print hex(i), name, description
if handler is not None:
handler(self, i)
if i == 0xFFDA: # start of scan
rawmode = self.mode
if self.mode == "CMYK":
rawmode = "CMYK;I" # assume adobe conventions
self.tile = [("jpeg", (0,0) + self.size, 0, (rawmode, ""))]
# self.__offset = self.fp.tell()
break
s = self.fp.read(1)
elif i == 0 or i == 65535:
# padded marker or junk; move on
s = "\xff"
else:
raise SyntaxError("no marker found")
Which looks like it couldread the whole file if it was malformed. If it reads the info marker OK however, it should break out early. The function handlerultimately sets self.sizewhich are the dimensions of the image.
如果文件格式错误,它看起来可以读取整个文件。但是,如果它读取信息标记正常,则它应该提前爆发。该函数handler最终设置self.size图像的尺寸。
回答by Paulo Scardine
If you don't care about the image contents, PIL is probably an overkill.
如果您不关心图像内容,PIL 可能是一种矫枉过正。
I suggest parsing the output of the python magic module:
我建议解析 python magic 模块的输出:
>>> t = magic.from_file('teste.png')
>>> t
'PNG image data, 782 x 602, 8-bit/color RGBA, non-interlaced'
>>> re.search('(\d+) x (\d+)', t).groups()
('782', '602')
This is a wrapper around libmagic which read as few bytes as possible in order to identify a file type signature.
这是 libmagic 的包装器,它读取尽可能少的字节以识别文件类型签名。
Relevant version of script:
相关版本的脚本:
https://raw.githubusercontent.com/scardine/image_size/master/get_image_size.py
https://raw.githubusercontent.com/scardine/image_size/master/get_image_size.py
[update]
[更新]
Hmmm, unfortunately, when applied to jpegs, the above gives "'JPEG image data, EXIF standard 2.21'". No image size! – Alex Flint
嗯,不幸的是,当应用于 jpeg 时,上面给出了“'JPEG 图像数据,EXIF 标准 2.21'”。没有图片大小!– 亚历克斯弗林特
Seems like jpegs are magic-resistant. :-)
好像 jpeg 是抗魔法的。:-)
I can see why: in order to get the image dimensions for JPEG files, you may have to read more bytes than libmagic likes to read.
我明白为什么:为了获得 JPEG 文件的图像尺寸,您可能需要读取比 libmagic 喜欢读取的更多字节。
Rolled up my sleeves and came with this very untested snippet (get it from GitHub)that requires no third-party modules.
卷起袖子,带来了这个非常未经测试的代码片段(从 GitHub 获取),它不需要第三方模块。


#-------------------------------------------------------------------------------
# Name: get_image_size
# Purpose: extract image dimensions given a file path using just
# core modules
#
# Author: Paulo Scardine (based on code from Emmanuel VA?SSE)
#
# Created: 26/09/2013
# Copyright: (c) Paulo Scardine 2013
# Licence: MIT
#-------------------------------------------------------------------------------
#!/usr/bin/env python
import os
import struct
class UnknownImageFormat(Exception):
pass
def get_image_size(file_path):
"""
Return (width, height) for a given img file content - no external
dependencies except the os and struct modules from core
"""
size = os.path.getsize(file_path)
with open(file_path) as input:
height = -1
width = -1
data = input.read(25)
if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
# GIFs
w, h = struct.unpack("<HH", data[6:10])
width = int(w)
height = int(h)
elif ((size >= 24) and data.startswith('1PNG\r\n2\n')
and (data[12:16] == 'IHDR')):
# PNGs
w, h = struct.unpack(">LL", data[16:24])
width = int(w)
height = int(h)
elif (size >= 16) and data.startswith('1PNG\r\n2\n'):
# older PNGs?
w, h = struct.unpack(">LL", data[8:16])
width = int(w)
height = int(h)
elif (size >= 2) and data.startswith('70'):
# JPEG
msg = " raised while trying to decode as JPEG."
input.seek(0)
input.read(2)
b = input.read(1)
try:
while (b and ord(b) != 0xDA):
while (ord(b) != 0xFF): b = input.read(1)
while (ord(b) == 0xFF): b = input.read(1)
if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
input.read(3)
h, w = struct.unpack(">HH", input.read(4))
break
else:
input.read(int(struct.unpack(">H", input.read(2))[0])-2)
b = input.read(1)
width = int(w)
height = int(h)
except struct.error:
raise UnknownImageFormat("StructError" + msg)
except ValueError:
raise UnknownImageFormat("ValueError" + msg)
except Exception as e:
raise UnknownImageFormat(e.__class__.__name__ + msg)
else:
raise UnknownImageFormat(
"Sorry, don't know how to get information from this file."
)
return width, height
[update 2019]
[2019年更新]
Check out a Rust implementation: https://github.com/scardine/imsz
查看 Rust 实现:https: //github.com/scardine/imsz
回答by Yantao Xie
This answerhas an another good resolution, but missing the pgmformat. This answerhas resolved the pgm. And I add the bmp.
这个答案有另一个很好的分辨率,但缺少pgm格式。这个答案已经解决了pgm。我添加了bmp。
Codes is below
代码如下
import struct, imghdr, re, magic
def get_image_size(fname):
'''Determine the image type of fhandle and return its size.
from draco'''
with open(fname, 'rb') as fhandle:
head = fhandle.read(32)
if len(head) != 32:
return
if imghdr.what(fname) == 'png':
check = struct.unpack('>i', head[4:8])[0]
if check != 0x0d0a1a0a:
return
width, height = struct.unpack('>ii', head[16:24])
elif imghdr.what(fname) == 'gif':
width, height = struct.unpack('<HH', head[6:10])
elif imghdr.what(fname) == 'jpeg':
try:
fhandle.seek(0) # Read 0xff next
size = 2
ftype = 0
while not 0xc0 <= ftype <= 0xcf:
fhandle.seek(size, 1)
byte = fhandle.read(1)
while ord(byte) == 0xff:
byte = fhandle.read(1)
ftype = ord(byte)
size = struct.unpack('>H', fhandle.read(2))[0] - 2
# We are at a SOFn block
fhandle.seek(1, 1) # Skip `precision' byte.
height, width = struct.unpack('>HH', fhandle.read(4))
except Exception: #IGNORE:W0703
return
elif imghdr.what(fname) == 'pgm':
header, width, height, maxval = re.search(
b"(^P5\s(?:\s*#.*[\r\n])*"
b"(\d+)\s(?:\s*#.*[\r\n])*"
b"(\d+)\s(?:\s*#.*[\r\n])*"
b"(\d+)\s(?:\s*#.*[\r\n]\s)*)", head).groups()
width = int(width)
height = int(height)
elif imghdr.what(fname) == 'bmp':
_, width, height, depth = re.search(
b"((\d+)\sx\s"
b"(\d+)\sx\s"
b"(\d+))", str).groups()
width = int(width)
height = int(height)
else:
return
return width, height
回答by user2923419
I often fetch image sizes on the Internet. Of course, you can't download the image and then load it to parse the information. It's too time consuming. My method is to feed chunks to an image container and test whether it can parse the image every time. Stop the loop when I get the information I want.
我经常在互联网上获取图像大小。当然不能下载图片然后加载来解析信息。太费时间了。我的方法是将 chunks 输入到一个图像容器中,并测试它是否每次都可以解析图像。当我得到我想要的信息时停止循环。
I extracted the core of my code and modified it to parse local files.
我提取了代码的核心并对其进行了修改以解析本地文件。
from PIL import ImageFile
ImPar=ImageFile.Parser()
with open(r"D:\testpic\test.jpg", "rb") as f:
ImPar=ImageFile.Parser()
chunk = f.read(2048)
count=2048
while chunk != "":
ImPar.feed(chunk)
if ImPar.image:
break
chunk = f.read(2048)
count+=2048
print(ImPar.image.size)
print(count)
Output:
输出:
(2240, 1488)
38912
The actual file size is 1,543,580 bytes and you only read 38,912 bytes to get the image size. Hope this will help.
实际文件大小为 1,543,580 字节,您只需读取 38,912 字节即可获得图像大小。希望这会有所帮助。
回答by Lenar Hoyt
Another short way of doing it on Unix systems. It depends on the output of filewhich I am not sure is standardized on all systems. This should probably not be used in production code. Moreover most JPEGs don't report the image size.
在 Unix 系统上执行此操作的另一种简短方法。这取决于file我不确定在所有系统上是否标准化的输出。这可能不应该在生产代码中使用。此外,大多数 JPEG 不报告图像大小。
import subprocess, re
image_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file " + filename))[-1]))
回答by Jonathan
There is a package on pypi called imagesizethat currently works for me, although it doesn't look like it is very active.
pypi 上有一个包imagesize,目前对我有用,尽管它看起来不太活跃。
Install:
安装:
pip install imagesize
Usage:
用法:
import imagesize
width, height = imagesize.get("test.png")
print(width, height)
Homepage: https://github.com/shibukawa/imagesize_py
主页:https: //github.com/shibukawa/imagesize_py
PyPi: https://pypi.org/project/imagesize/
PyPi:https://pypi.org/project/imagesize/

