How to save custom vertex properties with openmesh in Python? - python

I am working with openmesh installed in Python 3.6 via pip. I need to add custom properties to vertices of a mesh in order to store some data at each vertex. My code goes as follows :
import openmesh as OM
import numpy as np
mesh = OM.TriMesh()
#Some vertices
vh0 = mesh.add_vertex(np.array([0,0,0]));
vh1 = mesh.add_vertex(np.array([1,0,0]));
vh2 = mesh.add_vertex(np.array([1,1,0]));
vh3 = mesh.add_vertex(np.array([0,1,0]));
#Some data
data = np.arange(mesh.n_vertices)
#Add custom property
for vh in mesh.vertices():
mesh.set_vertex_property('prop1', vh, data[vh.idx()])
#Check properties have been added correctly
print(mesh.vertex_property('prop1'))
OM.write_mesh('mesh.om',mesh)
print returns [0, 1, 2, 3]. So far, so good. But when I read again the mesh, the custom property has disappeared :
mesh1 = OM.TriMesh()
mesh1 = OM.read_trimesh('mesh.om')
print(mesh1.vertex_property('prop1'))
returns [None, None, None, None]
I have two guesses :
1 - The property was not saved in the first place
2 - The reader does not know there is a custom property when it reads the file mesh.om
Does anybody know how to save and read properly a mesh with custom vertex properties with openmesh in Python? Or is it even possible (has anybody done it before?)?
Is it that there is something wrong with my code?
Thanks for your help,
Charles.

The OM writer currently does not support custom properties. If you are working with numeric properties, it is probably easiest to convert the data to a NumPy array and save it separately.
Say your mesh and properties are set up like this:
import openmesh as om
import numpy as np
# create example mesh
mesh1 = om.TriMesh()
v00 = mesh1.add_vertex([0,0,0])
v01 = mesh1.add_vertex([0,1,0])
v10 = mesh1.add_vertex([1,0,0])
v11 = mesh1.add_vertex([1,1,0])
mesh1.add_face(v00, v01, v11)
mesh1.add_face(v00, v11, v01)
# set property data
mesh1.set_vertex_property('color', v00, [1,0,0])
mesh1.set_vertex_property('color', v01, [0,1,0])
mesh1.set_vertex_property('color', v10, [0,0,1])
mesh1.set_vertex_property('color', v11, [1,1,1])
You can extract the property data as a numpy array using one of the *_property_array methods and save it alongside the mesh using NumPy's save function.
om.write_mesh('mesh.om', mesh1)
color_array1 = mesh1.vertex_property_array('color')
np.save('color.npy', color_array1)
Loading is similar:
mesh2 = om.read_trimesh('mesh.om')
color_array2 = np.load('color.npy')
mesh2.set_vertex_property_array('color', color_array2)
# verify property data is equal
for vh1, vh2 in zip(mesh1.vertices(), mesh2.vertices()):
color1 = mesh1.vertex_property('color', vh1)
color2 = mesh2.vertex_property('color', vh2)
assert np.allclose(color1, color2)

