RGB 3D volume rendering using Mayavi - python

I used Mayavi to render a 3D volume as the following code:
from mayavi import mlab
vol_mlab = mlab.pipeline.volume(mlab.pipeline.scalar_field(M))
mlab.colorbar()
mlab.show()
where M is a 3D matrix. My question is that if the voxel has three values which describe the (R,G,B) components, is it possible to render a volume with color information ?

Several suggestions:
1) If your scalars and colors correspond to a common colormap, you should be able to set the colormap directly of the volume
2) You can customize the ColorTransferFunction of your volume as illustrated here: http://docs.enthought.com/mayavi/mayavi/auto/mlab_pipeline_other_functions.html#volume (reproduced below). So for your example, as long as your colors are smoothly varying with the scalar values, pick a few waypoints of your colors and add them into the CTF
vol = mlab.pipeline.volume(src)
# Changing the ctf:
from tvtk.util.ctf import ColorTransferFunction
ctf = ColorTransferFunction()
ctf.add_rgb_point(value, r, g, b) # r, g, and b are float
# between 0 and 1
ctf.add_hsv_point(value, h, s, v)
# ...
vol._volume_property.set_color(ctf)
vol._ctf = ctf
vol.update_ctf = True
3) Do you really need the volume rendering? If not, it may be easier visualize as 3D points and set a custom colormap, e.g. https://stackoverflow.com/a/30266228/209246. So this would look like assigning a scalar to each voxel, and then placing the voxel's RGB color into the corresponding row of a custom colormap.

Related

Converting matplotlib's streamplot coordiantes to numpy coordiantes

I'm currently working with matplotlib in order to create a module of a specific vector field using matplotlib.pyplot.streamplot
after the creation and coloring of the lines in the streamplot, i'm trying to color the whitespace around the divergence points around the plot, im trying to achieve a gradient of color that is dependent on the distance of the white pixels around it.
The streamplot in question is built according to:
xs=np.linspace(-10,10,2000)
ys=np.linspace(-10,10,2000)
Therefore, if the divergence is located (for demonstration purposes) at (0,0) it will be located exactly in the middle of the plot.
Now, the only method i can think of for coloring according to distance from it, is kind of clunky since it requires me to:
add a matplotlib.patches.Rectangle on top of the divergence point in a specific color that is not in the image yet.
convert the plot, with the streamlines and rectangles (one rectangle for each divergence point in streamplot) to a np.array
find the new coordinates of the colors of the rectangles (they represent the location of the divergence point in the new np.array created from streamplot).
calculate the pixels like i want from the colored pixels.
This whole method feels way to clunky and over-complicating, and obviously slower than i could do. im sure theres a way to convert the coordinates from the matplotlib plot to the ones in np.array somehow or perhaps handle the coloring in matplotlib which will be even easier.
sadly i couldn't find a solution that answers this specific need yet.
thanks in advance for any help given!
EDIT
I'm adding an example (not my code, but a representation of what I wish to achieve).
I want to clarify that the solution of adding a patches.circle on top of a circle patch is not my go to, since i'm looking to keep my painting options more dynamic.
If you can define the color intensity you want as a 2-dimensional function, you can plot that function with plt.imshow() and then put the streamplot on top of it. You just need to transform the coordinates linearly to match the image coordinates.
Here is an example:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = [10, 10]
# plot 2d function
grid = np.arange(-1, 1, 0.001)
x, y = np.meshgrid(grid, grid)
z = 1 - (x ** 2 + y ** 2) ** 0.5
plt.imshow(z, cmap='Blues')
# streamplot example from matplotlib docs (modified)
w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = Y ** 2
V = X ** 2
# transform according to previous plot
n = len(grid) / 2
scale = n / w
X = (X + w) * scale
Y = (Y + w) * scale
U = (U + w) * scale
V = (V + w) * scale
plt.xticks(ticks=[0, n, 2*n],
labels=[-w, 0, w])
plt.yticks(ticks=[0, n, 2*n],
labels=[-w, 0, w])
plt.streamplot(X, Y, U, V);

How to get colour value from cmap with RGB tuple? [duplicate]

