Luts in matplotlib ...

HI again,

I have questions of how to generate new LUTs outside matplotlib.

3 main issues:

1/ how to create a new lut in the same way that cm.py/colors.py is doing it
   in matplotlib
2/ how to create a new lut which is given by a set of e.g., 256 colours levels (R, G, B)
  but not as a segmented array as in cm/colors
3/ How do you rescale a lut without rescaling the array itself?
(so an equivalent to load/itt log for example in Midas for those who know).
   For example I would like to use the jet lut but with a log increase
  of the lut so that e.g. displaying the color bar shows it is in ''log''

For 1, I am not so sure what to do, and for 2/ I give below
what I am doing at the moment. To be frank, it looks quite ugly
(mainly because I am a bad programmer and don't know so much about
python/matplotlib).

I got inspired by cm and colors.py and Midas lut
but really this is probably not the way to go.
In order to change the lut I just then do:

lut('mylut')

where 'mylut.lasc' is then the ascii file where the 256 colours are given in 3 columns
(R G B) of 256 rows

The problem is that since I do not have the corresponding segmented array for each
lut (and I don't want it to be that way) I need to define a ''dummy'' array.
Then each time I use imshow I must reload the lut (otherwise it uses this dummy segmented
array which is here a gray lut).

I am not sure this is all clear, but basically what I am trying to do here is to just
answer questions 1, 2, 3 above and below is an ugly solution for 2 (but incomplete).

Any help is welcome. Thanks.

Eric

···

#########################################################################################
"""######################################
   # MIDAS-LIKE LUT and ITT ??
   ######################################"""
_dummy_data = {'red': ((0., 0, 0), (1., 1, 1)),
               'green': ((0., 0, 0), (1., 1, 1)),
               'blue': ((0., 0, 0), (1., 1, 1))}

class myMidasLut(colors.LinearSegmentedColormap):
   """ Definition of luts a la Midas, with R, G, and B levels
   """
   def __init__(self, name='grey', alpha=1.0):
      self.LUT_PATH = '/softwares/python/pycral/midaslut/'
      self._red_lut = arange(256)/255.
      self._green_lut = arange(256)/255.
      self._blue_lut = arange(256)/255.
      self._isinit = True
      self.name = name
      self.N = 256
      cm.datad[name] = _dummy_data

   def new(self,name, alpha=1.0): sname = str(name)
      sname = self.LUT_PATH+sname+".lasc"
      if os.path.isfile(sname):
         f = open(sname)
         list = f.readlines()
         for i in range(256):
            self._red_lut[i] = float(list[i].split()[0])
            self._green_lut[i] = float(list[i].split()[1])
            self._blue_lut[i] = float(list[i].split()[2])
         f.close
         self.name = name
      else :
         print 'ERROR: Lut file', name,'does not exist'

      rc('image', cmap=name)
      im = gci()
      if im is not None:
         im.set_cmap(self)
         im.set_alpha(alpha)
         draw_if_interactive()

      cm.datad[name] = _dummy_data
   ################ Default Lut initialisation ##
pglut = myMidasLut()

################ Function to change the lut ##
def lut (name, alpha=1.0) :
   pglut.new(name, alpha)

#########################################################################################

--

Observatoire de Lyon emsellem@...419...
9 av. Charles-Andre tel: +33 4 78 86 83 84
69561 Saint-Genis Laval Cedex fax: +33 4 78 86 83 86
France http://www-obs.univ-lyon1.fr/eric.emsellem

I think it is important to realize that you don't need to base it on the existing colormapping mechanism, you just need to duplicate the interface of colormaps, namely that it has N, and name attributes, and that when the object is called with an array or scalar as an argument, it will return the appropriate array (or tuple) as the result. How you convert the array to rgba values is entirely up to you. But I think there is enough similarity to what LinearSegmentColormap that effectively its __call__ method can be resused without changed. You would only override the __init__ to create the _red_lut,
_green_lut, _blue_lut attribute directly from array arguments (if I remember what MIDAS does; it's been nearly 20 years since I've used it!).

That is something like this (untested, untried!)

class ArrayColormap(LinearSegmentColormap):
  """ really, it seems that linear should inherit from Array instead!""" def __init__(name, r,g,b):
    """create color map from supplied r,g,b arrays"""
         # put checks on r,g,b arrays here...
         self._red_lut = r
         self._green_lut = g
         self._blue_lut = b
         self.N = len(r)
         self.name = name

This is something we should add but you can try this in the meantime.

The second part is how to set the lut to this. You can provide the new colormap instance as a keyword (cmap) argument to imshow or figimage. But I understand you would like a functional way to update an existing display. There doesn't appear to be a function to do this, but a new one is easily defined. Something like this ought to work (again untested, and again, we should supply this in the next version)

from
def loadcm(cm):
     """change the default colormap to cm"""
     rc('image', cmap='yourcmname')
     im = gci()
     if im is not None:
         im.set_cmap(cm)
     draw_if_interactive()

But the problem with this is that default mechanism won't work since this eventually calls cm.get_cmap, which then looks for the named cm in the cm.datad array to construct a LinearSegmentedColormap instance. I think the solution here may need to have some sort of caching mechanism so that user defined colormaps can be used as a default (perhaps John can think of some other mechanism).

I agree that it should be simple to define your own on the fly and be able to use these. So we'll see how this is best handled.

I'm not sure what you mean by 3. If you are changing the lut (i.e., which colors map to which image intensity levels, the rescaling the image is required when displaying it (in the "old days" when image displays were only 8-bit and had hardware luts, one didn't have to do that). If all you mean is that you already had transformed the data to log format but want to show the lut as though the data were linearly displayed, I suppose one would display a colorbar where the intensities were suitably transformed from a linear ramp. But please elaborate.

Perry

···

On Jan 11, 2005, at 10:25 AM, Eric Emsellem wrote:

HI again,

I have questions of how to generate new LUTs outside matplotlib.

3 main issues:

1/ how to create a new lut in the same way that cm.py/colors.py is doing it
  in matplotlib
2/ how to create a new lut which is given by a set of e.g., 256 colours levels (R, G, B)
but not as a segmented array as in cm/colors
3/ How do you rescale a lut without rescaling the array itself?
(so an equivalent to load/itt log for example in Midas for those who know).
  For example I would like to use the jet lut but with a log increase
of the lut so that e.g. displaying the color bar shows it is in ''log''

For 1, I am not so sure what to do, and for 2/ I give below
what I am doing at the moment. To be frank, it looks quite ugly
(mainly because I am a bad programmer and don't know so much about
python/matplotlib).

I got inspired by cm and colors.py and Midas lut
but really this is probably not the way to go.
In order to change the lut I just then do:

lut('mylut')

where 'mylut.lasc' is then the ascii file where the 256 colours are given in 3 columns
(R G B) of 256 rows

The problem is that since I do not have the corresponding segmented array for each
lut (and I don't want it to be that way) I need to define a ''dummy'' array.
Then each time I use imshow I must reload the lut (otherwise it uses this dummy segmented
array which is here a gray lut).

I am not sure this is all clear, but basically what I am trying to do here is to just
answer questions 1, 2, 3 above and below is an ugly solution for 2 (but incomplete).

Hi,

I think what you sent me may help although what you describe looks very much
like I have done in the piece of code I sent. It is not a first priority since what I wrote is
''kind of working'' (if I reload things everytime I display) but indeed an ''easy''
solution to this problem would be most welcome. The worry I have is that at the
moment we cannot load the lut without describing the datad keys and so on
so that it looks like a never ending story if people ask the developers everytime
to update the cm/colors files. (otherwise it crashes when you do imshow repeatedly)

As for the Question 3:

- I wish to load my array in LINEAR but then load a lut in LOG, so that the sampling of
the SAME lut is done but in LOG instead of LINEAR (so the fact that it is in log
would only appear when you load the color bar). This is used often when I need
to display a set of images side by side, some being in LIN, others in LOG.

My idea is the following:
- it is I think (may be wrong) feasible to add a functionality during the mapping of the lut
(when the LinearSegmented array is sampled on 256 levels) so that it takes the
LOG10, LN, EXP, or '-' (* -1 to invert a lut) of the abscissa so that the
required effect appears. This would create a smashed lut, or an inverted one. etc...

See what I mean?

Eric

Perry Greenfield wrote:

···

On Jan 11, 2005, at 10:25 AM, Eric Emsellem wrote:

HI again,

I have questions of how to generate new LUTs outside matplotlib.

3 main issues:

1/ how to create a new lut in the same way that cm.py/colors.py is doing it
  in matplotlib
2/ how to create a new lut which is given by a set of e.g., 256 colours levels (R, G, B)
but not as a segmented array as in cm/colors
3/ How do you rescale a lut without rescaling the array itself?
(so an equivalent to load/itt log for example in Midas for those who know).
  For example I would like to use the jet lut but with a log increase
of the lut so that e.g. displaying the color bar shows it is in ''log''

For 1, I am not so sure what to do, and for 2/ I give below
what I am doing at the moment. To be frank, it looks quite ugly
(mainly because I am a bad programmer and don't know so much about
python/matplotlib).

I got inspired by cm and colors.py and Midas lut
but really this is probably not the way to go.
In order to change the lut I just then do:

lut('mylut')

where 'mylut.lasc' is then the ascii file where the 256 colours are given in 3 columns
(R G B) of 256 rows

The problem is that since I do not have the corresponding segmented array for each
lut (and I don't want it to be that way) I need to define a ''dummy'' array.
Then each time I use imshow I must reload the lut (otherwise it uses this dummy segmented
array which is here a gray lut).

I am not sure this is all clear, but basically what I am trying to do here is to just
answer questions 1, 2, 3 above and below is an ugly solution for 2 (but incomplete).

I think it is important to realize that you don't need to base it on the existing colormapping mechanism, you just need to duplicate the interface of colormaps, namely that it has N, and name attributes, and that when the object is called with an array or scalar as an argument, it will return the appropriate array (or tuple) as the result. How you convert the array to rgba values is entirely up to you. But I think there is enough similarity to what LinearSegmentColormap that effectively its __call__ method can be resused without changed. You would only override the __init__ to create the _red_lut,
_green_lut, _blue_lut attribute directly from array arguments (if I remember what MIDAS does; it's been nearly 20 years since I've used it!).

That is something like this (untested, untried!)

class ArrayColormap(LinearSegmentColormap):
    """ really, it seems that linear should inherit from Array instead!""" def __init__(name, r,g,b):
        """create color map from supplied r,g,b arrays"""
        # put checks on r,g,b arrays here...
        self._red_lut = r
        self._green_lut = g
        self._blue_lut = b
        self.N = len(r)
        self.name = name

This is something we should add but you can try this in the meantime.

The second part is how to set the lut to this. You can provide the new colormap instance as a keyword (cmap) argument to imshow or figimage. But I understand you would like a functional way to update an existing display. There doesn't appear to be a function to do this, but a new one is easily defined. Something like this ought to work (again untested, and again, we should supply this in the next version)

from
def loadcm(cm):
    """change the default colormap to cm"""
    rc('image', cmap='yourcmname')
    im = gci()
    if im is not None:
        im.set_cmap(cm)
    draw_if_interactive()

But the problem with this is that default mechanism won't work since this eventually calls cm.get_cmap, which then looks for the named cm in the cm.datad array to construct a LinearSegmentedColormap instance. I think the solution here may need to have some sort of caching mechanism so that user defined colormaps can be used as a default (perhaps John can think of some other mechanism).

I agree that it should be simple to define your own on the fly and be able to use these. So we'll see how this is best handled.

I'm not sure what you mean by 3. If you are changing the lut (i.e., which colors map to which image intensity levels, the rescaling the image is required when displaying it (in the "old days" when image displays were only 8-bit and had hardware luts, one didn't have to do that). If all you mean is that you already had transformed the data to log format but want to show the lut as though the data were linearly displayed, I suppose one would display a colorbar where the intensities were suitably transformed from a linear ramp. But please elaborate.

Perry

--

Observatoire de Lyon emsellem@...419...
9 av. Charles-Andre tel: +33 4 78 86 83 84
69561 Saint-Genis Laval Cedex fax: +33 4 78 86 83 86
France http://www-obs.univ-lyon1.fr/eric.emsellem