retrieve / find out an image's dimensions

A

Adam Teale

hey guys

Is there a builtin/standard install method in python for retrieving or
finding out an image's dimensions?

A quick google found me this:
http://www.pythonware.com/library/pil/handbook/introduction.htm

but it looks like it is something I will need to install - I'd like to
be able to pass my script around to people without them needing any
additional modules

Any help would be fantastic!

Cheers

Adam
python 2.3.5
osx 10.4.9
 
J

jigloo

hey guys

Is there a builtin/standard install method in python for retrieving or
finding out an image's dimensions?
Sorry, after i review these code in http://www.pycode.com/modules/?id=32,
i found some(not just a few) *BUGS* in it.
I must apologize to you for this.
I have rewrite the moudle.
please have a try.


"""Recognize image file formats and size based on their first few
bytes."""
# Perl Image::Size module clone
# see more http://search.cpan.org/author/RJRAY/Image-Size-3.01/lib/Image/Size.pm
# rewrited by jigloo([email protected])
# GPL-2 license

__all__ = ["what", "imgsz", "size"]

import os # for os.path os.error sys.stderr
import StringIO # for StringIO.StringIO
import struct # for unpack
import re # for regex

# jpegsize: gets the width and height (in pixels) of a jpeg file
#
def jpegsize(stream):
(x, y, error) = (None, None, "could not determine JPEG size")

# Dummy read to skip header ID
stream.read(2)
while True:
# Extract the segment header.
(marker, code, length) = struct.unpack("!BBH", stream.read(4))

# Verify that it's a valid segment.
if marker != 0xFF:
# Was it there?
error = "JPEG marker not found"
break
elif code >= 0xC0 and code <= 0xC3:
# Segments that contain size info
(y, x) = struct.unpack("!xHH", stream.read(5))
error = "no error"
break
else:
# Dummy read to skip over data
stream.read(length - 2)

return ("JPEG", x, y, error)


# bmpsize: size a Windows-ish BitMaP image
#
def bmpsize(stream):
(x, y, error) = (None, None, "Unable to determine size of BMP data")

# Dummy read to skip header data
stream.read(18)
(x, y) = struct.unpack("<LL", stream.read(8))
if x > 0 and y > 0:
error = "no error"

return ("BMP", x, y, error)


# pngsize : gets the width & height (in pixels) of a png file
# cor this program is on the cutting edge of technology! (pity it's
blunt!)
#
def pngsize(stream):
(x, y, error) = (None, None, "could not determine PNG size")

# Dummy read to skip header data
stream.read(12)
if stream.read(4) == "IHDR":
(x, y) = struct.unpack("!LL", stream.read(8))
error = "no error"

return ("PNG", x, y, error)

# gifsize : Subroutine gets the size of the specified GIF
#
# Default behavior for GIFs is to return the "screen" size
GIF_BEHAVIOR = 0
#
def gifsize(stream):

if GIF_BEHAVIOR > 2:
return ("GIF", 0, 0, "Out-of-range value for GIF_BEHAVIOR: %d" %
GIF_BEHAVIOR)

# Skip over the identifying string, since we already know this is a
GIF
type = stream.read(6)
buf = stream.read(5)
if len(buf) != 5:
return ("GIF", 0, 0, "Invalid/Corrupted GIF (bad header)")
(sw, sh, x) = struct.unpack("<HHB", buf)
if GIF_BEHAVIOR == 0:
return ("GIF", sw, sh, "no error")

return ("GIF", None, None, "Invalid/Corrupted GIF (missing image
header?)")