How do I invert a color mapped image?
I have a 2D image which plots data on a colormap. I'd like to read the image in and 'reverse' the color map, that is, look up a specific RGB value, and turn it into a float.
For example:
using this image: http://matplotlib.sourceforge.net/_images/mri_demo.png
I should be able to get a 440x360 matrix of floats, knowing the colormap was cm.jet
from pylab import imread
import matplotlib.cm as cm
a=imread('mri_demo.png')
b=colormap2float(a,cm.jet) #<-tricky part
There may be better ways to do this; I'm not sure.
If you read help(cm.jet) you will see the algorithm used to map values in the interval [0,1] to RGB 3-tuples. You could, with a little paper and pencil, work out formulas to invert the piecewise-linear functions which define the mapping.
However, there are a number of issues which make the paper and pencil solution somewhat unappealing:
It's a lot of laborious algebra, and
the solution is specific for cm.jet.
You'd have to do all this work again
if you change the color map. How to automate the solving of these algebraic equations is interesting, but not a problem I know how to solve.
In general, the color map may not be
invertible (more than one value may
be mapped to the same color). In the
case of cm.jet, values between 0.11
and 0.125 are all mapped to the RGB
3-tuple (0,0,1), for example. So if
your image contains a pure blue
pixel, there is really no way to
tell if it came from a value of 0.11
or a value of, say, 0.125.
The mapping from [0,1] to
3-tuples is a curve in 3-space. The
colors in your image may not lie
perfectly on this curve. There might
be round-off error, for example. So any practical solution has to be able to interpolate or somehow project points in 3-space onto the curve.
Due to the non-uniqueness issue, and the projection/interpolation issue, there can be many possible solutions to the problem you pose. Below is just one possibility.
Here is one way to resolve the uniqueness and projection/interpolation issues:
Create a gradient which acts as a "code book". The gradient is an array of RGBA 4-tuples in the cm.jet color map. The colors of the gradient correspond to values from 0 to 1. Use scipy's vector quantization function scipy.cluster.vq.vq to map all the colors in your image, mri_demo.png, onto the nearest color in gradient.
Since a color map may use the same color for many values, the gradient may contain duplicate colors. I leave it up to scipy.cluster.vq.vq to decide which (possibly) non-unique code book index to associate with a particular color.
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
import scipy.cluster.vq as scv
def colormap2arr(arr,cmap):
# http://stackoverflow.com/questions/3720840/how-to-reverse-color-map-image-to-scalar-values/3722674#3722674
gradient=cmap(np.linspace(0.0,1.0,100))
# Reshape arr to something like (240*240, 4), all the 4-tuples in a long list...
arr2=arr.reshape((arr.shape[0]*arr.shape[1],arr.shape[2]))
# Use vector quantization to shift the values in arr2 to the nearest point in
# the code book (gradient).
code,dist=scv.vq(arr2,gradient)
# code is an array of length arr2 (240*240), holding the code book index for
# each observation. (arr2 are the "observations".)
# Scale the values so they are from 0 to 1.
values=code.astype('float')/gradient.shape[0]
# Reshape values back to (240,240)
values=values.reshape(arr.shape[0],arr.shape[1])
values=values[::-1]
return values
arr=plt.imread('mri_demo.png')
values=colormap2arr(arr,cm.jet)
# Proof that it works:
plt.imshow(values,interpolation='bilinear', cmap=cm.jet,
origin='lower', extent=[-3,3,-3,3])
plt.show()
The image you see should be close to reproducing mri_demo.png:
(The original mri_demo.png had a white border. Since white is not a color in cm.jet, note that scipy.cluster.vq.vq maps white to to closest point in the gradient code book, which happens to be a pale green color.)
Here is a simpler approach, that works for many colormaps, e.g. viridis, though not for LinearSegmentedColormaps such as 'jet'.
The colormaps are stored as lists of [r,g,b] values. For lots of colormaps, this map has exactly 256 entries. A value between 0 and 1 is looked up using its nearest neighbor in the color list. So, you can't get the exact value back, only an approximation.
Some code to illustrate the concepts:
from matplotlib import pyplot as plt
def find_value_in_colormap(tup, cmap):
# for a cmap like viridis, the result of the colormap lookup is a tuple (r, g, b, a), with a always being 1
# but the colors array is stored as a list [r, g, b]
# for some colormaps, the situation is reversed: the lookup returns a list, while the colors array contains tuples
tup = list(tup)[:3]
colors = cmap.colors
if tup in colors:
ind = colors.index(tup)
elif tuple(tup) in colors:
ind = colors.index(tuple(tup))
else: # tup was not generated by this colormap
return None
return (ind + 0.5) / len(colors)
val = 0.3
tup = plt.cm.viridis(val)
print(find_value_in_colormap(tup, plt.cm.viridis))
This prints the approximate value:
0.298828125
being the value corresponding to the color triple.
To illustrate what happens, here is a visualization of the function looking up a color for a value, followed by getting the value corresponding to that color.
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(-0.1, 1.1, 10000)
y = [ find_value_in_colormap(plt.cm.viridis(x), plt.cm.viridis) for x in x]
fig, axes = plt.subplots(ncols=3, figsize=(12,4))
for ax in axes.ravel():
ax.plot(x, x, label='identity: y = x')
ax.plot(x, y, label='lookup, then reverse')
ax.legend(loc='best')
axes[0].set_title('overall view')
axes[1].set_title('zoom near x=0')
axes[1].set_xlim(-0.02, 0.02)
axes[1].set_ylim(-0.02, 0.02)
axes[2].set_title('zoom near x=1')
axes[2].set_xlim(0.98, 1.02)
axes[2].set_ylim(0.98, 1.02)
plt.show()
For a colormap with only a few colors, a plot can show the exact position where one color changes to the next. The plot is colored corresponding to the x-values.
Hy unutbu,
Thanks for your reply, I understand the process you explain, and reproduces it. It works very well, I use it to reverse IR camera shots in temperature grids, since a picture can be easily rework/reshape to fulfill my purpose using GIMP.
I'm able to create grids of scalar from camera shots that is really usefull in my tasks.
I use a palette file that I'm able to create using GIMP + Sample a Gradient Along a Path.
I pick the color bar of my original picture, convert it to palette then export as hex color sequence.
I read this palette file to create a colormap normalized by a temperature sample to be used as the code book.
I read the original image and use the vector quantization to reverse color into values.
I slightly improve the pythonic style of the code by using code book indices as index filter in the temperature sample array and apply some filters pass to smooth my results.
from numpy import linspace, savetxt
from matplotlib.colors import Normalize, LinearSegmentedColormap
from scipy.cluster.vq import vq
# sample the values to find from colorbar extremums
vmin = -20.
vmax = 120.
precision = 1.
resolution = 1 + vmax-vmin/precision
sample = linspace(vmin,vmax,resolution)
# create code_book from sample
cmap = LinearSegmentedColormap.from_list('Custom', hex_color_list)
norm = Normalize()
code_book = cmap(norm(sample))
# quantize colors
indices = vq(flat_image,code_book)[0]
# filter sample from quantization results **(improved)**
values = sample[indices]
savetxt(image_file_name[:-3]+'.csv',values ,delimiter=',',fmt='%-8.1f')
The results are finally exported in .csv
Most important thing is to create a well representative palette file to obtain a good precision. I start to obtain a good gradient (code book) using 12 colors and more.
This process is useful since sometimes camera shots cannot be translated to gray-scale easily and linearly.
Thanks to all contributors unutbu, Rob A, scipy community ;)
The LinearSegmentedColormap doesn't give me the same interpolation if I don't it manually during my test, so I prefer to use my own :
As an advantage, matplotlib is not more required since I integrate my code within an existing software.
def codeBook(color_list, N=256):
"""
return N colors interpolated from rgb color list
!!! workaround to matplotlib colormap to avoid dependency !!!
"""
# seperate r g b channel
rgb = np.array(color_list).T
# normalize data points sets
new_x = np.linspace(0., 1., N)
x = np.linspace(0., 1., len(color_list))
# interpolate each color channel
rgb = [np.interp(new_x, x, channel) for channel in rgb]
# round elements of the array to the nearest integer.
return np.rint(np.column_stack( rgb )).astype('int')

