home *** CD-ROM | disk | FTP | other *** search
- #
- # The Python Imaging Library.
- # $Id: //modules/pil/PIL/Image.py#17 $
- #
- # the Image class wrapper
- #
- # history:
- # 1995-09-09 fl Created
- # 1996-03-11 fl PIL release 0.0 (proof of concept)
- # 1996-04-30 fl PIL release 0.1b1
- # 1996-05-27 fl PIL release 0.1b2
- # 1996-11-04 fl PIL release 0.2b1
- # 1996-12-08 fl PIL release 0.2b2
- # 1996-12-16 fl PIL release 0.2b3
- # 1997-01-14 fl PIL release 0.2b4
- # 1998-07-02 fl PIL release 0.3b1
- # 1998-07-17 fl PIL release 0.3b2
- # 1999-01-01 fl PIL release 1.0b1
- # 1999-02-08 fl PIL release 1.0b2
- # 1999-07-28 fl PIL release 1.0 final
- # 1999-08-15 fl PIL release 1.0.1 (internal maintenance release)
- # 2000-06-07 fl PIL release 1.1
- # 2000-10-20 fl PIL release 1.1.1
- # 2001-05-07 fl PIL release 1.1.2
- # 2002-01-14 fl PIL release 1.2b1 (imToolkit)
- # 2002-03-15 fl PIL release 1.1.3
- #
- # Copyright (c) 1997-2002 by Secret Labs AB. All rights reserved.
- # Copyright (c) 1995-2002 by Fredrik Lundh.
- #
- # See the README file for information on usage and redistribution.
- #
-
- VERSION = "1.1.3"
-
- class _imaging_not_installed:
- # module placeholder
- def __getattr__(self, id):
- raise ImportError, "The _imaging C module is not installed"
-
- try:
- # give Tk a chance to set up the environment, in case we're
- # using an _imaging module linked against libtcl/libtk
- import FixTk
- except ImportError:
- pass
-
- try:
- # If the _imaging C module is not present, you can still use
- # the "open" function to identify files, but you cannot load
- # them. Note that other modules should not refer to _imaging
- # directly; import Image and use the Image.core variable instead.
- import _imaging
- core = _imaging
- del _imaging
- except ImportError:
- core = _imaging_not_installed()
-
- import ImagePalette
- import os, string, sys
-
- # type stuff
- from types import IntType, StringType, TupleType
-
- try:
- UnicodeStringType = type(unicode(""))
- def isStringType(t):
- return isinstance(t, StringType) or isinstance(t, UnicodeStringType)
- except NameError:
- def isStringType(t):
- return isinstance(t, StringType)
-
- def isTupleType(t):
- return isinstance(t, TupleType)
-
- def isImageType(t):
- return hasattr(t, "im")
-
- def isDirectory(f):
- return isStringType(f) and os.path.isdir(f)
-
- from operator import isNumberType, isSequenceType
-
- #
- # Debug level
-
- DEBUG = 0
-
- #
- # Constants (also defined in _imagingmodule.c!)
-
- NONE = 0
-
- # transpose
- FLIP_LEFT_RIGHT = 0
- FLIP_TOP_BOTTOM = 1
- ROTATE_90 = 2
- ROTATE_180 = 3
- ROTATE_270 = 4
-
- # transforms
- AFFINE = 0
- EXTENT = 1
- PERSPECTIVE = 2 # Not yet implemented
- QUAD = 3
- MESH = 4
-
- # resampling filters
- NONE = 0
- NEAREST = 0
- ANTIALIAS = 1 # 3-lobed lanczos
- LINEAR = BILINEAR = 2
- CUBIC = BICUBIC = 3
-
- # dithers
- NONE = 0
- NEAREST = 0
- ORDERED = 1 # Not yet implemented
- RASTERIZE = 2 # Not yet implemented
- FLOYDSTEINBERG = 3 # default
-
- # palettes/quantizers
- WEB = 0
- ADAPTIVE = 1
-
- # categories
- NORMAL = 0
- SEQUENCE = 1
- CONTAINER = 2
-
- # --------------------------------------------------------------------
- # Registries
-
- ID = []
- OPEN = {}
- MIME = {}
- SAVE = {}
- EXTENSION = {}
-
- # --------------------------------------------------------------------
- # Modes supported by this version
-
- _MODEINFO = {
-
- # official modes
- "1": ("L", "L", ("1",)),
- "L": ("L", "L", ("L",)),
- "I": ("L", "I", ("I",)),
- "F": ("L", "F", ("F",)),
- "P": ("RGB", "L", ("P",)),
- "RGB": ("RGB", "L", ("R", "G", "B")),
- "RGBX": ("RGB", "L", ("R", "G", "B", "X")),
- "RGBA": ("RGB", "L", ("R", "G", "B", "A")),
- "CMYK": ("RGB", "L", ("C", "M", "Y", "K")),
- "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")),
-
- # Experimental modes include I;16, I;16B, RGBa, BGR;15,
- # and BGR;24. Use these modes only if you know exactly
- # what you're doing...
-
- }
-
- MODES = _MODEINFO.keys()
- MODES.sort()
-
- def getmodebase(mode):
- # corresponding "base" mode (grayscale or colour)
- return _MODEINFO[mode][0]
-
- def getmodetype(mode):
- # storage type (per band)
- return _MODEINFO[mode][1]
-
- def getmodebands(mode):
- # subcomponents
- return len(_MODEINFO[mode][2])
-
- # --------------------------------------------------------------------
- # Helpers
-
- _initialized = 0
-
- def preinit():
- "Load standard file format drivers."
-
- global _initialized
- if _initialized >= 1:
- return
-
- for m in ("Bmp", "Gif", "Jpeg", "Ppm", "Png", "Tiff"):
- try:
- __import__("%sImagePlugin" % m, globals(), locals(), [])
- except ImportError:
- pass # ignore missing driver for now
-
- _initialized = 1
-
- def init():
- "Load all file format drivers."
-
- global _initialized
- if _initialized >= 2:
- return
-
- visited = {}
-
- directories = sys.path
-
- try:
- directories = directories + [os.path.dirname(__file__)]
- except NameError:
- pass
-
- # only check directories (including current, if present in the path)
- for directory in filter(isDirectory, directories):
- fullpath = os.path.abspath(directory)
- if visited.has_key(fullpath):
- continue
- for file in os.listdir(directory):
- if file[-14:] == "ImagePlugin.py":
- f, e = os.path.splitext(file)
- try:
- sys.path.insert(0, directory)
- try:
- __import__(f, globals(), locals(), [])
- finally:
- del sys.path[0]
- except ImportError:
- if DEBUG:
- print "Image: failed to import",
- print f, ":", sys.exc_value
- visited[fullpath] = None
-
- if OPEN or SAVE:
- _initialized = 2
-
-
- # --------------------------------------------------------------------
- # Codec factories (used by tostring/fromstring and ImageFile.load)
-
- def _getdecoder(mode, decoder_name, args, extra=()):
-
- # tweak arguments
- if args is None:
- args = ()
- elif not isTupleType(args):
- args = (args,)
-
- try:
- # get decoder
- decoder = getattr(core, decoder_name + "_decoder")
- # print decoder, (mode,) + args + extra
- return apply(decoder, (mode,) + args + extra)
- except AttributeError:
- raise IOError("decoder %s not available" % decoder_name)
-
- def _getencoder(mode, encoder_name, args, extra=()):
-
- # tweak arguments
- if args is None:
- args = ()
- elif not isTupleType(args):
- args = (args,)
-
- try:
- # get encoder
- encoder = getattr(core, encoder_name + "_encoder")
- # print encoder, (mode,) + args + extra
- return apply(encoder, (mode,) + args + extra)
- except AttributeError:
- raise IOError("encoder %s not available" % encoder_name)
-
-
- # --------------------------------------------------------------------
- # Simple expression analyzer
-
- class _E:
- def __init__(self, data): self.data = data
- def __coerce__(self, other): return self, _E(other)
- def __add__(self, other): return _E((self.data, "__add__", other.data))
- def __mul__(self, other): return _E((self.data, "__mul__", other.data))
-
- def _getscaleoffset(expr):
- stub = ["stub"]
- data = expr(_E(stub)).data
- try:
- (a, b, c) = data # simplified syntax
- if (a is stub and b == "__mul__" and isNumberType(c)):
- return c, 0.0
- if (a is stub and b == "__add__" and isNumberType(c)):
- return 1.0, c
- except TypeError: pass
- try:
- ((a, b, c), d, e) = data # full syntax
- if (a is stub and b == "__mul__" and isNumberType(c) and
- d == "__add__" and isNumberType(e)):
- return c, e
- except TypeError: pass
- raise ValueError("illegal expression")
-
-
- # --------------------------------------------------------------------
- # Implementation wrapper
-
- class Image:
-
- format = None
- format_description = None
-
- def __init__(self):
- self.im = None
- self.mode = ""
- self.size = (0, 0)
- self.palette = None
- self.info = {}
- self.category = NORMAL
- self.readonly = 0
-
- def _new(self, im):
- new = Image()
- new.im = im
- new.mode = im.mode
- new.size = im.size
- new.palette = self.palette
- try:
- new.info = self.info.copy()
- except AttributeError:
- # fallback (pre-1.5.2)
- new.info = {}
- for k, v in self.info:
- new.info[k] = v
- return new
-
- _makeself = _new # compatibility
-
- def _copy(self):
- self.load()
- self.im = self.im.copy()
- self.readonly = 0
-
- def _dump(self, file=None, format=None):
- import tempfile
- if not file:
- file = tempfile.mktemp()
- self.load()
- if not format or format == "PPM":
- self.im.save_ppm(file)
- else:
- file = file + "." + format
- self.save(file, format)
- return file
-
- def tostring(self, encoder_name="raw", *args):
- "Return image as a binary string"
-
- # may pass tuple instead of argument list
- if len(args) == 1 and isTupleType(args[0]):
- args = args[0]
-
- if encoder_name == "raw" and args == ():
- args = self.mode
-
- self.load()
-
- # unpack data
- e = _getencoder(self.mode, encoder_name, args)
- e.setimage(self.im)
-
- data = []
- while 1:
- l, s, d = e.encode(65536)
- data.append(d)
- if s:
- break
- if s < 0:
- raise RuntimeError("encoder error %d in tostring" % s)
-
- return string.join(data, "")
-
- def tobitmap(self, name="image"):
- "Return image as an XBM bitmap"
-
- self.load()
- if self.mode != "1":
- raise ValueError("not a bitmap")
- data = self.tostring("xbm")
- return string.join(["#define %s_width %d\n" % (name, self.size[0]),
- "#define %s_height %d\n"% (name, self.size[1]),
- "static char %s_bits[] = {\n" % name, data, "};"], "")
-
- def fromstring(self, data, decoder_name="raw", *args):
- "Load data to image from binary string"
-
- # may pass tuple instead of argument list
- if len(args) == 1 and isTupleType(args[0]):
- args = args[0]
-
- # default format
- if decoder_name == "raw" and args == ():
- args = self.mode
-
- # unpack data
- d = _getdecoder(self.mode, decoder_name, args)
- d.setimage(self.im)
- s = d.decode(data)
-
- if s[0] >= 0:
- raise ValueError("not enough image data")
- if s[1] != 0:
- raise ValueError("cannot decode image data")
-
- def load(self):
- if self.im and self.palette and self.palette.rawmode:
- self.im.putpalette(self.palette.rawmode, self.palette.data)
- self.palette.mode = "RGB"
- self.palette.rawmode = None
- if self.info.has_key("transparency"):
- self.im.putpalettealpha(self.info["transparency"], 0)
- self.palette.mode = "RGBA"
-
- #
- # function wrappers
-
- def convert(self, mode=None, data=None, dither=None,
- palette=WEB, colors=256):
- "Convert to other pixel format"
-
- if not mode:
- # determine default mode
- if self.mode == "P":
- self.load()
- if self.palette:
- mode = self.palette.mode
- else:
- mode = "RGB"
- else:
- return self.copy()
-
- self.load()
-
- if data:
- # matrix conversion
- if mode not in ("L", "RGB"):
- raise ValueError("illegal conversion")
- im = self.im.convert_matrix(mode, data)
- return self._new(im)
-
- if mode == "P" and palette == ADAPTIVE:
- im = self.im.quantize(colors)
- return self._new(im)
-
- # colourspace conversion
- if dither is None:
- dither = FLOYDSTEINBERG
-
- try:
- im = self.im.convert(mode, dither)
- except ValueError:
- try:
- # normalize source image and try again
- im = self.im.convert(getmodebase(self.mode))
- im = im.convert(mode, dither)
- except KeyError:
- raise ValueError("illegal conversion")
-
- return self._new(im)
-
- def quantize(self, colors=256, method=0, kmeans=0, palette=None):
-
- # methods:
- # 0 = median cut
- # 1 = maximum coverage
-
- # NOTE: this functionality will be moved to the extended
- # quantizer interface in a later versions of PIL.
-
- self.load()
-
- if palette:
- # use palette from reference image
- palette.load()
- if palette.mode != "P":
- raise ValueError("bad mode for palette image")
- if self.mode != "RGB" and self.mode != "L":
- raise ValueError(
- "only RGB or L mode images can be quantized to a palette"
- )
- im = self.im.convert("P", 1, palette.im)
- return self._makeself(im)
-
- im = self.im.quantize(colors, method, kmeans)
- return self._new(im)
-
- def copy(self):
- "Copy raster data"
-
- self.load()
- im = self.im.copy()
- return self._new(im)
-
- def crop(self, box=None):
- "Crop region from image"
-
- self.load()
- if box is None:
- return self.copy()
-
- # lazy operation
- return _ImageCrop(self, box)
-
- def draft(self, mode, size):
- "Configure image decoder"
-
- pass
-
- def filter(self, kernel):
- "Apply environment filter to image"
-
- if self.mode == "P":
- raise ValueError("cannot filter palette images")
- self.load()
- id = kernel.id
- if self.im.bands == 1:
- return self._new(self.im.filter(id))
- # fix to handle multiband images since _imaging doesn't
- ims = []
- for c in range(self.im.bands):
- ims.append(self._new(self.im.getband(c).filter(id)))
- return merge(self.mode, ims)
-
- def getbands(self):
- "Get band names"
-
- return _MODEINFO[self.mode][2]
-
- def getbbox(self):
- "Get bounding box of actual data (non-zero pixels) in image"
-
- self.load()
- return self.im.getbbox()
-
- def getdata(self, band = None):
- "Get image data as sequence object."
-
- self.load()
- if band is not None:
- return self.im.getband(band)
- return self.im # could be abused
-
- def getextrema(self):
- "Get min/max value"
-
- self.load()
- if self.im.bands > 1:
- extrema = []
- for i in range(self.im.bands):
- extrema.append(self.im.getband(i).getextrema())
- return tuple(extrema)
- return self.im.getextrema()
-
- def getpixel(self, xy):
- "Get pixel value"
-
- self.load()
- return self.im.getpixel(xy)
-
- def getprojection(self):
- "Get projection to x and y axes"
-
- self.load()
- x, y = self.im.getprojection()
- return map(ord, x), map(ord, y)
-
- def histogram(self, mask=None, extrema=None):
- "Take histogram of image"
-
- self.load()
- if mask:
- mask.load()
- return self.im.histogram((0, 0), mask.im)
- if self.mode in ("I", "F"):
- if extrema is None:
- extrema = self.getextrema()
- return self.im.histogram(extrema)
- return self.im.histogram()
-
- def offset(self, xoffset, yoffset=None):
- "(deprecated) Offset image in horizontal and/or vertical direction"
- import ImageChops
- return ImageChops.offset(self, xoffset, yoffset)
-
- def paste(self, im, box=None, mask=None):
- "Paste other image into region"
-
- if box is None:
- # cover all of self
- box = (0, 0) + self.size
-
- if len(box) == 2:
- # lower left corner given; get size from image or mask
- if isImageType(im):
- box = box + (box[0]+im.size[0], box[1]+im.size[1])
- else:
- box = box + (box[0]+mask.size[0], box[1]+mask.size[1])
-
- if isImageType(im):
- im.load()
- if self.mode != im.mode:
- if self.mode != "RGB" or im.mode not in ("RGBA", "RGBa"):
- # should use an adapter for this!
- im = im.convert(self.mode)
- im = im.im
-
- self.load()
-
- if self.readonly:
- self._copy()
-
- if mask:
- mask.load()
- self.im.paste(im, box, mask.im)
- else:
- self.im.paste(im, box)
-
- def point(self, lut, mode=None):
- "Map image through lookup table"
-
- if self.mode in ("I", "F"):
- # floating point; lut must be a valid expression
- scale, offset = _getscaleoffset(lut)
- self.load()
- im = self.im.point_transform(scale, offset);
- else:
- # integer image; use lut and mode
- self.load()
- if not isSequenceType(lut):
- # if it isn't a list, it should be a function
- lut = map(lut, range(256)) * self.im.bands
- im = self.im.point(lut, mode)
-
- return self._new(im)
-
- def putalpha(self, im):
- "Set alpha layer"
-
- if self.mode != "RGBA" or im.mode not in ("1", "L"):
- raise ValueError("illegal image mode")
-
- im.load()
- self.load()
-
- if im.mode == "1":
- im = im.convert("L")
-
- self.im.putband(im.im, 3)
-
- def putdata(self, data, scale=1.0, offset=0.0):
- "Put data from a sequence object into an image."
-
- self.load() # hmm...
- self.im.putdata(data, scale, offset)
-
- def putpalette(self, data, rawmode="RGB"):
- "Put palette data into an image."
-
- if self.mode not in ("L", "P"):
- raise ValueError("illegal image mode")
- if not isStringType(data):
- data = string.join(map(chr, data), "")
- self.mode = "P"
- self.palette = ImagePalette.raw(rawmode, data)
- self.palette.mode = "RGB"
-
- def putpixel(self, xy, value):
- "Set pixel value"
-
- self.load()
- return self.im.putpixel(xy, value)
-
- def resize(self, size, resample=NEAREST):
- "Resize image"
-
- if resample not in (NEAREST, BILINEAR, BICUBIC, ANTIALIAS):
- raise ValueError("unknown resampling filter")
-
- self.load()
-
- if self.mode in ("1", "P"):
- resample = NEAREST
-
- if resample == ANTIALIAS:
- # requires stretch support (imToolkit & PIL 1.1.3)
- try:
- im = self.im.stretch(size, resample)
- except AttributeError:
- raise ValueError("unsupported resampling filter")
- else:
- im = self.im.resize(size, resample)
-
- return self._new(im)
-
- def rotate(self, angle, resample=NEAREST):
- "Rotate image. Angle given as degrees counter-clockwise."
-
- if resample not in (NEAREST, BILINEAR, BICUBIC):
- raise ValueError("unknown resampling filter")
-
- self.load()
-
- if self.mode in ("1", "P"):
- resample = NEAREST
-
- return self._new(self.im.rotate(angle, resample))
-
- def save(self, fp, format=None, **params):
- "Save image to file or stream"
-
- if isStringType(fp):
- import __builtin__
- filename = fp
- fp = __builtin__.open(fp, "wb")
- close = 1
- else:
- filename = ""
- close = 0
-
- self.encoderinfo = params
- self.encoderconfig = ()
-
- self.load()
-
- preinit()
-
- ext = string.lower(os.path.splitext(filename)[1])
-
- try:
-
- if not format:
- format = EXTENSION[ext]
-
- SAVE[string.upper(format)](self, fp, filename)
-
- except KeyError, v:
-
- init()
-
- if not format:
- format = EXTENSION[ext]
-
- SAVE[string.upper(format)](self, fp, filename)
-
- if close:
- fp.close()
-
- def seek(self, frame):
- "Seek to given frame in sequence file"
-
- # overridden by file handlers
- if frame != 0:
- raise EOFError
-
- def show(self, title=None, command=None):
- "Display image (for debug purposes only)"
-
- try:
- import ImageTk
- ImageTk._show(self, title)
- # note: caller must enter mainloop!
- except:
- _showxv(self, title, command)
-
- def split(self):
- "Split image into bands"
-
- ims = []
- self.load()
- for i in range(self.im.bands):
- ims.append(self._new(self.im.getband(i)))
- return tuple(ims)
-
- def tell(self):
- "Return current frame number"
-
- return 0
-
- def thumbnail(self, size, resample=NEAREST):
- "Create thumbnail representation (modifies image in place)"
-
- # FIXME: the default resampling filter will be changed
- # to ANTIALIAS in future versions
-
- # preserve aspect ratio
- x, y = self.size
- if x > size[0]: y = y * size[0] / x; x = size[0]
- if y > size[1]: x = x * size[1] / y; y = size[1]
- size = x, y
-
- if size == self.size:
- return
-
- self.draft(None, size)
-
- self.load()
-
- try:
- im = self.resize(size, resample)
- except ValueError:
- if resample != ANTIALIAS:
- raise
- im = self.resize(size, NEAREST) # fallback
-
- self.im = im.im
- self.mode = im.mode
- self.size = size
-
- self.readonly = 0
-
- def transform(self, size, method, data, resample=NEAREST, fill=1):
- "Transform image"
-
- im = new(self.mode, size, None)
- if method == MESH:
- # list of quads
- for box, quad in data:
- im.__transformer(box, self, QUAD, quad, resample, fill)
- else:
- im.__transformer((0, 0)+size, self, method, data, resample, fill)
-
- return im
-
- def __transformer(self, box, image, method, data,
- resample=NEAREST, fill=1):
- "Transform into current image"
-
- # FIXME: this should be turned into a lazy operation (?)
-
- w = box[2]-box[0]
- h = box[3]-box[1]
-
- if method == AFFINE:
- # change argument order to match implementation
- data = (data[2], data[0], data[1],
- data[5], data[3], data[4])
- elif method == EXTENT:
- # convert extent to an affine transform
- x0, y0, x1, y1 = data
- xs = float(x1 - x0) / w
- ys = float(y1 - y0) / h
- method = AFFINE
- data = (x0 + xs/2, xs, 0, y0 + ys/2, 0, ys)
- elif method == QUAD:
- # quadrilateral warp. data specifies the four corners
- # given as NW, SW, SE, and NE.
- nw = data[0:2]; sw = data[2:4]; se = data[4:6]; ne = data[6:8]
- x0, y0 = nw; As = 1.0 / w; At = 1.0 / h
- data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At,
- (se[0]-sw[0]-ne[0]+x0)*As*At,
- y0, (ne[1]-y0)*As, (sw[1]-y0)*At,
- (se[1]-sw[1]-ne[1]+y0)*As*At)
- else:
- raise ValueError("unknown transformation method")
-
- if resample not in (NEAREST, BILINEAR, BICUBIC):
- raise ValueError("unknown resampling filter")
-
- image.load()
-
- self.load()
-
- if image.mode in ("1", "P"):
- resample = NEAREST
-
- self.im.transform2(box, image.im, method, data, resample, fill)
-
- def transpose(self, method):
- "Transpose image (flip or rotate in 90 degree steps)"
-
- self.load()
- im = self.im.transpose(method)
- return self._new(im)
-
- #
- # test/extension hooks (don't use in production code!)
-
- def _stretch(self, size, resample=NEAREST):
- # same as resize
-
- if resample not in (NEAREST, BILINEAR, BICUBIC, ANTIALIAS):
- raise ValueError("unknown resampling filter")
-
- self.load()
-
- if self.mode in ("1", "P"):
- resample = NEAREST
-
- return self._new(self.im.stretch(size, resample))
-
- # --------------------------------------------------------------------
- # Lazy operations
-
- class _ImageCrop(Image):
-
- def __init__(self, im, box):
-
- Image.__init__(self)
-
- self.mode = im.mode
- self.size = box[2]-box[0], box[3]-box[1]
-
- self.__crop = box
-
- self.im = im.im
-
- def load(self):
-
- # lazy evaluation!
- if self.__crop:
- self.im = self.im.crop(self.__crop)
- self.__crop = None
-
- # FIXME: future versions should optimize crop/paste
- # sequences!
-
- # --------------------------------------------------------------------
- # Factories
-
- #
- # Debugging
-
- def _wedge():
- "Create greyscale wedge (for debugging only)"
-
- return Image()._new(core.wedge("L"))
-
- #
- # Create/open images.
-
- def new(mode, size, color=0):
- "Create a new image"
-
- if color is None:
- # don't initialize
- return Image()._new(core.new(mode, size))
-
- return Image()._new(core.fill(mode, size, color))
-
-
- def fromstring(mode, size, data, decoder_name="raw", *args):
- "Load image from string"
-
- # may pass tuple instead of argument list
- if len(args) == 1 and isTupleType(args[0]):
- args = args[0]
-
- if decoder_name == "raw" and args == ():
- args = mode
-
- im = new(mode, size)
- im.fromstring(data, decoder_name, args)
- return im
-
- def open(fp, mode="r"):
- "Open an image file, without loading the raster data"
-
- if mode != "r":
- raise ValueError("bad mode")
-
- if isStringType(fp):
- import __builtin__
- filename = fp
- fp = __builtin__.open(fp, "rb")
- else:
- filename = ""
-
- prefix = fp.read(16)
-
- preinit()
-
- for i in ID:
- try:
- factory, accept = OPEN[i]
- if not accept or accept(prefix):
- fp.seek(0)
- return factory(fp, filename)
- except (SyntaxError, IndexError, TypeError):
- pass
-
- init()
-
- for i in ID:
- try:
- factory, accept = OPEN[i]
- if not accept or accept(prefix):
- fp.seek(0)
- return factory(fp, filename)
- except (SyntaxError, IndexError, TypeError):
- pass
-
- raise IOError("cannot identify image file")
-
- #
- # Image processing.
-
- def blend(im1, im2, alpha):
- "Interpolate between images."
-
- if alpha <= 0.0:
- return im1
- elif alpha >= 1.0:
- return im2
- im1.load()
- im2.load()
- return im1._new(core.blend(im1.im, im2.im, alpha))
-
- def composite(image1, image2, mask):
- "Create composite image by blending images using a transparency mask"
-
- image = image2.copy()
- image.paste(image1, None, mask)
- return image
-
- def eval(image, *args):
- "Evaluate image expression"
-
- return image.point(args[0])
-
- def merge(mode, bands):
- "Merge a set of single band images into a new multiband image."
-
- if getmodebands(mode) != len(bands) or "*" in mode:
- raise ValueError("wrong number of bands")
- for im in bands[1:]:
- if im.mode != getmodetype(mode):
- raise ValueError("mode mismatch")
- if im.size != bands[0].size:
- raise ValueError("size mismatch")
- im = core.new(mode, bands[0].size)
- for i in range(getmodebands(mode)):
- bands[i].load()
- im.putband(bands[i].im, i)
- return bands[0]._new(im)
-
- # --------------------------------------------------------------------
- # Plugin registry
-
- def register_open(id, factory, accept=None):
- id = string.upper(id)
- ID.append(id)
- OPEN[id] = factory, accept
-
- def register_mime(id, mimetype):
- MIME[string.upper(id)] = mimetype
-
- def register_save(id, driver):
- SAVE[string.upper(id)] = driver
-
- def register_extension(id, extension):
- EXTENSION[string.lower(extension)] = string.upper(id)
-
-
- # --------------------------------------------------------------------
- # Simple display support
-
- def _showxv(self, title=None, command=None):
-
- if os.name == "nt":
- format = "BMP"
- if not command:
- command = "start"
- else:
- format = None
- if not command:
- command = "xv"
- if title:
- command = command + " -name \"%s\"" % title
-
- base = getmodebase(self.mode)
- if base != self.mode and self.mode != "1":
- file = self.convert(base)._dump(format=format)
- else:
- file = self._dump(format=format)
-
- if os.name == "nt":
- os.system("%s %s" % (command, file))
- # FIXME: this leaves temporary files around...
- else:
- os.system("(%s %s; rm -f %s)&" % (command, file, file))
-