Calculating the volume under a surface - r

I have created a 3D plot (a surface) using wireframe function. I wonder if there is any functions by which I can calculate the volume under the surface in a 3D plot?
Here is a sample of my data plus the wrieframe syntax I used to create my 3D (surface) plot:
x1<-c(13,27,41,55,69,83,97,111,125,139)
x2<-c(27,55,83,111,139,166,194,222,250,278)
x3<-c(41,83,125,166,208,250,292,333,375,417)
x4<-c(55,111,166,222,278,333,389,445,500,556)
x5<-c(69,139,208,278,347,417,487,556,626,695)
x6<-c(83,166,250,333,417,500,584,667,751,834)
x7<-c(97,194,292,389,487,584,681,779,876,974)
x8<-c(111,222,333,445,556,667,779,890,1001,1113)
x9<-c(125,250,375,500,626,751,876,1001,1127,1252)
x10<-c(139,278,417,556,695,834,974,1113,1252,1391)
df<-data.frame(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)
df.matrix<-as.matrix(df)
wireframe(df.matrix,
aspect = c(61/87, 0.4),scales=list(arrows=FALSE,cex=.5,tick.number="10",z=list(arrows=T)),ylim=c(1:10),xlab=expression(phi1),ylab="Percentile",zlab=" Loss",main="Random Classifier",
light.source = c(10,10,10),drape=T,col.regions = rainbow(100, s = 1, v = 1, start = 0, end = max(1,100 - 1)/100, alpha = 1),screen=list(z=-60,x=-60))
Note: my real data is a 100X100 matrix
Thanks

The data you are feeding to wireframe is a grid of values. Hence one estimate of the volume of whatever underlying surface this is approximating is the sum of the grid values multiplied by the grid cell areas. This is just like adding up the heights of histogram bars to get the number of values in your histogram.
The problem I see with you doing this on your data is that the cell areas are going to be in odd units - percentiles on one axis, phi on the other has unknown units, so your volume is going to have units of loss times units of percentile times units of phi.
This isn't a problem if you want to compare volumes of similar things on exactly the same grid, but if you have surfaces on different grids (different values of phi, or different percentiles) then you need to be careful.
Now, noting that wireframe doesn't draw like a 3d histogram would (looking like square tower blocks) this gives us another way to estimate the volume. Your 10x10 matrix is plotted as 9x9 squares. Divide each of those squares into triangles and then compute the volume of the 192 right truncated triangular prisms (I think this is what they are - they are equilateral triangular prisms with a right angle and one sloping end). The formula for that should be out there somewhere. Probably base area times height to the centroid of the triangle or something.
I thought maybe this would be in the raster package, but it isn't. There's code for computing the surface area but not the volume! I'm sure the raster maintainer would be happy to have some code for this!

