获取图像大小,但不将图像加载到内存中

我知道你可以通过以下方式使用PIL来获取图像大小

from PIL import Image im = Image.open(image_filename) width, height = im.size 

不过,我想获得图像的宽度和高度, 不必在内存中加载图像。 那可能吗? 我只做图像大小统计,不关心图像内容。 我只是想让我的处理更快。

正如注释所暗示的,当调用.open时,PIL不会将图像加载到内存中。 看看PIL 1.1.7的文档, .open的文档string说:

 def open(fp, mode="r"): "Open an image file, without loading the raster data" 

源代码中有几个文件操作:

  ... prefix = fp.read(16) ... fp.seek(0) ... 

但是这些很难构成整个文件的阅读。 实际上.open只是返回一个文件对象和成功的文件名。 另外文档说:

打开(文件,模式=“R”)

打开并识别给定的图像文件。

这是一个懒惰的操作; 此函数标识文件,但在您尝试处理数据(或调用装入方法)之前,不会从文件中读取实际的图像数据。

深入挖掘,我们看到.open调用_open这是一个图像格式特定的过载。 每个到_open的实现都可以在新文件中find,例如。 .jpeg文件在JpegImagePlugin.py 。 我们来深入一看。

在这里,事情似乎有点棘手,其中有一个无限循环,当findjpeg标记时,

  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") 

看起来好像可以读取整个文件,如果它是畸形的。 如果它读取信息标记确定,但是,它应该提前爆发。 函数handler最终设置self.size这是图像的尺寸。

如果你不关心图像内容,PIL可能是一个矫枉过正。

我build议parsingpython魔术模块的输出:

 >>> 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') 

这是libmagic的一个包装,它读取尽可能less的字节以识别文件types签名。

[更新]

嗯不幸的是,当应用于JPEG,上面给出了“'JPEG图像数据,EXIF标准2.21'”。 没有图像大小! – 亚历克斯弗林特

看起来像JPEG是魔法抗性的。 🙂

我可以看到为什么:为了获得JPEG文件的图像尺寸,您可能必须读取比libmagic喜欢阅读更多的字节。

卷起我的袖子,并与这个非常未经testing的代码片段(从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('\211PNG\r\n\032\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('\211PNG\r\n\032\n'): # older PNGs? w, h = struct.unpack(">LL", data[8:16]) width = int(w) height = int(h) elif (size >= 2) and data.startswith('\377\330'): # 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 

我经常在互联网上获取图像大小。 当然,你不能下载图像,然后加载它来parsing信息。 这太耗时了。 我的方法是将块馈送到图像容器,并testing是否可以每次parsing图像。 当我得到我想要的信息时停止循环。

我提取了我的代码的核心,并修改它来parsing本地文件。

 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) 

输出:

 (2240, 1488) 38912 

实际文件大小为1,543,580字节,您只能读取38,912字节来获取图像大小。 希望这会有所帮助。

这个答案有另一个很好的解决scheme,但缺lessPGM格式。 这个答案已经解决了pgm 。 我添加了bmp

代码如下

 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 

在Unix系统上的另一个简短的方法。 这取决于我不确定的file的输出在所有系统上是否标准化。 这可能不应该用于生产代码。 此外,大多数JPEG不报告图像大小。

 import subprocess, re image_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file " + filename))[-1]))