Displaying multi-channel images

22 views
Skip to first unread message

Jason Morgan

unread,
Jun 20, 2024, 6:02:07 AMJun 20
to pyqtgraph
I'm working with confocal image stacks with images in 4 channels across a number of z positions. My aim is to be able to scroll through the z positions in 1 or more channels of my choosing. I can do this using matplotlib and pyqt (using a slider for z navigation and check boxes, colormaps and linear blending for the different channels) but its slow, I'm looking for a more efficient way of doing it. The pyqtgraph documentation mentions 3 and 4 channel images so I was hoping this might be the answer, but I'm struggling to work out the required data formats for working with multi-channel images and google is getting me nowhere.

Is what I'm trying to do possible? My images are currently 2d numpy arrays of 0-1 floats for each channel for each z-position, basically [z position][channel][2d array of floats for pixel values] but I'm in no way tied to that format, I'd just like something that works fairly efficiently.

Martin Chase

unread,
Jun 20, 2024, 10:25:22 AMJun 20
to pyqt...@googlegroups.com
Hey Jason,

Have a look at `python -m pyqtgraph.examples.ImageView`.
I know 3-channel is really easy: each of red, green and blue would be assigned to a different channel. The expected format for that would be [z position][col][row][channel] (row and col can be switched via `pg.setConfigOptions(imageAxisOrder="row-major", and in fact performs better that way)`. You could do 4 channels by assigning them to CMYK (and then transforming it to RGB; PIL will do this, or the math is easy enough to do by hand/numpy)? I've never tried this, so I don't know how well it would work in practice, but it should be easy enough.

Oli N

unread,
Jun 20, 2024, 11:56:46 AMJun 20
to pyqt...@googlegroups.com
I have to say I found the ImageView example a bit too complex to be a useful demo of the imageView() for me when I was learning this. Steep learning curve.

Basically what everyone else has said so far- the key is the input image array.
The 2d  arrays should ideally be stacked into a 3+ dimensional array, which you can then slice to extract whichever single or multiple channels you want.

I used a pg.ImageView instance, assigning the volume with .setImage(my_img_nparray)

This shows a horizontal slider which you can use to navigate through your z axis slices.  This (I think) shows whenever you load a 3d volume. Set the pixel data type (monochrome/rgba) with the levelMode param in .setImage() or when creating the imageView() object.

There is a monochrome example of the slider in the ‘Data Slicing’ example.  I have rewritten it as RGB[A] and have cut down the raw example as it was a little more complex than needed.

I imagine that you'll need to merge the different channels as necessary into a single numpy array - I have tried to suggest one way* of doing it with numpy in the code.  I am always surprised how fast this is with numpy.
(*Not necessarily the best way, or even a good way- I am not a professional developer)

Also I could never remember which way round the x, y, and z planes went in the numpy array so I always just went by trial and error. Please don't go by the x, y and z in the example- that is from the original Data Slicer code and is not mine.

Hope this helps.


"""

Data Slicer demo cut down and rewritten to be RGB[A] rather than monochrome.

"""

import numpy as np

import pyqtgraph as pg

from pyqtgraph.Qt import QtWidgets


app = pg.mkQApp("RGB[A] Data Slicing Example")


## Create window with two ImageView widgets

win = QtWidgets.QMainWindow()

win.resize(800,800)


imv = pg.ImageView()

win.setCentralWidget(imv)

win.show()


x1 = np.linspace(-30, 10, 128)[:, np.newaxis, np.newaxis]

x2 = np.linspace(-20, 20, 128)[:, np.newaxis, np.newaxis]

y = np.linspace(-30, 10, 128)[np.newaxis, :, np.newaxis]

z = np.linspace(-20, 20, 128)[np.newaxis, np.newaxis, :]


d1 = np.sqrt(x1**2 + y**2 + z**2)

d2 = 2*np.sqrt(x1[::-1]**2 + y**2 + z**2)

d3 = 4*np.sqrt(x2**2 + y[:,::-1]**2 + z**2)


# Create R, G and B channels

data = np.empty([128, 128, 128, 3]) # alternatively you can stack the arrays with numpy to make the 4th axis

data[:, :, :, 0] = (np.sin(d1) / d1**2)

data[:, :, :, 1] = (np.sin(d2) / d2**2)

data[:, :, :, 2] = (np.sin(d3) / d3**2)


## Display the data

imv.setImage(data, levelMode='rgba')

imv.setLevels(-0.003, 0.003)


# Hide the stuff that gets in the way

imv.ui.histogram.hide()

imv.ui.roiBtn.hide()

imv.ui.menuBtn.hide()


if __name__ == '__main__':

pg.exec()












--
You received this message because you are subscribed to the Google Groups "pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyqtgraph+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pyqtgraph/CAD_p8v2gwr81GY9FPA%3DoBXcmgsyp8g0oyUbK%2BbXe7ZiCvu8PPA%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages