On 6 16 ,   6 27 , Adam Teale <[EMAIL PROTECTED]> wrote:
> 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)

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


-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to