I have two lists with the coordinates of points that I want to draw using matplotlib: hx and hy, and i have a function which draws these points as images: plotImage.
Now I would like to plot a line between these points, but the line should go somehow around the pictures. It doesn't have to go excctly by the borders of the images, the important thing is that it doesn't overlap with the pictures.
Here is an example, the green line is what I have now (its the convex hull), and the red line is an example of how the line should look like.
Here's my code:
def plotImage(xData, yData, im):
for x, y in zip(xData, yData):
bb = Bbox.from_bounds(x,y,10,10)
bb2 = TransformedBbox(bb,ax.transData)
bbox_image = BboxImage(bb2,
norm = None,
origin=None,
clip_on=False)
bbox_image.set_data(im)
ax.add_artist(bbox_image)
fig=plt.figure()
ax=fig.add_subplot(111)
img = plt.imread('pic.png')
gx = list(map(lambda x: x - 5, hx)) # so the center of the picture is in the point I want to plot
gy = list(map(lambda y: y - 5, hy)) #(without this, the image is drawn so that the picture's left bottom corner is in my point)
plotImage(gx,gy,img)
plt.plot(hx, hy, 'g-', markersize=10)
So my question is: how to calculate this red hull?
A example using Incrementing area of convex hull can be applied to your case.
from scipy.spatial import ConvexHull
import matplotlib.pyplot as plt
import numpy as np
gx = [-2, 2, -2, 2, 1, -0.5]
gy = [3, 4, -2, -1, -1, 0.5]
points = np.array((gx, gy)).T
convh = ConvexHull(points)
stretchCoef = 1.2
pointsStretched = points[convh.vertices]*stretchCoef
gx_new = pointsStretched[..., 0]
gy_new = pointsStretched[..., 1]
Related
I want to code a program to generate an array with coordinates to follow for drawing a shape like the white here, given are the blue points. Does anyone know how to do something like that or at least can give me a tip?
You could use e.g. InterpolatedUnivariateSpline to interpolate the points. As these spline functions are usually 1D, you could calculate x and y positions separately, depending on a new variable t going from 0 to 1.
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
# positions of the given points
px = [1, 4, 3, 2, 5]
py = [1, 3, 4, 3, 1]
# 5 t-values, at t=0 in point 1, at t=1 reaching point 5
pt = np.linspace(0, 1, len(px))
# sx and sy are functions that interpolate the points at the given t-values
sx = interpolate.InterpolatedUnivariateSpline(pt, px)
sy = interpolate.InterpolatedUnivariateSpline(pt, py)
# calculate many intermediate values
t = np.linspace(0, 1, 500)
x = sx(t)
y = sy(t)
# show the original points together with the spline
fig, ax = plt.subplots(facecolor='black')
ax.axis('off')
plt.scatter(px, py, s=80, color='skyblue')
plt.plot(x, y, color='white')
for i, (xi, yi) in enumerate(zip(px, py), start=1):
ax.text(xi, yi, f'\n {i}', ha='left', va='center', size=30, color='yellow')
plt.show()
I have an image with multiple globs, and when I am using regionprops to find the centroid and BB for each. However, it always finds only 1 for all of them.
More specifically, I get the results on the left image, but I want the one on the right image:
regions_problem
My code:
regions = regionprops(itk_label)
fig, ax = plt.subplots()
ax.imshow(itk_label, cmap=plt.cm.gray)
i=0
for props in regions:
i=i+1
print(str(i))
y0, x0 = props.centroid
ax.plot(x0, y0, '.r', markersize=5)
minr, minc, maxr, maxc = props.bbox
bx = (minc, maxc, maxc, minc, minc)
by = (minr, minr, maxr, maxr, minr)
ax.plot(bx, by, '-b', linewidth=1.0)
ax.axis((0, 600, 600, 0))
plt.show()
Can anyone tell me what I am doing wrong?
Funny thing is that the image on the right was also created by me in the past. However, I cannot do it again.
It's likely that your features have the same label value. regionprops requires that your features are labelled independently, ie. 1, 2, 3, .... Here is an example:
In this case both features have label value 1, so they are measured as if they are one object:
import numpy as np
from skimage import measure
from matplotlib import pyplot as plt
im = np.zeros((600, 600), dtype=int)
im[350:400, 350:400] = 1
im[510:530, 200:240] = 1
np.unique(im)
>>> array([0, 1])
plt.matshow(im)
regions = measure.regionprops(im)
for r in regions:
plt.plot(*r.centroid[::-1], marker='x', color='r')
If you use the measure.label function you can label the separated features independently, ie. label values 1, 2:
plt.matshow(im)
labelled = measure.label(im)
np.unique(labelled)
>>> array([0, 1, 2])
regions = measure.regionprops(labelled)
for r in regions:
plt.plot(*r.centroid[::-1], marker='x', color='r')
and this gives you your desired result.
What I would like to have are the triangles as shown in the image:
Here is my code:
import matplotlib.pyplot as plt
data= [0.2855,0.3030,0.4995]
x = [1,2,3]
plt.plot(x, data)
plt.show
Is there a simple way of inserting these slope triangles as shown in the image in an automatic fashion? I would like to have the triangle in the middle between two points and with the slope written next to it.
Depending on your idea of "automatic fashion", this might be a suitable solution:
import matplotlib.pyplot as plt
import numpy as np
# Data
x = np.array([1, 2, 3])
y = np.array([0.2855, 0.3030, 0.4995])
# Calculate triangle coordinates values
x_mid = np.convolve(x, [0.5, 0.5], mode='valid')
x_tri = np.vstack((x_mid, x_mid + 0.3))
y_tri = np.interp(x_tri, x, y)
# Calculate slopes
slopes = np.diff(y) / np.diff(x)
# Plot
plt.plot(x, y)
plt.plot(x_tri, np.tile(y_tri[0, :], [2, 1]), 'r') # red horizontal line
plt.plot(np.tile(x_tri[1, :], [2, 1]), y_tri, 'r') # red vertical line
for i, slope in enumerate(slopes): # slope values
plt.text(x_tri[1, i] + 0.05, np.mean(y_tri[:, i]), r'{0:.3f}'.format(slope))
plt.show()
Output:
Put all the triangle stuff in a separate function, and it won't affect your main code too much.
Hope that helps!
The triangle construction can also be done using mpltools:
import matplotlib.pyplot as plt
from mpltools import annotation
import numpy as np
data = [0.2855, 0.3030, 0.4995]
x = [1, 2, 3]
# get midpoint coordinates
x_mid = np.convolve(x, [0.5, 0.5], mode='valid')
y_mid = np.interp(x_mid, x, data)
# compute the gradient of each segment
gradients = np.diff(data)/np.diff(x)
# plot
plt.plot(x, data)
axes = plt.gca()
for xm, ym, g in zip(x_mid, y_mid, gradients):
annotation.slope_marker((xm, ym), g)
plt.show()
The first argument of annotation.slope_marker is a tuple containing the coordinates for the left-hand corner of the triangle and the second argument is the gradient. So here we loop over the midpoints of the line segments and their gradients and annotate with a triangular slope marker for that gradient at those coordinates.
Expected Output:
I'm trying to determine the center of mass of a surface determined by a set of RANDOM non-equivalently spaced x, y points.
Here is a quick test set to show what I mean.
from scipy.spatial import ConvexHull
import numpy as np
import matplotlib.pyplot as plt
def PolyArea(x, y):
return 0.5*np.abs(np.dot(x, np.roll(y,1))-np.dot(y, np.roll(x,1)))
points = np.random.rand(30, 2) # 30 random points in 2-D
hull = ConvexHull(points)
plt.plot(points[:,0], points[:,1])
for simplex in hull.simplices:
plt.plot(points[simplex, 0], points[simplex, 1])
plt.plot(points[hull.vertices, 0], points[hull.vertices, 1], 'r--', lw=2)
plt.show()
From here we get:
x = points[hull.vertices, 0]
y = points[hull.vertices, 1]
surface_size = PolyArea(x, y)
I wish to determine the center of mass of the area from the points set (x, y), NOT the mean of the points. I know this to be calculated by a double integral of the surface (see: http://tutorial.math.lamar.edu/Classes/CalcII/CenterOfMass.aspx), but I don't know how to implement it in Python.
Thanks in advance.
There's surely a much more elegant solution, but here's a quick and dirty, probably slow and overkill, image based one which kind of works.
import skimage.measure
import skimage.draw
GRIDW = 1000
GRIDH = 1000
img = np.zeros((GRIDW, GRIDH))
rr, cc = skimage.draw.polygon(x*GRIDW,y*GRIDH)
img[rr,cc] = 1
label = skimage.measure.label(img)
rprops = skimage.measure.regionprops(label)
print rprops[0].centroid / np.asarray([GRIDW, GRIDH])
I have this problem. I try to triangulate points cloud by scipy.spatial.Delaunay. I used:
tri = Delaunay(points) # points: np.array() of 3d points
indices = tri.simplices
vertices = points[indices]
But, this code return tetrahedron. How is it possible return triangle of surface only?
Thanks
To get it to work as in code form, you have to parametrize the surface to 2D. For example in the case of ball (r,theta, psi), radius is constant (drop it out) and points are given by (theta,psi) which is 2D.
Scipy Delaunay is N-dimensional triangulation, so if you give 3D points it returns 3D objects. Give it 2D points and it returns 2D objects.
Below is a script that I used to create polyhedra for openSCAD. U and V are my parametrization (x and y) and these are the coordinates that I give to Delaunay. Note that now the "Delaunay triangulation properties" apply only in u,v coordinates (angles are maximized in uv -space not xyz -space, etc).
The example is a modified copy from http://matplotlib.org/1.3.1/mpl_toolkits/mplot3d/tutorial.html which originally uses Triangulation function (maps to Delaunay eventually?)
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri
from scipy.spatial import Delaunay
# u, v are parameterisation variables
u = np.array([0,0,0.5,1,1])
v = np.array([0,1,0.5,0,1])
x = u
y = v
z = np.array([0,0,1,0,0])
# Triangulate parameter space to determine the triangles
#tri = mtri.Triangulation(u, v)
tri = Delaunay(np.array([u,v]).T)
print 'polyhedron(faces = ['
#for vert in tri.triangles:
for vert in tri.simplices:
print '[%d,%d,%d],' % (vert[0],vert[1],vert[2]),
print '], points = ['
for i in range(x.shape[0]):
print '[%f,%f,%f],' % (x[i], y[i], z[i]),
print ']);'
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
# The triangles in parameter space determine which x, y, z points are
# connected by an edge
#ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.Spectral)
ax.plot_trisurf(x, y, z, triangles=tri.simplices, cmap=plt.cm.Spectral)
plt.show()
Below is the (slightly more structured) text output:
polyhedron(
faces = [[2,1,0], [3,2,0], [4,2,3], [2,4,1], ],
points = [[0.000000,0.000000,0.000000],
[0.000000,1.000000,0.000000],
[0.500000,0.500000,1.000000],
[1.000000,0.000000,0.000000],
[1.000000,1.000000,0.000000], ]);
It looks like you want to compute the convex hull of your point cloud. I think this is what you want to do:
from scipy.spatial import ConvexHull
hull = ConvexHull(points)
indices = hull.simplices
vertices = points[indices]
Following Jaime's answer, but elaborating a bit more with an example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
import numpy as np
import scipy as sp
from scipy import spatial as sp_spatial
def icosahedron():
h = 0.5*(1+np.sqrt(5))
p1 = np.array([[0, 1, h], [0, 1, -h], [0, -1, h], [0, -1, -h]])
p2 = p1[:, [1, 2, 0]]
p3 = p1[:, [2, 0, 1]]
return np.vstack((p1, p2, p3))
def cube():
points = np.array([
[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1],
[1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1],
])
return points
points = icosahedron()
# points = cube()
hull = sp_spatial.ConvexHull(points)
indices = hull.simplices
faces = points[indices]
print('area: ', hull.area)
print('volume: ', hull.volume)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.dist = 30
ax.azim = -140
ax.set_xlim([0, 2])
ax.set_ylim([0, 2])
ax.set_zlim([0, 2])
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
for f in faces:
face = a3.art3d.Poly3DCollection([f])
face.set_color(mpl.colors.rgb2hex(sp.rand(3)))
face.set_edgecolor('k')
face.set_alpha(0.5)
ax.add_collection3d(face)
plt.show()
Which should depict the following figure: