From: Benjamin R. <ben...@ou...> - 2013-04-08 18:38:46
|
On Sun, Apr 7, 2013 at 4:39 AM, gary ruben <gar...@gm...> wrote: > Hi, I haven't looked at this list for a long time, so hi to all the active > devs. > I just came across a problem in the image.py imsave() function. > > Problem: > At an ipython --pylab prompt, typing > > a = rand(29,29) > imsave('test.png', a) > > incorrectly generates a 28x28 pixel image. > This happens for several array sizes (I initially saw it for 226x226). > > Line 1272 of image.py is > figsize = [x / float(dpi) for x in (arr.shape[1], arr.shape[0])] > > figsize interacts with the bounding box code somehow to create off-by-one > results for certain values of array shape. > > > Possible solution: > To fix this properly, I think the float cast should be removed and > integer-based math should be used, but I thought that since it was rounding > down in the cases I saw, a change to the following should fix this: > > figsize = [(x + 0.5) / float(dpi) for x in (arr.shape[1], arr.shape[0])] > > and indeed this seems to work. > To verify this, I ran the following brute-force test of all image sizes up > to 1024x1024 at some common dpi values: > > import matplotlib.pyplot as plt > import numpy as np > import os > > for dpi in [75, 100, 150, 300, 600, 1200]: > for i in range(1,1025): > print dpi, i, > im = np.random.random((i,i)) > fname = 'test{:03d}.png'.format(i) > plt.imsave(fname, im, dpi=dpi) > im = plt.imread(fname)[:,:,0] > print im.shape > assert im.shape == (i, i) > os.remove(fname) > > and everything passed. > > For completeness I also explored the dpi-space with these loop ranges and > the above loop body: > > for dpi in range(1, 101): > for i in range(25, 36): > ... > > and all was still well. > > > Workaround: > For the moment I've set dpi=1 in my call to imsave which effectively > reverts its behaviour to the original imsave code prior to the introduction > of the dpi parameter. > > I think this testing is rigorous enough to make this change. > If you agree, has anyone got time to change this, or shall I do a pull > request when I have time? > > thanks, > Gary > > Good catch on this. So you are saying that this bug was introduced relatively recently with the dpi kwarg? I would suggest you make a PR so that this doesn't get lost (or at the very least file a bug report). As for the fix itself, off the top of my head, wouldn't math.ceil() do what we want? I prefer to be explicit in any intents rather than just simply (x + 0.5) / float(dpi). As for the test, I would wonder if some sort of restricted version of that test might not be good to do? I am thinking about 10 different sized plots and we wouldn't even need to have a the assert check or file removal test, as the image comparison framework would throw an exception when attempting to compare two images of different sizes (I think...). Cheers! Ben Root |