Extract points and polygons for a mesh using VTK - python

I want to reduce the number of triangles in a mesh (STL file). Here is my code:
import vtk
filename = 'E://stl_file.stl'
reader = vtk.vtkSTLReader()
reader.SetFileName('filename.stl')
##convert polygonal mesh into triangle mesh
tri = vtk.vtkTriangleFilter()
tri.SetInputConnection(reader.GetOutputPort());
##decimate triangle
deci = vtk.vtkDecimatePro()
deci.SetInputConnection(tri.GetOutputPort())
deci.SetTargetReduction(0.9)
deci.PreserveTopologyOn()
it seems to work (at least it runs without errors). Now how can I extract points and triangles of the mesh?

Just as with most other vtk filters, deci.GetOuptut() will give you the result, which in this case should be a vtkPolyData that is a decimated version of your input mesh. you can get the points from the output object by output.GetPoints(), triangles by output.GetPolys() etc., see the documentation pages at http://www.vtk.org/doc/nightly/html/classvtkPolyData.html
BTW, there is a whole page with examples of VTK filters that would have given you the anwer, e.g. http://www.vtk.org/Wiki/VTK/Examples/Cxx/Meshes/Decimation. It's in C++ but it works in python the same way.

Related

How to obtain the centroid of cells/elements in VTK?

I know how to get the coordinates of the points in an unstructured grid from this post: vtk to matplotlib using numpy
However, I am trying to find a function that takes the unstructured VTK grid and returns the coordinates of the "centers" of the cells/elements. In particular, I'm working with a cylinder composed of quad elements. Here's the code I have so far:
from srlife import writers #library that helps make vtk grid
import numpy as np
import vtk
from vtk.util.numpy_support import vtk_to_numpy
vtkTube1 = writers.VTKWriter(tube1, 'tube1.vtk')
grid = vtk.vtkUnstructuredGrid()
points = vtk.vtkPoints()
for x,y,z in zip(X.flatten(), Y.flatten(), Z.flatten()):
points.InsertNextPoint(x,y,z)
grid.SetPoints(points)
vtkTube1._set_grid(grid)
getCellLocations = vtk_to_numpy(grid.GetCellLocationsArray()) #this just returns 1D array of integers that I don't know what to do with
I tried looking into meshio and griddata from scipy.interpolate but the documentation wasn't very helpful.
GetCellLocationsArray does not return geometrical information (see doc) but the indices of the beginning of each cell in the cell array returned by GetCells
You can try the vtkCellCenters filter.
Note: doc is C++ but python API is really similar.

Converting VTK image (.vti) data to VTK poly (.vtp) data

I'm trying to take some VTK image data generated from a 3-D numpy array and convert it into poly data so it can be read by a package that only takes .vtp as an input format. I chose to use the marching cubes algorithm to take my point/node data as input and give poly data as an output. The data is segmented into two phases (0 = black, 255 = white), so only one contour is necessary. I tried using the vtkPolyDataReader class to create an object for the vtkMarchingCubes class, then using vtkPolyDataWriter to take the contoured marching cubes object and save it as a VTP file:
import vtk
input = 'mydata.vti'
reader = vtk.vtkPolydataReader()
reader.SetFileName(input)
reader.Update()
contour = vtk.vtkMarchingCubes()
contour.SetInputConnection(reader.GetOutputPort())
contour.SetValue(0, 128.)
contour.Update()
writer = vtk.vtkPolyDataWriter()
writer.SetInputData(contour.GetOutput())
writer.SetFileName('mydata.vtp')
writer.Update()
writer.Write()
When I run the code, it takes much less time than it ought to (the input file is about 2 GB), and the VTP file the code creates is less than 1 KB. I've been banging my head against a wall over this and poring over the VTK documentation and some provided examples, but I can't figure out what I've done wrong.
To read a .vtki file you need to use vtk.vtkXMLImageDataReader. You are trying to read an image file with a vtk.vtkPolyDataReader, which is designed for reading surface meshes.

save and write a .vtk polydata file

