I'm starting to get a better sense of the code now.  One of the features 
of the current implementation is that images are resampled before going 
into the output of the vector backends, so that we can a) control file 
size and b) control the interpolation algorithm used.  It looks like 
that separation is maintained with this approach, only difference is 
that images can be both prerotated as well as prescaled now.  Is that 
correct?  (I must admit, I've work on bits and pieces of the image code 
at the edges, but I don't understand it in great detail).

It looks like overriding "draw_unsampled_image" is the wrong thing to do 
here, though.  The purpose of that function is to draw an image in a 
vector backend without any resampling at all -- and here you seem to be 
adding that.  It also only gets called when interpolation is "none" or 
"nearest", so your helpful changes currently don't work with other forms 
of interpolation.  I think we need to either add a function 
"_draw_sampled_image", or modify make_image so it returns an offset with 
the new location to draw the image (because rotation may cause the 
bounds of the image to be shifted).

This is great work, and is looking to be fairly significant.  Would you 
be able to set up a personal git fork and branch for you work?  It would 
make it easier for the rest of us to evaluate what you have done and 
kick the tires a bit.  The outline of how to do this is here:

http://matplotlib.sourceforge.net/devel/gitwash/index.html

Cheers,
Mike

On 07/12/2011 06:01 AM, Martin Teichmann wrote:
> Dear List,
> dear Michael,
>
>> Looks good.  Does matplotlib still pass all regression tests with this
>> change?
> It does pass all regression tests that were passed with the git version
> I started with. (There were 10 failures which are still there).
>
> In the meantime, I also wrote a class that already uses my extension.
> With it, you can plot rotated or sheared images with all backends.
> (There were some quirks to get sheard images on some backends,
> see examples/api/demo_affine_image.py, but it worked on some backends
> only).
>
> While writing it, I found some inconsistencies in matplotlib:
>
> - bounding boxes are not correctly transformed. BboxBase.tranformed only
> transformes the outer points of a bounding box, if you rotate it, this will 
> give
> wrong results for several angles. I wrote a function that should be correct 
> for
> all affine transformations:
>
> def transform_bbox(bbox, trans):
>      x0, y0, x1, y1 = bbox.extents
>      tx0, ty0 = trans.transform([x0, y0])
>      tx1, ty1 = trans.transform([x1, y1])
>      tx2, ty2 = trans.transform([x1, y0])
>      tx3, ty3 = trans.transform([x0, y1])
>      return Bbox.from_extents(min(tx0, tx1, tx2, tx3), min(ty0, ty1, ty2, 
> ty3),
>          max(tx0, tx1, tx2, tx3), max(ty0, ty1, ty2, ty3))
>
> - The other inconsistency is that within matplotlib, extents are
> defined different:
> in imshow,  the parameter extent expects the order (left, right, bottom, top),
> while BboxBase.extents is (left, bottom, right, top). This should be changed 
> in
> the future, maybe the move to python 3 is a good time for that?
>
> But now to my code to draw images. It's a new class inheriting AxesImage,
> but is supposed to once replace AxesImage, as it is compatible.
>
> I'm re-writing _draw_unsampled_image, to actually draw a sampled image.
> Thats only because make_image, the method to be rewritten for a sampled
> image, is not flexible enough (the caller draws the image, but there is no way
> for make_image to tell where that image is to be put). In the future,
> the methods
> should be renamed (it's a private method, so that's no problem).
>
> class ShearImage(AxesImage):
>      def _check_unsampled_image(self, _):
>          return True
>
>      def _draw_unsampled_image(self, renderer, gc):
>          """
>          actually, draw sampled image. This method is more flexible than
>          make_image
>          """
>          mag = renderer.get_image_magnification()
>          trans = Affine2D().scale(mag, mag) + self.get_transform() + \
>              self.axes.transData.get_affine()
>          bbox = self.axes.bbox
>          viewLim = transform_bbox(bbox, trans.inverted())
>
>          im, xmin, ymin, dxintv, dyintv, sx, sy = \
>              self._get_unsampled_image(self._A, self.get_extent(), viewLim)
>
>          if im is None: return # I'm not if this check is required. -JJL
>          im.set_interpolation(self._interpd[self._interpolation])
>          im.set_resample(self._resample)
>
>          fc = self.axes.patch.get_facecolor()
>          bg = mcolors.colorConverter.to_rgba(fc, 0)
>          im.set_bg( *bg)
>     # uncomment the following line to see the extent to which the image
>     # is drawn
>     #     im.set_bg(0, 0, 0, 100)
>          numrows, numcols = im.get_size()
>
>          ex = self.get_extent()
>          tex = Bbox.from_extents([ex[0], ex[2], ex[1], ex[3]])
>          tex = transform_bbox(tex, trans)
>          if tex.xmin<  bbox.xmin:
>              left = bbox.xmin
>              tx = 0
>          else:
>              left = tex.xmin
>              tx = tex.xmin - bbox.xmin
>          if tex.ymin<  bbox.ymin:
>              bottom = bbox.ymin
>              ty = 0
>          else:
>              bottom = tex.ymin
>              ty = tex.ymin - bbox.ymin
>          trans = Affine2D().scale(dxintv / numcols,
>                  dyintv / numrows).translate(xmin, ymin) + \
>                  trans + \
>                  Affine2D().translate(-bbox.xmin - tx, -bbox.ymin - ty)
>          im.set_matrix(*trans.get_matrix()[:2, :].T.ravel())
>
>          width = min(tex.xmax, bbox.xmax) - left
>          height = min(tex.ymax, bbox.ymax) - bottom
>          if width<= 0 or height<= 0:
>              return
>          im.resize(width * mag, height * mag,
>              norm=self._filternorm, radius=self._filterrad)
>
>          im._url = self.get_url()
>
>          renderer.draw_image(gc, left, bottom, im)
>
> Last but not least, a little script to test the above. It shows a rotated
> image. You can scale and move the image nicely. If you uncomment the
> line mentioned above in ShearImage code, you can see where the
> image is actually drawn, and you will see that only the necessary parts
> are drawn if the image is smaller than the entire axes.
> The test script follows:
>
> from pylab import *
> ax = axes()
> im = ShearImage(ax)
> im.set_data(fromfunction(lambda x, y: sin(x + y ** 2), (100, 100)))
> im.set_extent(im.get_extent())
> transform = Affine2D().rotate_deg(30)
> im.set_transform(transform)
> ax.images.append(im)
> show()
>
> Greetings
>
> Martin
>
> ------------------------------------------------------------------------------
> AppSumo Presents a FREE Video for the SourceForge Community by Eric
> Ries, the creator of the Lean Startup Methodology on "Lean Startup
> Secrets Revealed." This video shows you how to validate your ideas,
> optimize your ideas and identify your business strategy.
> http://p.sf.net/sfu/appsumosfdev2dev
> _______________________________________________
> Matplotlib-devel mailing list
> Matplotlib-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/matplotlib-devel


------------------------------------------------------------------------------
AppSumo Presents a FREE Video for the SourceForge Community by Eric 
Ries, the creator of the Lean Startup Methodology on "Lean Startup 
Secrets Revealed." This video shows you how to validate your ideas, 
optimize your ideas and identify your business strategy.
http://p.sf.net/sfu/appsumosfdev2dev
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to