When you store data, you should set set_persistent function true like below.
(sorry for using c++, I don't know about python)
OpenMesh::VPropHandleT<float> vprop_float;
mesh.add_property(vprop_float, "vprop_float");
mesh.property(vprop_float).set_persistent(true);
OpenMesh::IO::write_mesh(mesh, "tmesh.om");
and then, you have to request this custom property in your mesh before loading it with the obj reader. Order is important.
TriMesh readmesh;
OpenMesh::VPropHandleT<float> vprop_float;
readmesh.add_property(vprop_float, "vprop_float");
OpenMesh::IO::read_mesh(readmesh, "tmesh.om");'
I refered below.
https://www.openmesh.org/media/Documentations/OpenMesh-4.0-Documentation/a00062.html
https://www.openmesh.org/media/Documentations/OpenMesh-4.0-Documentation/a00060.html

Related

How to render a simple 2D vtkImageData object in Paraview?

I would like to use Paraview to plot simple 2D meshes with either different colors per cell or different colors per vertices. As far as I can tell, the Paraview documentation does not explain how to Show() a user-defined VTK object.
I read from the Paraview guide how the VTK data model works and from the VTK User's Guide how to generate a vtkImageData object.
From what I could gather, the following code should yield a vtkImageData object of a 10x5 2D mesh spanning [0.;10.]x[0.;5.] with 50 blue elements.
But now I don't know how to actually plot it in Paraview.
from paraview import vtk
import paraview.simple as ps
import numpy as np
from paraview.vtk.util.numpy_support import numpy_to_vtk
def main():
# create the vtkImageData object
myMesh = vtk.vtkImageData()
myMesh.SetOrigin(0.,0.,0.)
myMesh.SetExtent(0,10,0,5,0,0)
myMesh.SetSpacing(1.,1.,0.)
# create the numpy colors for each cell
blue = np.array([15, 82, 186], dtype=np.ubyte) # 8 bits each [0, 255]
npColors = np.tile(blue, (myMesh.GetNumberOfCells(), 1))
# transform them to a vtkUnsignedCharArray
# organized as 50 tuples of 3
vtkColors = numpy_to_vtk(npColors, deep=1, array_type=vtk.VTK_UNSIGNED_CHAR)
# allocate the sets of 3 scalars to each cell
myMesh.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 3) # set 3 scalars per cell
myMesh.GetCellData().SetScalars(vtkColors) # returns 0, the index to which
# vtkColors is assigned
# Something... generate a proxy using servermanager??
ps.Show(myMesh?)
ps.Interact() # or ps.Render()
if __name__ == "__main__":
main()
From what I could gather is that I have to apply a geometric filter first, such as vtkImageDataGeometryFilter(). But this does not exist in paraview.vtk, only by importing the vtk module directly.
Another option, according to this VTK C++ SO question is using vtkMarchingSquares.
Either way, apparently paraview.simple.Show only accepts a proxy object as input. Which then begs the question of how to create a proxy out of the filtered vtkImageData object? To be honest, I have not quite grasped how the visualization pipeline works, despite reading the docs.
Thus far I've only found ways of visualizing VTK objects using vtk directly via the Kitware examples in GitHub, without using the higher level features of Paraview.
ProgrammableSource is what you want to use. See this example or this
I managed to render it using a TrivialProducer object and the method .GetClientSideObject(). This interfaces ParaView to serverside objects.
Sources: the source code and the tip given by Mathieu Westphal from ParaView support.
from paraview import simple as ps
from paraview import vtk
from paraview.vtk.util.numpy_support import numpy_to_vtk
import numpy as np
def main():
# Create an image (this is a data object)
myMesh = vtk.vtkImageData()
myMesh.SetOrigin(0., 0., 0.)
myMesh.SetSpacing(0.1, 0.1, 0.)
myMesh.SetExtent(0, 10, 0, 5, 0, 0)
# coloring
blue = np.array([15, 82, 186], dtype=np.ubyte)
# numpy colors
scalarsnp = np.tile(blue, (myMesh.GetNumberOfCells(), 1))
scalarsnp[[9, 49]] = np.array([255, 255, 0], dtype=np.ubyte) # yellow
# vtk array colors. Organized as 50 tuples of 3
scalarsvtk = numpy_to_vtk(scalarsnp, deep=1, array_type=vtk.VTK_UNSIGNED_CHAR)
scalarsvtk.SetName("colorsArray")
# allocate the scalars to the vtkImageData object
# myMesh.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 3) # set 3 scalars per cell
# myMesh.GetCellData().SetScalars(scalarsvtk) # do not use this in ParaView!!
colorArrayID = myMesh.GetCellData().AddArray(scalarsvtk)
myMesh.GetCellData().SetActiveScalars(scalarsvtk.GetName())
# TrivialProducer to interface ParaView to serverside objects
tp_mesh = ps.TrivialProducer(registrationName="tp_mesh")
myMeshClient = tp_mesh.GetClientSideObject()
# link the vtkImageData object to the proxy manager
myMeshClient.SetOutput(myMesh)
tp_mesh.UpdatePipeline()
# Filter for showing the ImageData to a plane
mapTexture2Plane = ps.TextureMaptoPlane(registrationName="TM2P_mesh", Input=tp_mesh)
renderViewMesh = ps.CreateView("RenderView")
renderViewMesh.Background = [1, 1, 1]
renderViewMesh.OrientationAxesVisibility = 0
display = ps.Show(proxy=mapTexture2Plane, view=renderViewMesh)
display.SetRepresentationType("Surface")
display.MapScalars = 0 # necessary so as to not generate a colormap
ps.Interact() # or just ps.Render()
if __name__ == "__main__":
main()

Storing terms in fipy as arrays instead of fipy objects

