如何使用标准Python类(不使用外部库)获取图像大小?


73

我正在使用Python 2.5。并使用Python中的标准类,我想确定文件的图像大小。

我听说过PIL(Python图像库),但是需要安装才能工作。

仅使用Python 2.5自己的模块,如何在不使用任何外部库的情况下获得图像的大小?

注意我想支持常见的图像格式,尤其是JPG和PNG。


1
什么任何建议格式,你想学的尺寸图像的?
拉里·卢斯蒂格

1
常见的图像格式(PNG和JPG)
eros

如果您不关心使用外部(但常用)库,请参阅我的回答
Martin Thoma

Answers:


91

这是一个python 3脚本,该脚本返回一个元组,其中包含.png,.gif和.jpeg的图像高度和宽度,而无需使用任何外部库(例如,上面提到的Kurt McKee)。将其传输到Python 2应该相对容易。

import struct
import imghdr

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(24)
        if len(head) != 24:
            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
        else:
            return
        return width, height

您的代码工作原理与2.7.3中的类似。我必须重写它,因为我已经有一个像object这样的文件。
xZise 2014年

这似乎失败,这个
Malady 2015年

并与此。,应该返回(640,480),但我得到(1281,1)。
Malady 2015年

我只测试了其中的PNG部分,但至少效果很好。
tremby '16

1
@Malandy提供的图像是DCT JEPG基准图像,而不是ICC / IPTC / JFIF兼容图像。
Mitoxys '18

64

Kurts的答案需要稍作修改才能对我有用。

首先,在ubuntu上: sudo apt-get install python-imaging

然后:

from PIL import Image
im=Image.open(filepath)
im.size # (width,height) tuple

查看手册以获取更多信息。


17
不回答问题-“(不使用外部库)?” 在标题中指定,然后用“我听说过PIL(Python图像库),但需要安装该库”来澄清问题。
露娜(Luna)2014年

13
@RossAllan:好的,但是这个问题在Google上是#1的变体Python Image dimensions,因此是我的#1 ,所以我向+1提出了一个无需重新设计的答案:)
克莱门特

20

尽管可以调用open(filename, 'rb')并检查二进制图像标题中的尺寸,但安装PIL并花时间编写出色的新软件似乎更加有用!您将获得更大的文件格式支持以及广泛使用带来的可靠性。在PIL文档中,看来完成任务所需的代码为:

from PIL import Image
im = Image.open('filename.png')
print 'width: %d - height: %d' % im.size # returns (width, height) tuple

至于自己编写代码,我不知道Python标准库中的模块会做什么。您必须以open()二进制模式处理图像,然后开始自己解码。您可以在以下位置阅读有关格式的信息:


2
+1为文件格式文档,但我的方向不是仅使用外部库来获取png和jpg图像文件的图像大小。
eros

3
你需要Image.open不只是Image每TJB的答案。
Ghopper21年

19

这是一种无需第三方模块即可获取png文件尺寸的方法。来自http://coreygoldberg.blogspot.com/2013/01/python-verify-png-file-and-get-image.html

import struct

def get_image_info(data):
    if is_png(data):
        w, h = struct.unpack('>LL', data[16:24])
        width = int(w)
        height = int(h)
    else:
        raise Exception('not a png image')
    return width, height

def is_png(data):
    return (data[:8] == '\211PNG\r\n\032\n'and (data[12:16] == 'IHDR'))

if __name__ == '__main__':
    with open('foo.png', 'rb') as f:
        data = f.read()

    print is_png(data)
    print get_image_info(data)

运行此命令时,它将返回:

True
(x, y)

另一个包含JPEG处理的示例:http : //markasread.net/post/17551554979/get-image-size-info-using-pure-python-code


如果您只需要标题数据,读取整个图像数据是否有点效率低下?
亚当·帕金

2
为了解决这个问题,可以重构get_image_info()以文件名作为参数(而不是二进制数据),然后只需执行一次f.read(25)即可读取标头信息。
亚当·帕金

7

关于弗雷德神奇的回答

并非所有JPEG标记之间都是C0-标记;我排除了DHT(),DNL()和DAC()。请注意,我还没有研究是否甚至可以分析比其他任何帧,并以这种方式。但是,其他选项似乎很少见(我个人除了和以外都没有遇到过)。CFSOFC4C8CCC0C2C0C2

无论哪种方式,这解决了问题,在注释中提到的MalandyBangles.jpg睾酮(DHT错误地解析为SOF)。

提到的另一个问题1431588037-WgsI3vK.jpg是由于imghdr只能检测到APP0(EXIF)和APP1(JFIF)标头。

可以通过向imghdr添加一个更宽松的测试(例如简单地FFD8或可能是FFD8FF?)或更复杂的测试(甚至可能是数据验证)来解决此问题。通过更复杂的方法,我仅发现以下问题:APP14(FFEE)(Adobe); 第一个标记是DQT(FFDB);和APP2以及嵌入式ICC_PROFILEs的问题

下面的修改后的代码也将调用更改为imghdr.what()

import struct
import imghdr

def test_jpeg(h, f):
    # SOI APP2 + ICC_PROFILE
    if h[0:4] == '\xff\xd8\xff\xe2' and h[6:17] == b'ICC_PROFILE':
        print "A"
        return 'jpeg'
    # SOI APP14 + Adobe
    if h[0:4] == '\xff\xd8\xff\xee' and h[6:11] == b'Adobe':
        return 'jpeg'
    # SOI DQT
    if h[0:4] == '\xff\xd8\xff\xdb':
        return 'jpeg'
