Basic Image Models and Views

ImageModel

The ImageModel class is the most basic model, that is essentially a container of 2D array-like data. It can be initialized completely empty, or some 2D data can be passed. Following code example is take from a test case:

import datavis as dv

# ...
imgModel = dv.models.ImageModel()  # Empty ImageModel

# Data and dimensions should be None
self.assertIsNone(imgModel.getData())
self.assertIsNone(imgModel.getDim())

We could also initializate the model with some data or use the setData() after creation.

import numpy as np

# ...
data = np.ones((100, 200))
imgModel.setData(data)

# Data should not be None, neither dimensions
self.assertIsNotNone(imgModel.getData())
self.assertEqual(imgModel.getDim(), (200, 100))
self.assertEqual(imgModel.getMinMax(), (1, 1))

The emcore library provides the Image and ImageFile classes to read CryoEM images from different formats as binary data. In emvis, there are utility classes and function that use emcore under-the-hood to read the data. For example, one can easily create an ImageModel for an image on disk:

import emvis as emv

# ...
im = emv.utils.ImageManager()
imgModel = dv.models.ImageModel(data=im.getData(imagePath))

ImageView

The ImageView widget use the ImageModel class to retrieve the data that will be displayed. The current implementation of the ImageView class is based on the pyqtgraph.ImageView class. Throw this widget, our ImageView class supports many display functions such as: zooming, dragging, axis display and histogram visualization, among others. Many of these options can be set through the keywords parameters of the ImageView.__init__ method.

A simple example (image_example.py) that displays a CryoEM image using ImageView is shown below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import sys
import PyQt5.QtWidgets as qtw
import datavis as dv
import emvis as emv

# Read the input filename from first argument
imgPath = sys.argv[1]
app = qtw.QApplication(sys.argv)
win = qtw.QMainWindow()
# Create the image model and view
data = emv.utils.ImageManager().getData(imgPath)
imgModel = dv.models.ImageModel(data=data)
imgView = dv.views.ImageView(model=imgModel, parent=win,
                             histogram=True)  # show histogram of pixel values
win.setCentralWidget(imgView)
win.show()
sys.exit(app.exec_())

Then, we can execute the script passing an input image:

python image_example.py relion_tutorial/micrographs/006.mrc

The following image should be shown:

../_images/image_view.png

SlicesModel

The SlicesModel can be seen as an N-dimesional ImageModel. So the main difference respect to it, is that the getData() method has an index argument to choose which slice’s data should be returned. Additionally, it provides the getImageModel() method as a shortcut to create an ImageModel from a given slice.

The VolumeModel class implements the getSlicesModel method that returns a SlicesModel for a given axis. Moreover, the EmStackModel() class inherits from SlicesModel and implement how to read each slice from the stack file. The SlicesModel provides a nice abstraction about how data is retrieved (i.g from disk, memory, computed, etc) and it is used in an homogeneous way by the View widgets.

SlicesView

The SlicesView widget is very similar to the ImageView one, but adds an slider for the user to select the slice that is being displayed.

It is very easy to adapt the example script shown above for using SlicesView, just replacing lines 12 and 13 by the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

if len(data.shape) == 2:
	imgModel = dv.models.ImageModel(data=data)
	imgView = dv.views.ImageView(model=imgModel, parent=win,
                                 histogram=True)
elif len(data.shape) == 3:
	imgModel = dv.models.SlicesModel(data=data)
	imgView = dv.views.SlicesView(model=imgModel, parent=win,
                                  histogram=True)
else:

In this way, if the read data is 2D, an ImageView will be created, if 3D a SlicesView and raise an exception in any other case. So now the script can be calling using 2D or 3D data as input.

MultiSliceView

The MultiSliceView widget is a composition of 3 SlicesView, one for each axis x, y and z. This view is a good option to visualize 3D volume data, since it allows to quickly go through the slices of any of the 3 axis. Although, the MultiSliceView is implemented to receive as input 3 independent SlicesView, in practice the input is commonly generated from a VolumeModel instance, that can provides the required models for each axis.

The following code snippet shows how to create a MultiSlicesView (all the PyQt5 application boilerplate code is not shown here). It uses the ModelsFactory class that creates the VolumeModel from the provided path.

def createView(self, volumePath):
    volModel = emv.models.ModelsFactory.createVolumeModel(volumePath)
    msv = dv.views.MultiSliceView(
        None, {axis: {'model': volModel.getSlicesModel(axis),
                      'normalize': True}
               for axis in [dv.models.AXIS_X, dv.models.AXIS_Y,
                            dv.models.AXIS_Z]})
    return msv

The MultiSliceView should looks like the image below:

../_images/multislice.png