I am new to fipy, so I apologise if this is a stupid question (and this doesn't seem to help me).
But is there a way to store fipy objects in human-readable (or python-readable) form, other than suggested in the question above? This is only applicable to the cell variable.
If I want to do some more fancy/customized plotting than what is in the default fipy viewer, how can I do it?
Take for example a simple 1D diffusion:
from fipy import *
# USER-DEFINED PARAMETERS
nx = 100
dx = 0.1
D = 1.0
bound1 = 30
bound2 = 70
# PREPARED FOR SOLUTION
mesh = Grid1D(nx=nx, dx=dx)
print "mesh", mesh
# define some parameters specific to this solution
T0 = bound2
Tinf = bound1
hour = 3600
day = hour*24
ndays = 1
duration = ndays*day
T = CellVariable(name="Temperature", mesh=mesh, value=bound1)
# Constant temperature boundary condition
T.constrain(T0, mesh.facesLeft)
T.constrain(Tinf, mesh.facesRight)
# SOLUTION
eq = (TransientTerm() == DiffusionTerm(coeff=D))
timeStepDuration = 0.5*hour
steps = int(duration/timeStepDuration)
for step in range(steps):
eqCirc.solve(var=T,dt=timeStepDuration)
But could I, for example, store the mesh as an array? Or could I store the value of the DiffusionTerm instead of the CellVariable in each step?
In my case, I would like to plot the thermal gradient (so extract it from the diffusion term) with distance for each time step.
Can I do it? How?
But is there a way to store fipy objects in human-readable (or
python-readable) form, other than suggested in the question above?
There are a number of options. Any FiPy object can be pickled using fipy.dump, which will gather data when running in parallel. For example,
import fipy
mesh = fipy.Grid2D(nx=3, ny=3)
var = fipy.CellVariable(mesh=mesh)
var[:] = mesh.x * mesh.y
fipy.dump.write(var, 'dump.gz')
You can then read this back in another Python session with
var = fipy.dump.read('dump.gz')
However, Pickle isn't great for long term storage as it depends on using the same version of the code to read the data back. An alternative is to save a Numpy array using,
np.save('dump.npy', var)
and then read in with
var_array = np.load('dump.npy')
var = fipy.CellVariable(mesh=mesh, value=var_array)
If I want to do some more fancy/customized plotting than what is in
the default fipy viewer, how can I do it? If I want to do some more
fancy/customized plotting than what is in the default fipy viewer, how
can I do it?
To save the data in a human readable form with the location and value data for plotting in another package, you might try using pandas
import pandas
df = pandas.DataFrame({'x' : mesh.x, 'y': mesh.y, 'value': var})
df.to_csv('dump.csv')
But could I, for example, store the mesh as an array?
You can of course Pickle any Python object, but using knowledge of the actual object is better for long term storage. For a grid mesh, only dx, dy, nx, ny are required to reinstantiate. Mesh objects have a __getstate__ method that gives the requirements for pickling the object. All that needs to be stored is what this method returns.
Or could I store the value of the DiffusionTerm instead of the
CellVariable in each step?
The DiffusionTerm doesn't really store anything other than its coefficient. The equation stores its matrix and b vector.

How can I create a graph in python (pyqtgraph) where verticies display images?

I would like to construct an interactive graph (as in G = (V, E) with vertices and edges) using python, and I would like to display images on top of each vertex.
I'm using this to visualize a medium to large sized clustering problem, so I'd like whatever backend I use preferably be very fast (so networkx doesnt seem to cut it).
I'm basically looking for creating a set of verticies and assigning an image (or path to an image, or function to create an image) to each. Then I want to specify connections between verticies and their weights.
Its not the end of the world if I have to specify positions of each vertex, but ideally I'd like a layout to be automatically generated using the weights on the graphs.
It would also be cool if I could move the nodes with my mouse, but again, not the end of the world if I have to build that in myself. I just need to get to a starting point.
I was using this demo code to build a graph.
import pyqtgraph as pg
import numpy as np
# Enable antialiasing for prettier plots
pg.setConfigOptions(antialias=True)
w = pg.GraphicsWindow()
w.setWindowTitle('pyqtgraph example: GraphItem')
v = w.addViewBox()
v.setAspectLocked()
g = pg.GraphItem()
v.addItem(g)
## Define positions of nodes
pos = np.array([
[0,0],
[10,0],
[0,10],
[10,10],
[5,5],
[15,5]
])
## Define the set of connections in the graph
adj = np.array([
[0,1],
[1,3],
[3,2],
[2,0],
[1,5],
[3,5],
])
## Define the symbol to use for each node (this is optional)
symbols = ['o','o','o','o','t','+']
## Define the line style for each connection (this is optional)
lines = np.array([
(255,0,0,255,1),
(255,0,255,255,2),
(255,0,255,255,3),
(255,255,0,255,2),
(255,0,0,255,1),
(255,255,255,255,4),
], dtype=[('red',np.ubyte),('green',np.ubyte),('blue',np.ubyte),('alpha',np.ubyte),('width',float)])
## Update the graph
g.setData(pos=pos, adj=adj, pen=lines, size=1, symbol=symbols, pxMode=False)
I tried chaning symbols to use a pyqtgraph image item, but that did not seem to work.
# My Code to make an image
img = ibs.get_annot_chips(cm.qaid)
img_item = pg.ImageItem(img)
# ....
# breaks...
symbols = [img_item,'o','o','o','t','+']
Any input or advice on how to do this?
PyQtGraph does not support this, but it would be a very nice feature. If you look in graphicsItems/ScatterPlotItem.py, near the top is a function called renderSymbol() which generates a QImage based on the symbol parameters specified by the user. You could probably modify this by adding:
if isinstance(symbol, QtGui.QImage):
return symbol
to the top of the function and expect everything to work as you expect (you might need to correct some type checking elsewhere as well).

GDAL Python Creating Contourlines

I'd like to generate contourlines from a SRTM image within Python. It seems to calculate but if I want to add my contourlines nothing shows up and the attribute table is empty as well. Please take a look at my code:
from osgeo import gdal, gdal_array
from osgeo.gdalconst import *
from numpy import *
from osgeo import ogr
#Read in SRTM data
indataset1 = gdal.Open( src_filename_1, GA_ReadOnly)
in1 = indataset1.GetRasterBand(1)
#Generate layer to save Contourlines in
ogr_ds = ogr.GetDriverByName("ESRI Shapefile").CreateDataSource(dst_filename)
contour_shp = ogr_ds.CreateLayer('contour')
field_defn = ogr.FieldDefn("ID", ogr.OFTInteger)
contour_shp.CreateField(field_defn)
field_defn = ogr.FieldDefn("elev", ogr.OFTReal)
contour_shp.CreateField(field_defn)
#Generate Contourlines
gdal.ContourGenerate(in1, 100, 0, [], 0, 0, contour_shp, 0, 1)
ogr_ds.Destroy()
Field ID and field elevation seem to be empty, but the contour_shape file is fairly huge ~100MB.
Any idea what might went wrong?
Update: I got it! I forgot to close the datasource with: ogr_ds.Destroy()
Don't use the Destroy() method, as described in the GDAL/OGR Python Gotchas.
To save and close a dataset, dereference the variable, and optionally delete it.
I typically use this at the end to save/close either a GDAL or OGR dataset:
ogr_ds = None
del ogr_ds

Render my map in Mapnik

Sorry for disturbing.
I cannot render my map, I don't know why...
I read a csv file using ogr, which use a .vrt file I created, associated to the csv:
Then, I have a simple code to render my map, but I cannot it work: a map with an empty background is created, nothing on it ...
I get a warning, but I think it is normal:
Warning 1: The 'LON' and/or 'LAT' fields of the source layer are not declared as numeric fields,
so the spatial filter cannot be turned into an attribute filter on them
Do you have an idea ?
Thanks!
My .csv (called ZZZ.csv), just the begining and the interesting fields:
RecordID,VehId,DateTime,LAT,LON
0,2232,2012-04-07 18:54:39,32.801926,-116.871742
0,2232,2012-04-07 18:54:40,32.801888,-116.871727
0,2232,2012-04-07 18:54:41,32.801849,-116.871704
My .vrt:
<OGRVRTDataSource>
<OGRVRTLayer name="ZZZ">
<SrcDataSource>ZZZ.csv</SrcDataSource>
<GeometryType>wkbPoint</GeometryType>
<LayerSRS>WGS84</LayerSRS>
<GeometryField encoding="PointFromColumns" x="LON" y="LAT"/>
</OGRVRTLayer>
</OGRVRTDataSource>
My python module to render the card:
"""module mapniktest"""
import mapnik
#Defining the envelope
MIN_LAT = 30
MAX_LAT = +35
MIN_LON = -120
MAX_LON =-110
MAP_WIDTH = 1000
MAP_HEIGHT = 500
#defining the datasource: the .vrt above
datasource = mapnik.Ogr(file="ZZZ.vrt",layer = "ZZZ")
#Creating layer, rules and styles
layer = mapnik.Layer("ZZZ")
layer.datasource = datasource
layer.styles.append("LineStyle")
stroke = mapnik.Stroke()
stroke.color = mapnik.Color("#008000")
stroke.add_dash(50, 100)
symbol = mapnik.LineSymbolizer(stroke)
rule = mapnik.Rule()
rule.symbols.append(symbol)
style = mapnik.Style()
style.rules.append(rule)
print style
#creating the map
map = mapnik.Map(MAP_WIDTH, MAP_HEIGHT, "+proj=longlat +datum=WGS84")
map.append_style("LineStyle", style)
map.background = mapnik.Color("#8080a0")
map.layers.append(layer)
#displaying the map
map.zoom_to_box(mapnik.Envelope(MIN_LON, MIN_LAT, MAX_LON, MAX_LAT))
mapnik.render_to_file(map, "map.png")
thks!!!!
The problem is that you are applying a LineSymbolizer to point data. You need to either apply a PointSymbolizer or a MarkersSymbolizer to point data.
Also Mapnik 2.1 and above supports reading directly from CSV files so you do not need to use a VRT and the OGR plugin, although both should work similarly.

Categories