From: Filipe P. A. F. <oc...@gm...> - 2010-03-28 03:55:00
|
Hello list I've trying for a while a "python only" solution to remove white spaces that Basemap generate to keep the aspect ratio. I found these two threads that explain the issue better: http://www.mail-archive.com/mat...@li.../msg14430.html http://www.mail-archive.com/mat...@li.../msg14262.html In the past I relied on ImageMagick's "convert" command with the "-trim white" option for the job. However, just recently I came across with this other list and a PIL solution: http://mail.python.org/pipermail/image-sig/2008-July/005092.html Here is how I'm doing: savefig('mapa.png', dpi=300) from PIL import Image im = Image.open("mapa.png") def trim(im, border): from PIL import ImageChops bg = Image.new(im.mode, im.size, border) diff = ImageChops.difference(im, bg) bbox = diff.getbbox() if bbox: return im.crop(bbox) else: # found no content raise ValueError("cannot trim; image was empty") im2=trim(im,'white') im2.show() This works and the aspect ratio is preserved, but as you can see, it is not a very smart implementation. I save and then reload it again... any suggestions to improve this are welcome. Also, I have not tried this on figures with labels and annotations. Thanks for any input. ***************************************************** Filipe Pires Alvarenga Fernandes University of Massachusetts Dartmouth 200 Mill Road - Fairhaven, MA Tel: (508) 910-6381 Email: fal...@um... oc...@ya... oc...@gm... http://ocefpaf.tiddlyspot.com/ ***************************************************** |
From: Friedrich R. <fri...@gm...> - 2010-03-28 19:35:33
|
2010/3/28 Filipe Pires Alvarenga Fernandes <oc...@gm...>: > Hello list > I've trying for a while a "python only" solution to remove white spaces that > Basemap generate to keep the aspect ratio. I found these two threads that > explain the issue better: I think maybe you can make use of the Agg Backend to achieve a Python-only solution to obtain a PIL Image from a figure without bypassing over the HDD: Furthemore, if this doesn't work, maybe you can use a StringIO as "file", then seek() the StringIO and reread with PIL from the "file-like" StringIO, or something like that? # # Render the Figure to a PIL Image ... # # Prepare figure to be of pixel extent *shape* ... dpi = figure.dpi figure.set_size_inches(float(shape[0]) / dpi, float(shape[1]) / dpi) # Create the rendering Canvas ... # # We also convert the picture to an RGB string. canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(figure) canvas.draw() image_string = canvas.tostring_rgb() # Create a PIL Image from RGB string ... image = Image.fromstring("RGB", shape, image_string) # Now do whatever you want with the Image. Friedrich |
From: Filipe P. A. F. <oc...@gm...> - 2010-03-30 16:31:42
Attachments:
blueearth-map.py
|
Thanks Friedrich, However, my knowledge of python is very limited, even though I think I understood what you suggested I do not know how to get the shape (of the figure?) for this part: >>> fig.set_size_inches(float(shape[0]) / dpi, float(shape[1]) / dpi) error is: Traceback (most recent call last): File "blueearth-map.py", line 108, in <module> fig.set_size_inches(float(shape[0]) / dpi, float(shape[1]) / dpi) TypeError: 'function' object is unsubscriptable It seems that it ended up calling a function shape instead of a variable shape. I'm sending attached the script if you are interested in looking at it. Thanks again. Filipe. On Sun, Mar 28, 2010 at 3:35 PM, Friedrich Romstedt <fri...@gm...> wrote: > > 2010/3/28 Filipe Pires Alvarenga Fernandes <oc...@gm...>: > > Hello list > > I've trying for a while a "python only" solution to remove white spaces that > > Basemap generate to keep the aspect ratio. I found these two threads that > > explain the issue better: > > I think maybe you can make use of the Agg Backend to achieve a > Python-only solution to obtain a PIL Image from a figure without > bypassing over the HDD: > > Furthemore, if this doesn't work, maybe you can use a StringIO as > "file", then seek() the StringIO and reread with PIL from the > "file-like" StringIO, or something like that? > > # > # Render the Figure to a PIL Image ... > # > > # Prepare figure to be of pixel extent *shape* ... > > dpi = figure.dpi > figure.set_size_inches(float(shape[0]) / dpi, float(shape[1]) / dpi) > > # Create the rendering Canvas ... > # > # We also convert the picture to an RGB string. > > canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(figure) > canvas.draw() > image_string = canvas.tostring_rgb() > > # Create a PIL Image from RGB string ... > > image = Image.fromstring("RGB", shape, image_string) > > # Now do whatever you want with the Image. > > Friedrich |
From: Friedrich R. <fri...@gm...> - 2010-03-30 21:10:11
|
2010/3/30 Filipe Pires Alvarenga Fernandes <oc...@gm...>: > However, my knowledge of python is very limited, even though I think I > understood what you suggested I do not know how to get the shape (of > the figure?) for this part: > >>>> fig.set_size_inches(float(shape[0]) / dpi, float(shape[1]) / dpi) > > error is: > > Traceback (most recent call last): > File "blueearth-map.py", line 108, in <module> > fig.set_size_inches(float(shape[0]) / dpi, float(shape[1]) / dpi) > TypeError: 'function' object is unsubscriptable > > It seems that it ended up calling a function shape instead of a variable shape. You're completely right, I assumed that you fill in some 2-element vector in *shape*, it was intended as an /argument/. The function attemted to be indexed is imported from numpy by matplotlib.pyplot: There is a code line "from numpy import *". So, simply put some line somewhere like: shape = [500, 500] . You have to set or have to calculate the figure's shape in pixels somewhere, because you need it to convert the rgb string back into a PIL image. It's not that nice, I know, for your problem? Friedrich |
From: Filipe F. <oc...@gm...> - 2010-04-08 12:55:26
|
Sorry, my bad. I always hit reply without checking... it is not the first time did that, Here is the solution for the list sake: """ trim image """ import StringIO, Image imgdata = StringIO.StringIO() fig.savefig(imgdata, dpi=300, format='png') imgdata.seek(0) im = Image.open(imgdata) def trim(im, border): from PIL import ImageChops bg = Image.new(im.mode, im.size, border) diff = ImageChops.difference(im, bg) bbox = diff.getbbox() if bbox: return im.crop(bbox) else: # found no content raise ValueError("cannot trim; image was empty") im = trim(im,'white') im.show() The StringIO trick is a copy-and-paste from the matplotlib faq. And the trim function I got from here: http://mail.python.org/pipermail/image-sig/2008-July/005092.html Thanks for the discussion, I learned a lot abouth the Agg backend. BTW: What I meant by limitation is the fact that Agg has no GUI like the nice QT window I was using before. The users of this script have no experience with scripting languages and enjoyed choosing the format and filename using a GUI interface. In addition, PIL's show() use an external linux program "xv". In the end I wanted to eliminate one external linux program (Imagemagik convert) but ended up with another one... Best, Filipe On 04/06/2010 05:01 PM, Friedrich Romstedt wrote: > 2010/4/5 Filipe Fernandes <oc...@gm...>: >> Thanks a lot. In the end the "StringIO-solution" worked fine. The only >> limitation is that this works only for the Agg backend. >> [...] > > I'm happy to hear this. As far as I know, using the Agg backend is > not a limitation, because it provides fully anti-aliased output. > Besides being available on all platforms (?). > > Let me make a small hint: The matplotlib-users mailing list is > configured that by default replies go to the sender only, not to > matplotlib-users, so maybe you want to let the list know about your > solution? > > Anyway I'm happy for your Thanks, > so thanks too, > Friedrich |
From: Friedrich R. <fri...@gm...> - 2010-04-08 14:46:53
|
2010/4/8 Filipe Fernandes <oc...@gm...>: > BTW: What I meant by limitation is the fact that Agg has no GUI like the > nice QT window I was using before. The users of this script have no > experience with scripting languages and enjoyed choosing the format and > filename using a GUI interface. I'm quite convinced that you can have the figure in multiple Canvases at the same time. Or create the FigureCanvasAgg or the call to .savefig() only on save time. In every widget framework, it should be fairly easy to build a custom gui having a customised "Save ..." button, I guess? > In addition, PIL's show() use an external linux program "xv". In the end > I wanted to eliminate one external linux program (Imagemagik convert) > but ended up with another one... When using Tkinter, you can use ImageTk: im = {some PIL Image} # Some image to be shown canvas = {some Tkinter.Canvas} # For drawing graphics viewport = ImageTk.PhotoImage(im) canvas.create_image((0, 0), image = viewport, anchor = 'nw') # Put the image on the Tkinter.Canvas. Modify according your needs. # Now the PIL Image has been rendered on the canvas. # Maybe do canvas.update() or call the master's .update(), # or call some .mainloop() entry function. For instance, use the # Tkinter.Tk instance's method .mainloop(). When renewing the graphics, don't forget to canvas.delete({tag}) using the {tag} returned by canvas.create_image(), otherwise you will probably loose perfomance. There are examples on matplotlib.sourceforge.net how to use Tkinter with matplotlib. hth, Friedrich |
From: Filipe P. A. F. <oc...@gm...> - 2010-04-12 13:57:41
Attachments:
make-map.py
|
Thanks for point TKinter to me. However, I'm stuck again. I've tried two approaches, one is following what you suggested: """ Tkinter """ import Tkinter as tk root = tk.Tk() from PIL import Image, ImageTk image = ImageTk.PhotoImage(Image.open('map.png')) # load saved image #image = ImageTk.PhotoImage(im) # load image from StringIO tk.Label(root, image=image).pack() The other is to convert the PIL image to array and show with imshow() """ PIL to array """ from matplotlib.image import pil_to_array rgba = pil_to_array(Image.open('map.png')) # load saved image #rgba = pil_to_array(im) # load image from StringIO rgba = rgba.astype(np.float32)/255. imshow(rgba) Both work fine for a saved image. However, they return strange errors when I tried to apply them to the image from "StringIO", or even for a saved image in the same script used to generate them. I guess that something I import from matplotlib is causing a conflict here. Here are the error messages: """ Tkinter """ _tkinter.TclError: image "pyimage9" doesn't exist """ PIL to array """ terminate called after throwing an instance of 'char const*' I'm attaching the script. Aborted |
From: Friedrich R. <fri...@gm...> - 2010-04-13 15:57:42
|
2010/4/12 Filipe Pires Alvarenga Fernandes <oc...@gm...>: > Thanks for point TKinter to me. However, I'm stuck again. > > I've tried two approaches, one is following what you suggested: > > """ Tkinter """ > import Tkinter as tk > root = tk.Tk() > from PIL import Image, ImageTk > image = ImageTk.PhotoImage(Image.open('map.png')) # load saved image > #image = ImageTk.PhotoImage(im) # load image from StringIO > tk.Label(root, image=image).pack() > > The other is to convert the PIL image to array and show with imshow() > > """ PIL to array """ > from matplotlib.image import pil_to_array > rgba = pil_to_array(Image.open('map.png')) # load saved image > #rgba = pil_to_array(im) # load image from StringIO > rgba = rgba.astype(np.float32)/255. > imshow(rgba) > > Both work fine for a saved image. However, they return strange errors > when I tried to apply them to the image from "StringIO", or even for a > saved image in the same script used to generate them. I guess that > something I import from matplotlib is causing a conflict here. > > Here are the error messages: > > """ Tkinter """ > _tkinter.TclError: image "pyimage9" doesn't exist > > > """ PIL to array """ > terminate called after throwing an instance of 'char const*' Hi, with the Tkinter PhotoImage, I think it's intended for use with Tkinter.Canvas, and not compatible with Label. For what I know, it works fine for me with Canvas (Windows). I guess what you end up with is not a real PhotoImage but something faky. Now, I cannot execute your script and dive in myself, because I switched to another laptop, where I have to compile everything ... So in some days, I will know more. With the pil_to_image issue, I really have no idea. It's really quite strange, isn't it? I mean, it should return a newly created numpy.ndarray, and shouldn't borrow any memory with the PIL Image? I really don't know at the moment, I'm sorry. For the Tkinter issue, from your script it seems that you simply want to display it, so give the Canvas a try. I used it in http://github.com/friedrichromstedt/diagram_cl/blob/master/panels/tk/diagram.py , start with update() and __init__(). hth for now, Friedrich |