How to join 2 surfaces on Z acess into one mesh? - python

So I have 2 surfaces (PolyData in PyVista) one on top of another:
They are shaped a little differently on Z access yet whenever a top one has a Z value on X, Y plane we are sure a-bottom one has the same. So how one can merge two surfaces X, Y aligned into one solid mesh?
What I try:
import numpy as np
import pyvista as pv
import vtk
def extruder(mesh, val_z):
extrude = vtk.vtkLinearExtrusionFilter()
extrude.SetInputData(mesh)
extrude.SetVector(0, 0, val_z)
extrude.Update()
extruded_mesh = pv.wrap(extrude.GetOutput())
return extruded_mesh
# generate two sheets of input data
noise = pv.perlin_noise(2, (0.2, 0.2, 0.2), (0, 0, 0))
bounds_2d = (-10, 10, -10, 10)
dim = (40, 50, 1)
bottom, top = [
pv.sample_function(noise, dim=dim, bounds=bounds_2d + (z, z)).warp_by_scalar()
for z in [-5, 5]
]
bottom = bottom.extract_surface(nonlinear_subdivision=5)
top = top.extract_surface(nonlinear_subdivision=5)
top = extruder(top, -50).triangulate()
bottom = extruder(bottom, 50).triangulate()
intersection = bottom.boolean_cut(top)
#top = top.clip_surface(bottom, invert=False, compute_distance=True)
#top = top.extrude([0, 0, -50]).triangulate()
#bottom = bottom.extrude([0, 0, 50]).triangulate()
#intersection = bottom.boolean_cut(top).triangulate()
p = pv.Plotter()
p.add_mesh(top, cmap="hot", opacity=0.15)
p.add_mesh(bottom, cmap="RdYlBu", opacity=0.15)
p.add_mesh(intersection, cmap="Dark2", opacity=1)
p.show()
What do I get:
What I expected:
only middle to be filled.

So had to do this:
import numpy as np
import pyvista as pv
# generate two sheets of input data
noise = pv.perlin_noise(2, (0.2, 0.2, 0.2), (0, 0, 0))
bounds_2d = (-10, 10, -10, 10)
dim = (40, 50, 1)
bottom, top = [
pv.sample_function(noise, dim=dim, bounds=bounds_2d + (z, z)).warp_by_scalar()
for z in [-5, 5]
]
bottom = bottom.extract_surface()
top = top.extract_surface()
topm = top.extrude([0, 0, -50]).triangulate().clean()
bottomm = bottom.extrude([0, 0, 50]).triangulate().clean()
topm = topm.clip_surface(bottom, invert=False)
bottomm = bottomm.clip_surface(top, invert=True)
intersection = topm.boolean_add(bottomm).triangulate().clean().subdivide(2).clean()
p = pv.Plotter()
#p.add_mesh(topm, cmap="hot", opacity=0.15)
#p.add_mesh(bottomm, cmap="gnuplot2", opacity=0.15)
p.add_mesh(intersection, cmap="Dark2", opacity=1)
p.show()
the resulting mesh is really bad, yet it has desired shape and gets to be computed in usable time:

Related

How to generate random placement and trajectory inside of polygon in vectorized way?

