From rburnham@cri-inc.com Mon May 4 18:49:27 1998 From: rburnham@cri-inc.com (Roger Burnham) Date: Sat, 2 May 1998 15:49:27 -5000 Subject: [IMAGE-SIG] Dib GDI leak/fix + contributed plugin Message-ID: <199805021944.AA07540@world.std.com> Hi, version: Imaging-0.3a3 I've found that deleting an ImageWin.Dib instance does not free the GDI resources created with: dib->bitmap = CreateDIBSection(...) In the app I'm developing, this caused the GDI resources to be consumed after acquiring/deleting ~200 images. The fix is ------------------dib.h------------------: ... struct ImagingDIBInstance { ... HGDIOBJ origobj; /* remember the original selected object */ }; ... ------------------dib.h------------------: ------------------dib.c------------------: in ImagingNewDIB(...) change: SelectObject(dib->dc, dib->bitmap); to: dib->origobj = SelectObject(dib->dc, dib->bitmap); change ImagingDeleteDIB(...) to be: void ImagingDeleteDIB(ImagingDIB dib) { /* Clean up */ if (dib->palette) DeleteObject(dib->palette); if (dib->dc) { SelectObject(dib->dc, dib->origobj); DeleteDC(dib->dc); } if (dib->bitmap) DeleteObject(dib->bitmap); free(dib->info); } E.g. the problem was that the bitmap was freed while still selected in the device context. Also, here is a module I wrote to present a "stack" of bmp images as a single image object. In the app alluded to, we collect a set of four images, and calculate a fifth and sixth plane. These are stored as a unit, and when displayed, the user can flip thru the planes with the up/down keys. ------------------BMPStackImagePlugin.py------------------: ''' Imaging module plugin to handle a stack of BMP images. Weve also added a persistent dictionary attached to each image. ''' import os import Image, BmpImagePlugin import cPickle, cStringIO _bmpStackPrefix = 'BMPSTK' def _accept(prefix): '''Image.open will call to see if we can handle prefix. ''' return prefix[:6] == _bmpStackPrefix class BMPStackFile(BmpImagePlugin.BmpImageFile): '''A stack of BmpImageFile images. ''' format = _bmpStackPrefix format_description = "Stack of Windows Bitmaps" def _open(self): '''See if our magic is at the file head. If so, reposition at the file start. ''' s = self.fp.read(6) if s[:6] != _bmpStackPrefix: raise SyntaxError, "Not a BMP stack" self.frame = -1 self.fp2 = self.fp self.offset = self.fp.tell() self.seek(0) def seek(self, frame): '''Read the next image in the file. ''' if frame != self.frame + 1: raise ValueError, "cannot seek to frame %d" % frame self.frame = frame self.fp = self.fp2 self.fp.seek(self.offset) try: self.info = cPickle.Unpickler(self.fp).load() except: raise SyntaxError, "file does not contain an info dict" s = self.fp.read(14) if s[:2] != "BM": raise SyntaxError, "stack does not contain a BM image" self._bitmap() offset = self.tile[0][2] stride = self.tile[0][3][1] self.offset = offset + stride*self.size[1] def tell(self): '''Return which image number we are positioned at. ''' return self.frame def _save(im, fp, filename): '''Image.save will call us to save the stack to a file. ''' fp.write(_bmpStackPrefix) params = im.encoderinfo for img in im: infoFd = cStringIO.StringIO() cPickle.Pickler(infoFd, 1).dump(img.info) fp.write(infoFd.getvalue()) img.params = params img.encoderconfig = () BmpImagePlugin._save(img, fp, filename) Image.register_open(BMPStackFile.format, BMPStackFile, _accept) Image.register_save(BMPStackFile.format, _save) Image.register_extension(BMPStackFile.format, '.bmpstk') class BMPStack(Image.Image): '''Class that presents a stack of images with the same interface as a single image. The active image is set via the member variable "cursor". ''' __vdict = None __vdict_name = '_BMPStack__vdict' def __init__(self, imageObj): '''Given a file path, or an image object, wrap it as a stack. ''' self.__dict__[self.__vdict_name] = {} Image.Image.__init__(self) if type(imageObj) == type(''): path = imageObj img = Image.open(path) size = img.size mode = img.mode i = 1 imgs = [] while 1: try: tile = img.tile if img.size != size: raise ValueError, 'Images in stack must have same size' next = img.convert(mode) next.tile = tile imgs.append(next) img.seek(i) i = i + 1 except: break else: mode = 'L' size = imageObj.size img = Image.new(mode, size) imgs = [imageObj] path = None self.__vdict['mode'] = mode self.__vdict['size'] = size self.__vdict['format'] = _bmpStackPrefix self.__vdict['filename'] = path self.__vdict['cursor'] = 0 self.__vdict['len'] = len(imgs) self.__vdict['imgs'] = imgs def __del__(self): del self.__vdict def __getattr__(self, name): '''Handle image.attr references. If attr==cursor, return the "active" image plane number (0...). If the active image has the attribute, return it. Otherwise attempt to get it from the wrapping object. ''' if name == 'cursor': return self.__vdict['cursor'] if self.__vdict.has_key('imgs'): if hasattr(self.__vdict['imgs'][self.__vdict['cursor']], name): return getattr(self.__vdict['imgs'][self.__vdict['cursor']], name) else: return self.__vdict[name] else: return self.__vdict[name] def __getitem__(self, i): '''Handle stack indexing, e.g. image[i] references, but do not change the cursor location. ''' return self.__vdict['imgs'][i] def __len__(self): '''Return the number of images in the stack. ''' return len(self.__vdict['imgs']) def __setattr__(self, name, value): '''Handle image.attr = value references. Look for cursor setting and wrap the value into the allowable range. If the stack does not exist yet, set the attr value in the wrapper context, otherwise, set the attribute for the current image (e.g. image[cursor].name = value. ''' if name == 'cursor': if value >= self.__vdict['len']: self.__vdict['cursor'] = 0 elif value < 0: self.__vdict['cursor'] = self.__vdict['len'] - 1 else: self.__vdict['cursor'] = value return if self.__vdict.has_key('imgs'): setattr(self.__vdict['imgs'][self.__vdict['cursor']], name, value) else: self.__vdict[name] = value def __setitem__(self, index, img): '''Handle image[i] = img references. If set beyond the current length, fill missing values with empyt strings (NEED to rethink this...). ''' imgs = len(self.__vdict['imgs']) if index >= imgs: diff = index - imgs + 1 while diff > 0: self.__vdict['imgs'].append('') diff = diff - 1 self.__vdict['imgs'][index] = img self.__vdict['len'] = len(self.__vdict['imgs']) def append(self, img): '''Handle image.append(img) references in the obvious way... ''' self.__vdict['imgs'].append(img) self.__vdict['len'] = len(self.__vdict['imgs']) ------------------BMPStackImagePlugin.py------------------: Cheers, Roger Burnham Cambridge Research & Instrumentation 80 Ashford Street Boston, MA 02134 rburnham@cri-inc.com www.cri-inc.com From richard.jones@bom.gov.au Fri May 15 08:26:43 1998 From: richard.jones@bom.gov.au (Richard Jones) Date: Fri, 15 May 1998 07:26:43 +0000 Subject: [Image-SIG] ImageFont problems Message-ID: <199805150726.HAA23434@mage.ho.bom.gov.au> Hey all, I've just started using the ImageFont facility of PIL, and have run into a 'bug' in the cropping code (Crop.c) of libImaging. I narrowed the problem down to a general problem with cropping to: >>> import Image >>> im = Image.new('1',(800,9)) >>> im.im.crop((300,0,310,6)) Traceback (innermost last): File "", line 1, in ? SystemError: NULL result without error in call_object Basically, I had to change the code in Crop.c to: /* Determine which region to copy */ xoff = yoff = 0; if (x0 < 0) xsize += x0, xoff = -x0, x0 = 0; if (xsize > imOut->xsize) xsize = imOut->xsize; if (y0 < 0) ysize += y0, yoff = -y0, y0 = 0; if (ysize > imOut->ysize) ysize = imOut->ysize; Now, that seems to work - it doesn't generate a SystemError any more. The xoff and yoff values don't seem to be used anyway. What should the behaviour be if out of bounds values are passed to crop()? But then I try: >>> import Image, ImageDraw, ImageFont >>> im=Image.new('1',(300,200)) >>> d=ImageDraw.ImageDraw(im) >>> f=ImageFont.load('/home/rtj/lib/BDF/luBS24.pil') >>> d.setfont(f) >>> d.text((10,10), 'hello world') >>> im.show() and I get a blank image. I I go further and try: >>> f.getmask('hello world').show() I get the text. What am I doing wrong? Richard -- Richard Jones, Satellite Section at the Bureau of Meteorology, Australia. Work phone: +61-3-9669-4539 From stone@parc.xerox.com Fri May 15 22:25:48 1998 From: stone@parc.xerox.com (Maureen Stone) Date: Fri, 15 May 1998 14:25:48 PDT Subject: [Image-SIG] PIL install confusion Message-ID: <355CB2DC.4C22BAD7@parc.xerox.com> Your imaging library looks great and I would like to use it. I have installed PythonWin 1.5.1 and want to add the imaging library to it. I'm really only interested in reading and writing image files, not the Tkinter display part. The problem is, I can't tell what to load from your description. What I installed from the python site is called g32d301p.exe, 5.8M, created 3/5/98. I double-click on it, it installs, I can now run PythonWin. If I say "import Image" it says "module not found" so I clearly need to load something more. The 0.2b3 binary release on your site is a zip file containing many files and, to me, cryptic instructions on how to install them. I have no idea where my Python and program search paths are in Win95, and I've loaded python15.dll, not python14.dll. I could put all these files and directories in the same place I put PythonWin, stick the dll's into C:/windows/system and hope for the best, but I don't really know what I'm doing. So I looked for a version with an installer and found a file also called g32d301p.exe which claims to be python 1.4, PIL and TK. It's clearly different than the one I loaded (different size and date), but I'm using python 1.5. And, how do I make sure PythonWin calls this one? Pick it up and move it into the same directory I currently have a python.exe? Suggestions? I realize I may have caught you between python updates, and I'm sorry I'm not more savvy about paths, dll's and the like. What I would really like is a single file that will install PIL so that it will coexist with my currently installed PythonWin. Maureen From stone@parc.xerox.com Fri May 15 22:37:59 1998 From: stone@parc.xerox.com (Maureen Stone) Date: Fri, 15 May 1998 14:37:59 PDT Subject: [Image-SIG] IGNORE PREVIOUS Message Message-ID: <355CB5B7.C9D5DEBD@parc.xerox.com> Sorry, I found a better set of readme files on the main python site. And, I was confused about how I loaded python in the first place (sheesh, I shouldn't try to work after noon on Friday). Anyway, while I still wish I had a single file that would install PIL, I think I can make it work. Maureen From Fred L. Drake, Jr." A new version of my interface to the "t1lib" Type1 font rasterizer library is now available as part of the standard t1lib distribution. The interface provides an object-oriented way to handle font rendering to high-quality bitmaps and greymaps, and can easily be used in conjunction with the Python Imaging Library. For more information on the rendering library, or to download the package, refer to: http://www.neuroinformatik.ruhr-uni-bochum.de/ini/PEOPLE/rmz/t1lib.html -Fred -- Fred L. Drake, Jr. Corporation for National Research Initiatives 1895 Preston White Drive Reston, VA 20191 From fredrik@pythonware.com Mon May 18 09:51:06 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Mon, 18 May 1998 09:51:06 +0100 Subject: [Image-SIG] PIL install confusion Message-ID: <01bd823a$184103f0$f29b12c2@panik.pythonware.com> >The problem is, I can't tell what to load from your description. What I >installed from the python site is called g32d301p.exe, 5.8M, created >3/5/98. I double-click on it, it installs, I can now run PythonWin. If >I say "import Image" it says "module not found" so I clearly need to >load something more. there's a PIL for 1.5 available at the official download site: http://www.python.org/download/download_windows.html (I haven't made that package myself, and don't have time to look at it myself -- if you're really stuck, and no one else on this list can help, comp.lang.python is probably the best place to ask) The only available all-in-one-package solution is pythonware.com's "core" distribution (currently 1.4, I'm afraid): http://www.pythonware.com/downloads.htm Cheers /F fredrik@pythonware.com http://www.pythonware.com From gcoats@usgs.gov Mon May 18 19:43:28 1998 From: gcoats@usgs.gov (Greg Coats) Date: Mon, 18 May 1998 14:43:28 -0400 Subject: [Image-SIG] library Message-ID: <3560814F.223902D0@usgs.gov> Hi, my name is Kathy Satterfield, I worked for the NHIC (USGS), can you please send me some information on how you guys do barcoding of checking in and out people????We are beginning to use a software call asscess. I need some information, if that would be possible. My address is: U.S. Geological Survey Kathy Satterfield 12201 Sunrise Valley Dr. Reston, VA. 22092 Ms. 521 or you can call me at 703-648-6280....We are trying to get some ideas.Thank you very much... I really appreciate it. have a nice day.... From richard.jones@bom.gov.au Wed May 27 06:31:11 1998 From: richard.jones@bom.gov.au (Richard Jones) Date: Wed, 27 May 1998 05:31:11 +0000 Subject: [Image-SIG] PIL patch Message-ID: <199805270531.FAA24278@mage.ho.bom.gov.au> Fredrik, Here's a small patch for path.c so PyPath_Flatten copes with sequences a little better... *** path.c.orig Wed May 27 05:04:43 1998 --- path.c Wed May 27 05:28:22 1998 *************** *** 111,116 **** --- 111,120 ---- j = 0; n = PyObject_Length(data); + /* just in case __len__ breaks (or doesn't exist) */ + if (PyErr_Occurred()) { + return -1; + } /* Allocate for worst case */ xy = malloc(2 * n * sizeof(double)); *************** *** 123,128 **** --- 127,142 ---- for (i = 0; i < n; i++) { double x, y; PyObject *op = PySequence_GetItem(data, i); + if (!op) { + /* IndexError is OK */ + if (PyErr_Occurred() == PyExc_IndexError) { + PyErr_Clear(); + break; + } else { + free(xy); + return -1; + } + } if (PyNumber_Check(op)) xy[j++] = PyFloat_AsDouble(op); else if (PyArg_ParseTuple(op, "dd", &x, &y)) { From Fred L. Drake, Jr." References: <199805270531.FAA24278@mage.ho.bom.gov.au> Message-ID: <13676.4737.961036.811227@weyr.cnri.reston.va.us> Richard Jones writes: > + /* IndexError is OK */ > + if (PyErr_Occurred() == PyExc_IndexError) { > + PyErr_Clear(); Richard, This is an inappropriate use of PyErr_Occurred() in recent versions of Python (starting from when the standard exceptions became classes). The right way to make this test is: /* IndexError is OK */ if ((PyErr_Occurred() != NULL) && PyErr_ExceptionMatches(PyExc_IndexError)) { PyErr_Clear(); This allows the sequence implementation to raise a subclass of IndexError and this will still work. Note that PyErr_Occurred() must be true before calling PyErr_ExceptionMatches(). -Fred -- Fred L. Drake, Jr. fdrake@cnri.reston.va.us Corporation for National Research Initiatives 1895 Preston White Drive Reston, VA 20191