10. Custom Data Analysis: Writing Plugins

Spectronon has the capability to support user-defined data analysis algorithms, commonly called plugins. Writing plugins allows the user to extend Spectronon’s functionality by incorporating custom tools designed for their specific applications. Furthermore plugins can create interactive user interfaces and integrate directly into Spectronon’s menu system.

Warning

Plugins have direct access to much of Spectronon’s internal code. We make this functionality available in the hope that it will be a useful tool. Use of this tool is at the user’s own risk. Resonon cannot support every situation that may arise from the use of this information. The methods and objects documented here may change at any time. Only the methods and attributes documented here are intended to be used in custom plugin development – all other methods and properties of the workbench, plugin, datacube, and spec classes are for internal use only and should not be called or overridden.

10.1. Getting started

Spectronon plugins are written in the Python programming language, and a working knowledge of Python is a prerequisite for writing Spectronon plugins. If you’re new to programming or to Python, the Beginner’s Guide to Python provides a collection of useful resources for getting started. Additionally, familiarity with the computing tools package Numpy is important, as Resonon datacubes are based on Numpy arrays. Finally, the SciPy library provides an excellent collection of tools for scientific computing in Python using Numpy arrays. Many Spectronon features make use of SciPy libraries, and SciPy is available to the user for use in developing additional plugins.

To develop a Spectronon plugin, create a file with a “.py” extension (such as MyCustomPlugin.py), and place it in the folder:

%userprofile%\SpectrononAppData\user_plugins\

This folder is typically located at

C:\Users\<your user name>\SpectrononAppData\user_plugins\.

In the file write a subclass of the plugin type you desire (as described below). You can use any text editor of your choice to create the plugin - a list of Python text editors can be found here. Upon loading, Spectronon will scan the user plugins folder for .py files and, if a valid plugin is found, add it to the Spectronon menu system.

10.2. Spectronon Plugin Overview

There are 4 types of plugins:

  • CubePlugin
  • RenderPlugin
  • SelectPlugin
  • FilterPlugin (built-in plugins only)

Plugins are creating by defining a subclass of one of the base plugin types, then placing the source file that defines that plugin in the %userprofile%\SpectrononAppData/user_plugins folder. The base plugin type definitions must be imported from the spectronon.workbench.plugin package and subclassed. Each plugin class has three methods that you can override to define your custom behavior. For example:

from spectronon.workbench.plugin import CubePlugin

