Combine MRI image with an heatmap plot - python

I have a jpg photo of the brain with a shape of (430,355) and a heatmap image (5x5 shape) relating to the different brain parts.
I want to combine these two in a way that shows which part of the brain is more active.
and
what I want is:

The easiest solution can be to add the second images with a weight, with OpenCv, after resizing the second one so that it matches the size of the original one:
heatmap = cv2.resize(heatmap, (brain.shape[1], brain.shape[0]))
combined = cv2.addWeighted(brain, 1, heatmap, 0.7, 1)
This is the output:
Modifying the parameters you can have the result that best fits your use case.
addWeighted is a function that calculates the weighted sum of two arrays.
Here you can find the documentation: https://docs.opencv.org/3.4/d2/de8/group__core__array.html#gafafb2513349db3bcff51f54ee5592a19
Is not exactly like the one in your question, but it is a fast and effective approach.

Related

Measuring shift between two images along one direction only

I have to measure shifts between two monochromatic images.
These images are actually spectra before calibration, which are very noisy and full of unwanted features, but they basically look like following
I know that between different images, they have shifts along x-direction, but not along y-direction. And I want to know the amount of the shift along x-direction between them.
Luckily I found a function in skimage, register_translation, which can be used for arbitrary subpixel precision. But the problem is, I want to know shift along x-direction only, and I want resulting y-direction shift to be 0, but the program finds the shift to x and y at the same time, presumably along the direction perpendicular to the features. (marked as blue arrow in the figure)
So, I am wondering :
is there any function or package in python that measures the shift between two images along one direction only, or even with any prior knowledge?
what is a correct way of finding shifts between two noisy images? Would finding maximum cross-correlation value in FFT space would do the job?
Some simple maths should do in this situation if register_translation gives you the xy shift, be it in vector or component form. You can calculate the movement in x that would be required if the y shift was non-existent, which is what you want. I am travelling so unfortunately can't give you the graph right now, would recommend drawing the triangles out.
The extra x shift required (x_extra) is defined by:
x_extra = y * tan[arctan(y_shift/x_shift)]
Which is simplified to:
x_extra = y_shift^2 / x_shift
Therefore, the total shift in x is:
x_shift_total = x_shift + x_extra
Where the x_shift is given to you by register_translation.
If you then move imageA by x_shift_total, it should be aligned with imageB, assuming the x_shift given by register_translation is correct.
#jni I would be keen to implement this as an option in register_translation!
I'm not positive it will work, but: one of the benefits of open source is that you can look at the implementation details of register_translation, then try to adapt it to your case. In your case, I would replace the fftn with fftn(..., axis=1), so that you only compute the fft along the columns axis. Then, multiply the two FFT signals together (this is equivalent to the convolution of each line, as suggested by #CypherX). Finally, you have to find a way to "coalesce" the shifts found along each line into a single measurement. One idea would be to take each shift (the maximum along that line) and plot a histogram. One would hope that you get a sharp peak around the true x shift.
If it works, it would be a pretty great contribution to scikit-image to add an "axis" keyword argument to register_translation. You can read the how to contribute guide and propose a change accordingly!
Another, much faster and simpler, approach would be to calculate the horizontal profile at the same location in both images. That would give you a 1D profile for each image horizontally. Simple peak finding will then give you the location of the lines, and the difference between the peak indexes will tell you the shift solely in the x-axis.
I use this approach routinely to do shift detection similar to your problem, and it is very very fast, very simple, and very robust.
# pick a row to use
row = 10
x_profile1 = np.mean(image1[row, :], axis=0)
x_profiel2 = np.mean(image2[row, :], axis=0)
# 'get_peaks' is a function to return indices of found peaks - several
# around
peaks1 = get_peaks(x_profile1)
peaks2 = get_peaks(x_profile2)
x_shift = peaks1[0] - peaks2[0]
Method-1
You could use convolution between the two images to find where you get a maximum. You could envision this as sliding the non-shifted images over the shifted image from left to right, and the convolution will produce maxima corresponding to the scenario when the identical sections of each image lies on top of one-another. Take a look at scipy.ndimage.convolution and scipy.signal.convolve and see which one suits your needs better.
Method-2
On the other hand, you could take a horizontal slice from each image and find the position of the peaks (assuming black strips are 1's and white regions are 0's).
Calculate the centroids of these peaks in each image. Find the difference between the positions of these centroids and that is the shift your are looking for.
For robustness, you could then apply this to various rows of the image-pairs and the average of all the such differences would be a more statistically viable result for a measure of horizontal shift.

How to visualize a matrix of categories as an RGB image?

