svg.image_noscale and others

Hello matplotlib developers,

I have implemented "svg.image_noscale" feature for ps and pdf backends. I think
that resampling/scaling should be avoided, when vector format can scale image
itself.

One advantage is that original image can be recovered from final file. Moreover
as it is vector format it should be dpi independent and always provide maximum
possible quality - that's original data.

As for svg backend I have commented some transformation changes which
I don't understand and which result in misaligned image and axes.
Without it the misalignment is still there but smaller.
I have also removed MixedModeRenderer from svg as it conflicts with "svg.image_noscale"
and does not seem to be used.

As last change, I restored commented self.figure.canvas.draw() in
FigureCanvasBase.print_figure as I got complaints about "I/O operation on
closed file" when I tried to "draw_artist" in my animation using blit
technique. (It used cached renderer that no longer existed,
self.figure.canvas.draw() updates this cache)

Further more please consider set_animated(False) on all artists before saving
hardcopy and restore animated state after (animated artist should be saved,
although they can not animate in hardcopy)

Thanks for reviewing my suggestions

Jozef Vesely
vesely@...670...

--- /usr/lib/python2.5/site-packages/matplotlib/backends/backend_svg.py 2008-08-03 20:15:03.000000000 +0200
+++ backend_svg.py 2008-11-12 10:08:54.000000000 +0100
@@ -254,9 +254,9 @@
         transstr = ''
         if rcParams['svg.image_noscale']:
             trans = list(im.get_matrix())
- if im.get_interpolation() != 0:
- trans[4] += trans[0]
- trans[5] += trans[3]
+ #if im.get_interpolation() != 0:
+ # trans[4] += trans[0]
+ # trans[5] += trans[3]
             trans[5] = -trans[5]
             transstr = 'transform="matrix(%s %s %s %s %s %s)" '%tuple(trans)
             assert trans[1] == 0
@@ -579,9 +579,9 @@
         self.figure.set_dpi(72.0)
         width, height = self.figure.get_size_inches()
         w, h = width*72, height*72

···

-
- renderer = MixedModeRenderer(
- width, height, 72.0, RendererSVG(w, h, svgwriter, filename))
+
+
+ renderer = RendererSVG(w, h, svgwriter, filename)
         self.figure.draw(renderer)
         renderer.finalize()
         if fh_to_close is not None:
