Custom coloration for matrix in matplotlib - python

I have three matrices that I'd like to plot, but the only solution I've come up with is just plotting one after the other, and that leaves me with the last matrix plotted.
ax.imshow(mat1, cmap='Blues', interpolation='nearest')
ax.imshow(mat2, cmap='binary', interpolation='nearest')
ax.imshow(mat3, cmap='autumn', interpolation='nearest') # actual plot
What I want is to display all 0s in the three matrices in white, and higher values in different tones depending on the matrix, e.g.: blue, black and red. Also, in that example, red cells would have precedence over black and black over blue. The solution I'm imagining to this is a function that, given a triple (blue, black, red) with the different values for each component, returns the color the cell should be colored, and feed it to a ColorMap, but I really don't know how to do so or if it's even possible.
Every kind of help and even different solutions (that's the most likely to happen) are welcome and appreciated. Thanks in advance.

You want a fourth image, with the RGB value at each point a function of the single value of the first three matrixes at the corresponding point? If so, can you produce the algebra to get from three values to the RGB fourth?
Your question suggests confusion about how plotting turns data into colors. A colormap takes single-valued data, normalizes it, and maps it into some named array of colors. 0 values might be mapped to any color, depending on the colormap and the rest of the data.
A bitmap defines (red, green, blue) values at each pixel. Proper bitmaps have header sections, but the data is an (m,n,3) array. imshow plots just that array; it expects the RGB values to be in the [0,1] range.
If you have three data matrices, you have to choose how to map the values to RGB values. Here's an example with three kinds of mapping to RGB. The first two rows are dummy data with a range of values, shown either with a colormap or as the simplest RGB representation. The last row shows ways of combining all three dummy matrices into one image using the whole colorspace.
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
#dummy data
x = 8
y = 15
mat = []
mat.append(np.arange(x * y).reshape((x, y)) / float(x * y) )
mat.append(np.arange(x * y).reshape((y, x)).T / float(x* y))
mat.append(np.arange(y) * np.arange(x)[:,np.newaxis] / float(99))
# NOTE: this data is approximately in the RGB range. If yours isn't, normalize,
# here or in your makeRGB function.
# (The colormap normalizes single-valued data).
fig, axs = plt.subplots(figsize=(7,4), nrows=3, ncols=3,
gridspec_kw={'hspace':0.6})
axs[0,0].imshow(mat[0], cmap='Reds', interpolation='nearest')
axs[0,1].imshow(mat[1], cmap='Greens', interpolation='nearest')
axs[0,2].imshow(mat[2], cmap='Blues', interpolation='nearest')
axs[0,0].set_xlabel('Reds Colormap')
axs[0,1].set_xlabel('Greens Colormap')
axs[0,2].set_xlabel('Blues Colormap')
def asOneHue(mat, hue):
"""
Use a single-valued matrix to represent one hue in a RGB file.'
"""
RGBout = np.zeros((len(mat),len(mat[0]),3))
RGBout[:,:,i] = mat
return RGBout
for i in (0,1,2):
axs[1,i].imshow(asOneHue(mat[i],i))
axs[1,0].set_xlabel('Reds bitmap')
axs[1,1].set_xlabel('Greens bitmap')
axs[1,2].set_xlabel('Blues bitmap')
# different ways to combine 3 values
def makeRGB0(mats):
RGBout = np.zeros((len(mats[0]),len(mats[0][0]),3))
#RGBout = np.ones((len(mats[0]),len(mats[0][0]),3))
for i in (0,1,2):
RGBout[:,:,i] = mats[i]
return RGBout
axs[2,0].imshow(makeRGB0(mat))
axs[2,0].set_xlabel('Color layers')
def makeRGB1(mats):
RGBout = np.zeros((len(mats[0]),len(mats[0][0]),3))
i,j,k = RGBout.shape
for x in range(i):
for y in range(j):
RGBout[x,y] = (mats[0][x][y] / 2,
mats[1][x][y],
1 - mats[2][x][y])
return RGBout
axs[2,1].imshow(makeRGB1(mat))
axs[2,1].set_xlabel('Algebraic')
def makeRGB2(mats):
RGBout = np.zeros((len(mats[0]),len(mats[0][0]),3))
i,j,k = RGBout.shape
for x in range(i):
for y in range(j):
if mats[0][x][y] > .8:
RGBout[x,y] = (mats[0][x][y],
0,
0)
elif mats[1][x][y] > .8:
RGBout[x,y] = (0,
mats[1][x][y],
0)
else:
RGBout[x,y] = (mats[0][x][y],
mats[1][x][y],
mats[2][x][y])
return RGBout
axs[2,2].imshow(makeRGB2(mat))
axs[2,2].set_xlabel('If-else')
plt.show()

Related

how to generate per-pixel histogram from many images in numpy?

I have tens of thousands of images. I want to generate a histogram for each pixel. I have come up with the following code using NumPy to do this that works:
import numpy as np
import matplotlib.pyplot as plt
nimages = 1000
im_shape = (64,64)
nbins = 100
#predefine the histogram bins
hist_bins = np.linspace(0,1,nbins)
#create an array to store histograms for each pixel
perpix_hist = np.zeros((64,64,nbins))
for ni in range(nimages):
#create a simple image with normally distributed pixel values
im = np.random.normal(loc=0.5,scale=0.05,size=im_shape)
#sort each pixel into the predefined histogram
bins_for_this_image = np.searchsorted(hist_bins, im.ravel())
bins_for_this_image = bins_for_this_image.reshape(im_shape)
#this next part adds one to each of those bins
#but this is slow as it loops through each pixel
#how to vectorize?
for i in range(im_shape[0]):
for j in range(im_shape[1]):
perpix_hist[i,j,bins_for_this_image[i,j]] += 1
#plot histogram for a single pixel
plt.plot(hist_bins,perpix_hist[0,0])
plt.xlabel('pixel values')
plt.ylabel('counts')
plt.title('histogram for a single pixel')
plt.show()
I would like to know if anyone can help me vectorize the for loops? I can't think of how to index into the perpix_hist array properly. I have tens/hundreds of thousands of images and each image is ~1500x1500 pixels, and this is too slow.
You can vectorize it using np.meshgrid and providing indices for first, second and third dimension (the last dimension you already have).
y_grid, x_grid = np.meshgrid(np.arange(64), np.arange(64))
for i in range(nimages):
#create a simple image with normally distributed pixel values
im = np.random.normal(loc=0.5,scale=0.05,size=im_shape)
#sort each pixel into the predefined histogram
bins_for_this_image = np.searchsorted(hist_bins, im.ravel())
bins_for_this_image = bins_for_this_image.reshape(im_shape)
perpix_hist[x_grid, y_grid, bins_for_this_image] += 1

Matplotlib: Using strings as values instead of numbers

My data is a 100 x 100 array of strings which are all hex codes:
[['#10060e' '#11070f' '#060409' ... '#08030a' '#09030d' '#12050f']
['#110600' '#09010e' '#0d0210' ... '#09030f' '#08060b' '#160a0a']
['#0a070e' '#13060f' '#0c040f' ... '#0c0610' '#0e040c' '#0a020f']
...
['#0c020d' '#09040b' '#10070c' ... '#0a090f' '#160613' '#08000f']
['#0a020f' '#09040a' '#150812' ... '#11040d' '#07040b' '#0b060d']
['#0d0715' '#0e020c' '#140710' ... '#0a0112' '#12090e' '#0c020d']]
Matplotlib: throws this error: TypeError: cannot perform reduce with flexible type
I think the issue it is having is it cannot give a colour to these as they are strings, not numbers. I can only find examples where all the data is numerical and has a colour map applied to it, nothing where every bit of data's colour is specified.
I would like to tell Matplotlib what colour I'd like all of these to be using, surprise surprise, the hex codes. How can I go about doing that?
Full(er) code sample:
z = np.asanyarray(pixel_arr)
x = np.arange(0, width, 1) # len = 100
y = np.arange(0, height, 1) # len = 100
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z)
plt.show()
I would like to tell Matplotlib what colour I'd like all of these to be using
It sounds like you have a bunch of pixel values you want to plot, i.e., an image. So you can treat it like one.
import matplotlib.pyplot as plt
import numpy as np
data = np.array([['#10060e', '#11070f', '#060409', '#08030a', '#09030d', '#12050f'],
['#110600', '#09010e', '#0d0210', '#09030f', '#08060b', '#160a0a'],
['#0a070e', '#13060f', '#0c040f', '#0c0610', '#0e040c', '#0a020f'],
['#0c020d', '#09040b', '#10070c', '#0a090f', '#160613', '#08000f'],
['#0a020f', '#09040a', '#150812', '#11040d', '#07040b', '#0b060d'],
['#0d0715', '#0e020c', '#140710', '#0a0112', '#12090e', '#0c020d']])
img = [[tuple(bytes.fromhex(pixel[1:])) for pixel in row] for row in data]
img = np.array(img, dtype=np.uint8)
plt.imshow(img)
plt.show()
The output may be dark at first glance, but that's because I used the data you showed us, which all happen to be very dark pixels.