class MyPlugin(CubePlugin):
    """
    The docstring text will appear on the user interface as help text when hovering
    over the plugin's menu item.
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
        """
        The setup method is where you will create the plugin's user interface.
        You can also define any custom initialization behavior here.  Do not override
        CubePlugin.__init__().
        """

    def update(self):
        """
        The update method is called each time the user changes the value of a Spec.
        You might need this for your plugin's book keeping.
        """

    def action(self):
        """
        The action method is called when the plugin is run, and should contain the
        logic of your custom behavior.
        """

Always define a label attribute as above, which will become the text that is displayed in Spectronon menus. The docstring used for the plugin class will become the help text when the user hovers over your custom plugin’s menu item.

Always define userLevel = 1 at the top of the class definition. Occasionally a user may use user level 2, which will render the plugin invisible unless Spectronon is invoked in expert-user mode.

Warning

Failure to set the userLevel to 1 or 2 at the top of the plugin definition will prevent the plugin from attaching to Spectronon’s menu system.

Writing any plugin requires at minimum that you write an action method.

def action(self):
    # your stuff here

The action method is called by Spectronon when the plugin is activated, and again each time the user interacts with any Specs defined by the plugin. This method returns the results of your plugin.

  • Cube plugins must return a datacube from their action method.
  • Render plugins return an image representation of a datacube as a numpy array.
  • Select plugins do not return a value, but interact directly with the workbench.
  • Filter plugins cannot be written by users.

If you want to get information from the user, you also define a ‘setup’ method and define some Specs (more on specs below) within it.

def setup(self):
    # define some specs here

If your UI can be altered based on user entry into other UI elements (for example, the user selects how many inputs they want to provide), then you must write an ‘update’ method.

def update(self):
    # add or remove specs or update paramaters of existing specs

None of these methods accepts arguments. Instead, there are attributes of the Plugin that you can use.

10.3. The attributes available to each plugin type

self.wb (All Plugins)

This is a handle on the spectronon workbench. The workbench gives you a lot of freedom, so use this reference wisely. Some common uses of self.wb are:

  • popup dialog messages such as: self.wb.postMessage("The combination of arguments is invalid for this cube")
  • retrieve data from the workbench: correctioncube = self.wb.getCube(self.correctCubeID.value)
  • plot something to the plotter: self.wb.plot(myarray)
  • and there are specs (SpecCube, SpecSpectrum) that expect the workbench as a parameter to help them work their magic

For more information, see Using the Workbench

self.datacube (CubePlugin, RenderPlugin, SelectPlugin)
This is the datacube on which this plugin will be applied. Cube plugins action methods are expected to return a new datacube based somehow on this one. Render plugins will use this datacube as a data source to build a viewable image.

For more information, see Using Datacubes

self.pointlist (SelectPlugin)
For any selection this is a list of points that was inside the selected area in the form of a numpy array of shape (number of points, 2) where self.pointlist[i,:] = (sample, line) index of the ith selected point.

10.4. Specs

A Spec is a class that manages a piece of data and a user interface to that data. They underly much of Spectronon’s codebase. A Spec maintains the connection between the user interface and the data in the back end. When a plugin class has a spec member, Spectronon will generate an appropriate graphical widget to allow the user to adjust that spec’s value in the plugin’s control panel. Specs can be imported from resonon.utils.spec. Usually you will access the spec by getting the current value in an action method using the spec’s ‘value’ member.

value = mySpec.value

Here is the list of available Specs:

General purpose input:
  • SpecBool - allows the user to specify a true of false value
  • SpecFloat - allows the user to specify a floating point value within a defined range
  • SpecInt - allows the user to specify an integer value within a defined range
  • SpecChoice - allows the user to choose an item from a defined list
Selecting references to cubes and spectra on the workbench:
  • SpecCube - allows the user to select one of the datacubes currently available on the workbench
  • SpecSpectrum - allows the user to select one of the spectra currently available on the workbench
Selecting portions of cubes or spectra:
  • SpecWavelength - a spec that accepts a datacube as an argument and allows the user to select a wavelength from that cube
  • SpecBandNumber - a spec that accepts a datacube as an argument and allows the user to select a single band from that cube.

All specs have certain members that can be set to impact the way the spec appears and behaves:

self.label: The label that will appear on the graphical user interface next to the Spec’s widget.

self.units: A string describing the Spec’s units that will appear on the GUI if not set to None.

self.help: A string that will appear if the user hovers the mouse over the Spec.

10.4.1. SpecBool

class SpecBool(Spec):
    """
    Creates button or a checkbox for entering a boolean.
    """
    def __init__(self, label, defaultValue=True):

By default, a SpecBool is represented on screen by a checkbox. To change the interface type, set the interfaceType member of the spec. Example:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecBool
from resonon.constants import INTERFACES

class MyPlugin(CubePlugin):
    """
    An example plugin
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
        self.my_bool = SpecBool(label='My Option', default=True)
        self.my_bool.units = 'my units'
        self.my_bool.help = 'my mouse hover help'
        self.my_bool.interfaceType = INTERFACES.CHECKBOX

    def action(self):
        if self.my_bool.value:
            pass
                #action if true
        else:
            pass
            #action if false

10.4.2. SpecFloat

class SpecFloat(SpecNumber):
    """Floa
    Creates a slider or spinner for setting a floating point number
    """
    def __init__(self, label, minval, maxval, stepsize=1, defaultValue=None):

The default interface type of a SpecFloat is a slider. A spin button can also be used. Example:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecFloat
from resonon.constants import INTERFACES

