Skip to content

Commit 08a0b52

Browse files
committed
fixes #546
1 parent 607aa2c commit 08a0b52

File tree

4 files changed

+175
-3
lines changed

4 files changed

+175
-3
lines changed

fastcore/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@
331331
'fastcore.foundation.working_directory': ( 'foundation.html#working_directory',
332332
'fastcore/foundation.py'),
333333
'fastcore.foundation.zip_cycle': ('foundation.html#zip_cycle', 'fastcore/foundation.py')},
334+
'fastcore.imghdr': {},
334335
'fastcore.imports': {},
335336
'fastcore.meta': { 'fastcore.meta.AutoInit': ('meta.html#autoinit', 'fastcore/meta.py'),
336337
'fastcore.meta.AutoInit.__pre_init__': ('meta.html#autoinit.__pre_init__', 'fastcore/meta.py'),

fastcore/imghdr.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""Recognize image file formats based on their first few bytes."""
2+
3+
from os import PathLike
4+
import warnings
5+
6+
__all__ = ["what"]
7+
8+
#-------------------------#
9+
# Recognize image headers #
10+
#-------------------------#
11+
12+
def what(file, h=None):
13+
f = None
14+
try:
15+
if h is None:
16+
if isinstance(file, (str, PathLike)):
17+
f = open(file, 'rb')
18+
h = f.read(32)
19+
else:
20+
location = file.tell()
21+
h = file.read(32)
22+
file.seek(location)
23+
for tf in tests:
24+
res = tf(h, f)
25+
if res:
26+
return res
27+
finally:
28+
if f: f.close()
29+
return None
30+
31+
32+
#---------------------------------#
33+
# Subroutines per image file type #
34+
#---------------------------------#
35+
36+
tests = []
37+
38+
def test_jpeg(h, f):
39+
"""JPEG data with JFIF or Exif markers; and raw JPEG"""
40+
if h[6:10] in (b'JFIF', b'Exif') or h[:4] in (b'\xff\xd8\xff\xdb',b'\xff\xd8\xff\xe2',b'\xff\xd8\xff\xe1'):
41+
return 'jpeg'
42+
43+
tests.append(test_jpeg)
44+
45+
def test_png(h, f):
46+
if h.startswith(b'\211PNG\r\n\032\n'):
47+
return 'png'
48+
49+
tests.append(test_png)
50+
51+
def test_gif(h, f):
52+
"""GIF ('87 and '89 variants)"""
53+
if h[:6] in (b'GIF87a', b'GIF89a'):
54+
return 'gif'
55+
56+
tests.append(test_gif)
57+
58+
def test_tiff(h, f):
59+
"""TIFF (can be in Motorola or Intel byte order)"""
60+
if h[:2] in (b'MM', b'II'):
61+
return 'tiff'
62+
63+
tests.append(test_tiff)
64+
65+
def test_rgb(h, f):
66+
"""SGI image library"""
67+
if h.startswith(b'\001\332'):
68+
return 'rgb'
69+
70+
tests.append(test_rgb)
71+
72+
def test_pbm(h, f):
73+
"""PBM (portable bitmap)"""
74+
if len(h) >= 3 and \
75+
h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r':
76+
return 'pbm'
77+
78+
tests.append(test_pbm)
79+
80+
def test_pgm(h, f):
81+
"""PGM (portable graymap)"""
82+
if len(h) >= 3 and \
83+
h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r':
84+
return 'pgm'
85+
86+
tests.append(test_pgm)
87+
88+
def test_ppm(h, f):
89+
"""PPM (portable pixmap)"""
90+
if len(h) >= 3 and \
91+
h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r':
92+
return 'ppm'
93+
94+
tests.append(test_ppm)
95+
96+
def test_rast(h, f):
97+
"""Sun raster file"""
98+
if h.startswith(b'\x59\xA6\x6A\x95'):
99+
return 'rast'
100+
101+
tests.append(test_rast)
102+
103+
def test_xbm(h, f):
104+
"""X bitmap (X10 or X11)"""
105+
if h.startswith(b'#define '):
106+
return 'xbm'
107+
108+
tests.append(test_xbm)
109+
110+
def test_bmp(h, f):
111+
if h.startswith(b'BM'):
112+
return 'bmp'
113+
114+
tests.append(test_bmp)
115+
116+
def test_webp(h, f):
117+
if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
118+
return 'webp'
119+
120+
tests.append(test_webp)
121+
122+
def test_exr(h, f):
123+
if h.startswith(b'\x76\x2f\x31\x01'):
124+
return 'exr'
125+
126+
tests.append(test_exr)
127+
128+
#--------------------#
129+
# Small test program #
130+
#--------------------#
131+
132+
def test():
133+
import sys
134+
recursive = 0
135+
if sys.argv[1:] and sys.argv[1] == '-r':
136+
del sys.argv[1:2]
137+
recursive = 1
138+
try:
139+
if sys.argv[1:]:
140+
testall(sys.argv[1:], recursive, 1)
141+
else:
142+
testall(['.'], recursive, 1)
143+
except KeyboardInterrupt:
144+
sys.stderr.write('\n[Interrupted]\n')
145+
sys.exit(1)
146+
147+
def testall(list, recursive, toplevel):
148+
import sys
149+
import os
150+
for filename in list:
151+
if os.path.isdir(filename):
152+
print(filename + '/:', end=' ')
153+
if recursive or toplevel:
154+
print('recursing down:')
155+
import glob
156+
names = glob.glob(os.path.join(glob.escape(filename), '*'))
157+
testall(names, recursive, 0)
158+
else:
159+
print('*** directory (use -r) ***')
160+
else:
161+
print(filename + ':', end=' ')
162+
sys.stdout.flush()
163+
try:
164+
print(what(filename))
165+
except OSError:
166+
print('*** not found ***')
167+
168+
if __name__ == '__main__':
169+
test()

fastcore/xtras.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ def mkdir(path, exist_ok=False, parents=False, overwrite=False, **kwargs):
9191
# %% ../nbs/03_xtras.ipynb 28
9292
def image_size(fn):
9393
"Tuple of (w,h) for png, gif, or jpg; `None` otherwise"
94-
import imghdr,struct
94+
from fastcore import imghdr
95+
import struct
9596
def _jpg_size(f):
9697
size,ftype = 2,0
9798
while not 0xc0 <= ftype <= 0xcf:

nbs/03_xtras.ipynb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208
"metadata": {},
209209
"outputs": [],
210210
"source": [
211-
"import imghdr"
211+
"from fastcore import imghdr"
212212
]
213213
},
214214
{
@@ -375,7 +375,8 @@
375375
"#|export\n",
376376
"def image_size(fn):\n",
377377
" \"Tuple of (w,h) for png, gif, or jpg; `None` otherwise\"\n",
378-
" import imghdr,struct\n",
378+
" from fastcore import imghdr\n",
379+
" import struct\n",
379380
" def _jpg_size(f):\n",
380381
" size,ftype = 2,0\n",
381382
" while not 0xc0 <= ftype <= 0xcf:\n",

0 commit comments

Comments
 (0)