मेमोरी में इमेज लोड किए बिना इमेज साइज प्राप्त करें


113

मैं समझता हूं कि आप निम्नलिखित फैशन में पीआईएल का उपयोग करके छवि आकार प्राप्त कर सकते हैं

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

हालाँकि, मैं छवि को मेमोरी में लोड किए बिना छवि की चौड़ाई और ऊंचाई प्राप्त करना चाहूंगा । क्या यह संभव है? मैं केवल छवि आकारों पर आंकड़े कर रहा हूं और छवि सामग्री की देखभाल नहीं कर रहा हूं। मैं सिर्फ अपनी प्रोसेसिंग तेज करना चाहता हूं।


8
मैं नहीं 100% यकीन है, लेकिन मैं नहीं मानता कि .open()स्मृति में पूरी फ़ाइल पढ़ता है ... (कि क्या .load()) करता है - ताकि तक मुझे पता है - यह अच्छा के रूप में है के रूप में उपयोग हो जाता हैPIL
जॉन क्लेमेंट्स

5
यहां तक ​​कि अगर आपको लगता है कि आपके पास एक फ़ंक्शन है जो केवल छवि हेडर जानकारी पढ़ता है, तो फाइल सिस्टम रीडहेड कोड अभी भी पूरी छवि को लोड कर सकता है। प्रदर्शन के बारे में चिंता करना तब तक अनुत्पादक है जब तक कि आपके आवेदन की आवश्यकता न हो।
स्टार्क

1
मैं आपके जवाबों का कायल हो गया। धन्यवाद @JonClements और स्टार्क
सामी ए।

9
एक pmapप्रक्रिया द्वारा उपयोग की जाने वाली स्मृति की निगरानी के लिए एक त्वरित मेमोरी टेस्ट मुझे दिखाता है कि वास्तव PILमें स्मृति में पूरी छवि को लोड नहीं करता है।
विंसेंट निवलियर्स

जवाबों:


63

टिप्पणियों के अनुसार, पीआईएल कॉल करते समय छवि को मेमोरी में लोड नहीं करता है .open। के डॉक्स को देखते हुए PIL 1.1.7, के लिए docstring .openकहते हैं:

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

स्रोत में कुछ फ़ाइल संचालन हैं:

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

लेकिन ये मुश्किल से पूरी फाइल को पढ़ते हैं। वास्तव में .openबस एक फ़ाइल वस्तु और सफलता पर फ़ाइल नाम देता है। इसके अलावा डॉक्स कहते हैं:

खुला (फ़ाइल, मोड = "आर")

दी गई छवि फ़ाइल को खोलता है और पहचानता है।

यह एक आलसी ऑपरेशन है; यह फ़ंक्शन फ़ाइल की पहचान करता है, लेकिन वास्तविक छवि डेटा फ़ाइल से तब तक नहीं पढ़ा जाता है जब तक आप डेटा को संसाधित करने की कोशिश नहीं करते (या लोड विधि को कॉल करते हैं )।

गहरी खुदाई करते हुए, हम उस .openकॉल को देखते हैं _openजो एक छवि-प्रारूप विशिष्ट अधिभार है। प्रत्येक कार्यान्वयन को _openएक नई फ़ाइल में पाया जा सकता है, जैसे। .jpeg फाइलें अंदर हैं JpegImagePlugin.py। आइए उस एक को गहराई से देखें।

यहाँ चीजें थोड़ी मुश्किल लग रही हैं, इसमें एक अनंत लूप है जो जेपीईजी मार्कर के मिलने पर टूट जाता है:

    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जो छवि के आयाम हैं।


1
यह सच है, लेकिन छवि openका आकार मिलता है या यह आलसी ऑपरेशन भी है? और अगर यह आलसी है, तो क्या यह एक ही समय में छवि डेटा पढ़ता है?
मार्क रैनसम

डॉक लिंक पिलो को पीआईएल से कांटे की ओर इशारा करता है। हालाँकि मुझे वेब पर आधिकारिक डॉक्टर लिंक नहीं मिल सकता है। अगर कोई इसे टिप्पणी के रूप में पोस्ट करता है तो मैं उत्तर को अपडेट करूंगा। फ़ाइल में उद्धरण पाया जा सकता है Docs/PIL.Image.html
झुका दिया

@MarkRansom मैंने आपके प्रश्न का उत्तर देने का प्रयास किया है, हालांकि यह सुनिश्चित करने के लिए कि यह प्रत्येक छवि-विशिष्ट कार्यान्वयन में गोता लगाने के लिए 100% है। .jpegप्रारूप जब तक ठीक लग रहा है के रूप में शीर्ष लेख पाया जाता है।
आदी

@ हूक: इस में देखने के लिए बहुत बहुत धन्यवाद। मैं स्वीकार करता हूं कि आप सही हैं, हालांकि मैं नीचे पाउलो के न्यूनतम न्यूनतम समाधान को पसंद करता हूं (भले ही निष्पक्ष होने के लिए ओपी ने पीआईएल निर्भरता से बचने के लिए उल्लेख नहीं किया है)
एलेक्स फ्लिंट