Color according to 3 variables - Maxwell triangle

I have a model consisting of three variables u,v,w which change with respect to time and with respect to space. I am especially interested in the ratio of the three variables.
But instead of showing three plots, each one for one variable, I would rather like to use only one plot.
My idea is to use the Maxwell triangle (color triangle, see http://homepages.abdn.ac.uk/npmuseum/article/Maxwell/MaxTri.html).
I can easily scale each variable that its maximum is at 1. But I don't know whether this idea is realizable. If it makes sense, it should already exist.
My question:
How do I convert the three variables to a single value which represents a color (e.g., if I have a filled contour plot, I want each grid cell to have "its ratio")?
Can I use the color triangle as a colorbar?
I try to give a short example to make it easier to understand:
import numpy as np
import matplotlib.pyplot as plt
# create three arrays for the state variables
# space is a 200x200 grid
size = 200
u = np.random.rand(size,size)
v = np.random.rand(size,size)
w = np.random.rand(size,size)
# now I could create 3 subplots and plot the spatial distribution
# for each variable
# but I want something like
col = np.zeros((200,200))
for i in range(200): # loop in x-direction
for j in range(200): # loop in y-direction
col[i,j] = colorTriangle(u[i,j],v[i,j],w[i,j])
plt.contourf(col)
The funtion colorTriangle does not exist. But I want something like this:
If each variable has the same value at (i,j), the color should be white (see Maxwell triangle). If we have only u, it should be green. If we have only v, it should be red. If we have only w, it should be blue.
If the combination is more complex, each variable should "pull" in one color direction and the color should be chosen according to the location in the Maxwell triangle.
Do you get the idea? It does not necessarily have to be a color triangle but I would have this kind of information in one contourf plot. And the color triangle would help interpreting the colors.
To answer the first question, there are many things that should be noted.
To start with, the only way that a single value can represent a color is using a colormap, which maps a scalar to a color. However, the Maxwell triangle cannot be reduced to a single value.
That does not mean that the maxwell triangle cannot be used as a kind of 3D colormap, mapping 3 values to a color. In fact it is quite natural to do, because the Maxwell uses 3 values a, b and c so that the color can be represented in RGB coordinates as (a,b,c). The only thing missing is the normalization.
In the link provided, the Maxwell triangle is defined such that a+b+c=1. However, matplotlib accepts RGB coordinates as 3 floats between 0 and 1, where white is obvsiously 1,1,1, instead of 1/3,1/3,1/3. Thus, each i,j will have three values that must be converted to 3 floats between 0 and 1 taking this into account.
Therefore, instead of normalizing dividing by the sum (to get a+b+c=1) we have to normalize dividing by the maximum value of each trio.
Eventually, the resulting image can be shown using imshow.
def colorTriangle(r,g,b):
image = np.stack([r,g,b],axis=2)
return image/image.max(axis=2)[:,:,None]
size = 200
X,Y = np.meshgrid(np.linspace(0,1,200),np.linspace(0,1,200))
u = np.full_like(X,.2)
v = Y
w = X**2
plt.imshow(colorTriangle(v,u,w),origin='lower',extent=(0,1,0,1))
# Note that v is first in order to be represented by red
The output image is the following:
Here it can be clearly seen that for small values of x and y, where v and w are zero, the color is green, as u is different than zero and much larger than v and w. For large x and small y, w dominates, and the color is indeed blue, and when v dominates (large y and small x), the color is red. It is also shown that for equal values of all three matrices, the resulting color is white.

Contouring non-uniform 2d data in python/matplotlib above terrain

I am having trouble contouring some data in matplotlib. I am trying to plot a vertical cross-section of temperature that I sliced from a 3d field of temperature.
My temperature array (T) is of size 50*300 where 300 is the number of horizontal levels which are evenly spaced. However, 50 is the number of vertical levels that are: a) non-uniformly spaced; and b) have a different starting level for each vertical column. As in there are always 50 vertical levels, but sometimes they span from 100 - 15000 m, and sometimes from 300 - 20000 m (due to terrain differences).
I also have a 2d array of height (Z; same shape as T), a 1d array of horizontal location (LAT), and a 1d array of terrain height (TER).
I am trying to get a similar plot to one like here in which you can see the terrain blacked out and the data is contoured around it.
My first attempt to plot this was to create a meshgrid of horizontal distance and height, and then contourf temperature with those arguments as well. However numpy.meshgrid requires 1d inputs, and my height is a 2d variable. Doing something like this only begins contouring upwards from the first column:
ax1 = plt.gca()
z1, x1 = np.meshgrid(LAT, Z[:,0])
plt.contourf(z1, x1, T)
ax1.fill_between(z1[0,:], 0, TER, facecolor='black')
Which produces this. If I use Z[:,-1] in the meshgrid, it contours underground for columns to the left, which obviously I don't want. What I really would like is to use some 2d array for Z in the meshgrid but I'm not sure how to go about that.
I've also looked into the griddata function but that requires 1D inputs as well. Anyone have any ideas on how to approach this? Any help is appreciated!
For what I understand your data is structured. Then you can directly use the contourf or contour option in matplotlib. The code you present have the right idea but you should use
x1, z1 = np.meshgrid(LAT, Z[:,0])
plt.contourf(x1, Z, T)
for the contours. I have an example below
import numpy as np
import matplotlib.pyplot as plt
L, H = np.pi*np.mgrid[-1:1:100j, -1:1:100j]
T = np.cos(L)*np.cos(2*H)
H = np.cos(L) + H
plt.contourf(L, H, T, cmap="hot")
plt.show()
Look that the grid is generated with the original bounding box, but the plot is made with the height that has been transformed and not the initial one. Also, you can use tricontour for nonstructured data (or in general), but then you will need to generate the triangulation (that in your case is straightforward).