# ppmsize : gets data on the PPM/PGM/PBM family.
#
def ppmsize(stream):
(mime, x, y, error) = ("PPM", None, None, "Unable to determine size
of PPM/PGM/PBM data")

header = stream.read(1024)
# PPM file of some sort
re.sub(r"^\#.*", "", header, re.M)
m = re.match(r"^(P[1-6])\s+(\d+)\s+(\d+)", header, re.S)
if m:
(n, x, y) = m.group(1, 2, 3)
mime = {"P1":"PBM", "P2":"PGM", "P3":"PPM", "P4":"BPM", "P5":"PGM",
"P6":"PPM"}[n]
if n == "P7":
mime = "XV"
m = re.match(r"IMGINFO:(\d+)x(\d+)", header, re.S)
if m:
(x, y) = m.group(1, 2)
error = "no error"
return (mime, int(x), int(y), error)
else:
return (mime, x, y, error)

# xbmsize :
#
def xbmsize(stream):
(x, y, error) = (None, None, "could not determine XBM size")

header = stream.read(1024)

m = re.match(r"^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)",
header, re.S|re.I)
if m:
(x, y) = m.group(1, 2)
error = "no error"
return ("XBM", int(x), int(y), error)
else:
return ("XBM", x, y, error)


# Size an XPM file by looking for the "X Y N W" line, where X and Y
are
# dimensions, N is the total number of colors defined, and W is the
width of
# a color in the ASCII representation, in characters. We only care
about X & Y.
def xpmsize(stream):
(x, y, error) = (None, None, "could not determine XPM size")

while True:
line = stream.readline()
if line == "":
break
m = re.compile(r"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*",
re.M).match(line, 1)
if m:
(x, y) = map(lambda x: int(x), m.group(1, 2))
error = "no error"
break

return ("XPM", x, y, error)


# tiffsize : size a TIFF image
#
def tiffsize(stream):
(x, y, error) = (None, None, "Unable to determine size of TIFF data")

be = "!" # Default to big-endian; I like it better
if stream.read(4) == "II\x2a\x00": # little-endian
be = "<"

# Set up an association between data types and their corresponding
# pack/unpack specification. Don't take any special pains to deal
with
# signed numbers; treat them as unsigned because none of the image
# dimensions should ever be negative. (I hope.)
packspec = [ None, # nothing (shouldn't happen)
"Bxxx", # BYTE (8-bit unsigned integer)
None, # ASCII
be+"Hxx", # SHORT (16-bit unsigned integer)
be+"L", # LONG (32-bit unsigned integer)
None, # RATIONAL
"bxxx", # SBYTE (8-bit signed integer)
None, # UNDEFINED
be+"Hxx", # SSHORT (16-bit unsigned integer)
be+"L" # SLONG (32-bit unsigned integer)
]

offset = struct.unpack(be+"L", stream.read(4))[0] # Get offset to IFD

ifd = stream.read(2) # Get number of directory entries
num_dirent = struct.unpack(be+"H", ifd)[0] # Make it useful
num_dirent = offset + (num_dirent * 12) # Calc. maximum offset of IFD

# Do all the work
ifd = ''
tag = 0
type = 0
while x is None or y is None:
ifd = stream.read(12) # Get first directory entry
if ifd == "" or stream.tell() > num_dirent:
break
tag = struct.unpack(be+"H", ifd[:2])[0] # ...and decode its tag
type = struct.unpack(be+"H", ifd[2:2+2])[0] # ...and the data type
# Check the type for sanity.
if type > len(packspec) or packspec[type] is None:
continue
if tag == 0x0100: # ImageWidth (x)
# Decode the value
x = struct.unpack(packspec[type], ifd[8:4+8])[0]
elif tag == 0x0101: # ImageLength (y)
# Decode the value
y = struct.unpack(packspec[type], ifd[8:4+8])[0]

# Decide if we were successful or not
if x and y:
error = "no error"
else:
error = ""
if x is None:
error = "ImageWidth "
if y is None:
if error != "":
error = error + "and "
error = error + "ImageWidth "
error = error + "tag(s) could not be found"

return ("TIFF", x, y, error)

# psdsize : determine the size of a PhotoShop save-file (*.PSD)
#
def psdsize(stream):
(x, y, error) = (None, None, "could not determine PSD size")

stream.read(14)
(y, x) = struct.unpack("!LL", stream.read(8))
if x > 0 and y > 0:
error = "no error"
return ("PSD", x, y, error)

# pcdsize :
# Kodak photo-CDs are weird. Don't ask me why, you really don't want
details.
PCD_MAP = { "base/16" : [ 192, 128 ],
"base/4" : [ 384, 256 ],
"base" : [ 768, 512 ],
"base4" : [ 1536, 1024 ],
"base16" : [ 3072, 2048 ],
"base64" : [ 6144, 4096 ]}
# Default scale for PCD images
PCD_SCALE = "base";
#
def pcdsize(stream):
(x, y, error) = (None, None, "Unable to determine size of PCD data")

buff = strean.read(0xf00)
if buff[0x800:3+0x800] != "PCD":
error = "Invalid/Corrupted PCD (bad header)"
return ("PCD", x, y, error)

orient = ord(buff[0x0e02:1+0x0e02]) & 1 # Clear down to one bit
if orient:
(x, y) = PCD_MAP[PCD_SCALE]
else:
(y, x) = PCD_MAP[PCD_SCALE]
error = "no error"
return ("PCD", x, y, error)


# swfsize :
#
def swfsize(stream):
(x, y, error) = (None, None, "not implemented. --I hate swf :(")

return ("SWF", x, y, error)

# swfmxsize :
#
def swfmxsize(stream):
(x, y, error) = (None, None, "not implemented. --I hate swf :(")

return ("SWF", x, y, error)

# mngsize : gets the width and height (in pixels) of an MNG file.
#
# Basically a copy of pngsize.
def mngsize(stream):
(x, y, error) = (None, None, "could not determine MNG size")

stream.read(12)
if stream.read(4) == "MHDR":
# MHDR = Image Header
(x, y) = struct.unpack("!LL", stream.read(8))
error = "MNG"
else:
error = "Invalid/Corrupted MNG (bad header)"

return ("MNG", x, y, error)

# type_map used in function type_map_match
type_map = { re.compile(r"^\xFF\xD8") : ["JPEG", jpegsize],
re.compile(r"^BM") : ["BMP", bmpsize],
re.compile(r"^\x89PNG\x0d\x0a\x1a\x0a") : ["PNG", pngsize],
re.compile(r"^P[1-7]") : ["PPM", ppmsize], # also XVpics
re.compile(r"\#define\s+\S+\s+\d+") : ["XBM", xbmsize],
re.compile(r"\/\* XPM \*\/") : ["XPM", xpmsize],
re.compile(r"^MM\x00\x2a") : ["TIFF", tiffsize],
re.compile(r"^II\x2a\x00") : ["TIFF", tiffsize],
re.compile(r"^8BPS") : ["PSD", psdsize],
re.compile(r"^PCD_OPA") : ["PCD", pcdsize],
re.compile(r"^FWS") : ["SWF", swfsize],
re.compile(r"^CWS") : ["SWF", swfmxsize],
re.compile(r"^\x8aMNG\x0d\x0a\x1a\x0a") : ["MNG", mngsize],
re.compile(r"^GIF8[7,9]a") : ["GIF", gifsize]}
# type_map_match to get MIME-TYPE and callback function
def type_map_match(buffer):
for rx in type_map.keys():
if rx.match(buffer):
return type_map[rx]
else:
return None

# Recognize image headers
#
def what(filename):
try:
f = open(filename, "rb")
h = f.read(512)
except IOError:
print "IOError %s\n" % os.error
finally:
if f: f.close()
m = type_map_match(h)
if m:
return m[0]
return None


# size: size a image from buffer
#
def size(buffer):
m = type_map_match(buffer)
if m:
return (m[1])(StringIO.StringIO(buffer))
else:
return (None, None, None, "Unable to Recognize image file format")

# imgsz: size a image by file name
#
def imgsz(path):
(type, x, y, error) = (None, None, None, "Unable to Recognize image
file format")
f = None
try:
f = open(path, "rb")
header = f.read(256)
f.seek(0)
m = type_map_match(header)
if m:
(type, x, y, error) = (m[1])(f)
except:
print "IOError %s\n" % os.error
return None
finally:
if f: f.close()

return (type, x, y, error)

if __name__ == "__main__":
for filename in [f for f in os.listdir(".") if os.path.isfile(f)]:
print filename, imgsz(filename)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top