Belleğe görüntü yüklemeden görüntü boyutunu alın


114

PIL kullanarak aşağıdaki şekilde görüntü boyutunu alabileceğinizi anlıyorum

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

Ancak, görüntüyü belleğe yüklemeye gerek kalmadan görüntü genişliğini ve yüksekliğini elde etmek istiyorum . Mümkün mü? Ben sadece resim boyutlarıyla ilgili istatistikler yapıyorum ve resim içerikleriyle ilgilenmiyorum. Sadece işlememi daha hızlı hale getirmek istiyorum.


8
% 100 emin değilim ama .open()bunun tüm dosyayı hafızaya okuduğuna inanmıyorum ... (bu .load()) işe yarıyor - bildiğim kadarıyla - kullandığı kadar iyiPIL
Jon Clements

5
Yalnızca görüntü başlığı bilgisini okuyan bir işleve sahip olduğunuzu düşünseniz bile, dosya sistemi önden okuma kodu yine de tüm görüntüyü yükleyebilir. Uygulamanız gerektirmedikçe performans konusunda endişelenmek verimsizdir.
Stark

1
Cevaplarınıza ikna oldum. Teşekkürler @JonClements ve Stark
Sami A. Haija

9
Bir pmapişlem tarafından kullanılan belleği izlemek için kullanılan hızlı bir bellek testi , gerçekten PILde tüm görüntünün belleğe yüklenmediğini gösteriyor .
Vincent Nivoliers

Yanıtlar:


63

Yorumların ima ettiği gibi, PIL arama sırasında görüntüyü belleğe yüklemez .open. Ait dokümanlar baktığımızda PIL 1.1.7için docstring'ini .opendiyor ki:

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

Kaynakta aşağıdaki gibi birkaç dosya işlemi vardır:

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

ancak bunlar tüm dosyayı okumayı oluşturmaz. Aslında .open, başarı durumunda bir dosya nesnesi ve dosya adını döndürür. Ek olarak, dokümanlar şunları söylüyor:

open (dosya, mod = ”r”)

Verilen görüntü dosyasını açar ve tanımlar.

Bu tembel bir işlemdir; bu işlev dosyayı tanımlar, ancak gerçek görüntü verileri, siz verileri işlemeye (veya yükleme yöntemini çağırmaya ) kadar dosyadan okunmaz .

Derin kazma, biz görüyoruz .opençağrıları _openbir görüntü formatlı özgü aşırı yük olduğunu. Uygulamaların her biri _openyeni bir dosyada bulunabilir, örn. .jpeg dosyaları bulunmaktadır JpegImagePlugin.py. Buna derinlemesine bakalım.

Burada işler biraz karmaşıklaşıyor gibi görünüyor, içinde jpeg işaretçisi bulunduğunda kırılan sonsuz bir döngü var:

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

Hangisi gibi görünüyor olabilir o şekil bozukluğuna eğer tüm dosyayı okuyun. Ancak bilgi işaretini okursa, erken çıkması gerekir. İşlev, handlernihayetinde self.sizegörüntünün boyutları olan boyutları belirler .


1
Doğru yeterli, ama yok openolsun boyutu görüntünün veya tembel operasyon bunu da mı? Ve tembel ise, görüntü verilerini aynı anda okur mu?
Mark Ransom

Doküman bağlantısı, PIL'den Pillow a fork'a işaret ediyor. Ancak web'de resmi bir belge bağlantısı bulamıyorum. Birisi bunu yorum olarak yayınlarsa, cevabı güncellerim. Alıntı dosyada bulunabilir Docs/PIL.Image.html.
Hooked

@MarkRansom Sorunuzu yanıtlamaya çalıştım, ancak% 100 emin olmak için her resme özgü uygulamaya dalmamız gerekiyor gibi görünüyor. .jpegBaşlık bulunur bulunmaz biçimi sürece Tamam görünüyor.
Hooked

@Hooked: Buna baktığınız için çok teşekkürler. Sana doğru olduğunu kabul Oldukça (OP PIL bağımlılığını önlemek isteyen bahsetmemişti adil olmak olsa bile) altında Paulo'nun oldukça az çözüm gibi olsa
Alex Flint

