Skip to content

Commit b7ad787

Browse files
committed
Add Radiance .hdr support + unit tests
Also change read() behavior such that an exception is raised if file parsing fails. Previously, an exception was raised in some cases, while in others, None was returned. Also, basic file attributes (name, type, size) are now returned even if the file is not recognized.
1 parent cac11d4 commit b7ad787

File tree

7 files changed

+63
-12
lines changed

7 files changed

+63
-12
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
[![Build Status](https://travis-ci.com/toaarnio/imsize.svg?branch=master)](https://travis-ci.com/github/toaarnio/imsize)
44

5-
Lightning-fast extraction of image dimensions & bit depth. Tested on Python 3.8 and Ubuntu 20.04.
5+
Lightning-fast extraction of image dimensions & bit depth. Tested on Python 3.8+ and Ubuntu 22.04.
66

7-
Supports PGM / PPM / PNM / PFM / PNG / BMP / JPG / TIFF / INSP / EXR / DNG / CR2 / NEF / RAW / NPY.
7+
Supports PGM / PPM / PNM / PFM / PNG / BMP / JPG / TIFF / INSP / EXR / HDR / DNG / CR2 / NEF / RAW / NPY.
88

99
**Installing on Linux:**
1010
```

imsize/consoleapp.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222

2323
FILETYPES = ["*.png", "*.pnm", "*.pgm", "*.ppm", "*.pfm", "*.bmp",
24-
"*.jpeg", "*.jpg", "*.insp", "*.tiff", "*.tif",
24+
"*.jpeg", "*.jpg", "*.insp", "*.tiff", "*.tif", "*.hdr",
2525
"*.exr", "*.dng", "*.cr2", "*.nef", "*.raw", "*.npy"]
2626

2727

imsize/imsize.py

+44-7
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class ImageInfo:
4444
4545
Attributes:
4646
filespec (str): The filespec given to read(), copied verbatim
47-
filetype (str): File type: png|pnm|pfm|bmp|jpeg|insp|tiff|exr|dng|cr2|nef|raw|npy
47+
filetype (str): File type: png|pnm|pfm|bmp|jpeg|insp|tiff|exr|hdr|dng|cr2|nef|raw|npy|...
4848
filesize (int): Size of the file on disk in bytes
4949
header_size (int): Size of .raw file header in bytes
5050
isfloat (bool): True if the image is in floating-point format
@@ -91,8 +91,10 @@ def read(filespec):
9191
"""
9292
Parses a lowest common denominator set of metadata from the given
9393
image, i.e., the dimensions and bit depth. Does not read the entire
94-
file but only what's necessary. Returns an ImageInfo with all fields
95-
filled in, or None in case of failure.
94+
file but only what's necessary. If the file type is recognized and
95+
parsing succeeds, returns an ImageInfo with all fields filled in.
96+
Otherwise, an exception is raised or only the basic file attributes
97+
(name, type, size) are filled in.
9698
9799
Example:
98100
info = imsize.read("myfile.jpg")
@@ -114,6 +116,7 @@ def read(filespec):
114116
"tiff": _read_tiff,
115117
"tif": _read_tiff,
116118
"exr": _read_exr,
119+
"hdr": _read_hdr,
117120
"dng": _read_dng,
118121
"cr2": _read_cr2,
119122
"nef": _read_nef,
@@ -123,7 +126,15 @@ def read(filespec):
123126
handler = handlers[filetype]
124127
info = handler(filespec)
125128
return info
126-
return None
129+
else:
130+
# unrecognized file extension
131+
info = ImageInfo()
132+
info.filespec = filespec
133+
info.filetype = filetype
134+
info.filesize = os.path.getsize(filespec)
135+
info.nbytes = info.filesize
136+
info.uncertain = True
137+
return info
127138

128139

129140
######################################################################################
@@ -154,6 +165,7 @@ def _read_png(filespec):
154165
6: 4}[ihdr[3]] # truecolor_alpha => 4 channels
155166
info = _complete(info)
156167
return info
168+
raise RuntimeError(f"File {filespec} is not a valid PNG file.")
157169

158170

159171
def _read_pnm(filespec):
@@ -188,6 +200,30 @@ def _read_pfm(filespec):
188200
return info
189201

190202

203+
def _read_hdr(filespec):
204+
info = ImageInfo()
205+
info.filespec = filespec
206+
info.filetype = "hdr"
207+
info.filesize = os.path.getsize(filespec)
208+
info.isfloat = True
209+
info.cfa_raw = False
210+
info.nchan = 3
211+
info.bitdepth = 32
212+
info.bytedepth = 4
213+
with open(filespec, "rb") as f:
214+
if f.readline() != b"#?RADIANCE\n":
215+
raise RuntimeError(f"File {filespec} is not a valid Radiance HDR file.")
216+
for line in f:
217+
if line == b"\n":
218+
dims = f.readline().decode("utf-8") # '-Y 480 +X 720'
219+
dims = dims.split(" ")
220+
info.height = int(dims[1])
221+
info.width = int(dims[3])
222+
info = _complete(info)
223+
return info
224+
raise RuntimeError(f"File {filespec} is not a valid Radiance HDR file.")
225+
226+
191227
def _read_bmp(filespec):
192228
info = ImageInfo()
193229
info.filespec = filespec
@@ -211,6 +247,7 @@ def _read_bmp(filespec):
211247
info.maxval = 255
212248
info = _complete(info)
213249
return info
250+
raise RuntimeError(f"File {filespec} is not a valid BMP file.")
214251

215252

216253
def _read_exr(filespec):
@@ -249,7 +286,8 @@ def _read_jpeg(filespec):
249286
info.width = sof[2]
250287
info.nchan = sof[3]
251288
info = _complete(info)
252-
return info
289+
return info
290+
raise RuntimeError(f"File {filespec} is not a valid JPEG file.")
253291

254292

255293
def _read_insp(filespec):
@@ -380,8 +418,6 @@ def _read_raw(filespec): # reading the whole file ==> SLOW
380418
minbits = max(minbits, 10) # 10, 12, 14, 16
381419
info.bitdepth = int(minbits) # can underestimate bpp if image is very dark
382420
info = _complete(info)
383-
else:
384-
info = None
385421
return info
386422

387423

@@ -408,6 +444,7 @@ def _read_npy(filespec):
408444
info.maxval = 1.0 if info.isfloat else 2 ** info.bitdepth - 1
409445
info = _complete(info)
410446
return info
447+
raise RuntimeError(f"File {filespec} is not a valid NPY file.")
411448

412449

413450
def _complete(info):

imsize/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.3.5"
1+
__version__ = "1.4.0"

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "imsize"
3-
version = "1.3.5"
3+
version = "1.4.0"
44
description="Lightning-fast extraction of image dimensions & bit depth."
55
authors = [{name = "Tomi Aarnio", email = "[email protected]"}]
66
readme = "README.md"

test/images/vinesunset.hdr

1.02 MB
Binary file not shown.

test/test_read.py

+14
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ def test_npy(self):
9292
self.assertEqual(info.bytedepth, 2)
9393
self.assertEqual(info.nbytes, 20 * 10 * 5 * 2)
9494

95+
def test_hdr(self):
96+
hdrs = glob.glob(os.path.join(imagedir, "*.hdr"))
97+
self.assertTrue(len(hdrs) > 0)
98+
for i, hdr in enumerate(sorted(hdrs)):
99+
info = imsize.read(hdr)
100+
self.assertEqual(info.filetype, "hdr")
101+
self.assertEqual(info.isfloat, True)
102+
self.assertEqual(info.width, 720)
103+
self.assertEqual(info.height, 480)
104+
self.assertEqual(info.nchan, 3)
105+
self.assertEqual(info.bitdepth, 32)
106+
self.assertEqual(info.bytedepth, 4)
107+
self.assertEqual(info.nbytes, info.width * info.height * info.nchan * info.bytedepth)
108+
95109
def test_orientations(self):
96110
jpegs = sorted(glob.glob(os.path.join(imagedir, "orientations", "*.jpg")))
97111
tiffs = sorted(glob.glob(os.path.join(imagedir, "orientations", "*.tif")))

0 commit comments

Comments
 (0)