跳转至

Quickly Read and Display Data From a New File Format - Paleontology with IDL

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/quickly-read-and-display-data-from-a-new-file-format-paleontology-with-idl

17374 Rate this article:

3.3

Quickly Read and Display Data From a New File Format - Paleontology with IDL

Jim Pendleton Thursday, March 17, 2016

Over time, you may run across data sets that you'd like to read in IDL, file formats that lack any built-in utilities in the language.

Quite frequently I use the following technique for building custom data readers for IDL in my work for the Custom Solutions Group.

A recent article I ran across described a newly-minted repository of paleontological data, including 3D models of bones of long-lost species. MorphoSource.org, is a Duke University project funded in part by grants from the National Science Foundation. Commercial use of the provided media is strictly prohibited, according to their website.

My goal was to see how easy it would be to read some of this data and display it in an interactive IDL display. It was very simple, indeed!

Searching the database I found a number of interesting data types, including 3D models. One example, shown above and copyright the Florida Museum of Natural History, is the tooth of a Calippus elachistus, an early horse-like mammal that may have roamed the area in Florida during the Miocene epoch.

I requested a user account from MorphoSource.org and their robot sent me back the OK to grab some data, for instructional and non-commercial use only, of course.

Drilling down a little further I located some ASCII-format "Avizo PLY"-format files. I've not worked with this type of file before, and IDL doesn't have a built-in reader, but opening the file in a standard text editor shows that the format is straightforward.

Given this information, it's easy to build a utility in IDL that will both read the data and display the 3D object.

Though we won't attempt to parse all the data in the file, we can quickly locate the two major pieces of data we need for visualization, the list of vertex coordinates and the connectivity of those vertices.

The header begins with the following

ply
format ascii 1.0
obj_info Format: Avizo PLY
element vertex 249741
property float32 x
property float32 y
property float32 z
element face 495182
property list uint8 int32 vertex_indices

What this tells me is that somewhere in the file there are 249741 vertices with floating point (X,Y,Z) coordinates and 495182 "faces" or connectivity lists. A connectivity list tells us how the indices in the vertex list connect to one another.

(In other files in the MorphoSource data sets, you may also find data for vertex coloring, which adds greater interest!)

There are some additional lines in the header which we will ignore.  The header ends with an "end_header" line.

The lines immediately after the header are series of three floating point elements, for example

end_header
34.6223 25.3743 11.5595
34.5367 25.4449 11.5501
34.6183 25.4799 11.5998

Gosh, those look like vertex coordinates, don't they?  Now we can follow a hunch that if we look down the file another 249741 lines (the vertex count), we'll find data in a different format.  Sure enough, we find a transition.

23.9367 23.4851 43.5914
24.1271 23.4578 43.5783
24.0427 23.5348 43.5879
3 0 4 1 0
3 1 2 0 0
3 3 8 7 0
3 8 3 0 0

The connectivity list of each line starts with the number of vertices in a polygon's face.In this case they're all triangles, therefore the value is "3". Following the vertex count are the three zero-based indices of the vertices that make up each triangular facet.The last number in each line may indicate whether the polygon is open or closed. They all have a value of 0, so we'll just ignore the fifth value.

Not coincidentally, these vertex and connectivity lists are precisely in the format that IDL's IDLgrPolygon object uses.

After downloading the file to my desktop, I'll read the contents of the file into a string array variable named "val".

file = 'UF-53577_M5794-5222_Calippus_elachistus.ply'
n = file_lines(file)
val = strarr(n)
openr, lun, file, /get_lun
readf, lun, val
free_lun, lun

Locate the line that starts with the text "element vertex". We'll split that line on spaces and pull out the third element.  That's the vertex count.

vertexline = where(val.startswith('element vertex'))
vertexcount = long((val[vertexline[0]].split(' '))[2])

Repeat for the polygon connectivity count, which starts with "element face".

polygonline = where(val.startswith('element face'))
polygoncount = long((val[polygonline[0]].split(' '))[2])

