Dear developers,matplotlib offers high quality interpolation filters which give excellent results if you scale up an image. However, for downscaling an image the results are worse. Depending on the precise scaling fine details (e.g., thin horizontal lines, single pixel points) disappear and aliasing effects are visible. After studying the source and the docs for Agg I figured out how to improve this. Agg provides all possibilities, just use them.
I attached a patch against the current svn version that adds a 'resample' argument to imshow. Additionally, this patch supports a 'image.resample' entry in the rc file. Setting this to false (default), the behaviour is unchanged. I also attached a simple test script (test_imshow.py) to show the difference between image display with and without resampling. To see the difference it might be necessary to zoom out.
Gregor Thalhammer
Index: src/_image.cpp =================================================================== --- src/_image.cpp (Revision 5430) +++ src/_image.cpp (Arbeitskopie) @@ -426,11 +426,22 @@ case HAMMING: filter.calculate(agg::image_filter_hamming(), norm); break; case HERMITE: filter.calculate(agg::image_filter_hermite(), norm); break; } - typedef agg::span_image_filter_rgba_2x2<img_accessor_type, interpolator_type> span_gen_type; - typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type; - span_gen_type sg(ia, interpolator, filter); - renderer_type ri(rb, sa, sg); - agg::render_scanlines(ras, sl, ri); + if (resample) + { + typedef agg::span_image_resample_rgba_affine<img_accessor_type> span_gen_type; + typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type; + span_gen_type sg(ia, interpolator, filter); + renderer_type ri(rb, sa, sg); + agg::render_scanlines(ras, sl, ri); + } + else + { + typedef agg::span_image_filter_rgba_2x2<img_accessor_type, interpolator_type> span_gen_type; + typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type; + span_gen_type sg(ia, interpolator, filter); + renderer_type ri(rb, sa, sg); + agg::render_scanlines(ras, sl, ri); + } } break; case BILINEAR: @@ -464,11 +475,22 @@ case LANCZOS: filter.calculate(agg::image_filter_lanczos(radius), norm); break; case BLACKMAN: filter.calculate(agg::image_filter_blackman(radius), norm); break; } - typedef agg::span_image_filter_rgba<img_accessor_type, interpolator_type> span_gen_type; - typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type; - span_gen_type sg(ia, interpolator, filter); - renderer_type ri(rb, sa, sg); - agg::render_scanlines(ras, sl, ri); + if (resample) + { + typedef agg::span_image_resample_rgba_affine<img_accessor_type> span_gen_type; + typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type; + span_gen_type sg(ia, interpolator, filter); + renderer_type ri(rb, sa, sg); + agg::render_scanlines(ras, sl, ri); + } + else + { + typedef agg::span_image_filter_rgba<img_accessor_type, interpolator_type> span_gen_type; + typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type; + span_gen_type sg(ia, interpolator, filter); + renderer_type ri(rb, sa, sg); + agg::render_scanlines(ras, sl, ri); + } } break; @@ -530,6 +552,20 @@ } +char Image::get_resample__doc__[] = +"get_resample()\n" +"\n" +"Get the resample flag." +; + +Py::Object +Image::get_resample(const Py::Tuple& args) { + _VERBOSE("Image::get_resample"); + + args.verify_length(0); + return Py::Int((int)resample); +} + char Image::get_size_out__doc__[] = "numrows, numcols = get_size()\n" "\n" @@ -593,6 +629,21 @@ } +char Image::set_resample__doc__[] = +"set_resample(boolean)\n" +"\n" +"Set the resample flag." +; + +Py::Object +Image::set_resample(const Py::Tuple& args) { + _VERBOSE("Image::set_resample"); + args.verify_length(1); + int flag = Py::Int(args[0]); + resample = (bool)flag; + return Py::Object(); +} + static void write_png_data(png_structp png_ptr, png_bytep data, png_size_t length) { PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr); PyObject* write_method = PyObject_GetAttrString(py_file_obj, "write"); @@ -752,12 +803,14 @@ add_varargs_method( "buffer_rgba", &Image::buffer_rgba, Image::buffer_rgba__doc__); add_varargs_method( "get_aspect", &Image::get_aspect, Image::get_aspect__doc__); add_varargs_method( "get_interpolation", &Image::get_interpolation, Image::get_interpolation__doc__); + add_varargs_method( "get_resample", &Image::get_resample, Image::get_resample__doc__); add_varargs_method( "get_size", &Image::get_size, Image::get_size__doc__); add_varargs_method( "get_size_out", &Image::get_size_out, Image::get_size_out__doc__); add_varargs_method( "reset_matrix", &Image::reset_matrix, Image::reset_matrix__doc__); add_varargs_method( "get_matrix", &Image::get_matrix, Image::get_matrix__doc__); add_keyword_method( "resize", &Image::resize, Image::resize__doc__); add_varargs_method( "set_interpolation", &Image::set_interpolation, Image::set_interpolation__doc__); + add_varargs_method( "set_resample", &Image::set_resample, Image::set_resample__doc__); add_varargs_method( "set_aspect", &Image::set_aspect, Image::set_aspect__doc__); add_varargs_method( "write_png", &Image::write_png, Image::write_png__doc__); add_varargs_method( "set_bg", &Image::set_bg, Image::set_bg__doc__); Index: src/_image.h =================================================================== --- src/_image.h (Revision 5430) +++ src/_image.h (Arbeitskopie) @@ -42,6 +42,8 @@ Py::Object set_bg(const Py::Tuple& args); Py::Object flipud_out(const Py::Tuple& args); Py::Object flipud_in(const Py::Tuple& args); + Py::Object set_resample(const Py::Tuple& args); + Py::Object get_resample(const Py::Tuple& args); std::pair<agg::int8u*, bool> _get_output_buffer(); @@ -78,6 +80,7 @@ unsigned interpolation, aspect; agg::rgba bg; + bool resample; private: Py::Dict __dict__; agg::trans_affine srcMatrix, imageMatrix; @@ -101,6 +104,8 @@ static char set_bg__doc__[]; static char flipud_out__doc__[]; static char flipud_in__doc__[]; + static char get_resample__doc__[]; + static char set_resample__doc__[]; }; Index: lib/matplotlib/axes.py =================================================================== --- lib/matplotlib/axes.py (Revision 5430) +++ lib/matplotlib/axes.py (Arbeitskopie) @@ -4974,7 +4974,7 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=1.0, vmin=None, vmax=None, origin=None, extent=None, shape=None, filternorm=1, - filterrad=4.0, imlim=None, **kwargs): + filterrad=4.0, imlim=None, resample=None, **kwargs): """ call signature:: @@ -5061,7 +5061,7 @@ self.set_aspect(aspect) im = mimage.AxesImage(self, cmap, norm, interpolation, origin, extent, filternorm=filternorm, - filterrad=filterrad, **kwargs) + filterrad=filterrad, resample=resample, **kwargs) im.set_data(X) im.set_alpha(alpha) Index: lib/matplotlib/image.py =================================================================== --- lib/matplotlib/image.py (Revision 5430) +++ lib/matplotlib/image.py (Arbeitskopie) @@ -57,6 +57,7 @@ extent=None, filternorm=1, filterrad=4.0, + resample = False, **kwargs ): @@ -86,6 +87,7 @@ self.set_interpolation(interpolation) + self.set_resample(resample) self.axes = ax @@ -200,6 +202,7 @@ im.set_interpolation(self._interpd[self._interpolation]) + im.set_resample(self._resample) # the viewport translation tx = (xmin-self.axes.viewLim.x0)/dxintv * numcols @@ -325,6 +328,13 @@ raise ValueError('Illegal interpolation string') self._interpolation = s + def set_resample(self, v): + if v is None: v = rcParams['image.resample'] + self._resample = v + + def get_interpolation(self): + return self._resample + def get_extent(self): 'get the image extent: left, right, bottom, top' if self._extent is not None: Index: lib/matplotlib/rcsetup.py =================================================================== --- lib/matplotlib/rcsetup.py (Revision 5430) +++ lib/matplotlib/rcsetup.py (Arbeitskopie) @@ -379,6 +379,7 @@ 'image.cmap' : ['jet', str], # one of gray, jet, etc 'image.lut' : [256, validate_int], # lookup table 'image.origin' : ['upper', str], # lookup table + 'image.resample' : [False, validate_bool], 'contour.negative_linestyle' : ['dashed', validate_negative_linestyle_legacy],
from pylab import * import numpy im = numpy.zeros((200, 200)) im[range(0,100,4)] = 1.0 im[range(100,200,5), 100] = 1.0 subplot(121) imshow(im, resample=False, interpolation = 'bicubic') gray() subplot(122) imshow(im, resample=True, interpolation = 'bicubic' ) gray() #show()
------------------------------------------------------------------------- Check out the new SourceForge.net Marketplace. It's the best place to buy or sell services for just about anything Open Source. http://sourceforge.net/services/buy/index.php
_______________________________________________ Matplotlib-devel mailing list Matplotlib-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-devel