Advanced display#

This project consists in introducing some additional graphical interactivity into the graphical application display.py which you have developed all along the exercices, for what concerns background computation, background removal and cluster identification. You will explore advanced features of matplotlib to interact dynamically with your display application and its data. This project is split in three independent parts, and you are adviced to check the final section of graphics explanations.

Interactive Suppression of the Background and Cluster Building#

You’ll be implementing a Slider widget to interactively select the level of background to be applied to (remove from) the current image, with the ability to redo the cluster finding to show the background change impact.

This will demonstrate how an interactive widget can be installed within the application so as to specify a numerical value for one application’s variable.

Principle#

A PyPlot widget is an object with specific behaviour, and specific properties. The behaviour is specified by declaring a user defined python function, as being connected with a given user action such as:

  • clicking the mouse

  • dragging the mouse

  • changing a value

In our case, we want to study the impact of selecting a background level to the signal/background separation.

Implementation details#

The main program for this project must be in a file called pjd_display.py. It can be started by duplicating the content of your previous display.py.

Background control#

One possibility is to use a matplotlib.widgets.Slider widget.

Some useful hints to help with this project:

  • create a specific display area (using the matplotlib.axes.Axes functions) specifying the size of the area

  • Create a slider widget inside this area, with the following arguments:

    • the display area where to install it

    • a title

    • the minimum value of the variable controled by the slider button

    • the maximum value of the variable controled by the slider button

    • the initial value of the variable controled by the slider button (this is a named argument valinit=x)

  • define an action function to be called whenever the slider changes.

    • this function receives one argument: the current value

    • don’t forget to use the generic redraw function (matplotlib.canvas.draw_idle) within this action function

  • declare the action function to the widget, using the my_widget.on_changed(my_function)

Cluster rebuild#

  • re-compute the number of clusters whenever the slider changes

  • you can refresh the displayed image using the update function my_plotting_area.set_data(my_new_image)

Expected Result#

When the user move the slider, she can see the effect on background removal and clustering.

Cluster discovery#

You are now asked to extend your pjd_display.py application with some kind of animation: each time the mouse pointer enter a cluster, you are asked to draw a red rectangle around this cluster. When the mouse pointer goes outside the cluster area, we want the red rectangle to disappear.

To do so, you must define a function onmove(), or an object-function OnMove(), that you will declare as the handler for any mouse motion event. The event is an object with several attributes, two of them indicating the position of the mouse:

  • event.xdata: the pixel x coordinate of the mouse (in the display coordinate system)

  • event.ydata: the pixel y coordinate of the mouse (in the display coordinate system)

Once written, your event handler has to be associated with the appropriate event (here mouse motion) to become active. This is done with the following pyplot method:

fig.canvas.mpl_connect('motion_notify_event', onmove)

Cluster identification#

Finally, when the mouse is over a cluster, and the user click, we ask you to display over the image the name of the associated celestial object. It must stay here until the mouse leave the cluster area.

Processing Mouse Clicks#

To be able to select interactively an object in the image by clicking on it, it is necessary to add another event handler that will process the click event, using the same mechanism that we used in the previous section. After writing the handler function (on_click() in the example below), you need to register it for the click event:

fig.canvas.mpl_connect('button_press_event', on_click)

As for the previous section, the click handler receives one argument which is a dictionary describing the event. The button entry in this dictionary is a number indicating which button has been pressed (1 for the leftmost one).

To check if a user clicked on a cluster (or outside of any cluster), you will need to implement a new method in the cluster object. The click coordinates received as part of the event are expressed in display coordinates which are different from the image coordinates (pixel x and y in the image array). To convert from the display coordinates to the image coordinates, you need to do:

# Create a transformation matrix
display_to_image = axes.transData.inverted()
# Image coordinates returned as a list
image_x_y = display_to_image.transform((event.x, event.y))

Displaying a Text on an Image#

To display some text onto the displayed image, you have to use the pyplot axes.text() function (which returns an identifier of the text block written). axes.text() requires the following arguments:

  • x coordinate: x position in display pixel coordinates where the text must be displayed (x coordinate of the cluster peak for example)

  • y coordinate: same remark as above for the y position

  • The string you want to display

  • One or several optional arguments (note that the suggested values are not strings but an argument name followed by a value). In the present case, you should define:

    • the font size: a typical value is fontsize=14

    • the text color: a typical value is color='white'

    • Coordinate system to use: must be transform=axes.transData if the coordinates are expressed as image pixel coordinates or transform=None if the coordinates are those returned by the event in the context of an event handler. If the coordinates are expressed as image pixel coordinates, this argument can be omitted (this is the default).

If you want to add the text inside the mouse motion handler, you first need, in the event handler, to retrieve the axes (image) where the mouse is located. This is done with:

axes = event.inaxes

Note that the axes can be undefined (None) if the mouse is outside an image (an event is generated wherever the mouse is located, as soon as it moves).

Once the text has been added, it is necessary to redraw the image for the text to be displayed. Inside the event handler, this is done as follow:

event.canvas.draw()