@AlexFlint कोई समस्या नहीं है, यह कोड के आसपास प्रहार करने के लिए हमेशा मजेदार है। मैं कहूंगा कि पाउलो ने अपना इनाम अर्जित किया, हालांकि यह एक अच्छा स्निपेट है जो उन्होंने आपके लिए वहां लिखा था।
हुक

88

यदि आप छवि सामग्री के बारे में परवाह नहीं करते हैं, तो PIL शायद एक ओवरकिल है।

मैं अजगर जादू मॉड्यूल के उत्पादन को पार्स करने का सुझाव देता हूं:

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

यह एक प्रकार से कामेच्छा के आसपास का आवरण है जो फ़ाइल प्रकार के हस्ताक्षर की पहचान करने के लिए संभव के रूप में कुछ बाइट्स के रूप में पढ़ा जाता है।

स्क्रिप्ट का प्रासंगिक संस्करण:

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

[अपडेट करें]

हम्म, दुर्भाग्य से, जब jpegs पर लागू होता है, तो उपरोक्त "JPEG छवि डेटा, EXIF ​​मानक 2.21" देता है। कोई छवि आकार नहीं! - एलेक्स फ्लिंट

Jpegs की तरह लगता जादू-प्रतिरोधी हैं। :-)

मैं देख सकता हूं कि क्यों: JPEG फ़ाइलों के लिए छवि आयाम प्राप्त करने के लिए, आपको अधिक बाइट्स पढ़ने पड़ सकते हैं, जैसे कि कामेच्छा पढ़ने के लिए पसंद है।

मेरी आस्तीन ऊपर लुढ़क गई और यह बहुत ही अनछुए स्निपेट के साथ आया (इसे 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

[अद्यतन २०१ ९]

एक जंग कार्यान्वयन की जाँच करें: https://github.com/scardine/imsz


3
मैंने ऊपर दिए गए संस्करण @EJEHardenberg के बाद टिप्पणी में चैनलों की संख्या (भ्रमित डब्ल्यू / बिट गहराई नहीं होने) को पुनः प्राप्त करने की क्षमता भी जोड़ी ।
ग्रेग क्रामिडा

2
अच्छा चीज़। मैंने गिटहब परियोजना में बिटमैप के लिए समर्थन जोड़ा। धन्यवाद!
मैलार्ड

2
नोट: वर्तमान संस्करण मेरे लिए काम नहीं करता है। @PauloScardine ने github.com/scardine/image_size
DankMasterDan

2
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byteMacOS पर जाओ , python3 ऑन data = input.read(25), fileइमेज देता हैPNG image data, 720 x 857, 8-bit/color RGB, non-interlaced
mrgloom


24

वहाँ पेपी नामक एक पैकेज है imagesizeजो वर्तमान में मेरे लिए काम करता है, हालांकि ऐसा नहीं लगता है कि यह बहुत सक्रिय है।

इंस्टॉल:

pip install imagesize

उपयोग:

import imagesize

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

होमपेज: https://github.com/shibukawa/imagesize_py

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


3
मैंने वास्तविक समय के हिसाब से वास्तविक छवि आकार प्राप्त करने के लिए स्पीड imagesize.get, magic.from_file और PIL छवि की तुलना की। परिणामों से पता चला है कि गति imagesize.get (0.019s)> PIL (0.104s)> रेगेक्स के साथ जादू (0.1699s)।
रेयानलियु

9

मैं अक्सर इंटरनेट पर छवि आकार प्राप्त करता हूं। बेशक, आप छवि को डाउनलोड नहीं कर सकते हैं और फिर जानकारी को पार्स करने के लिए इसे लोड कर सकते हैं। यह बहुत समय लगता है। मेरी विधि एक छवि कंटेनर में विखंडू को खिलाने की है और परीक्षण करें कि क्या यह छवि को हर बार पार्स कर सकता है। जब मुझे जानकारी मिले, तब लूप बंद कर दें।

मैंने अपने कोड का कोर निकाला और स्थानीय फाइलों को पार्स करने के लिए इसे संशोधित किया।

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 बाइट्स पढ़ते हैं। आशा है कि यह मदद करेगा।


1

यूनिक्स प्रणालियों पर इसे करने का एक और संक्षिप्त तरीका है। यह उस आउटपुट पर निर्भर करता है fileजिसके बारे में मुझे यकीन नहीं है कि सभी प्रणालियों पर मानकीकृत है। इसका उपयोग संभवतः उत्पादन कोड में नहीं किया जाना चाहिए। इसके अलावा अधिकांश JPEGs छवि आकार की रिपोर्ट नहीं करते हैं।

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

देता हैIndexError: list index out of range
mrgloom

0

इस उत्तर में एक और अच्छा रिज़ॉल्यूशन है, लेकिन पीजीएम प्रारूप को याद नहीं कर रहा है । इस उत्तर ने 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

imghdrहालाँकि कुछ ख़ास jpegs को बहुत खराब तरीके से हैंडल करता है।
मार्टिक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.