I want to generate a random placement and a random trajectory inside of a polygon. To speed up computation I am looking to do it in vectorized way (using Numpy).
I created code for it if we have square boundaries:
import numpy as np
# coordinates of square
square_coords = [-255, -255, 256, 256] # [xMin, yMin, xMax, yMax]
# convert to np.array
square_boundaries = np.array([(square_coords[0], square_coords[2]), (square_coords[1], square_coords[3])])
# specify speeds with corresponding probabilities of each speed
ue_speed = [3, 4, 8, 25]
ue_speed_prob = [0.4, 0.2, 0.3, 0.1] # should sum up to 1
steps = 50
time_interval = 10
nwalks = 1
# calcultation without boundaries
v = np.random.choice(ue_speed, size=steps, p=ue_speed_prob)
R = np.expand_dims((v * time_interval), axis=-1)
theta = 2 * np.pi * np.random.rand(nwalks, steps)
xy = np.dstack((np.cos(theta), np.sin(theta))) * R
trajectory_no_boundaries = np.hstack((np.zeros((nwalks, 1, 2)), np.cumsum(xy, axis=1)))
# generete random placement inside of boundaries
start = (np.random.randint(square_coords[0], square_coords[2]), np.random.randint(square_coords[1], square_coords[3]))
# generate random trajectory inside of boundaries
size = np.diff(square_boundaries, axis=1).ravel()
trajectory = np.abs((trajectory_no_boundaries[0] + start - square_boundaries[:, 0] + size) % (2 * size) - size) + square_boundaries[:, 0]
I am trying to understand if it is possible to transform this code if we have polygon coordinates instead of square coordinates e.g.:
polygon_coords = [(100, 100), (80, 130), (90, 130), (90, 140), (70, 140), (150, 200), (120, 150), (100, 100)]
To give an idea of how this polygon looks like here is a visualization with Shapely:
from shapely import geometry
shapely_polygon_coords = [geometry.Point(coord) for coord in polygon_coords]
geometry.Polygon([[p.x, p.y] for p in shapely_polygon_coords])
output:

Skew/rotate/otherwise transform 3D text in Matplotlib

I have a graph in Matplotlib that looks like this:
I would like to skew the text (by an x-shear of 10.3 and a y shear of 9.0) in order to make it appear to be parallel with the bars. Here is my code that I am using:
_x = [0,1,2,3]
_y = [1, 2]
_xx, _yy = np.meshgrid(_x, _y)
x, y = _xx.ravel(), _yy.ravel()
top = [1, 0, 0, 3, 0, 0, 0, 0]
bottom = np.zeros_like(top)
width = 1
depth = 0.25
# graph is set up here
for i, iXZ in enumerate(zip(x, top)):
iX, iZ = iXZ
if iZ:
t = ax1.text(iX + 0.5, 1, -0.85, ('U\n' if i == 0 else 'E\n') * iZ, (0, 0, 0),
ha='center', va='bottom', fontfamily="Century Gothic", color='black', fontsize=40)
# skew by 10.3 and 9.0
t.set_transform(mtrns.CompositeGenericTransform(t.get_transform(), mtrns.Affine2D().skew(10.3, 9.0)))
However, when this code is run, matplotlib outputs this:
I feel like I am using the wrong type of transform, since Affine2D implies that it's only for "2D" objects. Unfortunately, I cannot find the "right" method in the documentation. How would I be able to skew the text effectively?

Create matplotlib-style 3d scattergraph z-axis in Vispy

