Sam*_*ija 96 python image image-processing
我知道您可以使用PIL以下列方式获取图像大小
from PIL import Image
im = Image.open(image_filename)
width, height = im.size
Run Code Online (Sandbox Code Playgroud)
但是,我想获得图像的宽度和高度,而不必将图像加载到内存中.那可能吗?我只对图像大小进行统计,不关心图像内容.我只想让我的处理更快.
Pau*_*ine 81
如果您不关心图像内容,PIL可能是一种矫枉过正.
我建议解析python magic模块的输出:
>>> 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')
Run Code Online (Sandbox Code Playgroud)
这是libmagic的包装器,它读取尽可能少的字节以识别文件类型签名.
相关版本的脚本:
https://raw.githubusercontent.com/scardine/image_size/master/get_image_size.py
[更新]
嗯,不幸的是,当应用于jpegs时,上面给出了''JPEG图像数据,EXIF标准2.21'".没有图像尺寸! - 亚历克斯弗林特
看起来像jpegs是魔法抗性的.:-)
我明白为什么:为了获得JPEG文件的图像尺寸,你可能需要阅读比libmagic喜欢阅读更多的字节.
卷起袖子,带着这个非常未经测试的片段(从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
Run Code Online (Sandbox Code Playgroud)
Hoo*_*ked 58
正如评论所暗示的那样,PIL在调用时不会将图像加载到内存中.open.查看文档PIL 1.1.7,文档字符串.open说:
def open(fp, mode="r"):
"Open an image file, without loading the raster data"
Run Code Online (Sandbox Code Playgroud)
源中有一些文件操作,如:
...
prefix = fp.read(16)
...
fp.seek(0)
...
Run Code Online (Sandbox Code Playgroud)
但这些几乎不构成阅读整个文件.实际上,.open只需在成功时返回文件对象和文件名.此外,文档说:
打开(文件,模式="r")
打开并标识给定的图像文件.
这是一个懒惰的操作; 此函数标识文件,但在您尝试处理数据(或调用加载方法)之前,不会从文件中读取实际图像数据.
深入挖掘,我们看到.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")
Run Code Online (Sandbox Code Playgroud)
看起来它可能会读取整个文件,如果它格式不正确.如果它读取信息标记OK,它应该提前爆发.该函数handler最终设置self.size哪些是图像的尺寸.
Dre*_*her 15
OP 对“更快”的解决方案感兴趣,我对最快的解决方案感到好奇,我试图用现实世界的基准来回答这个问题。
我正在比较:
cv2.imread: https: //www.kite.com/python/docs/cv2.imreadPIL.open: https: //pillow.readthedocs.io/en/stable/reference/Image.htmlopsdroid/image_size: https: //github.com/opsdroid/image_size(打包版本的https://raw.githubusercontent.com/scardine/image_size/master/get_image_size.py)shibukawa/imagesize_py: https: //github.com/shibukawa/imagesize_pykobaltcore/pymage_size: https: //github.com/kobaltcore/pymage_size我在 202897 个主要是 JPG 文件上运行以下代码。
"""
pip install opsdroid-get-image-size --user
pip install pymage_size
pip install imagesize
"""
import concurrent.futures
from pathlib import Path
import cv2
import numpy as np
import pandas as pd
from tqdm import tqdm
from PIL import Image
import get_image_size
import imagesize
import pymage_size
files = [str(p.resolve())
for p in Path("/data/").glob("**/*")
if p.suffix in {".jpg", ".jpeg", ".JPEG", ".JPG", ".png", ".PNG"}]
def get_shape_cv2(fname):
img = cv2.imread(fname)
return (img.shape[0], img.shape[1])
with concurrent.futures.ProcessPoolExecutor(8) as executor:
results = list(tqdm(executor.map(get_shape_cv2, files), total=len(files)))
def get_shape_pil(fname):
img=Image.open(fname)
return (img.size[0], img.size[1])
with concurrent.futures.ProcessPoolExecutor(8) as executor:
results = list(tqdm(executor.map(get_shape_pil, files), total=len(files)))
def get_shape_scardine_size(fname):
try:
width, height = get_image_size.get_image_size(fname)
except get_image_size.UnknownImageFormat:
width, height = -1, -1
return (width, height)
with concurrent.futures.ProcessPoolExecutor(8) as executor:
results = list(tqdm(executor.map(get_shape_scardine_size, files), total=len(files)))
def get_shape_shibukawa(fname):
width, height = imagesize.get(fname)
return (width, height)
with concurrent.futures.ProcessPoolExecutor(8) as executor:
results = list(tqdm(executor.map(get_shape_shibukawa, files), total=len(files)))
def get_shape_pymage_size(fname):
img_format = pymage_size.get_image_size(fname)
width, height = img_format.get_dimensions()
return (width, height)
with concurrent.futures.ProcessPoolExecutor(8) as executor:
results = list(tqdm(executor.map(get_shape_pymage_size, files), total=len(files)))
Run Code Online (Sandbox Code Playgroud)
结果:
cv2.imread:8分23秒PIL.open:2分0秒opsdroid/image_size:29秒shibukawa/imagesize_py:29秒kobaltcore/pymage_size:29秒因此 opsdroid、shibukawa 和 kobaltcore 的执行速度相同。现在对我来说另一个有趣的点是更好地了解哪些库具有最好的格式支持。
[编辑] 所以我继续测试快速库是否提供不同的结果:
# test if the libs provide the same results
def show_size_differences(fname):
w1, h1 = get_shape_scardine_size(fname)
w2, h2 = get_shape_pymage_size(fname)
w3, h3 = get_shape_shibukawa(fname)
if w1 != w2 or w2 != w3 or h1 != h2 or h2 != h3:
print(f"scardine: {w1}x{h1}, pymage: {w2}x{h2}, shibukawa: {w3}x{h3}")
with concurrent.futures.ProcessPoolExecutor(8) as executor:
results = list(tqdm(executor.map(show_size_differences, files), total=len(files)))
Run Code Online (Sandbox Code Playgroud)
但他们没有。
Jon*_*han 14
在pypi上有一个名为的程序包imagesize目前对我有用,尽管它看起来不太活跃。
安装:
pip install imagesize
Run Code Online (Sandbox Code Playgroud)
用法:
import imagesize
width, height = imagesize.get("test.png")
print(width, height)
Run Code Online (Sandbox Code Playgroud)
主页:https://github.com/shibukawa/imagesize_py
PyPi:https://pypi.org/project/imagesize/
我经常在Internet上获取图像大小。当然,您不能下载图像然后加载它以解析信息。太浪费时间了。我的方法是将大块数据馈送到图像容器,并测试它是否每次都能解析图像。当我得到我想要的信息时,停止循环。
我提取了代码的核心,并对其进行了修改以解析本地文件。
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)
Run Code Online (Sandbox Code Playgroud)
输出:
(2240, 1488)
38912
Run Code Online (Sandbox Code Playgroud)
实际文件大小为1,543,580字节,您仅读取38,912字节即可获取图像大小。希望这会有所帮助。