--- backend_ps.py 2008-08-03 20:15:03.000000000 +0200
+++ backend_ps.py.new 2008-11-12 10:04:33.000000000 +0100
@@ -400,7 +400,13 @@
         bbox is a matplotlib.transforms.BBox instance for clipping, or
         None
         """
-
+ if rcParams['svg.image_noscale']:
+ trans = list(im.get_matrix())
+ numrows,numcols = im.get_size()
+ im.reset_matrix()
+ im.set_interpolation(0)
+ im.resize(numcols, numrows)
+
         im.flipud_out()

         if im.is_grayscale:
@@ -413,8 +419,12 @@

         xscale, yscale = (
             w/self.image_magnification, h/self.image_magnification)
-
- figh = self.height*72
+
+ if rcParams['svg.image_noscale']:
+ xscale *= trans[0]
+ yscale *= trans[3]
+
+ #figh = self.height*72
         #print 'values', origin, flipud, figh, h, y

         clip = []
--- backend_pdf.py 2008-08-03 20:15:03.000000000 +0200
+++ backend_pdf.py.new 2008-11-12 10:06:30.000000000 +0100
@@ -1226,9 +1226,17 @@
             gc.set_clip_rectangle(bbox)
         self.check_gc(gc)

+ if rcParams['svg.image_noscale']:
+ t = list(im.get_matrix())
+ numrows,numcols = im.get_size()
+ im.reset_matrix()
+ im.set_interpolation(0)
+ im.resize(numcols, numrows)
+
         h, w = im.get_size_out()
         imob = self.file.imageObject(im)
         self.file.output(Op.gsave, w, 0, 0, h, x, y, Op.concat_matrix,
+ t[0],0,0,t[3],0,0, Op.concat_matrix,
                          imob, Op.use_xobject, Op.grestore)

     def draw_path(self, gc, path, transform, rgbFace=None):

--- /usr/lib/python2.5/site-packages/matplotlib/backend_bases.py 2008-08-03 20:15:03.000000000 +0200
+++ backend_bases.py 2008-11-12 10:22:38.000000000 +0100
@@ -1313,7 +1313,7 @@
             self.figure.set_facecolor(origfacecolor)
             self.figure.set_edgecolor(origedgecolor)
             self.figure.set_canvas(self)
- #self.figure.canvas.draw() ## seems superfluous
+ self.figure.canvas.draw() ## not superfluous
         return result

     def get_default_filetype(self):

Jozef Vesely wrote:

Hello matplotlib developers,

I have implemented "svg.image_noscale" feature for ps and pdf backends. I think
that resampling/scaling should be avoided, when vector format can scale image
itself.

It seems to me best if there is an option to scale or not; depending on the situation, one might want to generate a file with images downscaled.

One advantage is that original image can be recovered from final file. Moreover
as it is vector format it should be dpi independent and always provide maximum
possible quality - that's original data.

As for svg backend I have commented some transformation changes which I don't understand and which result in misaligned image and axes. Without it the misalignment is still there but smaller.
I have also removed MixedModeRenderer from svg as it conflicts with "svg.image_noscale"
and does not seem to be used.

I think having the option of using the MixedModeRenderer is important in the long run for the vector backends; without it, one can end up with completely unwieldy and potentially unrenderable files. I'm not sure what its status is at present; I think Mike got it working to a considerable extent, but didn't quite finish, and therefore left it temporarily disabled.

Eric

Eric Firing wrote:

Jozef Vesely wrote:
  

Hello matplotlib developers,

I have implemented "svg.image_noscale" feature for ps and pdf backends. I think
that resampling/scaling should be avoided, when vector format can scale image
itself.
    

Unfortunately, the quality of interpolation is often subpar compared to what matplotlib (via Agg) provides. Worse, the quality will be different when using Acrobat Reader vs. xpdf, for instance. I don't think zooming in on individual pixels of data in Acroread is something we really are trying to support anyway -- for that you should use an interactive matplotlib GUI. The purpose of pdf, imho, is really for printing. In that case, you're likely to get better results and smaller file sizes by knowing the maximum resolution of your output device and letting matplotlib resample it -- and resample it with a method that is appropriate for the data, not the one in the printer or Acrobat that is (probably) optimized for photographs of the real world or whatever the driver is currently set to.

It seems to me best if there is an option to scale or not; depending on the situation, one might want to generate a file with images downscaled.
  

Right. All the above notwithstanding, I don't have a problem with this being a user option, I just can't imagine using it myself.

  

One advantage is that original image can be recovered from final file. Moreover
as it is vector format it should be dpi independent and always provide maximum
possible quality - that's original data.
    

The original image can theoretically be recovered from the final file. But not the original data, which may be floating point etc. If you anticipate users of your plot to need the original data, just distribute the original data alongside the plot.

As for svg backend I have commented some transformation changes which I don't understand and which result in misaligned image and axes. Without it the misalignment is still there but smaller.
    

Thanks for that. I'm not sure why that code is there. I see it looks much better without it.

I have also removed MixedModeRenderer from svg as it conflicts with "svg.image_noscale"
and does not seem to be used.
    

I think it would be better to turn off mixed mode rendering only when svg.image_noscale is True.

    
I think having the option of using the MixedModeRenderer is important in the long run for the vector backends; without it, one can end up with completely unwieldy and potentially unrenderable files. I'm not sure what its status is at present; I think Mike got it working to a considerable extent, but didn't quite finish, and therefore left it temporarily disabled.
  

It's fully functional in all the backends where it makes sense. The part that is unfinished is the user interface -- how to turn the functionality on and off. We couldn't find both a general and easy way to do it. But it would be nice to have another go at it.

Mike

···

--
Michael Droettboom
Science Software Branch
Operations and Engineering Division
Space Telescope Science Institute
Operated by AURA for NASA