If the points are arbitrary (ie, don't follow smooth function), it seems like you're looking for the volume of the convex hull (minimum surface) surrounding these points. One package to help you calculate this is alphashape3d.
You'll need a 3-column matrix of the coordinates to form the right type of object to make the calculation but it seems rather straight-forward.

Related

Calculate area on 3D surface mesh encolosed by four arbitrary points from coordinate data

I have human facial data as below:
library(Rvcg)
library(rgl)
data(humface)
lm <- matrix(c(1.0456182e+001, -3.5877686e+001, 5.0972912e+001, 2.2514189e+001,
8.4171227e+001, 6.6850304e+001, 8.3239525e+001, 9.8277359e+000,
6.5489395e+001, 4.2590347e+001, 4.0016006e+001, 5.9176712e+001),
4)
shade3d(humface, col="#add9ec", specular = "#202020", alpha = 0.8)
plot3d(lm, type = "s", col = "red", xlab = "x", ylab = "y", zlab = "z",
size = 1, aspect = FALSE,add=T)
for lm, four landmarks are placed on the surface of the mesh, in the following oder:
The yellow lines are drawn by hand for illustration purpose. I wish to calculate the surface area of the quarilateral enclosed by the four red dots, i.e., the surface area inside the yellow edges.
If surface area cannot be calculated, I also welcome methods to calculate the area (not area of the surface of the face) of the quadrilateral. I know one could calculate the sum of areas of triangle 123 and triangle 234. However, I my real application, I have no idea of the ordering and relative spatial position of the four points. Since I have thousands of qudrilateral areas to calculate, it is impossible to plot each quadrilateral and determine how to decompose the quadrilateral into two triangles. For example, I may accidentally pick triangle 123 and triangle 124, and the sum of these two triangle ares is not what I want.
Therefore, I am interested in either surface area or area of the quadrilateral. Solution to either is welcome. I just do not want to plot each quadrilateral and I want an area value directly computed from the coordinates.
The rgl::shadow3d function can compute a projection of the quad onto the face. Then you'd compute the area by summing the areas of triangles and quads in the result. #DiegoQueiroz gives you some pointers for doing that,
plus the Rvcg package contains vcgArea:
quad <- mesh3d(lm, triangles = cbind(c(1,2,4), c(1,4,3)))
projection <- shadow3d(humface, quad, plot = FALSE)
Here's what that looks like:
shade3d(projection, col = "yellow", polygon_offset = -1)
The projection ends up containing 3604 triangles; the area is
vcgArea(projection)
# [1] 5141.33
There are a few ambiguities in the problem: the quadrilateral isn't planar, so you'd get a different one if you split it into triangles along the other diagonal. And the projection of the quad onto the face is different depending on which direction you choose. I used the default of projecting along the z axis, but in fact the face isn't perfectly aligned that way.
EDITED TO ADD:
If you don't know how to decompose the 4 points into a single quadrilateral, then project all 4 triangles (which form a tetrahedron in 3-space):
triangles <- mesh3d(lm, triangles = cbind(c(1,2,3), c(1,2,4), c(1,3,4), c(2,3,4))
projection <- shadow3d(humface, triangles, plot = FALSE)
This gives a slightly different region than projecting the quad:
vcgArea(projection)
# [1] 5217.224
I think the reason for this is related to what I referred to in the comment above: the area depends on the "thickness" of the object being projected, since the quad is not planar.
I believe your question is more appropriate for math.stackexchange.com because I think it's more a question about the math behind the code than the code itself.
If you are concerned about precision, you may want to use techniques for smoothing the calculated area of a mesh, like the one presented in this paper.
However, if you don't really need that area to really model the surface, then you can ignore the face and compute the convex quadrilateral area using the many available formulas for that, however, the simplest one requires you to have the vectors that correspond to the quadrilateral's diagonals (which you can find by checking this question)
If you decide to find the diagonals and use the simplest vectorial formula (half the magnitude of the cross-product between the diagonals), you should use the cross() and Norm() functions from the pracma package as R's crossprod() computes a different type of cross product than the one you will need.

R - locate intersection of two curves

There are a number of questions in this forum on locating intersections between a fitted model and some raw data. However, in my case, I am in an early stage project where I am still evaluating data.
To begin with, I have created a data frame that contains a ratio value whose ideal value should be 1.0. I have plotted the data frame and also used abline() function to plot a horizontal line at y=1.0. This horizontal line and the plot of ratios intersect at some point.
plot(a$TIME.STAMP, a$PROCESS.RATIO,
xlab='Time (5s)',
ylab='Process ratio',
col='darkolivegreen',
type='l')
abline(h=1.0,col='red')
My aim is to locate the intersection point, say x and draw two vertical lines at x±k, as abline(v=x-k) and abline(v=x+k) where, k is certain band of tolerance.
Applying a grid on the plot is not really an option because this plot will be a part of a multi-panel plot. And, because ratio data is very tightly laid out, the plot will not be too readable. Finally, the x±k will be quite valuable in my discussions with the domain experts.
Can you please guide me how to achieve this?
Here are two solutions. The first one uses locator() and will be useful if you do not have too many charts to produce:
x <- 1:5
y <- log(1:5)
df1 <-data.frame(x= 1:5,y=log(1:5))
k <-0.5
plot(df1,type="o",lwd=2)
abline(h=1, col="red")
locator()
By clicking on the intersection (and stopping the locator top left of the chart), you will get the intersection:
> locator()
$x
[1] 2.765327
$y
[1] 1.002495
You would then add abline(v=2.765327).
If you need a more programmable way of finding the intersection, we will have to estimate the function of your data. Unfortunately, you haven’t provided us with PROCESS.RATIO, so we can only guess what your data looks like. Hopefully, the data is smooth. Here’s a solution that should work with nonlinear data. As you can see in the previous chart, all R does is draw a line between the dots. So, we have to fit a curve in there. Here I’m fitting the data with a polynomial of order 2. If your data is less linear, you can try increasing the order (2 here). If your data is linear, use a simple lm.
fit <-lm(y~poly(x,2))
newx <-data.frame(x=seq(0,5,0.01))
fitline = predict(fit, newdata=newx)
est <-data.frame(newx,fitline)
plot(df1,type="o",lwd=2)
abline(h=1, col="red")
lines(est, col="blue",lwd=2)
Using this fitted curve, we can then find the closest point to y=1. Once we have that point, we can draw vertical lines at the intersection and at +/-k.
cross <-est[which.min(abs(1-est$fitline)),] #find closest to 1
plot(df1,type="o",lwd=2)
abline(h=1)
abline(v=cross[1], col="green")
abline(v=cross[1]-k, col="purple")
abline(v=cross[1]+k, col="purple")

How to quantize surface normals

I am trying to quantize surface normals into let's say 8 bins.
For example, when computing features like HOG to quantize 2D gradients [x,y] into 8 bins we just take the angle with the y plane i.e. arctan(y/x) which will give us an angle between 0-360.
My question is, given a 3D direction [x,y,z], a surface normal in this case, how can we histogram it in a similar way? Do we just project onto one plane and use that angle i.e. the dot product of [x,y,z] and [0,1,0] for example?
Thanks
EDIT
I also read a paper recently where they quantized surface normals by measuring angles between normal and precomputed vectors that which are arranged around a right circular cone shape. I have added a link to this paper in the question (section 3.3.2 last paragraph), is this an effective approach? And if so, how do we compute these vectors?
Quantizing a continuous topological space corresponds to partitioning it and assigning labels to each partition. The straightforward standard approach for this scenario (quantizing normals) is as follows.
Choose your favorite uniform polyhedron:
http://en.wikipedia.org/wiki/Tetrahedron (4 faces)
http://en.wikipedia.org/wiki/Cube (6 faces)
http://en.wikipedia.org/wiki/Octahedron (8 faces)
http://en.wikipedia.org/wiki/Dodecahedron (12 faces)
http://en.wikipedia.org/wiki/Icosahedron (20 faces)
In general: http://en.wikipedia.org/wiki/Schl%C3%A4fli_symbol
Develop a mapping function from a normal on the unit sphere to the face of your chosen polyhedron that the normal intersects.
I would advise doing an argmax across polyhedron faces, taking the dot product of your normal and each polyhedron face normal. The one that gives the highest dot product is the face your normal should be binned into.
Use the face normal for each polyhedron face as the label for that face.
Prefer this approach to the approach suggested by others of mapping to spherical coordinates and then binning those. That approach suffers from too much sensitivity near the poles of the sphere.
Edit
In the paper you added to your question, the same idea is being used. There, however, the normals are restricted to a hemisphere - the only surfaces directly visible in an image have surface normals no more than 90 degrees away from the vector from the surface to the viewpoint.
The paper wants to quantize these surface normals into 8 values, represented by 8-bit integers with exactly one bit set to 1 and the rest set to 0. The 8 precomputed normals are computed as:
ntx = cos(a)*cos(t)
nty = cos(a)*sin(t)
ntz = sin(a)
where a = pi/4 and t = 0, pi/4, 2*pi/4, 3*pi/4, ..., 7*pi/4.
Notice
[cos(a)*cos(t)]2 + [cos(a)*sin(t)]2 + [sin(a)]2 = cos2(a)[cos2(t) + sin2(t)] + sin2(a) = cos2(a) + sin2(a) = 1
given a 3D direction [x,y,z], a surface normal in this case, how can
we histogram it in a similar way?
In the first case you quantize the polar orientation theta of the gradients. Now you need to quantize the spherical orientations theta and phi in a 2D histogram.
Do we just project onto one plane and use that angle
The binning of the sphere determines how you summarize the information to build a compact yet descriptive histogram.
Projecting the normal is not a good idea, if theta is more important than phi, just use more bins for theta
EDIT
Timothy Shields points in his comment and his answer that a regular binning of theta and phi won't produce a regular binning over the sphere as the bins will be bunched toward the poles.
His answer gives a solution. Alternatively, the non-regular binning described here can be hacked as follows:
Phi is quantized regularly in [0,pi]. For theta rather than quantizing the range [0,pi], the range [-1,1] is quantized instead;
For each quantized value u in [-1,1], theta is computed as
theta = arcsin(sqrt(1 - u * u)) * sign(u)
sign(u) returns -1 if u is negative, 1 otherwise.
The computed theta along with phi produce a regular quantization over the sphere.
To have an idea of the equation given above look at this article. It describes the situation in the context of random sampling though.
EDIT
In the above hack Timothy Shields points out that only the area of the bins is considered. The valence of the vertices (point of intersection of neighboring bins) won't be regular because of the poles singularity.
A hack for the previous hack would be to remesh the bins into a regular quadrilateral mesh and keep the regular area.
A heuristic to optimize this problem with the global constraints of having the same valence and the area can be inspired from Integer-Grid Maps Quad Meshing.
With the two hacks, this answer is too hacky and a little out of context as opposed to Timothy Shields answer.
A 3-dimensional normal cannot be quantized into a 1-D array as easily as for a 2-D normal (e.g., using arctan). I would recommend histogramming it into a 2-d space with a polar angle and an azimuth angle. For example, use spherical coordinates where the r (radius) value is always 1.0 (since your surface normal is normalized, length 1.0). In this case, you can throw away the r-value and just use polar angle θ (theta), and azimuthal angle φ (phi) to quantize the 3D normal.

Determine if a set of points lie on a regular grid

Problem: Suppose you have a collection of points in the 2D plane. I want to know if this set of points sits on a regular grid (if they are a subset of a 2D lattice). I would like some ideas on how to do this.
For now, let's say I'm only interested in whether these points form an axis-aligned rectangular grid (that the underlying lattice is rectangular, aligned with the x and y axes), and that it is a complete rectangle (the subset of the lattice has a rectangular boundary with no holes). Any solutions must be quite efficient (better than O(N^2)), since N can be hundreds of thousands or millions.
Context: I wrote a 2D vector field plot generator which works for an arbitrarily sampled vector field. In the case that the sampling is on a regular grid, there are simpler/more efficient interpolation schemes for generating the plot, and I would like to know when I can use this special case. The special case is sufficiently better that it merits doing. The program is written in C.
This might be dumb but if your points were to lie on a regular grid, then wouldn't peaks in the Fourier transform of the coordinates all be exact multiples of the grid resolution? You could do a separate Fourier transform the X and Y coordinates. If theres no holes on grid then the FT would be a delta function I think. FFT is O(nlog(n)).
p.s. I would have left this as a comment but my rep is too low..
Not quite sure if this is what you are after but for a collection of 2d points on a plane you can always fit them on a rectangular grid (down to the precision of your points anyway), the problem may be the grid they fit to may be too sparsly populated by the points to provide any benefit to your algorithm.
to find a rectangular grid that fits a set of points you essentially need to find the GCD of all the x coordinates and the GCD of all the y coordinates with the origin at xmin,ymin this should be O( n (log n)^2) I think.
How you decide if this grid is then too sparse is not clear however
If the points all come only from intersections on the grid then the hough transform of your set of points might help you. If you find that two mutually perpendicular sets of lines occur most often (meaning you find peaks at four values of theta all 90 degrees apart) and you find repeating peaks in gamma space then you have a grid. Otherwise not.
Here's a solution that works in O(ND log N), where N is the number of points and D is the number of dimensions (2 in your case).
Allocate D arrays with space for N numbers: X, Y, Z, etc. (Time: O(ND))
Iterate through your point list and add the x-coordinate to list X, the y-coordinate to list Y, etc. (Time: O(ND))
Sort each of the new lists. (Time: O(ND log N))
Count the number of unique values in each list and make sure the difference between successive unique values is the same across the whole list. (Time: O(ND))
If
the unique values in each dimension are equally spaced, and
if the product of the number of unique values of each coordinate is equal to the number of original points (length(uniq(X))*length(uniq(Y))* ... == N,
then the points are in a regular rectangular grid.
Let's say a grid is defined by an orientation Or (within 0 and 90 deg) and a resolution Res. You could compute a cost function that evaluate if a grid (Or, Res) sticks to your points. For example, you could compute the average distance of each point to its closest point of the grid.
Your problem is then to find the (Or, Res) pair that minimize the cost function. In order to narrow the search space and improve the , some a heuristic to test "good" candidate grids could be used.
This approach is the same as the one used in the Hough transform proposed by jilles. The (Or, Res) space is comparable to the Hough's gamma space.

2D Shape Scaling

Hi well I got a problem about scaling shapes.Well I m trying to scale two similar shapes.It is in 2d and each shape has n points .I found a statement like this from a paper I read
"The size of a shape is the root mean square distance between the shape points and
it's centroid."
So from this point if I calculate the size of both shapes S1 and S2 and lets say S1=xS2 so if I create scaling matrix like this
[x 0]
[0 x]
(i just wrote 2x2 matrix i know it should be different) and if I mulitply it with S2 are their shapes aligned? Thx
Well I think I found a solution .It is done using a scale metric instead of real scale value.
if the shape 3 points (x1,y1) (x2,y2) (x3,y3) a scale metric S is square root of sum of each points squared values like
mean x=(x1+x2+x3)/3
mean y=(y1+y2+y3)/3
S=((x1-x)^2 +(y1-y)^2+(x2-x)^2 +(y2-y)^2+(x3-x)^2 +(y3-y)^2)^1/2
and if this scale metric is calculated for both shapes there will be an equation like this S1=AS2
and if all points of shape 2 is multiplied with value of A they will have similar shapes.
It reminds me of Fant's Resampling Algorithm, smooth...
Not sure it fits your question.
If I'm understanding what you wrote, then multiplying your matrix by a shape, (say, S2) will scale Each of S2's points by a factor of x.
This says nothing about their alignment. this paper might help your understanding if you want to do it efficiently

Resources