Skip to main content

graphs and networks - Calculating morphometric properties


Is it possible to calculate segment length, segment diameter and branch angles for three dimensional geometry. Geometry contains vertex points and polygon surfaces as



  file = "http://i.stack.imgur.com/N6RYT.png";
Import[file, "Graphics3D"]

enter image description here


  data = Import[file, "VertexData"];
polysurface = Import[file, "PolygonData"];
Graphics3D[Point@data]

Answer



I decided to post another answer for several reasons:




  • I made up a full working contraption by using another approach

  • The previous answer could be useful for others, so I prefer to leave it there

  • Both answers are quite long, and having both in one post will clutter it


That said, the plan is the following:



  1. Collapse the points as before, pre-clustering them over an skeleton

  2. Calculate real clusters and collapse each point to the Center of Mass of its cluster

  3. From the polygon's description, get the surviving lines that form a 3D skeleton for the figure

  4. Find the branch points in the skeleton, and the incoming lines to each branch point


  5. Calculate the angles


Limitations/ To-dos:



  1. The method used is purely metric, without regarding the topological characteristics of the body. It works very well here, because the branches are not nearby parallel structures. May fail for tangled things

  2. There are two iterative loops that (perhaps) will be converted to something more Mathematica-ish if I find time/momentum and a way to do it.


Note: As the code here will be interspersed with graphics and comments, you may like to download a fully functional .nb in only one step. For that, execute the following and you'll get a new notebook ready for running, downloaded from imgur:


NotebookPut@ImportString[Uncompress@FromCharacterCode@
TakeWhile[Flatten@ImageData[Import["http://i.stack.imgur.com/tP8xo.png"],"Byte"],#!=0&],"NB"]


Ok. Let's get something done:


i = Import["http://dl.dropbox.com/u/68983831/final.vtk", "GraphicsComplex"];
j = i[[1]]; (*Point coordinates*)
(*Number of Nearest neighbors to take in account for collapsing the 3D structure*)
nn = 40;

j1 = j/2; (*some initial value for the iteration stopping test*)
(*Search for a quasi fixed point by moving towards the CoM of NN*)
While[Total[Abs[j1 - j], -1] > Length@j/100,

j1 = j;
(j[[#]] = Mean[Nearest[j, j[[#]], nn]]) & /@ Range@Length@j]
jp = j; (*To preserve j for later use*)
(*Show our proto-clusters*)
Show[Graphics3D[{Opacity[.1], EdgeForm[None],
GraphicsComplex[i[[1]], i[[2]]]}], ListPointPlot3D[j]]

Mathematica graphics


Now we have a nice arrangement of all our points over one skeleton, we need to proceed further and collapse each cluster to its Center of Mass, to reduce the number of points drastically and allow us to have segments linking those points. Sorry, iteration follows:


(*Now collpse every point to its corresponding CoM*)

(*maxdist is an estimation of a safe intercluster distance*)
maxdist = Mean[EuclideanDistance[j[[#]], First@Complement[
Nearest[j, j[[#]], 81],
Nearest[j, j[[#]], 80]]] & /@
RandomSample[Range@Length@j, IntegerPart[Length@j/5]]]/2;
h = 0;
agg (*buckets for clusters*) = n = {};
(*Calculate clusters, FindClusters[] doesn't do any good here :( *)
While[j != {},
h++;

AppendTo[agg, {First@j}];
While[j != {} && EuclideanDistance[agg[[h, 1]],
n = (Nearest[j, agg[[h, 1]], 1][[1]])] < maxdist,
j = DeleteCases[j, n];
AppendTo[agg[[h]], n];
]
];
(*Clusters calculated, collapse each cluster to its mean*)
rules = (Thread[Rule[#, x]] /. x -> Mean[#]) & /@ agg;
j = (jp /. Flatten[rules, 1]);


Now we have our points completely collapsed. Lets see what we found:


(*A plot showing the lines found*)
rul = Thread[Rule[Range@Length@j, j]];
vl = (i[[2]] /. rul) /. {a_List, b_List, c_List} /; a != b != c -> Sequence[];
uniLines = Union[Sort /@ Flatten[Subsets[#, {2}] & /@ vl[[1, 1]], 1]] /. {a_, a_} -> Sequence[];
Graphics3D@Table[{Thick, Hue@RandomReal[], Line@l}, {l, uniLines}]

Mathematica graphics


Nice. Now we can explore the endpoints for each segment:



Manipulate[Show[
Graphics3D@{Opacity[.1], EdgeForm[None], GraphicsComplex[i[[1]], i[[2]]]},
Graphics3D@Table[{Hue[o/Length@uniLines], Line@uniLines[[o]]}, {o, 1, Length@uniLines}],
Graphics3D[{PointSize[Medium], Point[Union@Flatten[uniLines, 1]]}],
Graphics3D[{Red, PointSize[Large], Point[uniLines[[w]]]}]
], {w, 1, Length@uniLines, 1}]

Mathematica graphics


We are almost there, now we identify the branch points and the related lines:


(*Now get and process the bifurcation points*)

branchPt = First /@ Extract[#, Position[Length /@ #, 3]] &@(Gather@Flatten[uniLines, 1]);
(*Get the incoming lines to each point *)
branchLn = Extract[uniLines, #] &/@ ((Position[uniLines, #] & /@ branchPt)[[All,All,1 ;;1]]);

Let's see if we identified our branch points correctly:


(*Plot Bifurcations*)
Show[Graphics3D[{Opacity[.1], EdgeForm[None],GraphicsComplex[i[[1]], i[[2]]]}],
Graphics3D[Line /@ branchLn],
Graphics3D[{Red, PointSize[Large], Point[branchPt]}]]


Mathematica graphics


Now we get normalized vectors along the lines, outgoing from each branch point


(*Get the normalized vectors at the branch points*)
aux[x_] := Map[# - First@Commonest[Flatten[x, 1]] &, x, {2}];
nv = Map[Normalize, ((Select[Flatten[aux[#], 1], # != {0, 0, 0} &]) & /@ branchLn), {2}];

Finally we are going to get our desired 9 angles. As you can observe, in all cases the three segments at each branch point are almost coplanar, so the sum of the angles ought to be near 2 Pi. We now calculate the angles and verify the coplanarity observation:


(*Angles and kind of Coplanarity test*)
(VectorAngle @@@ Subsets[#, {2}]) & /@ nv
Total /@ ((VectorAngle @@@ Subsets[#, {2}]) & /@ nv)


(* Angles: {{2.0326, 2.54025, 1.70803},
{1.71701, 2.4161, 2.14805},
{2.14213, 1.98282, 2.158}}*)

(* Coplanarity test (Very near 2 Pi): {6.28087, 6.28115, 6.28295} *)

Comments

Popular posts from this blog

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...