I have an RGB bitmap. Actually it is the contour plot of some scalar field plotted with the 'jet' colormap. I need to reverse the bitmap and obtain the source data. Is there a ready-to-use ond open-source tool for that? Python module is OK too.
Well, as nobody did it, this is a lasy algorithm that do the job:
import numpy as np
import scipy.misc
import matplotlib.pyplot as plt
## The digitized field will be scaled to range (0,1)
scale = np.linspace(0.0, 1.0, 300)
## Palette is a curve in RGB space
jet = plt.cm.get_cmap('jet')
palette = 255.0 * np.array([ jet(s)[:3] for s in scale ])
## Read the field as RGB image
field_0 = scipy.misc.imread('field.png')[:,:,:3]
ny, nx, _ = field_0.shape
## Use Euclidian norm to find a closest point in the palette
dist = lambda v : np.array([ np.linalg.norm(p - v) for p in palette ])
field = np.array([ [ scale[np.argmin(dist(field_0[i,j]))]
for j in range(nx) ]
for i in range(ny)[::-1] ])
## Plot
fig, ax = plt.subplots(1, 2)
ax[0].imshow(field_0)
ax[1].contourf(field, cmap='gray')
plt.show()
Thanks to everyone who cared.
Related
I'm trying to create a Cartopy map for the sub-polar region around Iceland. What I would like is a non-rectangular figure where the edges are parallel to the lines of longitude and latitude, like this figure created using PyGMT:
I've tried various Cartopy projections, but all result in a rectangular figure, e.g.,
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
fig = plt.figure()
proj = ccrs.LambertConformal(central_longitude=-25, central_latitude=58.0)
ax = plt.axes(projection = proj)
ax.set_extent((-45, -5, 45, 70))
ax.gridlines()
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='black')
There are reasons for not using PyGMT (I want to plot surface velocities using quiver, plus the extensive learning curve), so I'd like to know if it's possible to achieve the same result in cartopy.
Thanks
You can use the set_boundary method of an axes for this. When specifying it as lon/lat, for a different projection, you should sample some points accross the boundary to approximate the true curvature of the projection (compared to lon/lat). The example below takes 20 points on each edge.
Note that the shape of this boundary can be anything you want, it doesn't have to match the projection or lon/lat lines etc.
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import cartopy
import cartopy.crs as ccrs
import numpy as np
proj = ccrs.LambertConformal(central_longitude=-25, central_latitude=58.0)
fig, axs = plt.subplots(
1,2, figsize=(8, 3), facecolor="w",
subplot_kw=dict(projection=proj),
)
n = 20
aoi = mpath.Path(
list(zip(np.linspace(-45,-5, n), np.full(n, 70))) + \
list(zip(np.full(n, -5), np.linspace(70, 45, n))) + \
list(zip(np.linspace(-5, -45, n), np.full(n, 45))) + \
list(zip(np.full(n, -45), np.linspace(45, 70, n)))
)
axs[1].set_boundary(aoi, transform=ccrs.PlateCarree())
for ax in axs:
ax.set_extent((-45, -5, 45, 70))
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='k')
gl = ax.gridlines(
draw_labels=True, rotate_labels=False,
x_inline=False, y_inline=False,
)
The answer by #Rutger_Kassies is great. However there is an alternative that the readers should consider if he/she wants to try a different approach.
import cartopy.crs as ccrs
import cartopy
import matplotlib.pyplot as plt
import matplotlib.path as mpath
# The lat-long proj
noProj = ccrs.PlateCarree()
# The projection of the map
myProj = ccrs.LambertConformal(central_longitude=-25, central_latitude=58.0)
myProj._threshold = myProj._threshold/20. # Set for higher precision of the projection
ax = plt.axes(projection=myProj)
# This plots parallel and meridian arcs around a target area that will be used ...
# as the map boundary
[ax_hdl] = ax.plot([-45, -5, -5, -45, -45], [45, 45, 70, 70, 45],
color='black', linewidth=0.5, marker='none',
transform=noProj)
# Get the `Path` of the plot
tx_path = ax_hdl._get_transformed_path()
path_in_data_coords, _ = tx_path.get_transformed_path_and_affine()
# Use the path's vertices to create a polygon
polygon = mpath.Path( path_in_data_coords.vertices )
ax.set_boundary(polygon) #This masks-out unwanted part of the plot
ax.gridlines(draw_labels=True, x_inline=False, y_inline=False)
ax.add_feature(cartopy.feature.OCEAN, linewidth=.3, color='lightblue')
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='black')
ax.title.set_text("Meridians and Parallels as Boundary")
plt.show()
You can change some parameters in the code, for example, the type of arcs that are used as the map's boundary.
The second plot is obtained by changing these parts of code:
1. `transform=noProj` to
`transform=ccrs.Geodetic()`
2. `ax.title.set_text("Meridians and Parallels as Boundary")` to
`ax.title.set_text("Great-circle Arcs as Boundary")`
I believe that when the top edge of the map reaches high latitude, using parallel of latitude as the boundary is not optimum. Straight line may be better, but in some situation great circle arc should be considered.
I have a dataframe with some columns. I've created a Networkx undirected graph and I want to draw the corresponding network. I need also to change the color of the edges based on the weights of those edges, so I used a LinearColorMap. This is the code:
#my other stuff
cmap = LinearSegmentedColormap.from_list('RwG',['red','white','green'])
nx.draw(G, poss, node_size=1500, node_color='lightgrey', edgelist=edges,edge_color=weights,
width=list(map(lambda number: number * 16 / m, x)), edge_cmap=cmap)
However, I need to normalize my color map so that the white color is centered on a specific value (e.g. -76). The weights are in the [-60,-100] range.
How can I achieve that ?
Visually:
If you pass in a matplotlib colormap to networkx, networkx will normalize your numerical color argument linearly between the minimum and maximum value.
Personally, I think this is a somewhat shortsighted design decision but it is what it is: your non-linear mapping of weights to color is simply not possible.
You can, however, pre-compute the colors and pass those in instead (similarly how you are precomputing edge widths):
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def weight_to_color(w, threshold):
if w < threshold:
return 'green'
elif np.isclose(w , threshold):
return 'white'
else: # x > threshold
return 'red'
weight_matrix = 40 * np.random.rand(10, 10) - 100
g = nx.from_numpy_array(weight_matrix)
weights = [g.edges[edge]['weight'] for edge in g.edges]
nx.draw(g, node_color='lightgray', edgelist=g.edges, edge_color=[weight_to_color(w, -76) for w in weights])
plt.show()
I am wondering whether it is possible to plot a vertical bar over a 2-dimensional representation of a graph. Say I have a tree and I want to associate with any node a "potential" which can be represented as a vertical bar.
NetworkX can do that using the matplotlib drawing tools because the result is a matplotlib figure on which you can use matplotlib to draw anything else you'd like on top of the networkx drawing of the graph.
nx.draw(G)
mpl.plot([xpt, xpt], [ymin, ymax], '--b')
mpl.show()
This is a minimal example which does what I was looking for (in Python):
import networkx as nx
import random
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
degree = 3
N = 10
g = nx.random_regular_graph(degree, N)
fig = plt.figure(figsize=(10,7))
ax = Axes3D(fig)
for i,j in enumerate(g.edges()):
x = np.array((positions[j[0]][0], positions[j[1]][0]))
y = np.array((positions[j[0]][1], positions[j[1]][1]))
ax.plot(x, y, c='black', alpha=0.5)
for key, value in positions.items():
xi = value[0]
yi = value[1]
# Scatter plot
ax.scatter(xi, yi, c= 'red')
ax.bar3d(xi, yi, 0, 0.01, 0, random.random(), shade=False)
ax.set_axis_off()
It generates this kind of plot, which can be useful to represent additional information on a graph
How can I add a single point to an existing plot with pyplot?
For example, I'm plotting a shifted sin curve over $[0,2\pi]$ with the below
Pkg.add("PyPlot")
using PyPlot
r1 = 0; r2 = 2*pi
N = 100
t = collect(r1:(r2-r1)/N:r2)
fig, ax = subplots()
ax[:plot](t, 1+sin(t),
linewidth=2.0, color="red",linestyle="--",label="constraint")
and I want to add a blue point at the minimum. How can I do this?
I'm writing a research paper on the SIFT algorithm, and I want to create a graphic to help explain the concept of a Gaussian blur in the context of continuous functions before I describe the discrete process. The graphic I want to create is a graph of a standard Gaussian convolved with a sine function. I can plot a Gaussian, and I can plot sine, but I don't know how to plot their convolution. I don't know how to calculate their convolution to plot it, and I don't know of any software that will allow me to use a convolution operator in the plot. I am familiar with tikz and gnuplot, but I would not know how to do this with either of them. Any suggestions as to how I could go about this would be greatly appreciated. Thanks.
You could use python's matplotlib and np.convolve
Please see the following code
__author__ = 'kgeorge'
import os
import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib import gridspec
#create gaussian for the x values in x-axis
def create_gaussian(x_axis):
sigma = 1.0
denom = math.sqrt(2 * math.pi) * sigma
twoSigmaSq = 2.0*sigma**2
e=np.zeros_like(x_axis)
for i,x in enumerate(x_axis):
e[i]=math.exp (-(x*x)/twoSigmaSq)
e = e / denom
return e
def main():
#x_axis
sz=100
halfW = int(sz/2)
x_axis=np.linspace(-halfW, halfW, 1000)
#cos fun
cos_f=np.cos(x_axis)
#gaussian
gaussian_f=create_gaussian(x_axis)
fig = plt.figure()
gs = gridspec.GridSpec(3, 1)
ax1 = fig.add_subplot(gs[0,0])
ax1.plot(x_axis, cos_f)
ax1.set_title('cos')
ax2 = fig.add_subplot(gs[1,0])
ax2.plot(x_axis, gaussian_f)
ax2.set_title('gaussian')
ax3 = fig.add_subplot(gs[2,0])
convolved_ret=np.convolve(cos_f, gaussian_f, mode='same')
ax3.plot(x_axis, convolved_ret)
ax3.set_title('cos convolved with gaussian')
gs.update(wspace=0.5, hspace=0.5)
plt.show()
Please see the output here.