class MyPlugin(CubePlugin):
    """
    An example plugin
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
         self.my_float = SpecFloat(label='My Float', minval=0, maxval=100,
                                   stepsize=0.1, defaultValue=10.4)
         self.my_float.units = 'my units'
         self.my_float.help = 'my mouse hover help'

         # INTERFACES.SPIN is also valid.  This line is not needed if you
         # just want default behaviour.
         self.my_float.interfaceType = INTERFACES.SLIDER

    def action(self):
         my_product = self.my_float.value * 2.5
         # more plugin logic here....

10.4.3. SpecInt

class SpecInt(SpecNumber):
    """
    Creates a slider or spinner for setting an int
    """
    def __init__(self, label, minval, maxval, stepsize=1, defaultValue=None):

Using a SpecInt is just like using a SpecFloat. See SpecFloat for an example.

10.4.4. SpecChoice

class SpecChoice(Spec):
    """
    Creates a combo box depending allowing selection of a value from a list of choices
    """
    def __init__(self, label, values=None, defaultValue=None):

A Spec choice allows the user to choose an option from a combo box. It expects a list of strings as possible choices. It’s value member will be one of the strings in the list. Example:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecChoice

class MyPlugin(CubePlugin):
    """
    An example plugin
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
        self.my_options = SpecChoice(label='option',
                                     values = ['choice one',
                                               'choice two',
                                               'choice three'],
                                     defaultValue = 'choice one')
        self.my_options.help = 'my mouse hover help'

    def action(self):
        if self.my_options.value == 'choice one':
            pass # action for choice one
        elif self.my_options.value == 'choice two':
            pass # action for choice two
        elif self.my_options.value == 'choice three':
            pass # action for choice three

10.4.5. SpecCube

class SpecCube(Spec):
    """
    Displays a ComboBox for selecting one of the loaded datacubes
    """
    def __init__(self,
                 label,
                 datacube,
                 wb,
                 requireMatchedLineCount=False,
                 requireMatchedSampleCount=False,
                 requireMatchedBandCount=False,
                 requireBandCount=None,
                 defaultValue=None):

The SpecCube user interface is a combo box that will automatically be populated with the datacubes that are currently available on the Spectronon workbench (e.g. any cube that appears in the Spectronon resource tree). The parameters passed to SpecCube’s constructor allow you to provide criteria by which the list of applicable cubes will be filtered.

  • if requireMatchedLineCount=True the list will only show cubes whose line count is equal to that of the cube passed as datacube.
  • if requireMatchedSampleCount=True the list will only show cubes whose sample count is equal to that of the cube passed as datacube.
  • if requireMatchedBandCount=True the list will only show cubes whose band count is equal to that of the cube passed as datacube.
  • if requireBandCount=[an integer] the list will only show cubes whose band count is equal the specified integer. This value overrides that provided in requireMatchedBandCount.

Most commonly, you will pass your plugin’s datacube member to SpecCube’s constructor as the datacube argument, but any datacube object is a valid choice. You should always pass the plugin’s self.wb (workbench) member to SpecCub as the wb argument.

The value of a SpecCube is an index to a cube within the workbench. To get the cube object itself, you must call the wb.tree.getCube method.

Example:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecCube

class MyPlugin(CubePlugin):
    """
    An example plugin
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
        self.my_cube = SpecCube(label='Select a Datacube',
                                datacube=self.datacube,
                                wb=self.wb,
                                requireMatchedLineCount=False,
                                requireMatchedSampleCount=False,

                                # list only those cubes with the same number of
                                # bands as the cube we operate on
                                requireMatchedBandCount=True,
                                requireBandCount=None,
                                defaultValue=None)
        self.my_cube.help = 'my mouse hover help'

    def action(self):
        primary_cube = self.datacube
        secondary_cube = self.wb.tree.getCube(self.my_cube.value)
        # operate on the datacubes...

10.4.6. SpecSpectrum

class SpecSpectrum(Spec):
    """
    Displays a ComboBox for selecting one of the loaded spectra
    """
    def __init__(self,
                 label,
                 datacube,
                 wb,
                 requireMatchedWavelengths=False,
                 requireMatchedBandCount=False,
                 defaultValue=None):

SpecSpectrum is similar to SpecCube, but allows for choice of a spectrum object from the workbench.

  • if requireMatchedWavelengths=True, the list will only show spectra for which the wavelengths of the spectrum exactly match those of the cube passed as datacube.
  • if requireMatchedBandCount=True, the list will only show spectra for which the number of bands matches the number of bands of the cube passed as datacube.

Most commonly, you will pass your plugin’s datacube member to SpecCube’s constructor as the datacube argument, but any datacube object is a valid choice. You should always pass the plugin’s self.wb (workbench) member to SpecCub as the wb argument.

The value of a SpecSpectrum is an index to a spectrum object within the workbench. To get the spectrum object itself, you must call the wb.tree.getSpectrum method.

Example:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecSpectrum

class MyPlugin(CubePlugin):
    """
    An example plugin
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
        self.my_spectrum = SpecSpectrum(label='Select a Spectrum',
                                        datacube=self.datacube,
                                        wb=self.wb,
                                        requireMatchedWavelengths=True,
                                        requireMatchedBandCount=False,
                                        defaultValue=None)
        self.my_spectrum.help = 'my mouse hover help'

    def action(self):
        spectrum = self.wb.tree.getSpectrum(self.my_spectrum.value)
        # operate on the spectrum object...