I am using neural network to do semantic segmentation(human parsing), something like taking a photo of people as input and the neural network tells that every pixel is most likely to be head, leg, background or some other parts of human. The algorithm runs smoothly and giving a numpy.ndarray as output . The shape of the array is (1,23,600,400), where 600*400 is the resolution of the input image and 23 is the number of categories. The 3d matrix looks like a 23-layer stacked 2d matrices, where each layer using a matrix of float to tell the possibility that each pixel is of that category.
To visualize the matrix like the following figure, I used numpy.argmax to squash the 3d matrix into a 2d matrix that holds the index of the most possible category. But I don't have any idea how to proceed to get the visualization I want.
EDIT
Actually, I can do it in a trivial way. That is, use a for loop to traverse through every pixel and assign a color to it to get a image. However, this is not a vectorized coding, since numpy has built-in way to speed up matrix manipulation. And I need to save CPU cycles for real time segmentation.
It's fairly easy. All you need to have is a lookup table mapping the 23 labels into unique colors. The easiest way is to have a 23-by-3 numpy array with each row storing the RGB values for the corresponding label:
import numpy as np
import matplotlib.pyplot as plt
lut = np.random.rand(23, 3) # using random mapping - but you can do better
lb = np.argmax(prediction, axis=1) # converting probabilities to discrete labels
rgb = lut[lb[0, ...], :] # this is all it takes to do the mapping.
plt.imshow(rgb)
plt.show()
Alternatively, if you are only interested in the colormap for display purposes, you can use cmap argument of plt.imshow, but this will requires you to transform lut into a "colormap":
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list('new_map', lut, N=23)
plt.imshow(lb[0, ...], cmap=cmap)
plt.show()

How to normalize colors acquiring a single color?

I have to build an algorithm that takes an RBG image and returns the image turned into a wood-like mosaic. For this, I was given some wood tablets samples as seen in the image below:
I'd like to know how I can normalize the colors of each tablet, resulting in a single color, so I can build a map of reference colors to convert the input image colors to.
I've searched for how to achieve that, but I only found a Wikipedia article, but I couldn't understand much of it.
Thanks in advance for all help you might provide me.
PS: I'm considering using Python to develop this. So if you come up with something done using this language, I'd really appreciate it.
The way to get the average color is to simply take the average of the RGB values.
To get a more accurate average you should do this with linear color values. Usually RGB uses a gamma corrected value, but you can easily undo it then redo it once you have the average. Here's how you'd do it with Python's PIL using a gamma of 2.2:
def average_color(sample):
pix = sample.load()
totals = [0.0, 0.0, 0.0]
for y in range(sample.size[1]):
for x in range(sample.size[0]):
color = pix[x,y]
for c in range(3):
totals[c] += color[c] ** 2.2
count = sample.size[0] * sample.size[1]
color = tuple(int(round((totals[c] / count) ** (1/2.2))) for c in range(3))
return color
For the sample in the upper left of your examples, the result is (144, 82, 66). Here's a visual of all of them:
To make one color represent a tile, a simple option would be to find the mean color of a random sample of pixels in a specific tile. You can choose an appropriate sample size as a trade-off between speed and accuracy.
For your specific use case, I'd recommend further division of tiles, say into 3 columns (because of the top-to-bottom design of most wood panels). Find the mean color of each column and eliminate any which is beyond a certain measure of variance. This is to try to ensure that tiles such as the right most one in the 4th row don't get mapped to the darker shade.
An alternate approach would be to convert both your input image and these wood tiles in to and carry out your processing in grayscale. The opencv library has various simple functions for RGB2GRAYconversions.
One trivial way to normalize the colors is to simply force the mean and standard deviation of RGB values in all images to be the same.
Here is an example with the two panels at the top of the left column in the example image. I'm using MATLAB with DIPimage 3.0, because that is what I know, but this is trivial enough to implement in Python with NumPy, or any other desired language/library:
img = readim('https://i.stack.imgur.com/HK6VY.png')
tab1 = dipcrop; % Interactive cropping of a tile from the displayed image
tab2 = dipcrop;
m1 = mean(tab1);
s1 = std(tab1);
m2 = mean(tab2);
s2 = std(tab2);
tab2b = (tab2 - m2) ./ s2 .* s1 + m1;
What the code does to the image tab2 is, on a per-channel basis, to subtract the mean and divide by the standard deviation. Next, it multiplies each channel by the standard deviation of the corresponding channel of the template image, and adds the mean of that channel.

How to stack multiple images on top of each other using python or matlab?

How can I stack multiple images and save the new output image using python (or matlab)?
I need to set the alpha of each image and do i little translation, e.g.:
here's an example based on my comment:
mask=zeros(50,50,5);
for n=1:size(mask,3)
mask(randi(20):randi(20)+20,randi(20):randi(20)+20,n )=1;
mask(:,:,n)= bwperim( mask(:,:,n),8);
end
A=permute(mask,[3 2 1]);
% plottning
h=slice(A,[],1:5,[]);
set(h,'EdgeColor','none','FaceColor','interp');
alpha(0.3);
colormap(flipud(flag))
You could make such a stack of translated (shifted) images with Python, using the numpy and matplotlib module. Pillow (another Python module) by itself could probably do it as well, but I would have to look up how to ensure values of overlapping pixels get added, rather than overwritten.
So, here's a numpy + matplotlib solution, that starts off with a test image:
import numpy as np
import matplotlib.pyplot as plt
img1 = plt.imread('img.png')
For those following along, a very simply test image is shown at the end of this post, which will also serve to show the different options available for stacking (overwriting or additive which is weighted opacity with equal weights).
layers = 5 # How many images should be stacked.
x_offset, y_offset = 40, 20 # Number of pixels to offset each image.
new_shape = ((layers - 1)*y_offset + img1.shape[0],
(layers - 1)*x_offset + img1.shape[1],
4) # the last number, i.e. 4, refers to the 4 different channels, being RGB + alpha
stacked = np.zeros(new_shape, dtype=np.float)
for layer in range(layers):
stacked[layer*y_offset:layer*y_offset + img1.shape[0],
layer*x_offset:layer*x_offset + img1.shape[1],
...] += img1*1./layers
plt.imsave('stacked.png', stacked, vmin=0, vmax=1)
It's very simple really: you precalculate the size of the output image, initialize it to have full transparency and then you "drop" the base image in that file, each time offset by a certain offset vector. The interesting part comes when parts overlap. You then have some choices:
overwrite what was there before. In this case, change the += operator to simply =. Also, don't scale by the number of layers.
add in a weighted fashion. You should rescale all the intensity values in each channel by a certain weight (equal importance was taken in the example above) and then add those values. It is possible, depending a.o. on the weights, that you saturate pixels. You have the option then to clip the array (thereby resulting in loss of information) or simply rescale everything by the newly obtained maximum value. The example above uses clipping by specifying vmin and vmax in the call to imsave.
The test image shown here contains 4 transparent squares, but those are not easily distinguished from the 2 white ones in the top left row. They were added to illustrate the transparency addition and effect of rescaling (white becomes gray).
After running the above code, you end up with something like this (change your offsets though) ("add")
or like this ("overwrite")
There are a few more ways you can think of that reflect what you want to do when pixels overlap. The 2 situations here are probably the most common ones though. In any case, the approach laid out here should give you a good start.

Resizing image algorithm in python

So, I'm learning my self python by this tutorial and I'm stuck with exercise number 13 which says:
Write a function to uniformly shrink or enlarge an image. Your function should take an image along with a scaling factor. To shrink the image the scale factor should be between 0 and 1 to enlarge the image the scaling factor should be greater than 1.
This is not meant as a question about PIL, but to ask which algorithm to use so I can code it myself.
I've found some similar questions like this, but I dunno how to translate this into python.
Any help would be appreciated.
I've come to this:
import image
win = image.ImageWin()
img = image.Image("cy.png")
factor = 2
W = img.getWidth()
H = img.getHeight()
newW = int(W*factor)
newH = int(H*factor)
newImage = image.EmptyImage(newW, newH)
for col in range(newW):
for row in range(newH):
p = img.getPixel(col,row)
newImage.setPixel(col*factor,row*factor,p)
newImage.draw(win)
win.exitonclick()
I should do this in a function, but this doesn't matter right now. Arguments for function would be (image, factor). You can try it on OP tutorial in ActiveCode. It makes a stretched image with empty columns :.
Your code as shown is simple and effective for what's known as a Nearest Neighbor resize, except for one little bug:
p = img.getPixel(col/factor,row/factor)
newImage.setPixel(col,row,p)
Edit: since you're sending a floating point coordinate into getPixel you're not limited to Nearest Neighbor - you can implement any interpolation algorithm you want inside. The simplest thing to do is simply truncate the coordinates to int which will cause pixels to be replicated when factor is greater than 1, or skipped when factor is less than 1.
Mark has the correct approach. To get a smoother result, you replace:
p = img.getPixel(col/factor,row/factor)
with a function that takes floating point coordinates and returns a pixel interpolated from several neighboring points in the source image. For linear interpolation it takes the four nearest neigbors; for higher-order interpolation it takes a larger number of surrounding pixels.
For example, if col/factor = 3.75 and row/factor = 1.9, a linear interpolation would take the source pixels at (3,1), (3,2), (4,1), and (4,2) and give a result between those 4 rgb values, weighted most heavily to the pixel at (4,2).
You can do that using the Python Imaging Library.
Image.resize() should do what you want.
See http://effbot.org/imagingbook/image.htm
EDIT
Since you want to program this yourself without using a module, I have added an extra solution.
You will have to use the following algorithm.
load your image
extract it's size
calculate the desired size (height * factor, width * factor)
create a new EmptyImage with the desired size
Using a nested loop through the pixels (row by column) in your image.
Then (for shrinking) you remove some pixels every once in while, or for (enlarging) you duplicate some pixels in your image.
If you want you want to get fancy, you could smooth the added, or removed pixels, by averaging the rgb values with their neighbours.

Categories