I used this puyhon code to convert nifti file to .vtk polydata meshes
import itk
import vtk
input_filename = '/home/nour/Bureau/7ans/244/whatIneed/244seg_pve_2.nii.gz'
reader=itk.ImageFileReader[itk.Image[itk.UC,3]].New()
reader.SetFileName(input_filename)
reader.Update()
itkToVtkFilter = itk.ImageToVTKImageFilter[itk.Image[itk.UC,3]].New()
itkToVtkFilter.SetInput(reader.GetOutput())
myvtkImageData = itkToVtkFilter.GetOutput()
print("myvtkImageData")
and for saving and writing the .vtk file I used
writer = vtk.vtkPolyDataWriter()
writer.SetInputData()
writer.SetFileName("/home/nour/Bureau/7ans/244/whatIneed/Output.vtk")
writer.Write()
and here the error :
ERROR: In /work/standalone-x64-build/VTK-source/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx, line 809
vtkCompositeDataPipeline (0x4d9cac0): Input for connection index 0 on input port index 0 for algorithm vtkPolyDataWriter(0x4de3ea0) is of type vtkImageData, but a vtkPolyData is required.
I was wondering as to what would be a good way of writing a vtk Polydata file.
thanks
You need to transform your image (regular grid) into a polygonal mesh to be able to save it as a .vtk file.
For 2D meshes, this can be done using the vtkExtractSurface filter.
Another solution would be to use another format (.vtk is a legacy format):
If you want to save a regular grid, you can use the vtkXMLImageDataWriter, that uses the .vti extension.
If you want a unstructured mesh, you can use the vtkXMLPolyDataWriter, that uses the .vtp extension and gives a polygonal mesh. You can also use the vtkXMLUnstructuredGridWriter, that uses the .vtu extension and can contain 3D cells.
Images and polygonal meshes are fundamentally different types of data. You can't just cast an image into a mesh.
To get a mesh you would need to do some type of iso-surface extraction. Typically you would select some image intensity as the value of your surface, and then you would use an algorithm such as Marching Cubes to create a mesh of that value.
In VTK you can use the vtkContourFilter to create a mesh from an image. There are a number of examples on the VTK Example web site that show how to use the filter. Here is one:
https://lorensen.github.io/VTKExamples/site/Python/ImplicitFunctions/Sphere/
thank you very much. So as I understand :
1 reading nifti file (segmented file)
2 apply itktovtkfilter
3 creating meshes (using the vtkContourFilter)
4 and finaly convert it to polydata and save it to .vtk file
that's right ??

How can I cut a portion of a satellite image based on coordinates? (gdal)

