¿Cómo puedo obtener el tamaño de la imagen usando una clase estándar de Python (sin usar una biblioteca externa)?

7 minutos de lectura

avatar de usuario de eros
Eros

Estoy usando Python 2.5. Y usando las clases estándar de Python, quiero determinar el tamaño de imagen de un archivo.

He oído PIL (Biblioteca de imágenes de Python), pero requiere instalación para funcionar.

¿Cómo podría obtener el tamaño de una imagen sin usar ninguna biblioteca externa, solo usando los propios módulos de Python 2.5?

Tenga en cuenta que quiero admitir formatos de imagen comunes, particularmente JPG y PNG.

  • alguna sugerencia de que formato de la imagen que desea saber el tamaño de?

    – Larry Lustig

    7 de noviembre de 2011 a las 4:13

  • formatos de imagen comunes (PNG y JPG)

    – eros

    7 de noviembre de 2011 a las 4:25

  • Vea mi respuesta en otra pregunta si no le importa usar bibliotecas externas (pero de uso común)

    – Martín Tomas

    7 de marzo de 2017 a las 8:52

Avatar de usuario de Fred el Fantástico
Fred el fantástico

Aquí hay una secuencia de comandos de Python 3 que devuelve una tupla que contiene la altura y el ancho de una imagen para .png, .gif y .jpeg sin usar ninguna biblioteca externa (es decir, a lo que hace referencia Kurt McKee). Debería ser relativamente fácil transferirlo a 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

  • Su código funcionó mayormente así en 2.7.3. Tuve que reescribirlo porque ya tenía un archivo como objeto.

    – xZise

    12 de febrero de 2014 a las 22:12

  • parece fallar con este.

    – Enfermedad

    4 de diciembre de 2015 a las 14:18

  • Y con este.que debería devolver (640,480), pero obtengo (1281, 1).

    – Enfermedad

    6 de diciembre de 2015 a las 15:44


  • Probé solo la parte PNG de esto, pero al menos funciona bien.

    – tembloroso

    20/04/2016 a las 21:34

  • la imagen proporcionada por @Malandy es una línea de base DCT JEPG imagen, no una imagen compatible con ICC/IPTC/JFIF.

    – Mitoxis

    10 de mayo de 2018 a las 7:11

avatar de usuario de tjb
tjb

La respuesta de Kurt necesitaba ser ligeramente modificada para que funcionara para mí.

Primero, en ubuntu: sudo apt-get install python-imaging

Entonces:

from PIL import Image

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

Verificar el manual para más información.

  • No responde a la pregunta: “¿(sin usar una biblioteca externa)?” se especifica en el título, y la pregunta luego se aclara con “He escuchado PIL (Biblioteca de imágenes de Python), pero requiere instalar la biblioteca”.

    – Luna

    16/07/2014 a las 22:00

  • @RossAllan: Claro, pero esta pregunta es la número 1 en Google para variantes de Python Image dimensionsasí que +1 de mí para una respuesta que no necesita reinventar la rueda 🙂

    – Clemente

    1 de marzo de 2016 a las 7:20

avatar de usuario de cbautista
bautista

Esta es una forma de obtener las dimensiones de un archivo PNG sin necesidad de un módulo de terceros. De Python: verifique un archivo PNG y obtenga las dimensiones de la imagen:

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)

Cuando ejecutes esto, te devolverá:

True
(x, y)

Y otro ejemplo que también incluye el manejo de archivos JPEG:
http://markasread.net/post/17551554979/get-image-size-info-using-pure-python-code

  • ¿No es un poco ineficiente leer los datos completos de la imagen si todo lo que necesita son los datos del encabezado?

    – Adam Parkin

    25 de mayo de 2015 a las 2:46

  • Para evitar eso, uno puede refactorizar get_image_info() para tomar el nombre del archivo como un parámetro (en lugar de los datos binarios), y luego simplemente hacer un f.read(25) para leer la información del encabezado solamente.

    – Adam Parkin

    25 de mayo de 2015 a las 3:23

  • El último enlace está roto: “¡Vaya! Esa página no se puede encontrar”.

    -Peter Mortensen

    hace 2 días

Avatar de usuario de Kurt McKee
Kurt Mckee

Si bien es posible llamar open(filename, 'rb') y verifique las dimensiones de los encabezados de imágenes binarias, ¡parece mucho más útil instalar PIL y dedicar su tiempo a escribir un software nuevo y excelente! Obtiene una mayor compatibilidad con el formato de archivo y la confiabilidad que proviene del uso generalizado. De la documentación PILparece que el código que necesitarías para completar tu tarea sería:

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

En cuanto a escribir código usted mismo, no conozco un módulo en la biblioteca estándar de Python que haga lo que usted quiere. Tendrás que open() la imagen en modo binario y comience a decodificarla usted mismo. Puede leer sobre los formatos en:

Avatar de usuario de Dagh Bunnstad
Dagh Bunnstad

Acerca de Fred el fantásticorespuesta de:

No todos los marcadores JPEG entre C0CF son SOF marcadores; Excluí DHT (C4), DNL (C8) y CAD (CC). Tenga en cuenta que no he investigado si es posible analizar cualquier marco que no sea C0 y C2 de esta forma. Sin embargo, los otros parecen ser bastante raros (personalmente no he encontrado ninguno más que C0 y C2).

De cualquier manera, esto resuelve el problema mencionado en los comentarios de Malandy con Bangles.jpg (DHT analizado erróneamente como SOF).

El otro problema mencionado con 1431588037-WgsI3vK.jpg Es debido a imghdr solo pudiendo detectar los encabezados APP0 (EXIF) y APP1 (JFIF).

Esto se puede solucionar agregando una prueba más laxa a imghdr (por ejemplo, simplemente FFD8 o tal vez FFD8FF?) o algo mucho más complejo (posiblemente incluso la validación de datos). Con un enfoque más complejo, solo encontré problemas con: APP14 (FFEE) (Adobe); siendo el primer marcador DQT (FFDB); y APP2 y problemas con ICC_PROFILE incrustados.

Código revisado a continuación, también modificó la llamada a imghdr.what() levemente:

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

Nota: Creé una respuesta completa en lugar de un comentario, ya que todavía no tengo permiso para hacerlo.

avatar de usuario de jensph
jensph

Si tienes imagenmagia instalado, entonces puede usar ‘identificar‘. Por ejemplo, puedes llamarlo así:

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(',') ]

Avatar de usuario de Peter Mortensen
Pedro Mortensen

Encontré una buena solución en otra publicación de Stack Overflow (usando solo bibliotecas estándar + también tratando con JPEG): respuesta de JohnTESlade

Y otra solución (la forma rápida) para aquellos que pueden permitirse ejecutar el ‘archivo‘ dentro del intérprete de Python, ejecute:

import os

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

Producción:

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

Todo lo que tiene que hacer ahora es formatear la salida para capturar las dimensiones. 352×198 en mi caso.

¿Ha sido útil esta solución?