On Tue, Aug 10, 2010 at 7:39 PM, Benjamin Root <ben.r...@ou.edu> wrote:
> On Tue, Aug 10, 2010 at 7:20 PM, Eric Firing <efir...@hawaii.edu> wrote: > >> On 08/10/2010 11:58 AM, Friedrich Romstedt wrote: >> > 2010/8/10 Eric Firing<efir...@hawaii.edu>: >> >> On 08/10/2010 10:27 AM, Friedrich Romstedt wrote: >> >>> So I think it is probably best to code it into the Colormap object >> >>> itself, so that each and ever derived class can define its own method >> >>> of how to create a greyscale version. What do you think about that? >> >> >> >> Good idea. The base class could define a default to_grayscale() method >> >> that would do the conversion near the very end of the Colormap.__call__ >> >> method if an as_gray attribute, also defined in the base class, is >> True. >> >> No need for getters and setters--let it be a simple attribute. This >> >> is much simpler than generating a whole new colormap--instead, just set >> >> an attribute to switch an existing colormap to gray or back to color. >> I >> >> would leave the switch out of the __init__ args and kwargs. >> > >> >> If someone >> >> wants grey, they can add one more line of code to set the attribute. >> > >> > Hmm, one would have to do it for every colormap used. Also, it would >> > be not so obvious if using the default colormap. >> > >> >> I >> >> suspect only a small fraction of users will need this. >> > >> > I like this, it's a good idea enabling to not repeat all the stuff in >> > the code. But hey, wouldn't it be the same to just overload the >> > __call__ method? >> > >> > class GrayColorbar(RgbColorbar): >> > """A colorbar returning grayscaled version.""" >> > >> > def __call__(self, value): >> > """Transforms RgbColorbar's value into grayscale, and returns >> it."""" >> > >> > rgb = BaseClass.__call__(self, value) >> > [do stuff] >> > return grayscale >> >> Why make a whole new class instead of switching a behavior in an >> existing class? >> >> > >> > Agreed, one has to this for all the classes, while your solution works >> > generically in ColormapBase or whatever it's called. >> >> I think my solution (with slight modification) is also very compatible >> with your suggestion below to have an rc param that switches between >> color and gray. If as_gray is None, use the rc value; otherwise, let it >> act as described above. >> >> The typical use case is when one wants color initially, but then must >> regenerate plots in black and white to reduce publication costs. Your >> rc suggestion facilitates such switching. >> >> > >> > Another option would be, to wrap the Colormap into a thin layer with >> > __call__, but this appears way too clumsy and too hacky to me. It's a >> > clumsy way of deriving from the class. Otherwise it's maybe also >> > nice, because it's generic, for all Colormaps. __getattr__ and >> > __setattr__ (iirc) could be used to simulate the wrapped instance >> > fully. >> >> I don't follow you on this--I don't understand at all. >> >> >> > >> > Is there some layer dealing with colors in the renderers, where the >> > conversion could also happen? I'm asking because of displaying color >> > images etc. and printing them as grayscale. With this, we would get >> > rid of the alpha stuff completely, too. >> >> I don't understand why you would want to push this down into the >> renderers instead of keeping it at a higher level. And I don't know >> what you mean by "getting rid of the alpha stuff". >> >> > >> > Maybe a general matplotlib.rc switch 'greyscale' would be possible. >> > It would leave room for tons of improvement, e.g. automatically >> > setting the color cycle to - -- -. etc., there was some monthes ago >> > discussion about how to achieve that. Another advantage: It's zero >> > loc if set in the config file ;-) >> >> Yes, there does seem to be a need for these sorts of switching >> operations. Getting back to the colormap question, our discussions have >> been based on the assumption that an rgb_to_gray function is all that is >> needed. But is this true? Or will users actually need completely >> different graymaps rather than something resembling a black-and-white >> photo of a colormap? If so, then my suggested strategy is useless. >> >> Eric >> >> > >> > I think that would be a real neat new feature. >> > >> > I agree what's not needed is mixed greyscale and colour display in the >> > same Figure. >> > >> > Bear with me if my ideas are always too radical. >> > >> > Friedrich >> >> > I have to agree with Eric on this. Maybe I am just not understanding > Friedrich's thought process. > > If someone wants a specific greyscale colormapping, then they can make it > themselves just like any other colormap. However, it is as a matter of > convenience and ease to say "hey, I have this colormap that I have been > using in my code, but I need its greyscale equivalent". > > My idea is this. It appears that all colormappings get a lookup table > (self._lut) that contains rgba values. We could also simultaneously > maintain a second lookup table (initialized only when needed). Therefore, > one could take a colormap, switch on the greyscale attribute when desired > (which would swap the two tables), and switch back over to the color-scale > with the same amount of ease. > > It would be independent of any Colormap subclass because it would be in the > base class. My only concern is when do the various drawing elements > actually use the colormap info? If I create a b&w figure in one subplot, > but also a colored figure in another subplot (for whatever reason) using the > same colormap, would the figure show as expected at render-time? > > Thoughts, concerns, comments? > > Ben Root > Ok, I have made some code that let's me easily switch between modes and works quite nicely. However, something I suspected would happen occurs. If I were to use the colormap and ever change the mode before rendering, *all* places where that colormap was used is colored according to the mode last set before rendering. This could be considered a bug or a feature, depending on one's viewpoint. I suspect if one really needed independent colormaps, one could use deepcopy before applying the alternate colormap to the axes. I am including a patch to this email for a looking over (be brutal). Note, it is hardly ready for inclusion as I am sure there are some other behaviors I haven't thought of. Below is a quick example of how to use the new feature. Enjoy! Ben Root import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cm X, Y = np.mgrid[-10:10, -10:10] Z = np.abs(X * Y) cmap = cm.get_cmap() cmap.grey_mode = True plt.pcolor(X, Y, Z, cmap=cmap) plt.show()
Index: matplotlib/lib/matplotlib/colors.py =================================================================== --- matplotlib/lib/matplotlib/colors.py (revision 8624) +++ matplotlib/lib/matplotlib/colors.py (working copy) @@ -480,12 +480,29 @@ """ self.name = name self.N = N + + # These items are used for determining a color given on a scaled value + self._lut = None self._rgba_bad = (0.0, 0.0, 0.0, 0.0) # If bad, don't paint anything. self._rgba_under = None self._rgba_over = None + + # These two dictionaries will contain the color and greyscale + # versions of the lookup table. The contents will be applied + # to the above attributes as necessary. + self._lut_color = None + self._lut_grey = None + + # Switch to greyscale mode? + self.grey_mode = False + self._grey_used = False # to help indicate a need to swap + + # Array index to the locations in the lookup table (lut) + # for special values of under, over and bad. self._i_under = N self._i_over = N+1 self._i_bad = N+2 + self._isinit = False @@ -503,6 +520,9 @@ """ if not self._isinit: self._init() + self._init_colorstore() + if self.grey_mode != self._grey_used : self._swap_colormode() + mask_bad = None if not cbook.iterable(X): vtype = 'scalar' @@ -592,6 +612,58 @@ '''Generate the lookup table, self._lut''' raise NotImplementedError("Abstract class only") + def _swap_colormode(self) : + if not self._grey_used : + # Swap to grey. + + # Create the _lut_grey dict if needed. + if self._lut_grey is None : + self._lut_grey = dict(rgba_under=self._color_to_grey(self._lut_color['rgba_over']), + rgba_over=self._color_to_grey(self._lut_color['rgba_under']), + rgba_bad=self._color_to_grey(self._lut_color['rgba_bad']), + lut=self._color_to_grey(self._lut_color['lut'])) + + # Swap in the grey info + self._lut = self._lut_grey['lut'] + self._rgba_under = self._lut_grey['rgba_under'] + self._rgba_over = self._lut_grey['rgba_over'] + self._rgba_bad = self._lut_grey['rgba_bad'] + + + # We have completed the swap + self._grey_used = True + + else : + # Swap to color. + + # Swap in the color info + self._lut = self._lut_color['lut'] + self._rgba_under = self._lut_color['rgba_under'] + self._rgba_over = self._lut_color['rgba_over'] + self._rgba_bad = self._lut_color['rgba_bad'] + + # We have completed the swap + self._grey_used = False + + def _init_colorstore(self) : + if self._lut_color is None : + self._lut_color = dict(lut=self._lut, + rgba_under=self._rgba_under, + rgba_over=self._rgba_over, + rgba_bad=self._rgba_bad) + + + def _color_to_grey(self, rgba) : + """Use ITU-R 601-2 luma transform""" + if rgba is not None : + rgba = np.atleast_2d(rgba) + l = np.sum(rgba[:, :3] * np.array([0.299, 0.587, 0.114]), axis=1) + return np.squeeze(np.dstack([l, l, l, rgba[:, 3]])) + else : + # If I get None, return it in kind + return None + + def is_gray(self): if not self._isinit: self._init() return (np.alltrue(self._lut[:,0] == self._lut[:,1])
------------------------------------------------------------------------------ This SF.net email is sponsored by Make an app they can't live without Enter the BlackBerry Developer Challenge http://p.sf.net/sfu/RIM-dev2dev
_______________________________________________ Matplotlib-devel mailing list Matplotlib-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-devel