How to reverse a color map image to scalar values?

How do I invert a color mapped image?
I have a 2D image which plots data on a colormap. I'd like to read the image in and 'reverse' the color map, that is, look up a specific RGB value, and turn it into a float.
For example:
using this image: http://matplotlib.sourceforge.net/_images/mri_demo.png
I should be able to get a 440x360 matrix of floats, knowing the colormap was cm.jet
from pylab import imread
import matplotlib.cm as cm
a=imread('mri_demo.png')
b=colormap2float(a,cm.jet) #<-tricky part
There may be better ways to do this; I'm not sure.
If you read help(cm.jet) you will see the algorithm used to map values in the interval [0,1] to RGB 3-tuples. You could, with a little paper and pencil, work out formulas to invert the piecewise-linear functions which define the mapping.
However, there are a number of issues which make the paper and pencil solution somewhat unappealing:
It's a lot of laborious algebra, and
the solution is specific for cm.jet.
You'd have to do all this work again
if you change the color map. How to automate the solving of these algebraic equations is interesting, but not a problem I know how to solve.
In general, the color map may not be
invertible (more than one value may
be mapped to the same color). In the
case of cm.jet, values between 0.11
and 0.125 are all mapped to the RGB
3-tuple (0,0,1), for example. So if
your image contains a pure blue
pixel, there is really no way to
tell if it came from a value of 0.11
or a value of, say, 0.125.
The mapping from [0,1] to
3-tuples is a curve in 3-space. The
colors in your image may not lie
perfectly on this curve. There might
be round-off error, for example. So any practical solution has to be able to interpolate or somehow project points in 3-space onto the curve.
Due to the non-uniqueness issue, and the projection/interpolation issue, there can be many possible solutions to the problem you pose. Below is just one possibility.
Here is one way to resolve the uniqueness and projection/interpolation issues:
Create a gradient which acts as a "code book". The gradient is an array of RGBA 4-tuples in the cm.jet color map. The colors of the gradient correspond to values from 0 to 1. Use scipy's vector quantization function scipy.cluster.vq.vq to map all the colors in your image, mri_demo.png, onto the nearest color in gradient.
Since a color map may use the same color for many values, the gradient may contain duplicate colors. I leave it up to scipy.cluster.vq.vq to decide which (possibly) non-unique code book index to associate with a particular color.
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
import scipy.cluster.vq as scv
def colormap2arr(arr,cmap):
# http://stackoverflow.com/questions/3720840/how-to-reverse-color-map-image-to-scalar-values/3722674#3722674
gradient=cmap(np.linspace(0.0,1.0,100))
# Reshape arr to something like (240*240, 4), all the 4-tuples in a long list...
arr2=arr.reshape((arr.shape[0]*arr.shape[1],arr.shape[2]))
# Use vector quantization to shift the values in arr2 to the nearest point in
# the code book (gradient).
code,dist=scv.vq(arr2,gradient)
# code is an array of length arr2 (240*240), holding the code book index for
# each observation. (arr2 are the "observations".)
# Scale the values so they are from 0 to 1.
values=code.astype('float')/gradient.shape[0]
# Reshape values back to (240,240)
values=values.reshape(arr.shape[0],arr.shape[1])
values=values[::-1]
return values
arr=plt.imread('mri_demo.png')
values=colormap2arr(arr,cm.jet)
# Proof that it works:
plt.imshow(values,interpolation='bilinear', cmap=cm.jet,
origin='lower', extent=[-3,3,-3,3])
plt.show()
The image you see should be close to reproducing mri_demo.png:
(The original mri_demo.png had a white border. Since white is not a color in cm.jet, note that scipy.cluster.vq.vq maps white to to closest point in the gradient code book, which happens to be a pale green color.)
Here is a simpler approach, that works for many colormaps, e.g. viridis, though not for LinearSegmentedColormaps such as 'jet'.
The colormaps are stored as lists of [r,g,b] values. For lots of colormaps, this map has exactly 256 entries. A value between 0 and 1 is looked up using its nearest neighbor in the color list. So, you can't get the exact value back, only an approximation.
Some code to illustrate the concepts:
from matplotlib import pyplot as plt
def find_value_in_colormap(tup, cmap):
# for a cmap like viridis, the result of the colormap lookup is a tuple (r, g, b, a), with a always being 1
# but the colors array is stored as a list [r, g, b]
# for some colormaps, the situation is reversed: the lookup returns a list, while the colors array contains tuples
tup = list(tup)[:3]
colors = cmap.colors
if tup in colors:
ind = colors.index(tup)
elif tuple(tup) in colors:
ind = colors.index(tuple(tup))
else: # tup was not generated by this colormap
return None
return (ind + 0.5) / len(colors)
val = 0.3
tup = plt.cm.viridis(val)
print(find_value_in_colormap(tup, plt.cm.viridis))
This prints the approximate value:
0.298828125
being the value corresponding to the color triple.
To illustrate what happens, here is a visualization of the function looking up a color for a value, followed by getting the value corresponding to that color.
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(-0.1, 1.1, 10000)
y = [ find_value_in_colormap(plt.cm.viridis(x), plt.cm.viridis) for x in x]
fig, axes = plt.subplots(ncols=3, figsize=(12,4))
for ax in axes.ravel():
ax.plot(x, x, label='identity: y = x')
ax.plot(x, y, label='lookup, then reverse')
ax.legend(loc='best')
axes[0].set_title('overall view')
axes[1].set_title('zoom near x=0')
axes[1].set_xlim(-0.02, 0.02)
axes[1].set_ylim(-0.02, 0.02)
axes[2].set_title('zoom near x=1')
axes[2].set_xlim(0.98, 1.02)
axes[2].set_ylim(0.98, 1.02)
plt.show()
For a colormap with only a few colors, a plot can show the exact position where one color changes to the next. The plot is colored corresponding to the x-values.
Hy unutbu,
Thanks for your reply, I understand the process you explain, and reproduces it. It works very well, I use it to reverse IR camera shots in temperature grids, since a picture can be easily rework/reshape to fulfill my purpose using GIMP.
I'm able to create grids of scalar from camera shots that is really usefull in my tasks.
I use a palette file that I'm able to create using GIMP + Sample a Gradient Along a Path.
I pick the color bar of my original picture, convert it to palette then export as hex color sequence.
I read this palette file to create a colormap normalized by a temperature sample to be used as the code book.
I read the original image and use the vector quantization to reverse color into values.
I slightly improve the pythonic style of the code by using code book indices as index filter in the temperature sample array and apply some filters pass to smooth my results.
from numpy import linspace, savetxt
from matplotlib.colors import Normalize, LinearSegmentedColormap
from scipy.cluster.vq import vq
# sample the values to find from colorbar extremums
vmin = -20.
vmax = 120.
precision = 1.
resolution = 1 + vmax-vmin/precision
sample = linspace(vmin,vmax,resolution)
# create code_book from sample
cmap = LinearSegmentedColormap.from_list('Custom', hex_color_list)
norm = Normalize()
code_book = cmap(norm(sample))
# quantize colors
indices = vq(flat_image,code_book)[0]
# filter sample from quantization results **(improved)**
values = sample[indices]
savetxt(image_file_name[:-3]+'.csv',values ,delimiter=',',fmt='%-8.1f')
The results are finally exported in .csv
Most important thing is to create a well representative palette file to obtain a good precision. I start to obtain a good gradient (code book) using 12 colors and more.
This process is useful since sometimes camera shots cannot be translated to gray-scale easily and linearly.
Thanks to all contributors unutbu, Rob A, scipy community ;)
The LinearSegmentedColormap doesn't give me the same interpolation if I don't it manually during my test, so I prefer to use my own :
As an advantage, matplotlib is not more required since I integrate my code within an existing software.
def codeBook(color_list, N=256):
"""
return N colors interpolated from rgb color list
!!! workaround to matplotlib colormap to avoid dependency !!!
"""
# seperate r g b channel
rgb = np.array(color_list).T
# normalize data points sets
new_x = np.linspace(0., 1., N)
x = np.linspace(0., 1., len(color_list))
# interpolate each color channel
rgb = [np.interp(new_x, x, channel) for channel in rgb]
# round elements of the array to the nearest integer.
return np.rint(np.column_stack( rgb )).astype('int')

Categories

Resources