Displaying multichannel images in python

Hi all,

I have a small program I am converting from code I wrote in matlab. I have extracted the image data from each channel of a CZI and now want to display the composite image composed of each channel. My goal is to have this ubiquitous for any number of channels in a CZI and that the user can use a slider to gain each individual channel and push the new array data to the composite image array.

I’ll post the code below, but I think the issue is the brightfield (grayscale) channel since it has values in R, G, and B. Currently, the composite image that is outputted to me is very close but has strange artifacts in the brighter region (image also linked below).

Code (I’ll just link the important parts since it’s repetitive and will take up space):

c = list(range(img.shape[3])) list of the channel hexes pulled from the metadata.
rgbidm = np.zeros([img.shape[5], img.shape[5]], dtype=np.uint8)
compc = np.empty([img.shape[3], img.shape[5], img.shape[5], 3], dtype=np.dtype(img[0][0][0][0][0][0][0], copy=True))
ct = np.empty([img.shape[3], img.shape[5], img.shape[5]], dtype=np.dtype(img[0][0][0][0][0][0][0], copy=True))

for i in range(img.shape[3]):

if contains(chanidx[i], '#FFFFFF'):
    c[i] = 'gs'
    gs = img[0][0][0][i][0][:][:]
    gsc = np.dstack((gs, gs, gs))
    compc[c.index('gs')] = gsc
    ct[c.index('gs')] = gs
    gsci = ImageTk.PhotoImage(image=Image.fromarray(gsc))
    
elif contains(chanidx[i], '#FF0000'):
    c[i] = 'red'
    red = img[0][0][0][i][0][:][:]
    redc = np.dstack((red, rgbidm, rgbidm))
    compc[c.index('red')] = redc
    ct[c.index('red')] = red
    redci = ImageTk.PhotoImage(image=Image.fromarray(redc))

This repeats for the rest of the colors hexes we commonly use. This also sorts the colors in the order of channel number.

compm = np.empty([img.shape[3], img.shape[5], img.shape[5], 3], dtype=np.dtype(img[0][0][0][0][0][0][0], copy=True))

if len(compc) > 1:
compm = compc[0]
for i in range(1, len(compc)):
compm = compm + compc[i]

Composite array. In matlab I was able to just add the matrices iteratively and the resulting image was as I would see it in Zen/ImageJ. However, the above code gives me an image that looks like this:

So far I have tried:
-displaying compm in both matplotlib and PIL
-generating composite from the single array and RGB triplet array images
-adding the grayscale channel to each channel then creating a composite

Many thanks in advance and sorry if I missed something very basic.

It looks to me like your image values are rolling over, i.e. you add say 128 + 128 and you get 0 because the image range is 0-255. Maybe it would help to do the math at uint16 and then threshold to uint8 to make the final image?

I tried this but unfortunately it didn’t work. However, I did a little test and you are indeed correct, the numbers are definitely “rolling over.” If I add 2 matrices with values uint8 150 to each other, the result is 44 (difference between the sum, 300, and the max value, 256).

Knowing that, I’m going to see if I can play with how the matrix addition works so that it caps at 255, or at least see how matlab knows how to do this addition properly.

Update: Matlab does not roll the value over if it goes over 255, it caps it there. I’m going to see if I can find a way to add my matrices and prevent this from happening.

Found a workaround.

if len(compc) > 1:
compm = compc[0].astype(‘int64’)
for i in range(1, len(compc)):
compm = compm.astype(‘int64’) + compc[i].astype(‘int64’)
compm = np.where(compm > 255, 255, compm)
compm = compm.astype(‘uint8’)

else:
compm = compc[0]
compm = compm.astype(‘uint8’)

The matrices are imported as uint8 from the czi, but I do the addition as an int64, change all the values >255 to 255, then convert back to uint8. Works perfectly.

You might also look at numpy.clip() it is very useful for this purpose.