How do I overlay two images with Chaco?

686 Views Asked by At

Note: I will be answering this question myself to help other people who come across this problem in the future. Feel free to submit your own answers if you want, but know that it's already answered!

How can I overlay a masked image with one colormap onto another image with a different colormap in Chaco? Also, how can I add colorbars for each of these?

2

There are 2 best solutions below

0
On

Overlaying images in this manner in Chaco is not well documented, but definitely possible. Firstly, how do you plot a masked image with chaco? When plotting with Plot().img_plot(), Chaco uses np.nan values as transparent pixels. For example, plotting:

img = np.eye(100)
img[img==0] = np.nan

would plot a diagonal line with a transparent background.

But how do you actually overlay this image on another image?

There are two main methods to do this.

  1. Make two separate plots and stack them using an OverlayPlotContainer
  2. Make one plot and plot them both in the one plot

The advantage to the second method is that both images will use the same axes. Also if you plot a second image in the same plot as the first, it keeps the same pixel aspect ratio. This means that if you plot a 100x100 image and then overlay a 50x50 image on top of it, the overlaying image will only take up 25% of the whole plot starting at (0,0).

There are some problems with the second method, so I will explain how to correct them.

When you plot multiple images on the same Plot object (using img_plot()), they will both use the same color_mapper by default. This means that both will be scaled to the same range. This may not be the required result, so you must create new color_mappers for both images.

Here's some example code with TraitsUI, which was adapted from the Qt code.

from traits.api import HasTraits, Instance
from traitsui.api import Item, View
from enable.api import ComponentEditor
from chaco.api import ArrayPlotData, Plot, ColorBar, LinearMapper, HPlotContainer, DataRange1D, ImageData
import chaco.default_colormaps
#
import numpy as np

class ImagePlot(HasTraits):
    plot = Instance(HPlotContainer)

    traits_view = View(
        Item('plot', editor=ComponentEditor(), show_label=False), width=500, height=500, resizable=True, title="Chaco Plot")

    def _plot_default(self):
        bottomImage = np.reshape(np.repeat(np.linspace(0, 5, 100),100), (100,100))
        topImage = np.eye(50)
        topImage = topImage*np.reshape(np.repeat(np.linspace(-2, 2, 50),50), (50,50))
        topImage[topImage==0] = np.nan
        #
        bottomImageData = ImageData()
        bottomImageData.set_data(bottomImage)
        #
        topImageData = ImageData()
        topImageData.set_data(topImage)
        #
        plotData = ArrayPlotData(imgData=bottomImageData, imgData2=topImageData)
        plot = Plot(plotData, name='My Plot')
        plot.img_plot("imgData")
        plot.img_plot("imgData2")
        # Note: DO NOT specify a colormap in the img_plot!
        plot.aspect_ratio = 1.0
        #
        bottomRange = DataRange1D()
        bottomRange.sources = [plotData.get_data("imgData")]
        topRange = DataRange1D()
        topRange.sources = [plotData.get_data("imgData2")]
        plot.plots['plot0'][0].color_mapper = chaco.default_colormaps.gray(bottomRange)
        plot.plots['plot1'][0].color_mapper = chaco.default_colormaps.jet(topRange)
        #
        colormapperBottom = plot.plots['plot0'][0].color_mapper
        colormapperTop = plot.plots['plot1'][0].color_mapper
        #
        colorbarBottom = ColorBar(index_mapper=LinearMapper(range=colormapperBottom.range), color_mapper=colormapperBottom, orientation='v', resizable='v', width=30, padding=20)
        colorbarBottom.padding_top = plot.padding_top
        colorbarBottom.padding_bottom = plot.padding_bottom
        #
        colorbarTop = ColorBar(index_mapper=LinearMapper(range=colormapperTop.range), color_mapper=colormapperTop, orientation='v', resizable='v', width=30, padding=20)
        colorbarTop.padding_top = plot.padding_top
        colorbarTop.padding_bottom = plot.padding_bottom
        #
        container = HPlotContainer(resizable = "hv", bgcolor='transparent', fill_padding=True, padding=0)
        container.spacing = 0
        container.add(plot)
        container.add(colorbarBottom)
        container.add(colorbarTop)
        #
        return container

if __name__ == "__main__":
    ImagePlot().configure_traits()

Overlaying image plots in Chaco

1
On

I'm not taking 100% credit for this, with a quick search online i've found that you can do a simple overlay with the code below:

Source where found:

http://docs.enthought.com/chaco/user_manual/containers.html#overlayplotcontainer

Ref Code:

class OverlayImageExample(HasTraits):

plot = Instance(OverlayImage)

traits_view = View(
    Item('plot', editor=ComponentEditor(), show_label=False),
    width=800, height=600, resizable=True
)

def _plot_default(self):
    # Create data
    x = linspace(-5, 15.0, 100)
    y = jn(3, x)
    pd = ArrayPlotData(index=x, value=y)

    zoomable_plot = Plot(pd)
    zoomable_plot.plot(('index', 'value'),
                       name='external', color='red', line_width=3)

    # Attach tools to the plot
    zoom = ZoomTool(component=zoomable_plot,
                    tool_mode="box", always_on=False)
    zoomable_plot.overlays.append(zoom)
    zoomable_plot.tools.append(PanTool(zoomable_plot))

    # Create a second inset plot, not resizable, not zoom-able
    inset_plot = Plot(pd)
    inset_plot.plot(('index', 'value'), color='blue')
    inset_plot.set(resizable = '',
                   bounds = [250, 150],
                   position = [450, 350],
                   border_visible = True
                   )

    # Create a container and add our plots
    container = OverlayPlotContainer()
    container.add(zoomable_plot)
    container.add(inset_plot)
    return container