10.4.7. SpecWavelength

class SpecWavelength(SpecFloat):
    def __init__(self, label, datacube, defaultValue=None):

A SpecWavelength accepts a datacube as an argument and allows the user to select a wavelength that is present in that cube. It’s user interface is a slider with range equal to the wavelength range in the passed in datacube.

Example:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecWavelength

class MyPlugin(CubePlugin):
    """
    An example plugin
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
        self.my_wavelength = SpecWavelength(label='Wavelength',
                                            datacube=self.datacube,
                                            defaultValue=None)
        self.my_wavelength.help = 'my mouse hover help'

    def action(self):
       # a float
       wavelength = self.my_wavelength.value

       # a band at a user selected wavelength
       band = self.datacube.getBandAtWavelength(self.my_wavelength.value)

10.4.8. SpecBandNumber

class SpecBandNumber(SpecInt):
    def __init__(self, label, datacube, defaultValue=0):

A SpecBandNumber allows the user to select one of the bands present in a datacube by index. Example:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecBandNumber

class MyPlugin(CubePlugin):
    """
    An example plugin
    """
    label = "My Plugin"
    userLevel = 1

    def setup(self):
        self.my_band_num = SpecBandNumber(label='Band',
                                          datacube=self.datacube,
                                          defaultValue=0)
        self.my_band_num.help = 'my mouse hover help'

    def action(self):
       band = self.datacube.getBand(self.my_band_num.value)

10.5. Using the Workbench

Plugins automatically receive a reference to the Spectronon workbench, self.wb. The workbench gives you a lot of freedom (including the freedom to break things!), so use this reference wisely. Most often, you will use the workbench to get references to other datacubes or spectra on the resource tree, add additional items to the resource tree, or prompt the user for a filename or other information. SourceIDs in the below methods refer to IDs returned from SpecCubes or SpecSpectra.

Some important methods you can call are:

def getCube(self, sourceID=None):
    """
    Returns the requested datacube.  If sourceID is None,
    returns the current cube.
    """

def getRendering(self, renderID=None):
    """
    Returns the requested rendering.  If sourceID is None,
    returns the current rendering.
    """

def getSpectrum(self, sourceID=None):
    """
    Returns the requested spectrum.  If sourceID is None,
    returns the current spectrum.
    """

def addCube(self, datacube, name=None, render=True):
    """
    Adds a datacube to the workbench.
    """

def addSpectrum(self, spectrumObject, name=None):
    """
    Adds a spectrum to the workbench.
    """

def postMessage(self, message, title=''):
    """
    Post a message in a dialog box
    """

def postScrolledMessage(self, message, title=''):
    """
    Post a message in a dialog box that can be scrolled and text
    can be selected.  Good for long messages
    """

def postQuestion(self, message, title="Proceed?"):
    """
    post message.  returns True if user selects 'OK' and False if 'Cancel'

    params:
        message: the message to show
        title: the dialog box title
    """

def requestOpenFilename(self,
                        wildcard='',
                        message="Enter Filename To Open ",
                        multiple=False):
    """
    Requests a filename(s) from the user.  Returns
    the filename as a string, or a list of strings
    if multiple=True.  Returns None if the user selects
    'Cancel'.
    """

def requestDirectory(self,
                     message="Please give a directory path",
                     suggest=""):
    """
    Requests a directory form the user.  Returns
    the selected directory path as a string.  Returns
    None if the user selects 'Cancel'.
    """

10.6. Using Datacubes

Plugins get a reference to their relevant datacube as self.datacube. It is possible to get references to other datacubes directly from the workbench. Datacubes contain a data array in numpy format as well as a collection of metadata.

Some important methods you can call are:

def getSpectrumArray(self, sample, line):
    """
    return a 1D array at location (sample, line)

    params
        sample: the sample number (int)
        line: the line number (int)

    returns
        1D array of length 'bands'
    """

def getBandNumForWavelength(self, wavelength):
    """
    return the band number nearest the given wavelength

    params
        wavelength: wavelength of light in nanometers(float)
    returns
     (2d array)
    """

def getBandCount(self):
    """return The number of bands in this cube
    (aka. the number of different wavelengths)
    """

def getSampleCount(self):
    """
    return the number of samples for the cube
    (aka. the image height)
    """

def getLineCount(self):
    """
    return the number of lines for the cube
    (aka. the image width)
    """

def getBandAtWavelength(self, wavelength):
    """
    return band at given wavelength

    params
        wavelength: the wavelength of the band you want (float)
    returns
        the band for the given wavelength frequency (2d array)

    return the band with the closest wavelength to the given value
    in the case of equidistance between two bands, the lower of the two
    bands is returned
    """

def getBandWithName(self, name):
    """
    return band with given name

    params
        name: the name of the band you want (float)
    returns
        the band with the given name  (2d array)

    you may pass the string form of a known wavelength
    """
    names = self.getBandNames()
    if name not in names:
        raise KeyError("No Band with name %s" % name)
    return self.getBand(names.index(name))

def getBand(self, bandnumber):
    """
    Return band at given band number

    params
        bandnumber: band number between 0 and bands - 1 (integer)
    returns
        band: the image at the band number (2d array) always (lines, samples)
    """

def getFrame(self, line, asBIP=False):
    """
    return frame at given line number

    params
        line: the line number (first = 0) of a frame in the cube (int)
    returns:
        frame: the spectrum of each pixel in a line of the image (2D array)
        always (samples, bands)
    """

def appendFrame(self, frame):
    """
    append frame to cube

    params
        frame: the content of a frame of the same (samples, bands)
            dimensions as the cube (2d array)

    appends a frame and increases number of lines in the cube by one
    """

def appendBandWithName(self, band, bandname=None):
    """
    append band to cube

    params
        band: the content of a band of the same (lines, samples)
            dimensions as the cube (2d array)

    appends a band and increases number of bands in the cube by one
    """

def appendBandWithWavelength(self, band, wave=None):
    """
    append band to cube

    params
        band: the content of a band of the same (lines, samples)
            dimensions as the cube (2d array)
    keywords
        writeheader: default (True).  if headerfile exists, save header
            file to disk to reflect new larger dimensions of cube.

    appends a band and increases number of bands in the cube by one
    """

def getSubCube(self, minsample=0, minline=0, maxsample= -1,
               maxline= -1, mode="memory", interleave=None):
    """getSubCube

        return a cube with the data in the given range, mode, and interleave

        with no arguments, this will return a complete copy.
    """

def getFramelessCopy(self, makeTypeFloat=False, mode=None, asBIP=False):
    """
    return an empty cube object with a header based on the header of this cube

    optional makeTypeFloat will return
    """

def getBandlessCopy(self, makeTypeFloat=False, mode=None):
    """
    return an empty cube object with a header based on the header of this cube

    optional makeTypeFloat will return
    """
    newcube = self._getEmptyCubeWithHeaderCopy(makeTypeFloat, mode)
    newcube.setMetaValue("bands", 0)
    return newcube

def setBand(self, bandnumber, band):
    """
    writes given band at given band

    params
        bandnumber: the number of the band to overwrite
    returns
        None
    """

def setFrame(self, line, frame):
    """
    write a given frame into location at given line

    params
        line: the line number to overwrite (int)
        frame: the content of the frame (2d array)
    returns
        None

    This is only for replacing frames that are already there.
    to add Frames to a cube use appendFrame
    """

def getArray(self, asBIP=False):
     """
     get the numpy data array for this datacube

     params:
        asBIP: force the data to be in BIP interleave (lines, samples, bands)
     """

Additionally, the function util.makeEmptyCube() is convenient for constructing new datacubes. It’s usage is as follows:

from resonon.core.data import util

newcube = util.makeEmptyCube(mode="memory",
                             typechar='f',
                             rotationString=datacube.getRotationString())

10.7. Example Plugins

The following plugin examples are based on built-in plugins from Spectronon.

Warning

Each plugin in Spectronon needs to have a distinct class name. If you change the name of the following plugins, don’t just remove ‘Example’ from the class name or your plugin will conflict with a built-in plugin.

10.7.1. Cube Plugins

Cube plugins are the most common. They operate on a datacube and return a datacube. Cube plugins add themselves to the New Cube submenu and context menu.

A very simple example based on a Spectronon built-in plugin is shown below:

from spectronon.workbench.plugin import CubePlugin
from resonon.utils.spec import SpecWavelength
from resonon.core.data import util

class BandRatioExample(CubePlugin):
    """Create a new single band cube that results from divide two bands of an
       existing cube"""  # The plugin's help text

    label = "Band Ratio Example"  #name of plugin as it appears in Spectronon

    # default rendering, others include SingleBand, ThreshToColor, TriBand, etc
    defaultRenderer = "SingleBand"

    # allows control for what  which users the plugin is available.
    # 1 is for normal users
    userLevel = 1

    def setup(self):
         # GUI controls that allows the user to select wavelengths, as they
         # exist in 'datacube'
         self.numband  = SpecWavelength(label="Numerator Wavelength",
                                        datacube = self.datacube)
         self.denomband  = SpecWavelength(label="Denominator Wavelength",
                                          datacube = self.datacube)

    def action(self):
         #make an empty cube for the results, preserving metadata from the old cube
         newcube = util.makeEmptyCube(mode="memory",
                                      typechar='f',
                                      rotationString=self.datacube.getRotationString())

         #get the data array from the datacube at the two specific wavelengths
         topband = self.datacube.getBandAtWavelength(self.numband.value).astype('f')
         bottomband =self.datacube.getBandAtWavelength(self.denomband.value).astype('f')

         #perform the math on the numpy arrays
         result = topband / bottomband

         #put the results in the empty cube
         newcube.appendBandWithName(result,bandname=self.label)

         #return the new cube and it will be added to the Spectronon file tree
         return newcube

10.7.2. Render Plugins (Image Plugins)

Render Plugins create a 2D representation of a datacube, known as an ‘Image’ in Spectronon. They operate on a cube and return a 2D image of 3 or fewer bands (‘RGB’, or ‘colors’). A simple example is shown below:

import numpy
from spectronon.workbench.plugin import RenderPlugin

class BandAverageExample(RenderPlugin):
    "returns a greyscale image of the average across all bands"
    label = "Band Average Example"
    userLevel = 1

    def action(self):

        datacube = self.datacube

        #create an numpy array of the same spatial dimensions of the datacube
        out = numpy.zeros((datacube.getLineCount(), datacube.getSampleCount()),
                          dtype=numpy.float32)
        for band in range(datacube.getBandCount()):
            #for each band, add it to the accumulator
            out += datacube.getBand(band)

        #normalize the results by the number of bands
        out = out/datacube.getBandCount()

        return out

10.7.3. Filter Plugins

Filter Plugins operate on a render to enhance or otherwise filter its appearance. It is currently not possible for a user to write a Filter Plugin.

10.7.4. Select Plugins

Select Plugins operate on a Region of Interest created in Spectronon with the Lasso, Wand, or Marquee tools. An example is shown below that finds the spectral and spatial average value of the pixels inside of the ROI.

from spectronon.workbench.plugin import SelectPlugin

class AverageROI_Example(SelectPlugin):
    '''
    Calculate a single mean across all bands in a selected region
    '''
    label = "ROI Example"
    userLevel = 1

    def action(self):
        # get the raw numpy data from the datacube
        # a BIP is indexed as (lines, samples, bands)
        cube_array = self.datacube.getArray(asBIP=True)

        # Use a numpy slice to get the portion of the array that has been selected
        # the pointlist is given in order(sample, line).
        subarray = cube_array[self.pointlist[:, 1], self.pointlist[:, 0], :]

        # calculate the average of the selected region
        ave = subarray.mean()

        # display the calculated result for the user
        self.wb.postMessage('ROI Average : %s' % ave, title="Average")