From cgw@pgt.com Sat Jul 4 16:42:31 1998 From: cgw@pgt.com (Charles G Waldman) Date: Sat, 4 Jul 1998 11:42:31 -0400 (EDT) Subject: [Image-SIG] Inter-converting images and Numeric arrays Message-ID: <13726.19815.27733.767077@sirius> I'm working on some image analysis software. For what I'm doing it's much more efficient to have the pixel data in a Numeric array, rather than a PIL Image, to avoid the overhead of lots of getpixel/setpixel calls. I've found it's pretty straightforward to inter-convert images and arrays using fromstring/tostring.... but I have a few questions: 1: Shouldn't PIL support conversion directly to/from arrays? Using the intermediate string works, but seems like a hack... 2: Seems that if I start with an Array, make an Image, then convert back, the y-axis is inverted. Is this a feature or a bug? Example program: #!/usr/local/bin/python import Image,ImagePalette from Numeric import * a = zeros((4,4),'b') for y in range(0,4): for x in range(0,4): a[x,y]=x*10+y print "before:" print a i1 = Image.fromstring("P",(4,4),a.tostring()) a1 = fromstring(i1.tostring(),'b') a1.shape=(4,4) print "after:" print a1 And its output >>> before: [[ 0 1 2 3] [10 11 12 13] [20 21 22 23] [30 31 32 33]] after: [[30 31 32 33] [20 21 22 23] [10 11 12 13] [ 0 1 2 3]] >>> From fredrik@pythonware.com Sat Jul 4 18:24:47 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Sat, 4 Jul 1998 18:24:47 +0100 Subject: [Image-SIG] Inter-converting images and Numeric arrays Message-ID: <04e901bda770$a6051a90$f29b12c2@pythonware.com> >1: Shouldn't PIL support conversion directly to/from arrays? Using >the intermediate string works, but seems like a hack... Recent versions provide getdata and putdata methods (getdata is even documented): data = im1.getdata() im2 = Image.new(im1.mode, im2.size) im3.putdata(data) The sequence object returned by getdata is a bit fragile, at least under 1.4. If you get problems, explicitly convert it to a list or tuple: data = list(im1.getdata()) >2: Seems that if I start with an Array, make an Image, then convert >back, the y-axis is inverted. Is this a feature or a bug? To be compatible with old versions of PIL, the fromstring/tostring methods by default assumes that the data has the bottom-most line first, and RGB data has an extra pad byte after each pixel. To get other behaviour, you need to specify codec arguments. However, the recent fromstring *function* doesn't provide the same default behaviour -- instead, it defaults to more reasonable top-most line first, RGB data without padding settings. The problem is that if you pass the result from tostring directly to the fromstring *function*, the image is turned upside down. Again, you can modify this behaviour using codec arguments. See: http://www.pythonware.com/library/pil/handbook/decoder.htm ... We're about to release a new version of PIL; I'm tempted to change the method's default settings so that it matches the fromstring function. If this would break a huge amount of code for anyone on this list, please speak up! Cheers /F From esr@snark.thyrsus.com Wed Jul 8 23:41:04 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Wed, 8 Jul 1998 18:41:04 -0400 Subject: [Image-SIG] PIL Message-ID: <199807082241.SAA00186@snark.thyrsus.com> For some years I have maintained GIFLIB, a C library and utility toolkit for performing various slice and dice operations on raster images. See http://www.tuxedo.org/~esr/giflib for details. Recently, however, it became clear that Unisys's position on the LZW patent makes continued distribution of this library too risky for me and for its users. I want to drop supporting it, but I don't want to do so without leaving GIFLIB users some alternative supporting the toolkit capabilities. My original thought was to try to rescue the C code by teaching it to speak PNG. I announced this to a group that included Guido. He steered me to PIL. PIL is very impressive -- so much so that I think it will enable me to officially declare the utilities in GIFLIB obsolete (but not the library; that's a separate issue to which I'll return below). A couple of documentation bugs: On the index page, `Enhance' is spelled `Enchance'. The `ImageFont' page is missing (File Not Found). On the page describing the `Image' class, the `fromstring', `tostring', and `putdata' methods are listed but not described. The description of the resize method gave me pause until I guessed that the size argument is a tuple. Don't make the reader guess; specify the argument semantics there. You should specify the angular units in the `rotate' method. Degrees, radians, or grads? :-) I think the PIL documentation's use of the term `band' is rather unfortunate. To a native English speaker, the term `band' applied to a graphic has a strong primary meaning which is different from PIL's; that of a stripe or region (especially a horizontal one) extending the full width of the image. I recommend that you substitute the for `band' the term `channel', which is well-established in exactly this sense for RGB images. It is also the term used by the PNG folks and others in the graphics community (notably the author of the "Graphics File Formats" dictionary). Functional suggestions: Allow an optional argument in show() specifying the viewer command to use. There are aome places where PIL falls short of being a complete replacement for the GIFLIB utilities: 1. No ability to display images using (a) the Borland BGI driver, (b) an Epson printer, (c) a Hercules graphics card. I think we can safely write these off as obsolete, however. 2. No ability to handle RLE (Utah raster toolkit) images. I'm not too worried about this, either. Is anybody still using it? 3. No equivalent of the gifbg, gifwedge, and gifcolor test pattern generators. These would probably be trivial to write. 4. (Serious) I see no way to overlay text on an image. I can supply a clean 8x8 font and code for ImageDraw to overlay messages on an image using it. Later this method could be extended to support fonts from an X font server. Is this sort of thing what the missing ImageFont module is about? 5. (Serious) There's no equivalent of the `clip complement' feature in giflclip. Here's the GIF description: -c Complement. This removes horizontal and/or vertical bands of the image. For example `-c -i 638 3 658 13' would remove a horizontal band 11 pixels deep beginning at raster line 3, and a vertical band 21 pixels right beginning at pixel 658. This is useful more often than you'd think.... 6. I see no standard function or method for composing multiple images into a multi-file image in formats like GIF. (The gifovly utility in GIFLIB does this.) 7. (Serious) There doesn't seem to be any way to specify interlacing or the screen size on GIF output (GIFLIB's gifinter and gifpos utilities). 8. No utility to section an image into tiles (gifburst). 9. No equivalent of my icon2gif utility (see http://www.tuxedo.org/~esr/giflib/doc/icon2gif.html ). Never mind, this is very GIF-specific. If I hack in some of these things, can I stimulate a PIL release to replace GIFLIB? -- Eric S. Raymond .. a government and its agents are under no general duty to provide public services, such as police protection, to any particular individual citizen... -- Warren v. District of Columbia, 444 A.2d 1 (D.C. App.181) From da@skivs.ski.org Thu Jul 9 03:47:28 1998 From: da@skivs.ski.org (David Ascher) Date: Wed, 8 Jul 1998 19:47:28 -0700 (PDT) Subject: [Image-SIG] PIL In-Reply-To: <199807082241.SAA00186@snark.thyrsus.com> Message-ID: F/ (Fredrik) claimed he was on vacation a couple of days ago, so he may not see this for a little while. I'll let him answer most of your comments, but I thought I'd mention one thing re: fonts -- there is a 'pilfont' package at pythonware which is an improved version of ImageFont and friends, which works just fine w/ BDF and PCF fonts (and stores them in a bitmap as masks). Fredrik and I have also talked about adding Freetype support, which I use on the OpenGL side of things. Apparently an ex-colleague of F/'s has done a lot of that already but hasn't released the code yet. Anyway, font support in PIL is not a real issue. Also, FYI, a new release is promised for tomorrow (Friday), which makes me doubt F/'s claim of a vacation =). --david From Fred L. Drake, Jr." References: <199807082241.SAA00186@snark.thyrsus.com> Message-ID: <13732.47499.455957.523783@weyr.cnri.reston.va.us> David Ascher writes: > F/ (Fredrik) claimed he was on vacation a couple of days ago, so he may > not see this for a little while. I'll let him answer most of your > comments, but I thought I'd mention one thing re: fonts -- there is a ... > in a bitmap as masks). Fredrik and I have also talked about adding > Freetype support, which I use on the OpenGL side of things. Apparently an Perhaps this is a good chance to mention the t1lib library; this is a rasterizer for Adobe Type1 fonts. A Python interface (which I wrote) already exists and is bundled with the main t1lib package. See http://www.python.org/sigs/image-sig/t1lib/ for more information. -Fred -- Fred L. Drake, Jr. Corporation for National Research Initiatives 1895 Preston White Dr. Reston, VA 20191 From akuchlin@cnri.reston.va.us Thu Jul 9 19:10:56 1998 From: akuchlin@cnri.reston.va.us (Andrew Kuchling) Date: Thu, 9 Jul 1998 14:10:56 -0400 (EDT) Subject: [Image-SIG] PIL Wishlist Message-ID: <13733.1106.92053.321533@newcnri.cnri.reston.va.us> Distilling a wish list from the recent postings, I come up with this: * RLE format support * Test pattern generators * Complement: cutting out strips of an image * Support for multiple-frame images should be documented; there are some demo scripts in Scripts/ (gifmaker.py, explode.py) but they're not very well-known. * There are other font rendering solutions such as t1lib and pilfont. t1lib seems too large to add to PIL, but maybe pilfont can be added. * Freetype support * t1lib * Clean up and document the SANE interface (my job). Other suggestions? -- A.M. Kuchling http://starship.skyport.net/crew/amk/ Well, there was this doggy. He was a very clever doggy. He said things like... like... "I would feel infinitely more comfortable in your presence if you would agree to treat gravity as a law, rather than one of a number of suggested options." -- Delirium describes her dog Barnabas, in SANDMAN #63: "The Kindly Ones:7" From Kevin Hilman Thu Jul 9 19:20:33 1998 From: Kevin Hilman (Kevin Hilman) Date: 09 Jul 1998 11:20:33 -0700 Subject: [Image-SIG] PIL Wishlist In-Reply-To: Andrew Kuchling's message of "Thu, 9 Jul 1998 14:10:56 -0400 (EDT)" References: <13733.1106.92053.321533@newcnri.cnri.reston.va.us> Message-ID: How about additional file formats like YUV, UYVY. Or using ImageMagick to convert to formats PIL can understand. -kev- Andrew Kuchling writes: > Distilling a wish list from the recent postings, I come up with this: > > * RLE format support > > * Test pattern generators > > * Complement: cutting out strips of an image > > * Support for multiple-frame images should be documented; > there are some demo scripts in Scripts/ (gifmaker.py, explode.py) but > they're not very well-known. > > * There are other font rendering solutions such as t1lib and > pilfont. t1lib seems too large to add to PIL, but maybe pilfont can > be added. > > * Freetype support > > * t1lib > > * Clean up and document the SANE interface (my job). > > Other suggestions? > > -- > A.M. Kuchling http://starship.skyport.net/crew/amk/ > Well, there was this doggy. He was a very clever doggy. He said things like... > like... "I would feel infinitely more comfortable in your presence if you > would agree to treat gravity as a law, rather than one of a number of > suggested options." > -- Delirium describes her dog Barnabas, in SANDMAN #63: "The Kindly Ones:7" > > > _______________________________________________ > Image-SIG maillist - Image-SIG@python.org > http://www.python.org/mailman/listinfo/image-sig From hannu@trust.ee Thu Jul 9 19:46:21 1998 From: hannu@trust.ee (Hannu Krosing) Date: Thu, 09 Jul 1998 21:46:21 +0300 Subject: [Image-SIG] PIL Wishlist References: <13733.1106.92053.321533@newcnri.cnri.reston.va.us> Message-ID: <35A50FFD.EE83874C@trust.ee> Andrew Kuchling wrote: > > Distilling a wish list from the recent postings, I come up with this: > ... > > Other suggestions? Could we have compressed GIF's as well ? To circumvent the Unisys LZW patent, we could do as xpdf does: unix command compress + some converion code ? It would be a little slower, but probably fast enough for most uses. Hannu From richard.jones@BoM.GOV.AU Thu Jul 9 23:57:02 1998 From: richard.jones@BoM.GOV.AU (Richard Jones) Date: Thu, 09 Jul 1998 22:57:02 +0000 Subject: [Image-SIG] PIL Wishlist In-Reply-To: Message from Hannu Krosing of 1998-Jul-9 21:46:21, <35A50FFD.EE83874C@trust.ee> Message-ID: <199807092257.WAA28006@mage.ho.bom.gov.au> Hannu Krosing wrote: > Could we have compressed GIF's as well ? Some correspondence with /F I had once covered this topic. He approached Unisys about it. He did not come away with warm fuzzies. In fact, there were going to be serious problems licensing a _free_ package like PIL. So it's not in there. > To circumvent the Unisys LZW patent, we could do as xpdf does: > unix command compress + some converion code ? > > It would be a little slower, but probably fast enough for most uses. I've simply stopped using GIF now in favour of PNG. Unfortunately, the PNG implementation in PIL is bare-bones, and I'd remedy that if I had the time (which I will do soon, but it's just one thing on a list...). Also, the PNG support in WWW browsers is a little bare-bones also --- though it _is_ there. And with PNG you have license-free compression and a _bundle_ of other features GIF probably never even thought of :) Richard From Dirk.Engelmann@IWR.Uni-Heidelberg.De Fri Jul 10 09:52:10 1998 From: Dirk.Engelmann@IWR.Uni-Heidelberg.De (Dirk Engelmann) Date: Fri, 10 Jul 1998 10:52:10 +0200 (CEST) Subject: [Image-SIG] PIL Wishlist In-Reply-To: <13733.1106.92053.321533@newcnri.cnri.reston.va.us> Message-ID: Hi! Just to add a point on the wish list: Once there was a discussion about replacing the fromstring and tostring method. I would like to have a method which doesn't convert the image to/from strings, but passes a pointer (or pointer list). The NumPy library (don't know how it is done exactely) has methods to do this, and it would be nice to have an efficient access to image data from other python modules like NumPy. Cheers, Dirk On Thu, 9 Jul 1998, Andrew Kuchling wrote: > Distilling a wish list from the recent postings, I come up with this: > > * RLE format support > > * Test pattern generators > > * Complement: cutting out strips of an image > > * Support for multiple-frame images should be documented; > there are some demo scripts in Scripts/ (gifmaker.py, explode.py) but > they're not very well-known. > > * There are other font rendering solutions such as t1lib and > pilfont. t1lib seems too large to add to PIL, but maybe pilfont can > be added. > > * Freetype support > > * t1lib > > * Clean up and document the SANE interface (my job). > > Other suggestions? > > -- > A.M. Kuchling http://starship.skyport.net/crew/amk/ > Well, there was this doggy. He was a very clever doggy. He said things like... > like... "I would feel infinitely more comfortable in your presence if you > would agree to treat gravity as a law, rather than one of a number of > suggested options." > -- Delirium describes her dog Barnabas, in SANDMAN #63: "The Kindly Ones:7" > > > _______________________________________________ > Image-SIG maillist - Image-SIG@python.org > http://www.python.org/mailman/listinfo/image-sig > From fredrik@pythonware.com Fri Jul 10 18:01:03 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Fri, 10 Jul 1998 18:01:03 +0100 Subject: [Image-SIG] Your wish is my command (long) Message-ID: <025901bdac24$56c43020$f29b12c2@pythonware.com> lot's of traffic on the image sig... david> Also, FYI, a new release is promised for tomorrow (Friday), david> which makes me doubt F/'s claim of a vacation =). Well, I was away from work yesterday (almost). but the PIL release might be somewhat delayed -- our local internet provider messed up their DNS servers, and our website host has messed up their NFS servers, so I cannot upload the new PIL just yet. they've promised me that everything will work again later tonight. sigh. ... in the meantime, here are some comments... eric> A couple of documentation bugs Interesting. The PIL docs has been unchanged for about a year, and suddently three people reports the "Enchance" typo within two days! I've changed that; the rest will be taken care of in the next documentation update. eric> I think the PIL documentation's use of the term `band' is rather eric> unfortunate. To a native English speaker, the term `band' applied to eric> a graphic has a strong primary meaning which is different from PIL's; eric> that of a stripe or region (especially a horizontal one) extending the eric> full width of the image. Well, the term "band" is actually take from an ISO 12087, which AFAIK is the only international standard discussing image processing libraries... there's a few other historical things in PIL, including the name "mode" for the pixel format, the string "L" for greyscale images, etc. All these might be debatable, but I think changing it would cause as much confusion as keeping it as it is. eric> I announced this to a group that included Guido. He steered me to eric> PIL. PIL is very impressive We're pretty impressed ourselves -- people have built some really awesome applications on top of PIL... (if my old bosses had known how many commercial applications people have built on PIL, they wouldn't have allowed me to give it away... on the other hand, they've using it themselves, so they shouldn't complain ;-) eric> so much so that I think it will enable eric> me to officially declare the utilities in GIFLIB obsolete (but not the eric> library; that's a separate issue to which I'll return below). Are you going to write a new set of utilities based on PIL, or will you abandon GIFLIB completely? The reason I'm asking is that some of the missing stuff should probably be implemented on top of the existing functionality, rather than added to the core library (I don't mind shipping such utilities with PIL, of course). eric> Allow an optional argument in show() specifying the viewer eric> command to use. I'll do something about this in the next release (not 0.3b1). eric> 1. No ability to display images using (a) the Borland BGI driver, eric> (b) an Epson printer, (c) a Hercules graphics card. I think we eric> can safely write these off as obsolete, however. Last time I wrote a Hercules driver was ten years ago. We had trouble finding a card testing it on... eric> 2. No ability to handle RLE (Utah raster toolkit) images. I'm not eric> too worried about this, either. Is anybody still using it? andrew> * RLE format support Well, show me anyone using the Utah RLE format (or the Utah raster toolkit, for that matter). If you find anyone, tell him/her to send me some samples and a spec, and I'll see what I can do... (afaik, the Utah stuff is about as obsolete as the Hercules mono- chrome graphics adapter). (PIL already supports a few other RLE formats, btw). eric> 3. No equivalent of the gifbg, gifwedge, and gifcolor test pattern eric> generators. These would probably be trivial to write. andrew> * Test pattern generators There's one undocumented generator available in the Image module: im = Image._wedge() This generates a 256x256 greyscale wedge. Use convert, resize, and rotate/transpose to process it anyway you want. There's in fact a few more generators in the libImaging code; see the Fill.c and Effects.c sources for details. Still haven't made up my mind on how/if those should be exposed... The other patterns provided by GIFLIB should be pretty straight- forward to implement using the ImageDraw module. Best done in a utility. eric> 4. (Serious) I see no way to overlay text on an image. I can supply a eric> clean 8x8 font and code for ImageDraw to overlay messages on an image eric> using it. Later this method could be extended to support fonts from eric> an X font server. Is this sort of thing what the missing ImageFont eric> module is about? Yep. Todays 0.3b1 release includes the "pilfont" package which contains converters for PCF and BDF raster fonts. See the sources for details. More font info below. eric> 5. (Serious) There's no equivalent of the `clip complement' feature in eric> giflclip. Here's the GIF description: andrew> * Complement: cutting out strips of an image Well, it takes about five lines of code to implement this (one new, followed by four crop/paste operations). So again, I don't think PIL needs to be modified. Better done in a utility. eric> 6. I see no standard function or method for composing multiple images into a eric> multi-file image in formats like GIF. (The gifovly utility in GIFLIB eric> does this.) andrew> * Support for multiple-frame images should be documented; andrew>there are some demo scripts in Scripts/ (gifmaker.py, explode.py) but andrew>they're not very well-known. The seek/tell methods that can be used to read multiple-frame formats are documented (they work for at least GIF, FLI/FLC, and TIFF, plus the lesser known ARG and IM formats). Also see the ImageSequence module. To write GIF animations, you can import "gifmaker" and use the "makedelta" method on a list of images. See the source for details. Adding a general mechanism that works with more formats is harder; the best way is to redesign the save plugin interface (there's other reasons for this), but there's no way to do that in time for 0.3 final. Maybe in 0.4 (aka 1.0). eric> 7. (Serious) There doesn't seem to be any way to specify interlacing or the eric> screen size on GIF output (GIFLIB's gifinter and gifpos utilities). I just added interlace write support; it's in the 0.3b1 release. If you have the time, patches for screen size (and other relevant options) would be welcome. eric> 8. No utility to section an image into tiles (gifburst). eric> 9. No equivalent of my icon2gif utility (see eric> http://www.tuxedo.org/~esr/giflib/doc/icon2gif.html eric>). Never mind, this is very GIF-specific. Shouldn't be hard to write utilities for these (crop/paste are your best friends). eric> If I hack in some of these things, can I stimulate a PIL release to eric> replace GIFLIB? If you do it as a set of utilities, I'll ship them with the next release. If there's anything you want to change in the core library, let's discuss that case for case. ... Now to the remaining points on Andrew's wishlist: andrew> * There are other font rendering solutions such as t1lib and andrew>pilfont. t1lib seems too large to add to PIL, but maybe pilfont can andrew>be added. the pilfont stuff is included in 0.3b1. This includes the pilfont "compiler", and the upgraded ImageFont module. andrew> * Freetype support andrew> * t1lib Fred's written a t1lib interface, and I'm waiting for someone (you know who you are!) to release his freetype interface... when that's out, and I find some spare time, I'll try to come up with a unified raster font interface. It would include: -- a font registry -- a font selection mechanism (something similar to tkFont, most likely) -- some optimizations in PIL (the current ImageFont implementation is pretty gross...) andrew> * Clean up and document the SANE interface (my job). The current release is shipped with 0.3b1; it would be great to have the updated interface in time for the final 0.3 release (nudge, nudge) ... More wishes: kevin> How about additional file formats like YUV, UYVY. Is this the SGI flavour? There appears to be no shortage of formats calling themselves YUV -- and they all use different resampling and colour weights... PIL 0.3b1 adds direct support for YCrCb (the JFIF flavour: CCIR 601-1 without chroma headroom). It can also read PhotoYCC via the PCD decoder. Other YUV versions can be read as RGB, and converted via the matrix option to the convert method (see the manual for details). Anyway, if you have specs and samples for the SGI version, just send them to me, and I'll add support if I can. kevin> Or using ImageMagick to convert to formats PIL can understand. Zach Roadhouse has written an ImageMagick interface for Python, which includes a PIL interface. http://starship.skyport.net/crew/zack/pymagick/ http://starship.skyport.net/crew/zack/pymagick/#pil hannu> Could we have compressed GIF's as well ? hannu> To circumvent the Unisys LZW patent, we could do as xpdf hannu> does: unix command compress + some converion code ? PIL reads compressed GIF files. The Ghostscript folks have researched this, and all the experts they consulted claimed that Unisys' LZW patent cannot prevent you from doing this (on the other hand, most experts claim that Unisys' patent is moot anyway since IBM also happens to have a patent on the same thing... and on the third hand, Unisys' LZW patent isn't valid in Sweden anyway ;-). Also note that you can disable all kinds of LZW support via the WITH_LZW option in _imaging.c. When writing GIF files, PIL uses a simpleminded encoding that, by some odd reason, LZW decoders have no trouble reading. To write compressed GIF files, there are a number of options: -- install NETPBM, and hack GifImagePlugin so it uses _save_ppm instead of _save (just remove "_ppm" from the latter). -- write an _imaging_gif module that takes a PIL image and writes a GIF version of it. how you implement that is up to you... Unfortunately, I don't think you can use compress to write GIF files; most compress implementations use something called "deferred clear codes" to get better compression. This is explicitly allowed by the GIF specification, but nobody seems to get that right. For example, Navigator 2 and 3 bombs if you give them such files (don't know if it's fixed in version 4). FWIW, Internet Explorer has always handled them just fine, though... richard> Unfortunately, the PNG implementation in PIL is bare-bones, richard> and I'd remedy that if I had the time (which I will do soon, richard> but it's just one thing on a list...). What do you have in mind here? The only thing I can think of is that the current implementation doesn't support interlaced PNG's (patches are wel- come). What else have I missed? dirk> Once there was a discussion about replacing the fromstring and dirk> tostring method. I would like to have a method which doesn't dirk> convert the image to/from strings, but passes a pointer (or pointer dirk> list). The NumPy library (don't know how it is done exactely) has dirk> methods to do this, and it would be nice to have an efficient dirk> access to image data from other python modules like NumPy. Hey, I didn't know that Python had support for pointers ;-) I don't use NumPy myself (I have colleagues that do, but they haven't told me about that feature -- maybe they haven't looked close enough). Can any NumPy guru explain this? For example, it would be trivial to add a method that returns a list of long integers, each pointing to the beginning of a line. For small images, there's actually a way to do this today: im.load() ptr = im.im.isblock() If ptr is non-zero, it's a pointer to the first byte in the image memory. ... that's all for today. now I'm off to see armageddon. the PIL sources might make it to our site later tonight. stay tuned. Cheers /F From Fred L. Drake, Jr." References: <025901bdac24$56c43020$f29b12c2@pythonware.com> Message-ID: <13734.15205.788715.987342@weyr.cnri.reston.va.us> Fredrik Lundh writes: > richard> Unfortunately, the PNG implementation in PIL is bare-bones, > richard> and I'd remedy that if I had the time (which I will do soon, > richard> but it's just one thing on a list...). > > What do you have in mind here? The only thing I can think of is that the > current implementation doesn't support interlaced PNG's (patches are wel- > come). What else have I missed? I have some patches for the PNG support that I'll send in as soon as I have a chance to clean it up a little. It adds support for some additional chunk types and changes the handling of tEXt chunks a little. I'll describe it in more detail when I post the patch. (Probably next week sometime.) -Fred -- Fred L. Drake, Jr. Corporation for National Research Initiatives 1895 Preston White Dr. Reston, VA 20191 From esr@snark.thyrsus.com Fri Jul 10 18:54:07 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Fri, 10 Jul 1998 13:54:07 -0400 Subject: [Image-SIG] Re: Your wish is my command (long) In-Reply-To: <025901bdac24$56c43020$f29b12c2@pythonware.com>; from Fredrik Lundh on Fri, Jul 10, 1998 at 06:01:03PM +0100 References: <025901bdac24$56c43020$f29b12c2@pythonware.com> Message-ID: <19980710135407.A6888@snark.thyrsus.com> Fredrik Lundh : > eric> I think the PIL documentation's use of the term `band' is rather > eric> unfortunate. To a native English speaker, the term `band' applied to > eric> a graphic has a strong primary meaning which is different from PIL's; > eric> that of a stripe or region (especially a horizontal one) extending the > eric> full width of the image. > > Well, the term "band" is actually take from an ISO 12087, which AFAIK is > the only international standard discussing image processing libraries... > there's a few other historical things in PIL, including the name "mode" for > the pixel format, the string "L" for greyscale images, etc. That explains it. ISO 12087 was probably composed in French. I bet you that `band' is a literal translation of a French word by someone who didn't know what a connotative mess he was making. Please don't propagate this error. > All these might > be debatable, but I think changing it would cause as much confusion as > keeping it as it is. Depends on whether you think your "market" is mostly English-speaking. If so, I would strongly recommend "band" -> "channel". > Are you going to write a new set of utilities based on PIL, or will > you abandon GIFLIB completely? The reason I'm asking is that some of > the missing stuff should probably be implemented on top of the existing > functionality, rather than added to the core library (I don't mind shipping > such utilities with PIL, of course). What I'm planning (and have in fact already started writing) is a Python script called `pildriver' which allows the user to specify a pipeline of PIL image transformations by specifying command-line switches. The intention is that it should take an image stream on stdin and emit an image stream on stdout. Usage like this: pildriver eric> Allow an optional argument in show() specifying the viewer > eric> command to use. > > I'll do something about this in the next release (not 0.3b1). Actually I have another show method change for you. For pipeline-construction purposes, automatically backgrounding the image display is not the right thing. So: --- Image.py 1998/07/10 05:25:57 1.1 +++ Image.py 1998/07/10 16:34:05 @@ -823,7 +823,7 @@ # -------------------------------------------------------------------- # Unix display support -def _showxv(self, title = None): +def _showxv(self, background=0, title=None): if self.mode == "P": file = self.convert("RGB")._dump() @@ -835,4 +835,7 @@ else: opt = "" - os.system("(xv %s %s; rm -f %s)&" % (opt, file, file)) + command = "xv %s %s && rm -f %s" % (opt, file, file) + if background: + command = "(%s)&" (command,) + os.system(command) > eric> 1. No ability to display images using (a) the Borland BGI driver, > eric> (b) an Epson printer, (c) a Hercules graphics card. I think we > eric> can safely write these off as obsolete, however. > > Last time I wrote a Hercules driver was ten years ago. We had > trouble finding a card testing it on... OK, we can agree to forget this. > eric> 2. No ability to handle RLE (Utah raster toolkit) images. I'm not > eric> too worried about this, either. Is anybody still using it? > > andrew> * RLE format support > > Well, show me anyone using the Utah RLE format (or the Utah > raster toolkit, for that matter). If you find anyone, tell him/her > to send me some samples and a spec, and I'll see what I can do... > > (afaik, the Utah stuff is about as obsolete as the Hercules mono- > chrome graphics adapter). You're probably right. As I said, I'm not going to worry about it. GIFLIB dates from '89; I started maintaining it around '91. There's a lot of stuff in there that's now only of historical interest. > eric> 3. No equivalent of the gifbg, gifwedge, and gifcolor test pattern > eric> generators. These would probably be trivial to write. > > andrew> * Test pattern generators > > There's one undocumented generator available in the Image module: > > im = Image._wedge() > > This generates a 256x256 greyscale wedge. Use convert, resize, > and rotate/transpose to process it anyway you want. > > There's in fact a few more generators in the libImaging code; see > the Fill.c and Effects.c sources for details. Still haven't made up > my mind on how/if those should be exposed... > > The other patterns provided by GIFLIB should be pretty straight- > forward to implement using the ImageDraw module. Best done > in a utility. I agree. > eric> 4. (Serious) I see no way to overlay text on an image. I can supply a > eric> clean 8x8 font and code for ImageDraw to overlay messages on an image > eric> using it. Later this method could be extended to support fonts from > eric> an X font server. Is this sort of thing what the missing ImageFont > eric> module is about? > > Yep. Todays 0.3b1 release includes the "pilfont" package which contains > converters for PCF and BDF raster fonts. See the sources for details. Which brings up a question. Why aren't docs included with the PIL distribution? The only docs I've found are the (incomplete) PIL web pages. > eric> 5. (Serious) There's no equivalent of the `clip complement' feature in > eric> giflclip. Here's the GIF description: > > andrew> * Complement: cutting out strips of an image > > Well, it takes about five lines of code to implement this (one new, > followed by four crop/paste operations). So again, I don't think PIL > needs to be modified. Better done in a utility. OK, I can live with this. > eric> 6. I see no standard function or method for composing multiple images > eric> into a multi-file image in formats like GIF. (The gifovly utility in > eric> GIFLIB does this.) > > andrew> * Support for multiple-frame images should be documented; > andrew>there are some demo scripts in Scripts/ (gifmaker.py, explode.py) but > andrew>they're not very well-known. > [...] > Adding a general mechanism that works with more formats is harder; > the best way is to redesign the save plugin interface (there's other > reasons for this), but there's no way to do that in time for 0.3 final. > Maybe in 0.4 (aka 1.0). Yes, I'd figured this out myself. It's not an immediate issue for me, but I'm glad to know it's on the to-doi list. > eric> 7. (Serious) There doesn't seem to be any way to specify interlacing > eric> or the screen size on GIF output (GIFLIB's gifinter and gifpos > utilities). > > I just added interlace write support; it's in the 0.3b1 release. If you > have the time, patches for screen size (and other relevant options) > would be welcome. I'll look into that once I get the new release. > eric> 8. No utility to section an image into tiles (gifburst). > > eric> 9. No equivalent of my icon2gif utility (see > eric> http://www.tuxedo.org/~esr/giflib/doc/icon2gif.html > eric>). Never mind, this is very GIF-specific. > > Shouldn't be hard to write utilities for these (crop/paste are your > best friends). True. > eric> If I hack in some of these things, can I stimulate a PIL release to > eric> replace GIFLIB? > > If you do it as a set of utilities, I'll ship them with the next release. > If there's anything you want to change in the core library, let's discuss > that case for case. OK, what I'll do is put together pildriver for the next release. -- Eric S. Raymond According to the National Crime Survey administered by the Bureau of the Census and the National Institute of Justice, it was found that only 12 percent of those who use a gun to resist assault are injured, as are 17 percent of those who use a gun to resist robbery. These percentages are 27 and 25 percent, respectively, if they passively comply with the felon's demands. Three times as many were injured if they used other means of resistance. -- G. Kleck, "Policy Lessons from Recent Gun Control Research," From akuchlin@cnri.reston.va.us Fri Jul 10 22:06:56 1998 From: akuchlin@cnri.reston.va.us (Andrew Kuchling) Date: Fri, 10 Jul 1998 17:06:56 -0400 (EDT) Subject: [Image-SIG] Re: Your wish is my command (long) In-Reply-To: <19980710135407.A6888@snark.thyrsus.com> References: <025901bdac24$56c43020$f29b12c2@pythonware.com> <19980710135407.A6888@snark.thyrsus.com> Message-ID: <13734.32935.3371.768238@newcnri.cnri.reston.va.us> Fredrik Lundh writes: Eric S. Raymond writes: >That explains it. ISO 12087 was probably composed in French. I bet you >that `band' is a literal translation of a French word by someone who didn't >know what a connotative mess he was making. Please don't propagate this >error. If we're taking a vote, I quite agree; the usage of 'band' seemed strange to me when I read the documentation, and I kept having to substitute 'channel' for it. >-def _showxv(self, title = None): >+def _showxv(self, background=0, title=None): Better to exchange the two arguments in this patch; that won't break any code which does X._showxv("foo.gif") >Which brings up a question. Why aren't docs included with the PIL >distribution? The only docs I've found are the (incomplete) PIL >web pages. Fredrik, I'd be willing to help convert the docs to LaTeX if that would be helpful. >OK, what I'll do is put together pildriver for the next release. Bet that'll be misread as 'piledriver' a lot. :) -- A.M. Kuchling http://starship.skyport.net/crew/amk/ "They never liked us, did they?" "Gods don't 'like'. They love and they hate and they ignore." -- Stheno and Euryale, in SANDMAN #61: "The Kindly Ones:5" From esr@snark.thyrsus.com Fri Jul 10 22:47:40 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Fri, 10 Jul 1998 17:47:40 -0400 Subject: [Image-SIG] Re: Your wish is my command (long) In-Reply-To: <13734.32935.3371.768238@newcnri.cnri.reston.va.us>; from Andrew Kuchling on Fri, Jul 10, 1998 at 05:06:56PM -0400 References: <025901bdac24$56c43020$f29b12c2@pythonware.com> <19980710135407.A6888@snark.thyrsus.com> <13734.32935.3371.768238@newcnri.cnri.reston.va.us> Message-ID: <19980710174740.A7170@snark.thyrsus.com> Andrew Kuchling : > >-def _showxv(self, title = None): > >+def _showxv(self, background=0, title=None): > > Better to exchange the two arguments in this patch; that won't > break any code which does X._showxv("foo.gif") I can live with that. > >Which brings up a question. Why aren't docs included with the PIL > >distribution? The only docs I've found are the (incomplete) PIL > >web pages. > > Fredrik, I'd be willing to help convert the docs to LaTeX if > that would be helpful. And I'm willing to document what I do, and help edit other peoples' docs, provided I know what the master format is. > >OK, what I'll do is put together pildriver for the next release. > > Bet that'll be misread as 'piledriver' a lot. :) The play on words was intentional. It should have occurred to me that it might be a misfeature... -- Eric S. Raymond There's a truism that the road to Hell is often paved with good intentions. The corollary is that evil is best known not by its motives but by its *methods*. From fredrik@pythonware.com Fri Jul 10 23:49:34 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Fri, 10 Jul 1998 23:49:34 +0100 Subject: [Image-SIG] ANN: PIL 0.3b1 is out Message-ID: <04fc01bdac55$04158a00$f29b12c2@pythonware.com> This is the first, and hopefully only beta release for PIL 0.3. The source kit has been built and tested on Windows 95 and NT, as well as on Linux (Redhat 4.2) and Digital Unix. It should build out of the box on most mainstream Unix versions. Get your copy of the sources from: http://www.pythonware.com Note: Some minor changes were made to the sources between the py15 binary release and the source release. A new py15 kit will be released as soon as our py15 wranglers get back from their vacation. --the PIL team special thanks to: the image sig folks and massive attack no thanks to: algonet, lejonet, and michael bay (who all worked really hard to ruin our day). From Anthony Baxter Sat Jul 11 02:09:11 1998 From: Anthony Baxter (Anthony Baxter) Date: Sat, 11 Jul 1998 11:09:11 +1000 Subject: [Image-SIG] initial PIL 0.3b1 compile notes. In-Reply-To: Your message of "Fri, 10 Jul 1998 18:01:03 +0100." <025901bdac24$56c43020$f29b12c2@pythonware.com> Message-ID: <199807110109.LAA06086@koro.off.connect.com.au> Following are random notes from building PIL on Solaris 2.5.1. had some annoyances with libImaging, caused by libz being a shared library, and configure's stupidity in not adding the library given by --with-zlib to either a -R option, or LD_LIBRARY_PATH. I'd also like to see the hardcoded: JPEGINCLUDE= /usr/local/include removed from Makefile.in, and made an option to configure... The README file talks about a "test" directory - this doesn't seem to be in the distribution - neither does the "Lib" directory. ImageFont still seems to be broken - I'll put more about this in a seperate message... Other things I'd like to see added/fixed in PIL [1] I'd like to see it made a package - right now, you can either rebuild your python interpreter to set a new sys.path, or dump all the files in the site directory. ew. Can the "pilplot" package (which I picked up from Fredrik somewhere along the line) be added to the standard distribution? Enough for now, Anthony [1] and which, given an infinite supply of round tuits, I'd fix myself From arb@connect.com.au Sat Jul 11 05:07:26 1998 From: arb@connect.com.au (Anthony Baxter) Date: Sat, 11 Jul 1998 14:07:26 +1000 Subject: [Image-SIG] more on ImageFont. Message-ID: <199807110407.OAA17101@koro.off.connect.com.au> Ok, as of 0.3b1, I canna get ImageFont to do anything but error. I've tried regenerating the .pil font files, and I've even dug into the C source code (horrors). File "test", line 10, in text m=self.font.getmask(text) File "/opt/python/lib/python1.5.1/site/ImageFont.py", line 87, in getmask im.im.paste(self.image.im.crop(tuple(m[6:10])), SystemError: NULL result without error in call_object Breaking this down into tmp2 = tuple(m[6:10]) # tmp2 = (55, 0, 59, 7) tmp = self.image.im.crop(tmp2) im.im.paste(tmp, (x0+x, y0, x1+x, y1)) shows the error occurs on the "crop()" line. The problem appears to be in libImaging/Crop.c, in the ImagingCrop function. This section of code was added between 0.2b3 and 0.3something. I'm stuffed if I can figure out what it's meant to do, or how it's meant to work properly. ifdef'ing this section out at least makes the getmask() calls seem to work. Anyone know what it's meant to do? /* Determine which region to copy */ xoff = yoff = 0; if (x0 < 0) xsize += x0, xoff = -x0, x0 = 0; if (x0 + xsize > imOut->xsize) xsize = imOut->xsize - x0; if (y0 < 0) ysize += y0, yoff = -y0, y0 = 0; if (y0 + ysize > imOut->ysize) ysize = imOut->ysize - y0; if (xsize <= 0 || ysize <= 0) return 0; Anthony From lorenzo@argon.roma2.infn.it Sun Jul 12 13:44:38 1998 From: lorenzo@argon.roma2.infn.it (Lorenzo M. Catucci) Date: Sun, 12 Jul 1998 14:44:38 +0200 (CEST) Subject: [Image-SIG] Latest PIL and tiff class F Message-ID: Now, I can understand the reason why fredrick prefers a pure python solution as long as it is feasible, and see there have been added as supported tags the ones for g3 and g4 fax formats, but still there is no support for the image format. I tried reading the class F docs, but got to nowhere, confused by 1d, 2d compression, references to T.4 and T.6, and the like. Worst of all, I'm unable to capitalise onto pymagick's tiff reading, since it just ignores the density/resolution tags, which are in between the most needed for me... Help, lorenzo PS How did you manage to have PIL installed just before midnight, /F? It seems all of that problems you talk about your ISP were simply a way to warm up our appetite, and put us on hold just until the end of the day! From arb@connect.com.au Sun Jul 12 13:54:27 1998 From: arb@connect.com.au (Anthony Baxter) Date: Sun, 12 Jul 1998 22:54:27 +1000 Subject: [Image-SIG] tiny no-brainer patch for pilconvert.py Message-ID: <199807121254.WAA07697@koro.off.connect.com.au> This makes the -f flag work. *** pilconvert.py.orig Sun Jul 12 22:52:48 1998 --- pilconvert.py Sun Jul 12 22:53:00 1998 *************** *** 38,44 **** usage() try: ! opt, argv = getopt.getopt(sys.argv[1:], "dc:gopq:r") except getopt.error, v: print v sys.exit(1) --- 38,44 ---- usage() try: ! opt, argv = getopt.getopt(sys.argv[1:], "dc:gopq:rf") except getopt.error, v: print v sys.exit(1) *************** *** 52,57 **** --- 52,58 ---- if o == "-f": Image.init() + id = Image.ID[:] id.sort() print "Supported formats:" for i in id: From richard.jones@BoM.GOV.AU Mon Jul 13 02:11:56 1998 From: richard.jones@BoM.GOV.AU (Richard Jones) Date: Mon, 13 Jul 1998 01:11:56 +0000 Subject: [Image-SIG] initial PIL 0.3b1 compile notes. In-Reply-To: Message from Anthony Baxter of 1998-Jul-11 11:9:11, <199807110109.LAA06086@koro.off.connect.com.au> Message-ID: <199807130111.BAA05067@mage.ho.bom.gov.au> Anthony Baxter wrote: > Following are random notes from building PIL on Solaris 2.5.1. I'm on HPUX 10.20. > had some annoyances with libImaging, caused by libz being a shared library, > and configure's stupidity in not adding the library given by --with-zlib > to either a -R option, or LD_LIBRARY_PATH. Yeh, I end up linking statically to get around these hassles. > I'd also like to see the hardcoded: > JPEGINCLUDE= /usr/local/include > removed from Makefile.in, and made an option to configure... Yeh, the configure and make really only seems to work (I assume) if the jpeg and zlib libraries are in /usr/local or in the system path somewhere, and therefore aren't reliant upon configure command-line options. For example, my configure line of: configure --with-jpeg=/opt/jpeg-6 --with-zlib=/opt/zlib-1.1.2 doesn't work. (Note that I'm on HPUX and the configure line is also prefixed with CC="cc -Aa -D_HPUX_SOURCE" *blech*). Anyway, I'm back to editing the Makefile by hand just like The Old Days :) Does anyone know the appropriate configure magic to make this work? Richard From richard.jones@BoM.GOV.AU Mon Jul 13 02:36:00 1998 From: richard.jones@BoM.GOV.AU (Richard Jones) Date: Mon, 13 Jul 1998 01:36:00 +0000 Subject: [Image-SIG] initial PIL 0.3b1 compile notes. In-Reply-To: Message from Richard Jones of 1998-Jul-13 1:11:56, <199807130111.BAA05067@mage.ho.bom.gov.au> Message-ID: <199807130136.BAA06171@mage.ho.bom.gov.au> Richard Jones wrote: > Anthony Baxter wrote: > > Following are random notes from building PIL on Solaris 2.5.1. > > I'm on HPUX 10.20. > > > I'd also like to see the hardcoded: > > JPEGINCLUDE= /usr/local/include > > removed from Makefile.in, and made an option to configure... > > Yeh, the configure and make really only seems to work (I assume) if > the jpeg and zlib libraries are in /usr/local or in the system path > somewhere, and therefore aren't reliant upon configure command-line > options. For example, my configure line of: > > configure --with-jpeg=/opt/jpeg-6 --with-zlib=/opt/zlib-1.1.2 > > doesn't work. (Note that I'm on HPUX and the configure line is also > prefixed with CC="cc -Aa -D_HPUX_SOURCE" *blech*). Make that "cc +z -Aa -D_HPUX_SOURCE" in libImaging to produce PIC objects. Is there any way that the configure in there could pick up stuff from the Python install since it figures most of this platform-specific stuff out (except of course the library location stuff). Also, the mmap test fails for reasons unknown to me. config.log doesn't have any error messages. I tried compiling and running the source from config.log (I had to remove confdefs.h since I don't know how to recreate it) and it seemed to work fine... HPUX 10.20 mmap man page says: STANDARDS CONFORMANCE mmap(): AES, SVID3 for whatever that's worth... Richard From fredrik@pythonware.com Thu Jul 16 17:56:45 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Thu, 16 Jul 1998 17:56:45 +0100 Subject: [Image-SIG] more on ImageFont. Message-ID: <024f01bdb0db$17a704b0$f29b12c2@pythonware.com> Anthony Baxter wrote: >Ok, as of 0.3b1, I canna get ImageFont to do anything but error. I've tried >regenerating the .pil font files, and I've even dug into the C source code >(horrors). As I've told Anthony in private mail, this is because we missed a file when we added pilfont to the 0.3b1 release. And by some odd reason, the ImageDraw text method wasn't covered by PIL's test suite... Expect an 0.3b2 release any day soon (friday or saturday, most likely), with the following changes: -- working font support -- YCbCr instead of YCrCb! (to avoid more headaches...) -- the "offset" method now works for GIF and IM images -- support for 16-bit PNG files (read/written as mode "I") Cheers /F fredrik@pythonware.com http://www.pythonware.com From fredrik@pythonware.com Thu Jul 16 17:50:19 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Thu, 16 Jul 1998 17:50:19 +0100 Subject: [Image-SIG] Re: Your wish is my command (long) Message-ID: <024e01bdb0db$178e9ab0$f29b12c2@pythonware.com> >> Well, the term "band" is actually take from an ISO 12087, which AFAIK is >> the only international standard discussing image processing libraries... >> there's a few other historical things in PIL, including the name "mode" for >> the pixel format, the string "L" for greyscale images, etc. > >That explains it. ISO 12087 was probably composed in French. I bet you >that `band' is a literal translation of a French word by someone who didn't >know what a connotative mess he was making. Please don't propagate this >error. Nope. This has nothing to do with weird european languages; it's more of an "image processing" vs. "computer graphics" issue. Image processing folks (like me) usually get their images from some kind of sensor (usually an RGB scanner or camera, but there's many other sources). And most sensors have bandpass characteristics, so the term "band" is more accurate than "channel"... If you still think this is a european plot, take a look at XIE (X's Image Extension, designed by 100% americans), or XIL (Sun's Image Processing Library). Or look at your nearest remote sensing FAQ; it should explain acronyms like: BIP, Band Interleaved by Pixel BIL, Band Interleaved by Line BSQ, Band SeQuential IMHO, the term "band" is much more well-defined in the image processing universe than words like "object", "instance", "type", and "garbage collection" are in the programming universe... Cheers /F fredrik@pythonware.com http://www.pythonware.com From fredrik@pythonware.com Thu Jul 16 18:43:02 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Thu, 16 Jul 1998 18:43:02 +0100 Subject: [Image-SIG] Re: Your wish is my command (long) Message-ID: <02e601bdb0e1$329508c0$f29b12c2@pythonware.com> >Implementation problem: I don't see how to read an image from a file stream >object. ImageFileIO isn't documented and I can't find the code. (Sigh. I really thought I'd eliminated all traces of ImageFileIO from the docs...) This question is a bit trickier than it sounds, since many formats require random access to the image data. Here's how I would do it: -- create a file-like class which wraps a file object, and provides read, seek, and tell methods. this class should also keep track of the current file position. -- the read method calls "read" for the underlying file object, and updates the current position. -- the seek method returns immediately if the caller seeked to the current position. if the wrapped file object is seekable, call "seek" as usual and update the current position. if the wrapped file object is not seekable, skip data if the caller seeked beyond the current position, and raise a suitable exception otherwise. -- the tell method returns the current position. Then wrap sys.stdin in an object of this kind, and pass it to Image.open. If PIL can read the image in streaming mode, it will. Otherwise, you'll get an exception. Cheers /F fredrik@pythonware.com http://www.pythonware.com From dcs@lems.brown.edu Fri Jul 17 19:14:42 1998 From: dcs@lems.brown.edu (Daniel C. Sokoloff) Date: Fri, 17 Jul 1998 14:14:42 -0400 Subject: [Image-SIG] PythonMagick on NT Message-ID: <35AF9492.511@lems.brown.edu> Hello, I am wondering if anyone has worked at getting ImageMagick or PythonMagick running on a Windows NT system. I am trying to port a graphic manipulation program from unix to NT and the problem has been with ImageMagick (not directly portable, had to get NT binaries). Otherwise, are there any other imaging packages for NT that work with PIL? thanks, Dan. From esr@snark.thyrsus.com Sat Jul 18 17:08:48 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Sat, 18 Jul 1998 12:08:48 -0400 Subject: [Image-SIG] Proof-of-concept version of pildriver Message-ID: <19980718120848.A27192@snark.thyrsus.com> --ibTvN161/egqYuK8 Content-Type: text/plain; charset=us-ascii Here's the first alpha version. It's not complete, and there are some things in it that don't yet work right (either due to underlying PIL problems or because I don't understand intended usage). It should demonstrate, however, that pildriver has the potential to replace several of the ad-hoc scripts in the present distribution with a single simple utility that serves as an image batch processor, a test driver, and a tutorial exerciser. I'd have documented this, but I don't know what the preferred format for PIL documentation is, or if there is one. Feedback, comments, and corrections are solicited. -- Eric S. Raymond To make inexpensive guns impossible to get is to say that you're putting a money test on getting a gun. It's racism in its worst form. -- Roy Innis, president of the Congress of Racial Equality (CORE), 1988 --ibTvN161/egqYuK8 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=pildriver #!/usr/bin/env python # # pildriver -- an image-processing calculator for PIL # # by Eric S. Raymond # $Id: pildriver,v 1.1 1998/07/18 16:08:25 esr Exp $ # # This is essentially a Polish-notation interpreter for sequencing # PIL image transformations. All operations consume their arguments # off the stack (use `dup' to keep copies around). Use `verbose 1` # to see the stack state before each operation. # # PILDriver doesn't catch any exceptions, on the theory that these # are actually diagnostic information that should be interpreted by # the calling code. # # TO DO: # 1. Add PILFont capabilities, once that's documented. # 2. Add PILDraw operations. # 3. Add support for composing and decomposing multiple-image files. # import Image, string class PILDriver: verbose = 0 def do_verbose(self): # Set verbosity flag from top of stack self.verbose = self.do_pop() # The evaluation stack (internal only) stack = [] # Stack of pending operations def push(self, item): # Push an argument onto the stack self.stack = [item] + self.stack def top(self): # Return the top-of-stack element return self.stack[0] # Stack manipulation (callable) def do_clear(self): # Clear the stack self.stack = [] def do_pop(self): # Pop (and return) the top element on the stack top = self.stack[0] self.stack = self.stack[1:] return top def do_dup(self): # Duplicate the top-of-stack item if hasattr(self, 'format'): # If it's an image, do a real copy dup = self.stack[0].copy() else: dup = self.stack[0] self.stack = [dup] + self.stack def do_swap(self): # Swap the top-of-stack item with the next one down self.stack = [self.stack[1], self.stack[0]] + self.stack[2:] # Image module functions (callable) def do_new(self): # Create a greyscale image of given size and color, push on stack color = int(self.do_pop()) ysize = int(self.do_pop()) xsize = int(self.do_pop()) self.push(Image.new("L", (xsize, ysize), color)) def do_open(self): # Open the indicated image, read it, push it on the stack self.push(Image.open(self.do_pop())) def do_blend(self): # Replace two images and an alpha with the blended image alpha = float(self.do_pop()) image2 = self.do_pop() image1 = self.do_pop() self.push(Image.blend(image1, image2, alpha)) def do_composite(self): # Replace two images and a mask with their composite mask = self.do_pop() image2 = self.do_pop() image1 = self.do_pop() self.push(Image.composite(image1, image2, mask)) def do_merge(self): # Merge top-of stack images in a way described by the mode bandlist = [] mode = self.do_pop() for band in mode: bandlist.append(self.do_pop()) bandlist.reverse() self.push(Image.merge(mode, bandlist)) # Image class methods def do_convert(self): # Convert the top image to the given mode mode = self.do_pop() image = self.do_pop() self.push(image.convert(mode)) def do_copy(self): # Make a true copy of the top image, push it on the stack self.dup() def do_crop(self): # Crop a rectangular region from the current image lower = int(self.do_pop()) right = int(self.do_pop()) upper = int(self.do_pop()) left = int(self.do_pop()) image = self.do_pop() self.push(image.crop((left, upper, right, lower))) def do_draft(self): # Configure the loader for a given mode ysize = int(self.do_pop()) xsize = int(self.do_pop()) mode = self.do_pop() self.push(self.draft(mode, (xsize, ysize))) def do_filter(self): # Filter the top image with the given filter import ImageFilter filter = eval("ImageFilter." + string.upper(self.do_pop())) image = self.do_pop() self.push(image.filter(filter)) def do_offset(): # Offset the top image yoff = int(self.do_pop()) xoff = int(self.do_pop()) image = self.do_pop() self.push(image.offset(xoff, yoff)) def do_paste(self): # Interpret top image as figure, next down as ground yoff = int(self.do_pop()) xoff = int(self.do_pop()) figure = self.do_pop() ground = self.do_pop() if figure.mode == "RGBA": self.push(ground.paste(figure, (xoff, yoff), figure)) else: self.push(ground.paste(figure, (xoff, yoff))) def do_resize(self): # Resize the top image ysize = int(self.do_pop()) xsize = int(self.do_pop()) image = self.do_pop() self.push(image.resize(xsize, ysize)) def do_rotate(self): # Rotate image through a given angle angle = int(self.do_pop()) image = self.do_pop() self.push(image.rotate(angle)) def do_save(self): # Save image with default options filename = self.do_pop() image = self.do_pop() image.save(filename) def do_save2(self): # Save image with specified options filename = self.do_pop() options = self.do_pop() image = self.do_pop() image.save(filename, None, options) def do_show(self): # Display the top image in the stack self.pop().show() def do_thumbnail(self): # Modify the top image in the stack to contain a thumbnail of itself ysize = int(self.do_pop()) xsize = int(self.do_pop()) self.top().thumbnail((xsize, ysize)) def do_transpose(self): # Transpose the top image transpose = string.upper(self.do_pop()) image = self.do_pop() self.push(image.transpose(transpose)) # Image attributes def do_format(self): # Push the format of the top image onto the stack self.push(self.pop().format) def do_mode(self): # Push the mode of the top image onto the stack self.push(self.pop().mode) def do_size(self): # Push the image size on the stack as (y, x) size = self.pop().size self.push(size[0]) self.push(size[1]) # ImageChops operations def do_invert(self): # Invert the top image import ImageChops self.push(ImageChops.invert(self.do_pop())) def do_lighter(self): # Pop the two top images, push an image of the lighter pixels of both import ImageChops image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.lighter(image1, image2)) def do_darker(self): # Pop the two top images, push an image of the darker pixels of both import ImageChops image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.darker(image1, image2)) def do_difference(self): # Pop the two top images, push the difference image import ImageChops image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.difference(image1, image2)) def do_multiply(self): # Pop the two top images, push the multiplication image import ImageChops image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.multiply(image1, image2)) def do_screen(self): # Pop the two top images, superimpose their inverted versions import ImageChops image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.screen(image1, image2)) def do_add(self): # Pop the two top images, produce the scaled sum with offset import ImageChops offset = float(self.do_pop()) scale = float(self.do_pop()) image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.add(image1, image2, scale, offset)) def do_subtract(self): # Pop the two top images, produce the scaled difference with offset import ImageChops offset = float(self.do_pop()) scale = float(self.do_pop()) image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.subtract(image1, image2, scale, offset)) # ImageEnhance classes def do_color(self): # Enhance color in the top image import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) def do_contrast(self): # Enhance contrast in the top image import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) def do_brightness(self): # Enhance brightness in the top image import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) def do_sharpness(self): # Enhance sharpness in the top image import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) # The interpreter loop def execute(self, list): # Interpret a list of PILDriver commands list.reverse() while len(list) > 0: self.push(list[0]) list = list[1:] if self.verbose: print "Stack: " + `self.stack` top = self.top() if type(top) != type(""): continue; funcname = "do_" + top if not hasattr(self, funcname): continue else: self.do_pop() func = getattr(self, funcname) func() if __name__ == '__main__': import readline, sys # If we see command-line arguments, interpret them as a stack state # and execute. Otherwise go interactive. driver = PILDriver() if len(sys.argv[1:]) > 0: driver.execute(sys.argv[1:]) else: print "PILDriver says hello" while 1: try: line = raw_input('pildriver> '); except EOFError: print "\nPILDriver says goodbye" break driver.execute(string.split(line)) print driver.stack # The following sets edit modes for GNU EMACS # Local Variables: # mode:python # End: --ibTvN161/egqYuK8-- From esr@snark.thyrsus.com Sun Jul 19 04:52:27 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Sat, 18 Jul 1998 23:52:27 -0400 Subject: [Image-SIG] pildriver, take 2 Message-ID: <19980718235227.A27526@snark.thyrsus.com> --zhXaljGHf11kAtnf Content-Type: text/plain; charset=us-ascii Here's a better, more fully documented version incorporating Mike McLay's suggestions. As before, feedback and comments welcome. With a few simple tweaks I could take this from interpreting Polish or prefix-operator notation to reverse-Polish or suffix-operator notation. Is there any general feeling as to which would be more intuitive? -- Eric S. Raymond "Are we to understand," asked the judge, "that you hold your own interests above the interests of the public?" "I hold that such a question can never arise except in a society of cannibals." -- Ayn Rand --zhXaljGHf11kAtnf Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=pildriver #!/usr/bin/env python """PILDdriver, an image-processing calculator using PIL. An instance of class PILDriver is essentially a software stack machine (Polish-notation interpreter) for sequencing PIL image transformations. The state of the instance is the interpreter stack. The only method one will normally invoke after initialization is the `execute' method. This takes an argument list of tokens, pushes them onto the instance's stack, and then tries to clear the stack by successive evaluation of PILdriver operators. Any part of the stack not cleaned off persists and is part of the evaluation context for the next call of the execute method. PILDriver doesn't catch any exceptions, on the theory that these are actually diagnostic information that should be interpreted by the calling code. When called as a script, the command-line arguments are passed to a PILDriver instance. If there are no command-line arguments, the module runs an interactive interpreter, each line of which is split into space-separated tokens and passed to the execute method. In the method descriptions below, a first line beginning with the string `usage:' means this method can be invokeed with the token that follows it. Following <>-enclosed arguments describe how the method interprets the entries on the stack. Each argument specification begins with a type specification: either `int', `float', `string', or `image'. All operations consume their arguments off the stack (use `dup' to keep copies around). Use `verbose 1' to see the stack state displayed before each operation. Usage examples: `show crop 0 0 200 300 open test.png' loads test.png, crops out a portion of its upper-left-hand corner and displays the cropped portion. `save rotated.png rotate 30 open test.tiff' loads test.tiff, rotates it 30 degrees, and saves the result as rotated.png (in PNG format). """ # by Eric S. Raymond # $Id: pildriver,v 1.6 1998/07/19 03:45:44 esr Exp $ # TO DO: # 1. Add PILFont capabilities, once that's documented. # 2. Add PILDraw operations. # 3. Add support for composing and decomposing multiple-image files. # import Image, string class PILDriver: verbose = 0 def do_verbose(self): """usage: verbose Set verbosity flag from top of stack. """ self.verbose = self.do_pop() # The evaluation stack (internal only) stack = [] # Stack of pending operations def push(self, item): "Push an argument onto the evaluation stack." self.stack = [item] + self.stack def top(self): "Return the top-of-stack element." return self.stack[0] # Stack manipulation (callable) def do_clear(self): """usage: clear Clear the stack. """ self.stack = [] def do_pop(self): """usage: pop Discard the top element on the stack. """ top = self.stack[0] self.stack = self.stack[1:] return top def do_dup(self): """usage: dup Duplicate the top-of-stack item. """ if hasattr(self, 'format'): # If it's an image, do a real copy dup = self.stack[0].copy() else: dup = self.stack[0] self.stack = [dup] + self.stack def do_swap(self): """usage: swap Swap the top-of-stack item with the next one down. """ self.stack = [self.stack[1], self.stack[0]] + self.stack[2:] # Image module functions (callable) def do_new(self): """usage: new : Create and push a greyscale image of given size and color. """ xsize = int(self.do_pop()) ysize = int(self.do_pop()) color = int(self.do_pop()) self.push(Image.new("L", (xsize, ysize), color)) def do_open(self): """usage: open Open the indicated image, read it, push the image on the stack. """ self.push(Image.open(self.do_pop())) def do_blend(self): """usage: blend Replace two images and an alpha with the blended image. """ image1 = self.do_pop() image2 = self.do_pop() alpha = float(self.do_pop()) self.push(Image.blend(image1, image2, alpha)) def do_composite(self): """usage: composite Replace two images and a mask with their composite. """ image1 = self.do_pop() image2 = self.do_pop() mask = self.do_pop() self.push(Image.composite(image1, image2, mask)) def do_merge(self): """usage: merge [ [ []]] Merge top-of stack images in a way described by the mode. """ mode = self.do_pop() bandlist = [] for band in mode: bandlist.append(self.do_pop()) self.push(Image.merge(mode, bandlist)) # Image class methods def do_convert(self): """usage: convert Convert the top image to the given mode. """ mode = self.do_pop() image = self.do_pop() self.push(image.convert(mode)) def do_copy(self): """usage: copy Make and push a true copy of the top image. """ self.dup() def do_crop(self): """usage: crop Crop and push a rectangular region from the current image. """ left = int(self.do_pop()) upper = int(self.do_pop()) right = int(self.do_pop()) lower = int(self.do_pop()) image = self.do_pop() self.push(image.crop((left, upper, right, lower))) def do_draft(self): """usage: draft Configure the loader for a given mode and size. """ mode = self.do_pop() xsize = int(self.do_pop()) ysize = int(self.do_pop()) self.push(self.draft(mode, (xsize, ysize))) def do_filter(self): """usage: filter Process the top image with the given filter. """ import ImageFilter filter = eval("ImageFilter." + string.upper(self.do_pop())) image = self.do_pop() self.push(image.filter(filter)) def do_offset(self): """usage: offset Offset the pixels in the top image. """ xoff = int(self.do_pop()) yoff = int(self.do_pop()) image = self.do_pop() self.push(image.offset(xoff, yoff)) def do_paste(self): """usage: paste Paste figure image into ground with upper left at given offsets. """ figure = self.do_pop() xoff = int(self.do_pop()) yoff = int(self.do_pop()) ground = self.do_pop() if figure.mode == "RGBA": self.push(ground.paste(figure, (xoff, yoff), figure)) else: self.push(ground.paste(figure, (xoff, yoff))) def do_resize(self): """usage: resize Resize the top image. """ ysize = int(self.do_pop()) xsize = int(self.do_pop()) image = self.do_pop() self.push(image.resize(xsize, ysize)) def do_rotate(self): """usage: rotate Rotate image through a given angle """ angle = int(self.do_pop()) image = self.do_pop() self.push(image.rotate(angle)) def do_save(self): """usage: save Save image with default options. """ filename = self.do_pop() image = self.do_pop() image.save(filename) def do_save2(self): """usage: save2 Save image with specified options. """ filename = self.do_pop() options = self.do_pop() image = self.do_pop() image.save(filename, None, options) def do_show(self): """usage: show Display and pop the top image. """ self.do_pop().show() def do_thumbnail(self): """usage: thumbnail Modify the top image in the stack to contain a thumbnail of itself. """ ysize = int(self.do_pop()) xsize = int(self.do_pop()) self.top().thumbnail((xsize, ysize)) def do_transpose(self): """usage: transpose Transpose the top image. """ transpose = string.upper(self.do_pop()) image = self.do_pop() self.push(image.transpose(transpose)) # Image attributes def do_format(self): """usage: format Push the format of the top image onto the stack. """ self.push(self.pop().format) def do_mode(self): """usage: mode Push the mode of the top image onto the stack. """ self.push(self.pop().mode) def do_size(self): """usage: size Push the image size on the stack as (y, x). """ size = self.pop().size self.push(size[0]) self.push(size[1]) # ImageChops operations def do_invert(self): """usage: invert Invert the top image. """ import ImageChops self.push(ImageChops.invert(self.do_pop())) def do_lighter(self): """usage: lighter Pop the two top images, push an image of the lighter pixels of both. """ import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.lighter(image1, image2)) def do_darker(self): """usage: darker Pop the two top images, push an image of the darker pixels of both. """ import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.darker(image1, image2)) def do_difference(self): """usage: difference Pop the two top images, push the difference image """ import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.difference(image1, image2)) def do_multiply(self): """usage: multiply Pop the two top images, push the multiplication image. """ import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.multiply(image1, image2)) def do_screen(self): """usage: screen Pop the two top images, superimpose their inverted versions. """ import ImageChops image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.screen(image1, image2)) def do_add(self): """usage: add Pop the two top images, produce the scaled sum with offset. """ import ImageChops image1 = self.do_pop() image2 = self.do_pop() scale = float(self.do_pop()) offset = int(self.do_pop()) self.push(ImageChops.add(image1, image2, scale, offset)) def do_subtract(self): """usage: subtract Pop the two top images, produce the scaled difference with offset. """ import ImageChops image1 = self.do_pop() image2 = self.do_pop() scale = float(self.do_pop()) offset = int(self.do_pop()) self.push(ImageChops.subtract(image1, image2, scale, offset)) # ImageEnhance classes def do_color(self): """usage: color Enhance color in the top image. """ import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) def do_contrast(self): """usage: contrast Enhance contrast in the top image. """ import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) def do_brightness(self): """usage: brightness Enhance brightness in the top image. """ import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) def do_sharpness(self): """usage: sharpness Enhance sharpness in the top image. """ import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) self.push(enhancer.enhance(factor)) # The interpreter loop def execute(self, list): "Interpret a list of PILDriver commands." list.reverse() while len(list) > 0: self.push(list[0]) list = list[1:] if self.verbose: print "Stack: " + `self.stack` top = self.top() if type(top) != type(""): continue; funcname = "do_" + top if not hasattr(self, funcname): continue else: self.do_pop() func = getattr(self, funcname) func() if __name__ == '__main__': import readline, sys # If we see command-line arguments, interpret them as a stack state # and execute. Otherwise go interactive. driver = PILDriver() if len(sys.argv[1:]) > 0: driver.execute(sys.argv[1:]) else: print "PILDriver says hello." while 1: try: line = raw_input('pildriver> '); except EOFError: print "\nPILDriver says goodbye." break driver.execute(string.split(line)) print driver.stack # The following sets edit modes for GNU EMACS # Local Variables: # mode:python # End: --zhXaljGHf11kAtnf-- From fredrik@pythonware.com Sun Jul 19 16:57:40 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Sun, 19 Jul 1998 16:57:40 +0100 Subject: [Image-SIG] PIL 0.3b2 is now available Message-ID: <013b01bdb32d$fcec3280$f29b12c2@pythonware.com> The Python Imaging Library (PIL) adds image processing capabilities to your Python interpreter. This library provides extensive file format support, an efficient internal representation, and fairly powerful image processing capabilities. This is the second (and hopefully the last) beta release for PIL 0.3. The source kit has been built and tested on Windows 95 and NT, as well as on Linux (Redhat 4.2) and Digital Unix. It should build out of the box on most mainstream Unix versions. Get your copy of the sources from: http://www.pythonware.com Here are the changes from the previous beta: + An Image "getbands" method has been added. It returns a tuple containing the individual band names for this image. To figure out how many bands an image has, use "len(im.getbands())". + An Image "putpixel" method has been added. + The Image "point" method can now be used to convert "L" images to any other format, via a lookup table. That table should contain 256 values for each band in the output image. + Some file drivers (including FLI/FLC, GIF, and IM) accidently overwrote the offset method with an internal attribute. All drivers have been updated to use private attributes where possible. + The Image "histogram" method now works for "I" and "F" images. For these modes, PIL divides the range between the min and max values used in the image into 256 bins. You can also pass in your own min and max values via the "extrema" option: h = im.histogram(extrema=(0, 255)) + An Image "getextrema" method has been added. It returns the min and max values used in the image. In this release, this works for single band images only. + Changed the PNG driver to load and save mode "I" images as 16-bit images. When saving, values outside the range 0..65535 are clipped. + Fixed ImageFont.py to work with the new "pilfont" compiler. + Added JPEG "save" and "draft" support for mode "YCbCr" images. Note that if you save an "YCbCr" image as a JPEG file and read it back, it is read as an RGB file. To get around this, you can use the "draft" method: im = Image.open("color.jpg") im.draft("YCbCr", im.size) + Read "RGBA" TGA images. Also fixed the orientation bug; all images should now come out the right way. + Changed mode name (and internal representation) from "YCrCb" to "YCbCr" (!) --the PIL team From esr@snark.thyrsus.com Sun Jul 19 17:41:32 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Sun, 19 Jul 1998 12:41:32 -0400 Subject: [Image-SIG] New PIL release Message-ID: <19980719124132.A28724@snark.thyrsus.com> A few documentation bugs... You changed the name of the shared library, but the change is not reflected in the installation instructions. Still no pilfont docs; the link on the handbook page is broken. Why isn't the documentation included with the source distribution? -- Eric S. Raymond The danger (where there is any) from armed citizens, is only to the *government*, not to *society*; and as long as they have nothing to revenge in the government (which they cannot have while it is in their own hands) there are many advantages in their being accustomed to the use of arms, and no possible disadvantage. -- Joel Barlow, "Advice to the Privileged Orders", 1792-93 From fredrik@pythonware.com Sun Jul 19 19:03:47 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Sun, 19 Jul 1998 19:03:47 +0100 Subject: [Image-SIG] New PIL release Message-ID: <003a01bdb342$27d07bf0$f29b12c2@pythonware.com> >A few documentation bugs... > >You changed the name of the shared library, but the change is not >reflected in the installation instructions. Did you grab the 0.3b2 release just after you received the announcement? If you were unlucky, you might have gotten a file called "Imaging-0.3b1.tgz"... If you try again, you should get the right file... >Still no pilfont docs; the link on the handbook page is broken. > >Why isn't the documentation included with the source distribution? It will be in the final release, once it's up to date. Cheers /F From esr@snark.thyrsus.com Sun Jul 19 18:53:40 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Sun, 19 Jul 1998 13:53:40 -0400 Subject: [Image-SIG] New PIL release In-Reply-To: <003a01bdb342$27d07bf0$f29b12c2@pythonware.com>; from Fredrik Lundh on Sun, Jul 19, 1998 at 07:03:47PM +0100 References: <003a01bdb342$27d07bf0$f29b12c2@pythonware.com> Message-ID: <19980719135340.A28847@snark.thyrsus.com> Fredrik Lundh : > Did you grab the 0.3b2 release just after you received the > announcement? If you were unlucky, you might have gotten > a file called "Imaging-0.3b1.tgz"... That's what happened. > If you try again, you should get the right file... Will do. > >Still no pilfont docs; the link on the handbook page is broken. > > > >Why isn't the documentation included with the source distribution? > > It will be in the final release, once it's up to date. Please push the docs higher on your priority list. The pildriver script is getting mature enough that the gaps in the documentation are now a serious impediment to its further development. Also, I have observed in the past that when the documentation is left until last, it tends to get left unfinished and be of poor quality. Even very good developers (which it's clear you are) often fall into this trap under pressure to dive into the next project. For this reason, I often document my projects *before* coding, as I design them in my head. (For an example, see the Trove design document at .) This is a practice I recommend to you. I think you would be doing everyone greater service by concentrating on documentation for a while rather than just coding. Please, at least catch the documentation up with the current state of things and include an editable master form in the distribution. That way the rest of us can help you improve the documentation as well as the code. -- Eric S. Raymond The people cannot delegate to government the power to do anything which would be unlawful for them to do themselves. -- John Locke, "A Treatise Concerning Civil Government" From esr@snark.thyrsus.com Sun Jul 19 20:46:32 1998 From: esr@snark.thyrsus.com (Eric S. Raymond) Date: Sun, 19 Jul 1998 15:46:32 -0400 Subject: [Image-SIG] Core dump in 0.3b2 offset method Message-ID: <19980719154632.A29670@snark.thyrsus.com> --ew6BAiZeqk4r7MaW Content-Type: text/plain; charset=us-ascii Enclosed: a simple Python script that core-dumps PIL, and the image to do it with. Seems to work with any image, I just picked a small one. -- Eric S. Raymond & what country can preserve its liberties, if its rulers are not warned from time to time that his people preserve the spirit of resistance? Let them take arms...The tree of liberty must be refreshed from time to time, with the blood of patriots and tyrants. -- Thomas Jefferson, letter to Col. William S. Smith, 1787 --ew6BAiZeqk4r7MaW Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="offsetcore.py" #!/usr/bin/python import Image image = Image.open("arrow.gif") image = image.offset(50, 60) --ew6BAiZeqk4r7MaW Content-Type: image/gif Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="arrow.gif" R0lGODdhEAAJAPAAAP///wAAACwAAAAAEAAJAAACFoSPEcitDc0Dqtq7sN58xeR9UihORgEA Ow== --ew6BAiZeqk4r7MaW-- From Fred L. Drake, Jr." --5ydeQFjoGe Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit I've attached a patch to PngImagePlugin.py which adds support for the zTXt, bKGD, and gIFg chunks. The tEXt and zTXt chunks contain text information; this is now stored in the "text" element of the info dictionary; the value is another dictionary of key/value pairs. Each value is a list of (string, ) pairs, where is a boolean indicating whether the string was compressed (a zTXt chunk) or uncompressed (a tEXt chunk). The bKGD chunk is used to add a "background" field to the info dictionary, and the gIFg chunk is used to determine a "duration" field. -Fred -- Fred L. Drake, Jr. Corporation for National Research Initiatives 1895 Preston White Dr. Reston, VA 20191 --5ydeQFjoGe Content-Type: text/plain Content-Description: improve PNG support for PIL Content-Disposition: inline; filename="PngImagePlugin.patch" Content-Transfer-Encoding: 7bit *** PIL/PngImagePlugin.py Sun Jul 19 10:25:14 1998 --- /home/fdrake/lib/python/PngImagePlugin.py Mon Jul 20 10:40:38 1998 *************** *** 88,93 **** --- 88,96 ---- def close(self): del self.queue self.fp = None + if self.__dict__.has_key("crc"): + # using crc_skip(); remove circular reference + del self.__dict__["crc"] def push(self, cid, pos, len): *************** *** 167,172 **** --- 170,178 ---- # image data self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)] self.im_idat = len + + # This error is to separate the "header" chunks from the remaining + # chunks; see PngImageFile.__init__(). raise EOFError def chunk_IEND(self, pos, len): *************** *** 193,202 **** # text s = self.fp.read(len) ! [k, v] = string.split(s, "\0") ! self.im_info[k] = v return s # -------------------------------------------------------------------- # PNG reader --- 199,266 ---- # text s = self.fp.read(len) ! [k, v] = string.split(s, "\0", 1) ! self.add_text(k, v) return s + def chunk_zTXt(self, pos, len): + s = self.fp.read(len) + [k, d] = string.split(s, "\0", 1) + if not d: + raise SyntaxError, "zTXt chunk missing compression method" + m, z = d[0], d[1:] + v = _inflate(z) + self.add_text(k, v, 1) + return s + + def add_text(self, keyword, value, compressed=0): + # If there is already a chunk with this keyword, the value of + # info[k] will become a list containing all values, otherwise (if + # there is no value already), only a single string will be used. + value = (value, compressed) + try: + text = self.im_info["text"] + except KeyError: + text = self.im_info["text"] = {} + if text.has_key(keyword): + text[keyword].append(value) + else: + text[keyword] = [value] + + def chunk_bKGD(self, pos, len): + s = self.fp.read(len) + if len == 1: + self.im_info["background"] = ord(s) + elif len == 2: + self.im_info["background"] = ord(s[0]) << 8 | ord(s[1]) + elif len == 6: + # tuple (R, G, B) + self.im_info["background"] = ((ord(s[0]) << 8 | ord(s[1])), + (ord(s[2]) << 8 | ord(s[3])), + (ord(s[4]) << 8 | ord(s[5]))) + elif Image.DEBUG: + print "unsupported background chunk in mode", self.im_mode + return s + + def chunk_gIFg(self, pos, len): + s = self.fp.read(len) + if len != 4: + raise SyntaxError, "invalid gIFg chunk length" + import struct + disp, input, delay = struct.unpack(">BBH", s) + self.im_info["duration"] = int(delay) + return s + + + def _deflate(data): + import zlib + co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, + -zlib.MAX_WBITS) + return co.compress(data) + co.flush() + + def _inflate(data): + import zlib + return zlib.decompress(data, -zlib.MAX_WBITS) # -------------------------------------------------------------------- # PNG reader *************** *** 232,237 **** --- 296,302 ---- break except AttributeError: + # presumably an unknown chunk type if Image.DEBUG: print cid, pos, len, "(unknown)" s = self.fp.read(len) *************** *** 311,317 **** "L;2": ("L;2", chr(2)+chr(0)), "L;4": ("L;4", chr(4)+chr(0)), "L": ("L", chr(8)+chr(0)), - "I": ("I;16B", chr(16)+chr(0)), "P;1": ("P;1", chr(1)+chr(3)), "P;2": ("P;2", chr(2)+chr(3)), "P;4": ("P;4", chr(4)+chr(3)), --- 376,381 ---- *************** *** 386,392 **** raise IOError, "cannot write mode %s as PNG" % mode if check: ! return check # # write minimal PNG file --- 450,456 ---- raise IOError, "cannot write mode %s as PNG" % mode if check: ! return check # # write minimal PNG file *************** *** 400,413 **** chr(0), # 11: filter category chr(0)) # 12: interlace flag if im.mode == "P": ! chunk(fp, "PLTE", im.im.getpalette("RGB")) if 0: # FIXME: to be supported some day chunk(fp, "gAMA", o32(int(gamma * 100000.0))) ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) chunk(fp, "IEND", "") --- 464,512 ---- chr(0), # 11: filter category chr(0)) # 12: interlace flag + colortype = ord(mode[-1]) # "color type" from the spec. + info = im.info.copy() if im.mode == "P": ! pal = im.im.getpalette("RGB") ! chunk(fp, "PLTE", pal) ! if im.info.has_key("transparency"): ! tRNS_data = None ! if colortype == 3: ! v = [0] * (len(pal) / 3) ! v[im.info["transparency"]] = 1 ! tRNS_data = string.join(map(chr, v), '') ! elif colortype in (4, 6): ! # prohibited ! pass ! elif colortype in (0, 2): ! # not yet implemented ! pass ! else: ! raise RuntimeError, "bad colortype value -- internal error" ! if tRNS_data: ! chunk(fp, "tRNS", tRNS_data) ! if im.info.has_key("background") and colortype == 3: ! bKGD_data = chr(im.info["background"]) ! chunk(fp, "bKGD", bKGD_data) ! if im.info.has_key("duration"): ! import struct ! disp, input, delay = 0, 0, im.info["duration"] ! gIFg_data = struct.pack(">BBH", disp, input, delay) ! chunk(fp, "gIFg", gIFg_data) if 0: # FIXME: to be supported some day chunk(fp, "gAMA", o32(int(gamma * 100000.0))) ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) + + if im.info.has_key("text"): + for k, v in info["text"].items(): + if type(v) is type(()) and len(v) == 2: + v, c = v + if c: + v = _deflate(v) + chunk(fp, c and "zTXt" or "tEXt", v) chunk(fp, "IEND", "") --5ydeQFjoGe-- From edcjones@erols.com Fri Jul 24 06:40:41 1998 From: edcjones@erols.com (Edward C. Jones) Date: Fri, 24 Jul 1998 01:40:41 -0400 Subject: [Image-SIG] Newbie bug Message-ID: <35B81E59.7A3824AA@erols.com> I am new at PIL. The following program gave an error. What to do? -------- Traceback (innermost last): File "./thresh.py", line 17, in ? outim = im.point(lut, im.mode) File "/usr/lib/python1.5/site-packages/PIL/Image.py", line 525, in point im = self.im.point(lut, mode) ValueError: Images do not match -------- #! /usr/bin/python import Image, sys, string if len(sys.argv) != 4: raise 'must have exactly three parameters' t = string.atoi(sys.argv[1]) im = Image.open(sys.argv[2]) lut = (3*256) * [0] for i in range(t,256): lut[i] = 1 lut[i+256] = 1 lut[i+512] = 1 outim = im.point(lut, im.mode) outim.save(sys.argv[3]) From fredrik@pythonware.com Fri Jul 24 15:23:10 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Fri, 24 Jul 1998 15:23:10 +0100 Subject: [Image-SIG] Newbie bug Message-ID: <005901bdb70e$9a4b6760$f29b12c2@pythonware.com> >I am new at PIL. The following program gave an error. What to do? What are you trying to do? What data did you use as input? What's the value of "im.mode"? Does it work as expected if you just drop the second argument to "im.point"? Cheers /F fredrik@pythonware.com http://www.pythonware.com From fredrik@pythonware.com Fri Jul 24 15:29:34 1998 From: fredrik@pythonware.com (Fredrik Lundh) Date: Fri, 24 Jul 1998 15:29:34 +0100 Subject: [Image-SIG] creating palettes on the fly Message-ID: <009501bdb70f$7da1a560$f29b12c2@pythonware.com> This is a multi-part message in MIME format. ------=_NextPart_000_0090_01BDB717.DC3DCB50 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit I've attached a small class that makes it much easier to generate palette images via the ImageDraw module. See the test code at the end for details. Something similar to this will be shipped with the next PIL release. Cheers /F fredrik@pythonware.com http://www.pythonware.com ------=_NextPart_000_0090_01BDB717.DC3DCB50 Content-Type: application/octet-stream; name="palette.py" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="palette.py" # # a simple palette wrapper class # # fredrik lundh, july 1998 # import string _inks = { # HTML 3.2 colour names "aqua": (0x00,0xFF,0xFF), "black": (0x00,0x00,0x00), "blue": (0x00,0x00,0xFF), "fuchsia": (0xFF,0x00,0xFF), "gray": (0x80,0x80,0x80), "green": (0x00,0x80,0x00), "lime": (0x00,0xFF,0x00), "maroon": (0x80,0x00,0x00), "navy": (0x00,0x00,0x80), "olive": (0x80,0x80,0x00), "purple": (0x80,0x00,0x80), "red": (0xFF,0x00,0x00), "silver": (0xC0,0xC0,0xC0), "teal": (0x00,0x80,0x80), "white": (0xFF,0xFF,0xFF), "yellow": (0xFF,0xFF,0x00), } class Palette: def __init__(self): self.palette = {} def __getitem__(self, rgb): # lookup colour in current palette try: # tuple r, g, b = rgb except ValueError: try: # named colour r, g, b = _inks[string.lower(rgb)] except KeyError: # hex string if rgb[0] == "#" and len(rgb) == 7: rgb = string.atoi(rgb[1:], 16) r = rgb >> 16 g = rgb >> 8 & 255 b = rgb & 255 rgb = chr(r) + chr(g) + chr(b) try: return self.palette[rgb] except KeyError: # allocate new entry ink = len(self.palette) if ink == 256: raise RuntimeError, "palette can hold only 256 entries" self.palette[rgb] = ink return ink def attach(self, image): # attach palette to an image memory palette = ["\0\0\0"] * 256 for rgb, i in self.palette.items(): if i < 256: palette[i] = rgb image.putpalette(string.join(palette, ""), "RGB") if __name__ == "__main__": import Image, ImageDraw ink = Palette() im = Image.new("P", (400, 400), ink["#99ccff"]) d = ImageDraw.ImageDraw(im) d.setink(ink["white"]) for i in range(0, 400, 10): d.line((i,0,200,200)) d.line((0,i,200,200)) d.setink(ink[(0,0,128)]) d.setfill(1) d.rectangle((150,150,250,250)) ink.attach(im) im.save("out.png") ------=_NextPart_000_0090_01BDB717.DC3DCB50--