imghdr.tests.append(test_jpeg)

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(24)
        if len(head) != 24:
            return
        what = imghdr.what(None, head)
        if what == 'png':
            check = struct.unpack('>i', head[4:8])[0]
            if check != 0x0d0a1a0a:
                return
            width, height = struct.unpack('>ii', head[16:24])
        elif what == 'gif':
            width, height = struct.unpack('<HH', head[6:10])
        elif what == 'jpeg':
            try:
                fhandle.seek(0) # Read 0xff next
                size = 2
                ftype = 0
                while not 0xc0 <= ftype <= 0xcf or ftype in (0xc4, 0xc8, 0xcc):
                    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
        else:
            return
        return width, height

注意:由于我尚未被允许,因此创建了完整的答案而不是评论。


4

如果您恰好安装了ImageMagick,则可以使用“ identify ”。例如,您可以这样称呼它:

path = "//folder/image.jpg"
dim = subprocess.Popen(["identify","-format","\"%w,%h\"",path], stdout=subprocess.PIPE).communicate()[0]
(width, height) = [ int(x) for x in re.sub('[\t\r\n"]', '', dim).split(',') ]

这是一个好主意,但无需调用正则表达式机制或列表理解: width, height = list( map( int, dim.decode('utf-8').strip('"').split(',')))
Giacomo Lacava

2

在另一个Stackoverflow帖子中找到了一个不错的解决方案(仅使用标准库+还处理jpg):JohnTESlade回答

对于那些有能力承担在python中运行' file '命令的人的另一种解决方案(快速方法),请运行:

import os
info = os.popen("file foo.jpg").read()
print info

输出

foo.jpg: JPEG image data...density 28x28, segment length 16, baseline, precision 8, 352x198, frames 3

您现在要做的就是格式化输出以捕获尺寸。在我的情况下为352x198


1

该代码确实完成了两件事:

  • 获取图像尺寸

  • 查找jpg文件的真实EOF

好吧,当谷歌搜索时,我对下一个更感兴趣。任务是从数据流中剪切出jpg文件。由于II找不到任何使用Python's image'的方法来获得jpg-File的EOF的方法,所以我做了这个。

此示例中有趣的事情/更改/注释:

  • 使用uInt16方法扩展了普通的Python文件类,使源代码更易于阅读和维护。迅速使用struct.unpack()弄乱代码,使代码看起来难看

  • 用搜索替换“无趣”区域/块的读取

  • 如果您只想获取尺寸,则可以删除该行:

    hasChunk = ord(byte) not in range( 0xD0, 0xDA) + [0x00] 
    

    ->因为只有在读取图像数据块并在其中注释时才变得重要

    #break
    

    找到尺寸后立即停止阅读。...但是微笑我在说什么-您是编码员;)

      import struct
      import io,os
    
      class myFile(file):
    
          def byte( self ):
               return file.read( self,  1);
    
          def uInt16( self ):
               tmp = file.read( self,  2)
               return struct.unpack( ">H", tmp )[0];
    
      jpeg = myFile('grafx_ui.s00_\\08521678_Unknown.jpg', 'rb')
    
      try:
          height = -1
          width  = -1
          EOI    = -1
    
          type_check = jpeg.read(2)
          if type_check != b'\xff\xd8':
            print("Not a JPG")
    
          else:
    
            byte = jpeg.byte()
    
            while byte != b"":
    
              while byte != b'\xff': byte = jpeg.byte()
              while byte == b'\xff': byte = jpeg.byte()
    
    
              # FF D8       SOI Start of Image
              # FF D0..7  RST DRI Define Restart Interval inside CompressedData
              # FF 00           Masked FF inside CompressedData
              # FF D9       EOI End of Image
              # http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure
              hasChunk = ord(byte) not in range( 0xD0, 0xDA) + [0x00]
              if hasChunk:
                   ChunkSize   =  jpeg.uInt16()  - 2
                   ChunkOffset =  jpeg.tell()
                   Next_ChunkOffset = ChunkOffset + ChunkSize
    
    
              # Find bytes \xFF \xC0..C3 That marks the Start of Frame
              if (byte >= b'\xC0' and byte <= b'\xC3'):
    
                # Found  SOF1..3 data chunk - Read it and quit
                jpeg.seek(1, os.SEEK_CUR)
                h = jpeg.uInt16()
                w = jpeg.uInt16()
    
    
                #break
    
    
              elif (byte == b'\xD9'):
                   # Found End of Image
                   EOI = jpeg.tell()
                   break
              else:
                  # Seek to next data chunk
                 print "Pos: %.4x %x" % (jpeg.tell(), ChunkSize)
    
              if hasChunk:       
                 jpeg.seek(Next_ChunkOffset)
    
              byte = jpeg.byte()
    
            width  = int(w)
            height = int(h)
    
            print("Width: %s, Height: %s  JpgFileDataSize: %x" % (width, height, EOI))
      finally:
          jpeg.close()
    

0

这取决于文件的输出,我不确定该文件是否在所有系统上都是标准化的。某些JPEG不报告图像尺寸

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

-2

偶然发现了这个,但是只要导入numpy,就可以使用以下命令来获取它。

import numpy as np

[y, x] = np.shape(img[:,:,0])

之所以起作用,是因为您忽略了除一种颜色外的所有颜色,然后图像只是2D,因此形状告诉您出价是多少。对Python来说还是一个新手,但似乎是一种简单的方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.