@AlexFlint Sorun değil, kodun etrafından dolaşmak her zaman eğlencelidir. Paulo'nun ödülünü kazandığını söyleyebilirim, bu sizin için orada yazdığı güzel bir pasaj.
Hooked

88

Görüntü içeriğini umursamıyorsanız, PIL muhtemelen abartılıdır.

Python sihirli modülünün çıktısını ayrıştırmanızı öneririm:

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

Bu, bir dosya tipi imzasını tanımlamak için mümkün olduğunca az bayt okuyan libmagic etrafında bir sarmalayıcıdır.

Komut dosyasının ilgili sürümü:

https://raw.githubusercontent.com/scardine/image_size/master/get_image_size.py

[Güncelleme]

Hmmm, maalesef jpeg'lere uygulandığında, yukarıdaki "'JPEG görüntü verisi, EXIF ​​standardı 2.21" verir. Görüntü boyutu yok! - Alex Flint

Görünüşe göre jpeg'ler sihire dayanıklı. :-)

Nedenini anlayabiliyorum: JPEG dosyalarının görüntü boyutlarını elde etmek için libmagic'in okumayı sevdiğinden daha fazla bayt okumanız gerekebilir.

Kollarımı sıvadım ve üçüncü taraf modülleri gerektirmeyen bu çok denenmemiş snippet (GitHub'dan alın) ile geldi .

Bak anne!  Deps yok!

#-------------------------------------------------------------------------------
# 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

[güncelleme 2019]

Rust uygulamasına göz atın: https://github.com/scardine/imsz


3
Ayrıca @EJEHardenberg sürümünün yukarıda sağladığından sonra yoruma kanal sayısını (bit derinliğiyle karıştırılmaması gereken) alma özelliğini ekledim .
Greg Kramida

2
Güzel şey. GitHub projesine bitmap desteği ekledim. Teşekkürler!
Mallard

2
NOT: mevcut sürüm benim için çalışmıyor. @PauloScardine, github.com/scardine/image_size adresinde güncellenmiş bir çalışma sürümüne sahip
DankMasterDan

2
Get UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byteMacOS üzerinde python3 üzerinde data = input.read(25), filegörüntü verir üzerindekiPNG image data, 720 x 857, 8-bit/color RGB, non-interlaced
mrgloom


24

imagesizeÇok aktif görünmese de şu anda benim için çalışan pypi adında bir paket var .

Yüklemek:

pip install imagesize

Kullanımı:

import imagesize

width, height = imagesize.get("test.png")
print(width, height)

Ana Sayfa: https://github.com/shibukawa/imagesize_py

PyPi: https://pypi.org/project/imagesize/


3
Gerçek görüntü boyutunu zamana göre elde etmek için imagesize.get, magic.from_file ve PIL görüntüsünü karşılaştırdım. Sonuçlar, hızlı imagesize.get (0.019s)> PIL (0.104s)> magic with regex (0.1699s) olduğunu gösterdi.
RyanLiu

9

İnternetten sık sık resim boyutları getiriyorum. Elbette, görüntüyü indirip ardından bilgileri ayrıştırmak için yükleyemezsiniz. Çok zaman alıyor. Benim yöntemim, parçaları bir görüntü kabına beslemek ve görüntüyü her seferinde ayrıştırıp ayrıştıramadığını test etmektir. İstediğim bilgiyi aldığımda döngüyü durdur.

Kodumun çekirdeğini çıkardım ve yerel dosyaları ayrıştırmak için değiştirdim.

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)

Çıktı:

(2240, 1488)
38912

Gerçek dosya boyutu 1.543.580 bayttır ve görüntü boyutunu elde etmek için yalnızca 38.912 bayt okursunuz. Umarım bu yardımcı olur.


1

Unix sistemlerinde yapmanın başka bir kısa yolu. fileTüm sistemlerde standardize edildiğinden emin olmadığım çıktıya bağlı . Bu muhtemelen üretim kodunda kullanılmamalıdır. Dahası, çoğu JPEG görüntü boyutunu bildirmez.

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

VerirIndexError: list index out of range
mrgloom

0

Bu cevabın başka bir iyi çözünürlüğü var, ancak pgm formatı eksik . Bu cevap pgm'yi çözdü . Ve bmp ekliyorum .

Kodlar aşağıda

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

imghdrancak bazı jpeg'leri oldukça kötü işler.
martixy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.