I have a satellite image of 7-channels (Basically I have seven .tif files, one for each band). And I have a .csv file with coordinates of points-of-interest that are in the region shot by the satellite. I want to cut small portions of the image in the surroundings of each coordinate point. How could I do that?
As I don't have a full working code right now, it really doesn't matter the size of those small portions of image. For the explanation of this question let's say that I want them to be 15x15 pixels. So for the moment, my final objective is to obtain a lot of 15x15x7 vectors, one for every coordinate point that I have in the .csv file. And that is what I am stucked with. (the "7" in the "15x15x7" is because the image has 7 channels)
Just to give some background in case it's relevant: I will use those vectors later to train a CNN model in keras.
This is what I did so far: (I am using jupyter notebook, anaconda environment)
imported gdal, numpy, matplotlib, geopandas, among other libraries.
Opened the .gif files using gdal, converted them into arrays
Opened the .csv file using pandas.
Created a numpy array called "imagen" of shape (7931, 7901, 3) that will host the 7 bands of the satellite image (in form of numbers). At this point I just need to know which rows and colums of the array "imagen" correspond to each coordinate point. In other words I need to convert every coordinate point into a pair of numbers (row,colum). And that is what I am stucked with.
After that, I think that the "cutting part" will be easy.
#I import libraries
from osgeo import gdal_array
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import geopandas
from geopandas import GeoDataFrame
from shapely.geometry import Point
#I access the satellite images (I just show one here to make it short)
b1 = r"E:\Imágenes Satelitales\2017\226_86\1\LC08_L1TP_226086_20170116_20170311_01_T1_sr_band1.tif"
band1 = gdal.Open(b1, gdal.GA_ReadOnly)
#I open the .csv file
file_svc = "C:\\Users\\Administrador\Desktop\DeepLearningInternship\Crop Yield Prediction\Crop Type Classification model - CNN\First\T28_Pringles4.csv"
df = pd.read_csv(file_svc)
print(df.head())
That prints something like this:
Lat1 Long1 CropingState
-37.75737 -61.14537 Barbecho
-37.78152 -61.15872 Verdeo invierno
-37.78248 -61.17755 Barbecho
-37.78018 -61.17357 Campo natural
-37.78850 -61.18501 Campo natural
#I create the array "imagen" (I only show one channel here to make it short)
imagen = (np.zeros(7931*7901*7, dtype = np.float32)).reshape(7931,7901,7)
imagen[:,:,0] = band1.ReadAsArray().astype(np.float32)
#And then I can plot it:
plt.imshow(imagen[:,:,0], cmap = 'hot')
plt.plot()
Which plots something like this:
(https://github.com/jamesluc007/DeepLearningInternship/blob/master/Crop%20Yield%20Prediction/Crop%20Type%20Classification%20model%20-%20CNN/First/red_band.png)
I want to transform those (-37,-61) into something like (2230,1750). But I haven't figured it how yet. Any clues?

Check if a geopoint with latitude and longitude is within a shapefile

How can I check if a geopoint is within the area of a given shapefile?
I managed to load a shapefile in python, but can't get any further.
Another option is to use Shapely (a Python library based on GEOS, the engine for PostGIS) and Fiona (which is basically for reading/writing files):
import fiona
import shapely
with fiona.open("path/to/shapefile.shp") as fiona_collection:
# In this case, we'll assume the shapefile only has one record/layer (e.g., the shapefile
# is just for the borders of a single country, etc.).
shapefile_record = fiona_collection.next()
# Use Shapely to create the polygon
shape = shapely.geometry.asShape( shapefile_record['geometry'] )
point = shapely.geometry.Point(32.398516, -39.754028) # longitude, latitude
# Alternative: if point.within(shape)
if shape.contains(point):
print "Found shape for point."
Note that doing point-in-polygon tests can be expensive if the polygon is large/complicated (e.g., shapefiles for some countries with extremely irregular coastlines). In some cases it can help to use bounding boxes to quickly rule things out before doing the more intensive test:
minx, miny, maxx, maxy = shape.bounds
bounding_box = shapely.geometry.box(minx, miny, maxx, maxy)
if bounding_box.contains(point):
...
Lastly, keep in mind that it takes some time to load and parse large/irregular shapefiles (unfortunately, those types of polygons are often expensive to keep in memory, too).
This is an adaptation of yosukesabai's answer.
I wanted to ensure that the point I was searching for was in the same projection system as the shapefile, so I've added code for that.
I couldn't understand why he was doing a contains test on ply = feat_in.GetGeometryRef() (in my testing things seemed to work just as well without it), so I removed that.
I've also improved the commenting to better explain what's going on (as I understand it).
#!/usr/bin/python
import ogr
from IPython import embed
import sys
drv = ogr.GetDriverByName('ESRI Shapefile') #We will load a shape file
ds_in = drv.Open("MN.shp") #Get the contents of the shape file
lyr_in = ds_in.GetLayer(0) #Get the shape file's first layer
#Put the title of the field you are interested in here
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("P_Loc_Nm")
#If the latitude/longitude we're going to use is not in the projection
#of the shapefile, then we will get erroneous results.
#The following assumes that the latitude longitude is in WGS84
#This is identified by the number "4326", as in "EPSG:4326"
#We will create a transformation between this and the shapefile's
#project, whatever it may be
geo_ref = lyr_in.GetSpatialRef()
point_ref=ogr.osr.SpatialReference()
point_ref.ImportFromEPSG(4326)
ctran=ogr.osr.CoordinateTransformation(point_ref,geo_ref)
def check(lon, lat):
#Transform incoming longitude/latitude to the shapefile's projection
[lon,lat,z]=ctran.TransformPoint(lon,lat)
#Create a point
pt = ogr.Geometry(ogr.wkbPoint)
pt.SetPoint_2D(0, lon, lat)
#Set up a spatial filter such that the only features we see when we
#loop through "lyr_in" are those which overlap the point defined above
lyr_in.SetSpatialFilter(pt)
#Loop through the overlapped features and display the field of interest
for feat_in in lyr_in:
print lon, lat, feat_in.GetFieldAsString(idx_reg)
#Take command-line input and do all this
check(float(sys.argv[1]),float(sys.argv[2]))
#check(-95,47)
This site, this site, and this site were helpful regarding the projection check. EPSG:4326
Here is a simple solution based on pyshp and shapely.
Let's assume that your shapefile only contains one polygon (but you can easily adapt for multiple polygons):
import shapefile
from shapely.geometry import shape, Point
# read your shapefile
r = shapefile.Reader("your_shapefile.shp")
# get the shapes
shapes = r.shapes()
# build a shapely polygon from your shape
polygon = shape(shapes[0])
def check(lon, lat):
# build a shapely point from your geopoint
point = Point(lon, lat)
# the contains function does exactly what you want
return polygon.contains(point)
i did almost exactly what you are doing yesterday using gdal's ogr with python binding. It looked like this.
import ogr
# load the shape file as a layer
drv = ogr.GetDriverByName('ESRI Shapefile')
ds_in = drv.Open("./shp_reg/satreg_etx12_wgs84.shp")
lyr_in = ds_in.GetLayer(0)
# field index for which i want the data extracted
# ("satreg2" was what i was looking for)
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("satreg2")
def check(lon, lat):
# create point geometry
pt = ogr.Geometry(ogr.wkbPoint)
pt.SetPoint_2D(0, lon, lat)
lyr_in.SetSpatialFilter(pt)
# go over all the polygons in the layer see if one include the point
for feat_in in lyr_in:
# roughly subsets features, instead of go over everything
ply = feat_in.GetGeometryRef()
# test
if ply.Contains(pt):
# TODO do what you need to do here
print(lon, lat, feat_in.GetFieldAsString(idx_reg))
Checkout http://geospatialpython.com/2011/01/point-in-polygon.html and http://geospatialpython.com/2011/08/point-in-polygon-2-on-line.html
One way to do this is to read the ESRI Shape file using the OGR
library Link and then use the GEOS geometry
library http://trac.osgeo.org/geos/ to do the point-in-polygon test.
This requires some C/C++ programming.
There is also a python interface to GEOS at http://sgillies.net/blog/14/python-geos-module/ (which I have never used). Maybe that is what you want?
Another solution is to use the http://geotools.org/ library.
That is in Java.
I also have my own Java software to do this (which you can download
from http://www.mapyrus.org plus jts.jar from http://www.vividsolutions.com/products.asp ). You need only a text command
file inside.mapyrus containing
the following lines to check if a point lays inside the
first polygon in the ESRI Shape file:
dataset "shapefile", "us_states.shp"
fetch
print contains(GEOMETRY, -120, 46)
And run with:
java -cp mapyrus.jar:jts-1.8.jar org.mapyrus.Mapyrus inside.mapyrus
It will print a 1 if the point is inside, 0 otherwise.
You might also get some good answers if you post this question on
https://gis.stackexchange.com/
If you want to find out which polygon (from a shapefile full of them) contains a given point (and you have a bunch of points as well), the fastest way is using postgis. I actually implemented a fiona based version, using the answers here, but it was painfully slow (I was using multiprocessing and checking bounding box first). 400 minutes of processing = 50k points. Using postgis, that took less than 10seconds. B tree indexes are efficient!
shp2pgsql -s 4326 shapes.shp > shapes.sql
That will generate a sql file with the information from the shapefiles, create a database with postgis support and run that sql. Create a gist index on the geom column. Then, to find the name of the polygon:
sql="SELECT name FROM shapes WHERE ST_Contains(geom,ST_SetSRID(ST_MakePoint(%s,%s),4326));"
cur.execute(sql,(x,y))

Categories

Resources