I am conducting PhD research into extremely high density lidar point clouds(1M points +), and am having real difficulty plotting these files on a 3D scattergraph. Matplotlib isn't optimised for datasets that large, so I am attempting to use Vispy to achieve this. Right now, I am having real trouble trying to get a scattergraph-style plot up and running, so I can start building my pipeline.
I'm after something like this:
matplotlib 3D scattergraph
Using Vispy, I am finding it difficult to apply a z-axis and I am having trouble finding what part of documentation can help me with this. Here's my code:
"""
This example demonstrates the use of the SurfacePlot visual.
"""
import sys
import numpy as np
from vispy import app, scene
from vispy.util.filter import gaussian_filter
canvas = scene.SceneCanvas(keys='interactive', bgcolor='w')
view = canvas.central_widget.add_view()
view.camera = scene.TurntableCamera(up='z', fov=60)
# Simple surface plot example
# x, y values are not specified, so assumed to be 0:50
z = np.random.normal(size=(250, 250), scale=200)
z[100, 100] += 50000
z = gaussian_filter(z, (10, 10))
print("This is z {0}".format(z))
p1 = scene.visuals.SurfacePlot(z=z, color=(0.3, 0.3, 1, 1))
p1.transform = scene.transforms.MatrixTransform()
p1.transform.scale([1/249., 1/249., 1/249.])
p1.transform.translate([-0.5, -0.5, 0])
view.add(p1)
# p1._update_data() # cheating.
# cf = scene.filters.ZColormapFilter('fire', zrange=(z.max(), z.min()))
# p1.attach(cf)
xax = scene.Axis(pos=[[-0.5, -0.5], [0.5, -0.5]], tick_direction=(0, -1),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
xax.transform = scene.STTransform(translate=(0, 0, -0.2))
yax = scene.Axis(pos=[[-0.5, -0.5], [-0.5, 0.5]], tick_direction=(-1, 0),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
yax.transform = scene.STTransform(translate=(0, 0, -0.2))
zax = scene.Axis(pos=[[1.0, 1.0], [-1.0, 1.0]], tick_direction=(-1, 0),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
zax.transform = scene.STTransform(translate=(0.0, 0.0, -0.2))
# Add a 3D axis to keep us oriented
axis = scene.visuals.XYZAxis(parent=view.scene)
if __name__ == '__main__':
canvas.show()
if sys.flags.interactive == 0:
app.run()
And it makes a graph that looks like this:
vispy_example_plot
You can see in the above image my problem. I want to orient the third axis perpendicular to the others, and a grid to the to the walls of the plot, so the data is more defined.
Unfortunately sub-sampling and other tricks to display the data using matplotlib, are not optimal for me because I have to display the original data, rather than showing a subset. Of course, if there are other strategies I can use to display my data as I would like, then I am all ears.
Thanks in advance, hopefully someone can help
i faced the same issue doing research on camera depth images and cfar radar data. since vispy is supporting 2d only for axes, you could rotate the z-axis after creation. here, a slightly modified version of your code:
import sys
import numpy as np
from vispy import app, scene
from vispy.util.filter import gaussian_filter
canvas = scene.SceneCanvas(keys='interactive', bgcolor='w')
view = canvas.central_widget.add_view()
view.camera = scene.TurntableCamera(up='z', fov=60)
z = np.random.normal(size=(250, 250), scale=200)
z[100, 100] += 50000
z = gaussian_filter(z, (10, 10))
p1 = scene.visuals.SurfacePlot(z=z, color=(0.3, 0.3, 1, 1))
p1.transform = scene.transforms.MatrixTransform()
p1.transform.scale([1/249., 1/249., 1/249.])
view.add(p1)
xax = scene.Axis(pos=[[0, 0], [1, 0]], tick_direction=(0, -1), axis_color='r', tick_color='r', text_color='r', font_size=16, parent=view.scene)
yax = scene.Axis(pos=[[0, 0], [0, 1]], tick_direction=(-1, 0), axis_color='g', tick_color='g', text_color='g', font_size=16, parent=view.scene)
zax = scene.Axis(pos=[[0, 0], [-1, 0]], tick_direction=(0, -1), axis_color='b', tick_color='b', text_color='b', font_size=16, parent=view.scene)
zax.transform = scene.transforms.MatrixTransform() # its acutally an inverted xaxis
zax.transform.rotate(90, (0, 1, 0)) # rotate cw around yaxis
zax.transform.rotate(-45, (0, 0, 1)) # tick direction towards (-1,-1)
if __name__ == '__main__':
canvas.show()
if sys.flags.interactive == 0:
app.run()
Regards

Python inquiry using a neural network

I am trying to modify a code written by a software developer (Kyle Dickerson) and have written it up like this:
So I have this code:
from __future__ import division
## Kyle Dickerson
## kyle.dickerson#gmail.com
## Jan 15, 2008
##
## Self-organizing map using scipy
## This code is licensed and released under the GNU GPL
## This code uses a square grid rather than hexagonal grid, as scipy allows for fast square grid computation.
## I designed sompy for speed, so attempting to read the code may not be very intuitive.
## If you're trying to learn how SOMs work, I would suggest starting with Paras Chopras SOMPython code:
## http://www.paraschopra.com/sourcecode/SOM/index.php
## It has a more intuitive structure for those unfamiliar with scipy, however it is much slower.
## If you do use this code for something, please let me know, I'd like to know if has been useful to anyone.
from random import *
from math import *
import sys
import scipy
import numpy
class SOM:
def __init__(self, height=4, width=4, FV_size=3, learning_rate=0.005):
self.height = height
self.width = width
self.FV_size = FV_size
self.radius = (height+width)/3
self.learning_rate = learning_rate
self.nodes = scipy.array([[ [random()*255 for
i in range(FV_size)] for x in range(width)] for y in range(height)])
self.nodes = scipy.array([[1,2,3],[4,5,6],[4,5,6],
[4,5,6],[4,5,6], [4,5,6],[4,5,6],[4,5,6],[4,5,6],
[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6]])
print "SOM",self.nodes
def train(self, iterations=1000, train_vector=[[]]):
for t in range(len(train_vector)):
train_vector[t] = scipy.array(train_vector[t])
print "training",train_vector[t],t
time_constant = iterations/log(self.radius)
delta_nodes = scipy.array([[[0 for i in range(self.FV_size)]
for x in range(self.width)] for y in range(self.height)])
for i in range(1, iterations+1):
delta_nodes.fill(0)
radius_decaying=self.radius*exp(-1.0*i/time_constant)
rad_div_val = 2 * radius_decaying * i
learning_rate_decaying=self.learning_rate*exp(-1.0*i/time_constant)
sys.stdout.write("\rTraining Iteration:
" + str(i) + "/" + str(iterations))
for j in range(len(train_vector)):
best = self.best_match(train_vector[j])
for loc in self.find_neighborhood(best, radius_decaying):
influence = exp( (-1.0 * (loc[2]**2)) / rad_div_val)
inf_lrd = influence*learning_rate_decaying
delta_nodes[loc[0],loc[1]] += inf_lrd*
(train_vector[j]- self.nodes[loc[0],loc[1]])
self.nodes += delta_nodes
sys.stdout.write("\n")
# Returns a list of points which live within 'dist' of 'pt'
# Uses the Chessboard distance
# pt is (row, column)
def find_neighborhood(self, pt, dist):
min_y = max(int(pt[0] - dist), 0)
max_y = min(int(pt[0] + dist), self.height)
min_x = max(int(pt[1] - dist), 0)
max_x = min(int(pt[1] + dist), self.width)
neighbors = []
for y in range(min_y, max_y):
for x in range(min_x, max_x):
dist = abs(y-pt[0]) + abs(x-pt[1])
neighbors.append((y,x,dist))
return neighbors
# Returns location of best match, uses Euclidean distance
# target_FV is a scipy array
def best_match(self, target_FV):
loc = scipy.argmin((((self.nodes - target_FV)**2).sum(axis=2))**0.5)
r = 0
while loc > self.width:
loc -= self.width
r += 1
c = loc
return (r, c)
# returns the Euclidean distance between two Feature Vectors
# FV_1, FV_2 are scipy arrays
def FV_distance(self, FV_1, FV_2):
return (sum((FV_1 - FV_2)**2))**0.5
if __name__ == "__main__":
print "Initialization..."
colors = [ [0, 0, 0], [0, 0, 255], [0, 255, 0],
[0, 255, 255], [255, 0, 0], [255, 0, 255],
[255, 255, 0], [255, 255, 255]]
width = 32
height = 32
color_som = SOM(width,height,3,0.05)
print "Training colors..."
color_som.train(1000, colors)
try:
from PIL import Image
print "Saving Image: sompy_test_colors.png..."
img = Image.new("RGB", (width, height))
for r in range(height):
for c in range(width):
img.putpixel((c,r),(int(color_som.nodes[r,c,0]),
int(color_som.nodes[r,c,1]), int(color_som.nodes[r,c,2])))
print "color nodes",color_som.nodes
img = img.resize((width*10, height*10),Image.NEAREST)
img.save("sompy_test_colors.png")
except:
print "Error saving the image, do you have PIL (Python Imaging Library) installed?"
but when I try to go from
self.nodes = scipy.array([[ [random()*255
for i in range(FV_size)] for x in range(width)]
for y in range(height)])
which was in the original code to something like this:
self.nodes = scipy.array([[1,2,3],[4,5,6],[4,5,6],
[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],
[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6]])
I get the error message:
File "sompy5.py", line 112, in <module>
color_som.train(1000, colors)
File "sompy5.py", line 65, in train
best = self.best_match(train_vector[j])
File "sompy5.py", line 92, in best_match
loc = scipy.argmin((((self.nodes - target_FV)**2).sum(axis=2))**0.5)
File "/usr/lib/python2.7/dist-packages/numpy/core/_methods.py",
line 25, in _sum
out=out, keepdims=keepdims)
ValueError: 'axis' entry is out of bounds
Is there something that has to be done to get the vectors to match up?
This part is a 3-D array (3 square brackets to begin the argument):
self.nodes = scipy.array([[ [random()*255 for
i in range(FV_size)] for x in range(width)] for y in range(height)])
This part is a 2-D array:
self.nodes = scipy.array([[1,2,3],[4,5,6],[4,5,6],
[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],
[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6],[4,5,6]])
So you need to turn self.nodes into the appropriate 3-D array.
EDIT: an example of the required syntax:
self.nodes = scipy.array([[ [1,2,3],[4,5,6]] , [[7,8,9],[10,11,12]]])
print(self.nodes)
>>> array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
EDIT 2:
Another option is to build a linear array and then reshape():
myarray = scipy.array([1,2,3,4,5,6,7,8,9,10,11,12])
myarray = myarray.reshape( (2, 2, 3) ) ## 3 numbers for 3 dimensions, but the product must be the same as the number of elements of the original array
print(myarray)
>>> array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])

Plotting sort of a heatmap whose colors are a result of a function x,y -> r,g,b

I have a dictionary which maps XY tuples to RGB tuples. For example,
d = {
(0, 0): (0, 0, 0),
(0, 1): (0, 0, 200),
}
I wish to plot some sort of heatmap, which, at a given XY coordinate, will have color which is the average of the colors in the dict, weighted by their reciprocal distances; as if they were "sources of light" or so.
In the given example, the coordinate (0, 0.5) should be colored with (0, 0, 100) and the coordinate (0, 0.1) should be colored with (0, 0, 20).
My question is rather technical: how do I let pyplot plot a pixel image with colors retrieved from a function f(x, y) -> (r, g, b)?
If you have your X-Y grid:
import numpy
from matplotlib import pyplot as plt
width, height = 300, 500
xs = numpy.arange(width)
ys = numpy.arange(height)
data = numpy.dstack(numpy.meshgrid(xs, ys))
You should just map these to (r, g, b) tuples. The following is pretty slow, but how to speed it up depends on what your function does.
from colorsys import hsv_to_rgb
import random
def data_to_color(x, y):
return (
(x/width)**(0.5+random.random()*2),
(y/height)**3,
(x/width*y/height)*0.6 + random.random()*0.4
)
colors = [[data_to_color(x, y) for x, y in row] for row in data]
colors = numpy.array(colors)
colors.shape
#>>> (500, 300, 3)
Then imshow can give the wanted output:
plt.imshow(colors, origin='lower')
plt.show()
Now, if you want to interpolate from your points as you say, you can use scipy.interpolate. I'll make a dictionary to extrapolate from the function above:
from scipy.interpolate import griddata
gridpoints = data.reshape(width*height, 2)
d = {(x, y): data_to_color(x, y) for x, y in gridpoints if not random.randint(0, 1000)}
len(d)
#>>> 142
Extract the dictionary into numpy arrays, and separate the colours (it might be possible to avoid the separation, but you can test that yourself):
points, values = zip(*d.items())
points = numpy.array(points)
values = numpy.array(values)
reds = values[:, 0]
greens = values[:, 1]
blues = values[:, 2]
Then run griddata on the points:
new_reds = griddata(points, reds, (data[:, :, 0], data[:, :, 1]), method='linear')
new_greens = griddata(points, greens, (data[:, :, 0], data[:, :, 1]), method='linear')
new_blues = griddata(points, blues, (data[:, :, 0], data[:, :, 1]), method='linear')
new_colors = numpy.dstack([new_reds, new_greens, new_blues])
new_colors[numpy.isnan(new_colors)] = 0.5
And plot:
plt.triplot(points[:,0], points[:,1], 'k-', linewidth=1, alpha=0.5)
plt.imshow(new_colors, extent=(0, width, 0, height), origin='lower')
plt.show()
And, finally, if you want extrapolation too, I copied some code from here:
import scipy
def extrapolate_nans(x, y, v):
'''
Extrapolate the NaNs or masked values in a grid INPLACE using nearest
value.
.. warning:: Replaces the NaN or masked values of the original array!
Parameters:
* x, y : 1D arrays
Arrays with the x and y coordinates of the data points.
* v : 1D array
Array with the scalar value assigned to the data points.
Returns:
* v : 1D array
The array with NaNs or masked values extrapolated.
'''
if numpy.ma.is_masked(v):
nans = v.mask
else:
nans = numpy.isnan(v)
notnans = numpy.logical_not(nans)
v[nans] = scipy.interpolate.griddata((x[notnans], y[notnans]), v[notnans],
(x[nans], y[nans]), method='nearest').ravel()
return v
new_reds = extrapolate_nans(data[:, :, 0], data[:, :, 1], new_reds)
new_greens = extrapolate_nans(data[:, :, 0], data[:, :, 1], new_greens)
new_blues = extrapolate_nans(data[:, :, 0], data[:, :, 1], new_blues)
new_colors = numpy.dstack([new_reds, new_greens, new_blues])
plt.imshow(new_colors, extent=(0, width, 0, height), origin='lower')
plt.show()
EDIT: Maybe something more like
import numpy
from matplotlib import pyplot as plt
from numpy.core.umath_tests import inner1d
width, height = 300, 500
xs, ys = numpy.mgrid[:width, :height]
coordinates = numpy.dstack([xs, ys])
light_sources = {
(0, 0): (0, 0, 0),
(300, 0): (0, 0, 0),
(0, 0): (0, 0, 0),
(300, 500): (0, 0, 0),
(100, 0): (0, 0, 200),
(200, 150): (100, 70, 0),
(220, 400): (255, 255, 255),
(80, 220): (255, 0, 0),
}
weights = numpy.zeros([width, height])
values = numpy.zeros([width, height, 3])
For each light source:
for coordinate, value in light_sources.items():
Compute the (inverse) distances. Use +1e9 to prevent infinities, although this will have silly failures so a more rigorous fix would be important later:
shifted_coordinates = coordinates - coordinate + 1e-9
inverse_distances = (shifted_coordinates ** 2).sum(axis=-1) ** (-1/2)
Add it to the sum and the sum weighting:
weights += inverse_distances
values += inverse_distances[:, :, numpy.newaxis].repeat(3, axis=-1) * value / 255
Divide by the weighting to have the average:
values /= weights[..., numpy.newaxis]
And show...
plt.imshow(values, origin='lower')
plt.show()
For this:
The reason I didn't go for this originally is because the value at (0, 0.1) in your example is not (0, 0, 20) but:
distances = [0.9, 0.1]
inverse_distances = [10/9, 10]
sum_weighting = 100 / 9
blue_levels = 200 / (109/90) = 18
so it should be (0, 0, 18) by this definition.

Categories

Resources