Color gradient on one contour line

I'm very very new to Python, i usually do my animations with AfterEffects, but it requires a lot of computation time for quite simple things.
• So I would like to create this kind of animation (or at least image) :
AfterEffects graph (forget the shadows, i don't really need it at this point)
Those are circles merging together as they collide, one of them being highlighted (the orange one).
• For now i only managed to do the "merging thing" computing a "distance map" and ploting a contour line :
Python + Matplotlib graph with the following code :
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
part_size = 0.0002
nb_part = 200
mesh_res = 500 # resolution of grid
x = np.linspace(0, 1.9, mesh_res)
y = np.linspace(0, 1, mesh_res)
Xgrid, Ygrid = np.meshgrid(x, y)
centers = np.random.uniform(0,1,(nb_part,2)) # array filled with disks centers positions
sizes = part_size*np.ones(nb_part) # array filled whith disks sizes
#sizes = np.random.uniform(0,part_size,nb_part)
dist_map = np.zeros((mesh_res,mesh_res),float) # array to plot the contour of
for i in range(nb_part):
dist_map += sizes[i] / ((Xgrid - centers[i][0]) ** 2 + (Ygrid - centers[i][1]) ** 2) # function with (almost) value of 1 when on a cricle, so we want the contour of this array
fig, ax = plt.subplots()
contour_opts = {'levels': np.linspace(0.9, 1., 1), 'color':'red', 'linewidths': 4} # to plot only the one-ish values of contour
ax.contour(x, y, dist_map, **contour_opts)
def update(frame_number):
ax.collections = [] # reset the graph
centers[:] += 0.01*np.sin(2*np.pi*frame_number/100+np.stack((np.arange(nb_part),np.arange(nb_part)),axis=-1)) # just to move circles "randomly"
dist_map = np.zeros((mesh_res, mesh_res), float) # updating array of distances
for i in range(nb_part):
dist_map += sizes[i] / ((Xgrid - centers[i][0]) ** 2 + (Ygrid - centers[i][1]) ** 2)
ax.contour(x, y, dist_map, **contour_opts) # calculate the new contour
ani = FuncAnimation(fig, update, interval=20)
plt.show()
The result is not that bad but :
i can't figure how to highlight just one circle keeping the merging effect (ideally, the colors should merge as well, and i would like to keep the image transparency when exported)
it still requires some time to compute each frame (it is way faster than AfterEffects though), so i guess i'm still very far from using optimally python, numpy, and matplotlib. Maybe there are even libraries able to do that kind of things ? So if there is a better strategy to implement it, i'll take it.

matplotlib create figure without frames, axes, plot a 2D array with a colormap, save plot to numpy array of same size as input

I wrote a function with this purpose:
to create a matplotlib figure, but not display it
with no frames, axes, etc.
to plot in the figure an input 2D array using a user-passed colormap
to save the colormapped 2D array from the canvas to a numpy array
that the output array should be the same size as the input
There are lots of questions with answers for tasks similar to either points 1-2 or point 4; for me it was also important to automate point 5. So I started by combining parts from both #joe-kington 's answer and from #matehat 's answer and comments to it, and with small modifications I got to this:
def mk_cmapped_data(data, mpl_cmap_name):
# This is to define figure & ouptput dimensions from input
r, c = data.shape
dpi = 72
w = round(c/dpi, 2)
h = round(r/dpi, 2)
# This part modified from #matehat's SO answer:
# https://stackoverflow.com/a/8218887/1034648
fig = plt.figure(frameon=False)
fig.set_size_inches((w, h))
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
plt.set_cmap(mpl_cmap_name)
ax.imshow(data, aspect='auto', cmap = mpl_cmap_name, interpolation = 'none')
fig.canvas.draw()
# This part is to save the canvas to numpy array
# Adapted rom Joe Kington's SO answer:
# https://stackoverflow.com/a/7821917/1034648
mat = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
mat = mat.reshape(fig.canvas.get_width_height()[::-1] + (3,))
mat = normalise(mat) # this is just using a helper function to normalize output range
plt.close(fig=None)
return mat
The function does what it is supposed to do and is fast enough.
My question is whether I can make it more efficient and or more pythonic in any way.
If you're wanting RGB output that exactly matches the shape of the input array, it's probably easiest to not create a figure, and instead use the colormap objects directly. For example:
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# Random data with a non 0-1 range.
data = 500 * np.random.random((100, 100)) - 200
# We'll use `LinearSegementedColormap` and `Normalize` instances directly
cmap = plt.get_cmap('viridis')
norm = plt.Normalize(data.min(), data.max())
# The norm instance scales data to a 0-1 range, cmap makes it RGB
rgb = cmap(norm(data))
# MPL uses a 0-1 float RGB representation, so we'll scale to 0-255
rgb = (255 * rgb).astype(np.uint8)
Image.fromarray(rgb).save('test.png')
Note that you likely don't want the additional step of saving it as a PNG, but I wanted to be able to show the result visually. This is exactly a 100x100 image where each pixel corresponds to the original input data.
This is what matplotlib does behind-the-scenes when you call imshow. The data is first run through a Normalize instance to scale it from its original range to 0-1. Then any Colormap instance can be called directly with the 0-1 results to turn the scalar data into RGB data.
One letter variables are hard to understand.
Change:
r -> n_rows
c -> n_cols
w -> width
h -> height

Visualize multiple 2d Array with same color scheme

I am currently trying to visualize three 2D arrays with the same color. The arrays are 13x13 and contain integers. In an external file I have a color code in hex for each integer.
When I now try to visualize the arrays, two out of three arrays look good. All numbers match the color codes and display the arrays correctly. But in the last picture a part of the data is not assigned correctly.
.
color_names = [c.strip() for c in open(colors).readlines()]
color_dict = {v: k for v, k in enumerate(color_names)}
unique_classes = (np.unique(np.asarray(feature_map))).tolist()
number_classes = len(unique_classes)
color_code = [color_dict.get(cla) for cla in unique_classes]
cmap = plt.colors.ListedColormap(color_code)
norm = plt.colors.BoundaryNorm(unique_classes, cmap.N)
img = pyplot.imshow(feature_map[0],interpolation='nearest',
cmap = cmap,norm=norm)
pyplot.colorbar(img,cmap=cmap,
norm=norm,boundaries=unique_classes)
pyplot.show()
img1 = pyplot.imshow(feature_map[1],interpolation='nearest',
cmap = cmap,norm=norm)
pyplot.show()
img2 = pyplot.imshow(feature_map[2],interpolation='nearest',
cmap = cmap,norm=norm)
pyplot.colorbar(img2,cmap=cmap,
norm=norm,boundaries=unique_classes)
pyplot.show()
Exactly the same data as on the picture:
feature_map = [[[25,25,25,25,56,56,2,2,2,2,2,2,25],[25,25,25,25,25,25,59,7,72,72,72,72,2],[25,25,25,25,25,25,59,72,72,72,72,72,2],[25,25,25,24,24,24,62,0,0,0,0,25,25],[25,25,24,24,24,24,24,24,24,24,25,25,25],[26,26,24,24,24,24,24,26,26,26,6,6,6],[26,26,26,24,24,26,26,26,26,26,26,6,6],[26,26,26,0,0,26,26,26,26,26,26,6,6],[28,28,28,28,28,28,28,26,26,26,26,6,6],[28,28,28,28,28,28,28,26,26,26,13,13,6],[28,28,28,28,28,28,28,26,13,13,13,13,13],[28,28,28,28,28,28,28,13,13,13,13,13,13],[28,28,28,28,28,28,28,13,13,13,13,13,13]],[[25,25,25,25,59,56,59,2,0,0,0,0,0],[25,25,25,25,25,59,59,7,72,72,72,72,72],[25,25,25,25,25,25,59,72,72,72,72,72,72],[25,25,25,0,0,25,25,6,0,0,0,72,0],[25,25,0,0,0,0,6,0,0,0,0,25,6],[26,26,26,0,0,0,24,26,0,0,6,6,6],[26,26,26,0,0,0,26,26,26,26,26,6,6],[0,26,0,0,0,0,26,26,0,26,26,6,6],[0,28,28,28,28,28,28,26,0,26,26,6,6],[28,28,28,28,28,28,28,26,0,26,0,0,0],[28,28,28,28,28,28,28,26,13,13,13,13,0],[56,56,28,28,28,28,28,13,13,13,13,13,13]],[[0,28,28,28,28,28,28,13,13,13,13,13,0],[25,25,25,25,59,59,59,4,0,0,0,0,0],[25,25,25,25,59,59,59,7,7,7,72,72,6],[25,25,25,25,25,25,59,7,7,73,73,25,0],[25,25,25,0,0,25,6,7,0,6,6,6,0],[25,0,0,0,6,6,6,6,0,0,6,6,6],[0,0,0,0,0,6,6,6,0,0,6,6,6],[0,0,0,0,0,0,6,6,0,0,6,6,6],[0,0,0,0,0,0,6,0,0,0,6,6,6],[0,0,28,0,28,28,13,0,0,0,6,6,6],[28,28,28,28,28,28,13,13,13,0,13,6,6],[28,28,28,28,28,28,28,13,13,13,13,13,13],[56,28,28,28,28,28,28,13,13,13,13,13,13],[28,28,28,28,28,28,28,13,13,13,13,13,13]]]
The color code file is simply a file where each line contains a single hex code such as: #deb887
I have been working on this problem for several hours and can't reproduce the problem at the moment
I have tried to reproduce your results and something got my attention.
If you look closely to the feature_map[2] values you might see that the pixel you claim miss classified has actually a different value than the pixels around it. So it actually has the correct color for its value. So I think it is not because of a misclassification it is beacause of your data. That would be my answer IF what you mean by "part of the data" is the pixel at position (0,11) otherwise i have gotten it all wrong and sorry about this answer.
NOTE: About colors, I just picked some random colors. Don't worry if they don't match.

Categories

Resources