Create IDL arrays in which to store the vertices and polygons.

vertices = fltarr(3, vertexcount)
poly = lonarr(4, polygoncount)

Remove the header from the string array so we're positioned at the first line of vertices.

last = (where(val.startswith('end_header')))[0]
val = val[last + 1: *]

Loop over the lines containing the vertex data, and extract the values into the vertices array.

for i = 0L,  vertexcount - 1 do $
  vertices[0, i] = float(((val[i]).trim()).split(' '))

Remove the vertices from the start of the string array and repeat the procedure for the connectivity.

val = val[vertexcount:*]
for i = 0L, polygoncount - 1 do $
  poly[0, i] = (long(((val[i]).trim()).split(' ')))[0:3]

For the display, we only need two lines of code.  Populate an IDLgrPolygon object with our data.

opolygon = idlgrpolygon(vertices, poly = poly, style = 2, color = !color.gold)

Finally, display the data in one of IDL's interactive tools, XOBJVIEW.

xobjview, opolygon

Did this horse have a cavity, or is the hole in the crown a spot that's been drilled for sampling?

Below is an example routine that incorporates the code shown above, and generates an MP4 animation in your temporary directory.  You will need to request a MorphoSource account to download your own PLY-format file as input. Feel free to generalize the code for a more robust PLY-format solution.



pro read_ply, dir, vertices

file = filepath('UF-53577_M5794-5222_Calippus_elachistus.ply')

n = file_lines(file)

val = strarr(n)

openr, lun, file, /get_lun

readf, lun, val

free_lun, lun

vertexline = where(val.startswith('element vertex'))

vertexcount = long((val[vertexline[0]].split(' '))[2])

polygonline = where(val.startswith('element face'))

polygoncount = long((val[polygonline[0]].split(' '))[2])

vertices = fltarr(3, vertexcount)

poly = lonarr(4, polygoncount)

last = (where(val.startswith('end_header')))[0]

val = val[last + 1: *]

for i = 0L,  vertexcount - 1 do $

vertices[0, i] = float(((val[i]).trim()).split(' '))

val = val[vertexcount:*]

for i = 0L, polygoncount - 1 do $

poly[0, i] = (long(((val[i]).trim()).split(' ')))[0:3]

;poly = reform(poly, poly.length, /overwrite)

om = idlgrmodel()

opolygon = idlgrpolygon(vertices, style = 2, poly = poly, color = !color.gold)

om->add, opolygon

mx = mean(vertices[0, *])

my = mean(vertices[1, *])

mz = mean(vertices[2, *])

om->translate, -mx, -my, -mz

xobjview, om, tlb = tlb, xsize = 256, ysize = 256

frames = list()

temppng = filepath(/tmp, 'frame.png')

for i = 0, 360, 3 do begin ; rotate

om->rotate, [1, 1, 0], 3

xobjview, refresh = tlb

xobjview_write_image, temppng, 'png'

frames.add, read_png(temppng)

file_delete, temppng

endfor

for i = 1, 100, 3 do begin ; zoom in

om->scale, 1.03, 1.03, 1.03

xobjview, refresh = tlb

xobjview_write_image, temppng, 'png'

frames.add, read_png(temppng)

file_delete, temppng

endfor

for i = 1, 100, 3 do begin ; zoom out

om->scale, 1/1.03, 1/1.03, 1/1.03

xobjview, refresh = tlb

xobjview_write_image, temppng, 'png'

frames.add, read_png(temppng)

file_delete, temppng

endfor

tempvid = filepath(/tmp, 'horsetooth.mp4')

ovid = IDLffVideoWrite(tempvid)

fs = (frames[0]).dim

vidstream = ovid.AddVideoStream(fs[1], fs[2], 24)

foreach frame, frames do $

t = ovid.put(vidstream, frame)

obj_destroy, ovid

spawn, tempvid

end

A Fresh Perspective is Everything! Spatiotemporal Analysis: Red is Fled, Blue is New!