이미지를 메모리에로드하지 않고 이미지 크기 가져 오기


113

PIL을 사용하여 다음과 같은 방식으로 이미지 크기를 얻을 수 있음을 이해합니다.

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

그러나 이미지를 메모리에로드 하지 않고도 이미지 너비와 높이를 얻고 싶습니다 . 가능합니까? 저는 이미지 크기에 대한 통계 만하고 있으며 이미지 내용은 신경 쓰지 않습니다. 처리 속도를 높이고 싶습니다.


8
100 % 확실하지는 않지만 이것이 .open()전체 파일을 메모리로 읽는 다고 믿지 않습니다 . (그게 무슨 일인지 .load())-제가 아는 한-이것은 사용하는 것만 큼 좋습니다PIL
Jon Clements

5
이미지 헤더 정보 만 읽는 기능이 있다고 생각하더라도 파일 시스템 미리 읽기 코드는 여전히 전체 이미지를로드 할 수 있습니다. 애플리케이션에서 요구하지 않는 한 성능에 대한 걱정은 비생산적입니다.
스탁

1
나는 당신의 대답을 확신하게되었습니다. 감사합니다 @JonClements 및 스탁
사미 A. Haija

9
pmap프로세스에서 사용하는 메모리를 모니터링하는 데 사용하는 빠른 메모리 테스트 는 실제로 PIL전체 이미지를 메모리에로드하지 않는다는 것을 보여줍니다 .
Vincent Nivoliers 2013

답변:


63

주석에서 알 수 있듯이 PIL은를 호출 할 때 이미지를 메모리에로드하지 않습니다 .open. 의 문서를 보면에 대한 PIL 1.1.7독 스트링 .open은 다음과 같습니다.

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

소스에는 다음과 같은 몇 가지 파일 작업이 있습니다.

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

그러나 이것들은 전체 파일을 읽는 것을 구성하지 않습니다. 실제로 .open성공하면 단순히 파일 객체와 파일 이름을 반환합니다. 또한 문서 는 다음과 같이 말합니다.

열기 (파일, 모드 =”r”)

주어진 이미지 파일을 열고 식별합니다.

이것은 게으른 작업입니다. 이 함수는 파일을 식별하지만 실제 이미지 데이터는 데이터 처리 (또는 load 메소드 호출)를 시도 할 때까지 파일에서 읽히지 않습니다 .

더 자세히 살펴보면 이미지 형식 특정 오버로드 인 .open호출 _open을 볼 수 있습니다. 각 구현 _open은 새 파일에서 찾을 수 있습니다. .jpeg 파일은 JpegImagePlugin.py. 자세히 살펴 보겠습니다.

여기에서 약간 까다로운 것 같습니다. jpeg 마커가 발견되면 끊기는 무한 루프가 있습니다.

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

형식 이 잘못된 경우 전체 파일을 읽을 수있는 것처럼 보입니다 . 그러나 정보 마커 OK를 읽으면 일찍 나옵니다. 이 기능은 handler궁극적으로 self.size이미지의 크기를 설정합니다.


1
충분히 사실이지만 이미지 open크기 를 얻 습니까? 아니면 게으른 작업입니까? 게으른 경우 이미지 데이터를 동시에 읽습니까?
Mark Ransom 2013 년

문서 링크는 PIL의 Pillow a 포크를 가리 킵니다. 그러나 웹에서 공식 문서 링크를 찾을 수 없습니다. 누군가 댓글로 게시하면 답변을 업데이트하겠습니다. 견적은 파일에서 찾을 수 있습니다 Docs/PIL.Image.html.
Hooked

@MarkRansom 귀하의 질문에 대답하려고 시도했지만 각 이미지 별 구현에 대해 자세히 살펴 봐야 할 것 같습니다. .jpeg헤더가 있는 한 형식은 괜찮아 보입니다.
Hooked

@Hooked : 이것을 조사해 주셔서 대단히 감사합니다. 난 당신이 올바른지 동의를 나는 아주 (영업 이익이 PIL 의존성을 피하기 위해 원하는 언급하지 않았다 공정하게하더라도) 아래 파울로의이 아니라 최소한의 솔루션과 같은 비록
알렉스 플린트

@AlexFlint 문제 없습니다. 코드를 둘러 보는 것은 항상 재미 있습니다. 나는 Paulo가 그의 현상금을 받았다고 말하고 싶습니다. 그것은 그가 거기에서 당신을 위해 썼던 멋진 스 니펫입니다.
Hooked

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

이것은 파일 유형 서명을 식별하기 위해 가능한 한 적은 바이트를 읽는 libmagic을 둘러싼 래퍼입니다.

관련 스크립트 버전 :

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

[최신 정보]

흠, 안타깝게도 jpeg에 적용하면 위의 내용은 " 'JPEG 이미지 데이터, EXIF ​​표준 2.21'"을 제공합니다. 이미지 크기가 없습니다! – 알렉스 부싯돌

jpeg는 마법에 강합니다. :-)

이유를 알 수 있습니다. JPEG 파일의 이미지 크기를 얻으려면 libmagic이 읽는 것보다 더 많은 바이트를 읽어야 할 수 있습니다.

내 소매 를 감고 타사 모듈이 필요하지 않은 테스트되지 않은 코드 조각 (GitHub에서 가져 오기)과 함께 제공되었습니다 .

봐, 엄마!  아니 deps!

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

[2019 년 업데이트]

Rust 구현을 확인하세요 : https://github.com/scardine/imsz


3
또한 @EJEHardenberg가 위에 제공 한 버전 이후 주석에 채널 수 (비트 깊이와 혼동하지 말 것)를 검색하는 기능을 추가했습니다 .
Greg Kramida

2
훌륭합니다. GitHub 프로젝트에서 비트 맵에 대한 지원을 추가했습니다. 감사!
Mallard

2
참고 : 현재 버전은 나를 위해 작동하지 않습니다. @PauloScardine은 github.com/scardine/image_size에 업데이트 된 작업 버전을 가지고 있습니다
DankMasterDan

2
가져 오기 UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte맥 OS에 python3에 data = input.read(25), file이미지 제공에PNG image data, 720 x 857, 8-bit/color RGB, non-interlaced
mrgloom

3
raw.githubusercontent.com/scardine/image_size/master/…의 코드가 작동하는 것 같습니다 .
mrgloom

24

pypi에는 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
timeit별로 실제 이미지 크기를 얻기 위해 speed imagesize.get, magic.from_file 및 PIL 이미지를 비교했습니다. 결과는 speed imagesize.get (0.019s)> PIL (0.104s)> magic with regex (0.1699s)로 나타났습니다.
RyanLiu

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

Unix 시스템에서이를 수행하는 또 다른 간단한 방법입니다. file모든 시스템에서 표준화되었는지 확실하지 않은 출력에 따라 다릅니다 . 프로덕션 코드에서는 사용하지 않아야합니다. 게다가 대부분의 JPEG는 이미지 크기를보고하지 않습니다.

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 형식이 없습니다 . 이 답변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그러나 특정 jpeg를 매우 열악하게 처리합니다.
martixy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.