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.
Related
I need a method to project 3d conics to 2d. None of the articles tell how to do this with rational beziers. Another thing I need a method for is moving 3d or 2d conics to 4d or 3d respectively (as in a reverse projection). I read somewhere that rational beziers can be split by moving them to a higher dimension and splitting the resulting non-rational curve with de Casteljau and then moving back. I seem to recall that perspective projection of conic beziers can be represented exactly with conic beziers, and that it may involve splitting into several curves. I don't understand any of the articles on any site on beziers.
Since there aren't any better answers, here's what I can offer off the top of my head...
Perspective transformation can change parabolas into ellipses or hyperbolas and vice-versa, so even though P0, P1, and P2 can be directly mapped, the weights will change.
Assuming a conic with weights (1,w,1), however, the distance along the line from (P0+P2)/2 to P1 at which it intersects the curve is simply related to the weight w, and that lets you find the new weight as follows:
Map P0, P1, and P2 to P0', P1', P2'
Calculate the midpoint M' = (P1'+P2')/2
Inverse map M' to M, and calculate the intersection point I of the line M-P1 with the original curve.
Map the intersection point I to I', to get the point at which the new curve should intersect M'-P1'
Calculate the new weight w' from the position of the intersection I'. The curve gets to I at t=0.5, so w' = (M'-I')/(P1'-I'). Note that this division makes sense, because the vectors being divided are collinear. You can divide their lengths or just the largest coordinate.
If you expand out all the steps, I'm sure there are ways to simplify this procedure.
The calculation is for games so a approximation is better than a computational intense correct calculation. What i need to find is the radius of the given arc.
In this case the "curve" can be thought of a arc as this approximation is good enough. So the situation look like this:
I know:
the length the green lines (which are equal)
the length of the blue arc
the value of α in degrees
What i need to know:
the radius r
Background - Actually i need the radius for two things:
To calculate the length of a arc B with offset of x from the center arc. So that r of B would be r + x
To calculate the centrifugal force of a vehicle driving on that curve
What i tried:
I know how to calculate the radius if i have the circumference and the inner angle of the arc. But i am completely stuck with the given information though i am sure it should not be too complicated ..
If you think a triangle with right angle at the middle of the green line and another point at the center of the circle, then the angle at the intersection of the green segments in that triangle is α/2 and the cosine ratio for that angle is
cos(α/2)*r = g/2
g the length of a green segment.
The angle at the top of a pie slice is π-α so that for the length b of the blue curve segments you should get
b = (π-α)*r
The radius values you get from both formulas should not differ more than the measurement errors let expect.
Alternatively, you could use Catmull-Rom (https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline), Hermite (https://en.wikipedia.org/wiki/Cubic_Hermite_spline), or natural (https://en.wikipedia.org/wiki/Spline_interpolation) cubic spline interpolation to calculate the path between the points.
This will give you cubic polynomials for the (x,y) coordinates, and it's easy to take their 2nd derivatives to get the direction and magnitude of acceleration.
Given a point p exterior to an axially aligned, origin centered ellipse E, find the (upto) four unique normals to E passing through p.
This is not a Mathematica question. Direct computation is too slow; I am willing to sacrifice precision and accuracy for speed.
I have searched the web, but all I found involved overly complex calculations which if implemented directly appear to lack the performance I need. Is there a more "programmatical" way to do this, like using matrices or scaling the ellipse into a circle?
Let's assume the ellipse E is in "standard position", center at the origin and axes parallel to the coordinate axes:
(x/a)^2 + (y/b)^2 = 1 where a > b > 0
The boundary cases a=b are circles, where the normal lines are simply ones that pass through the center (origin) and are thus easy to find. So we omit discussion of these cases.
The slope of the tangent to the ellipse at any point (x,y) may be found by implicit differentiation:
dy/dx = -(b^2 x)/(a^2 y)
For the line passing through (x,y) and a specified point p = (u,v) not on the ellipse, that is normal to ellipse E when its slope is the negative reciprocal of dy/dx:
(y-v)/(x-u) * (-b^2 x)/(a^2 y) = -1 (N)
which simplifies to:
(x - (1+g)u) * (y + gv) = -g(1+g)uv where g = b^2/(a^2 - b^2)
In this form we recognize it is the equation for a right rectangular hyperbola. Depending on how many points of intersection there are between the ellipse and the hyperbola (2,3,4), we have that many normals to E passing through p.
By reflected symmetry, if p is assumed exterior to E, we may take p to be in the first quadrant:
(u/a)^2 + (v/b)^2 > 1 (exterior to E)
u,v > 0 (1'st quadrant)
We could have boundary cases where u=0 or v=0, i.e. point p lies on an axis of E, but these cases may be reduced to solving a quadratic, because two normals are the (coinciding) lines through the endpoints of that axis. We defer further discussion of these special cases for the moment.
Here's an illustration with a=u=5,b=v=3 in which only one branch of the hyperbola intersects E, and there will be only two normals:
If the system of two equations in two unknowns (x,y) is reduced to one equation in one unknown, the simplest root-finding method to code is a bisection method, but knowing something about the possible locations of roots/intersections will expedite our search. The intersection in the first quadrant is the nearest point of E to p, and likewise the intersection in the third quadrant is the farthest point of E from p. If the point p were a good bit closer to the upper endpoint of the minor axis, the branches of the hyperbola would shift together enough to create up to two more points of intersection in the fourth quadrant.
One approach would be to parameterize E by points of intersection with the x-axis. The lines from p normal to the ellipse must intersect the major axis which is a finite interval [-a,+a]. We can test both the upper and lower points of intersection q=(x,y) of a line passing through p=(u,v) and (z,0) as z sweeps from -a to +a, looking for places where the ellipse and hyperbola intersect.
In more detail:
1. Find the upper and lower points `q` of intersection of E with the
line through `p` and `(z,0)` (amounts to solving a quadratic)
3. Check the sign of a^2 y(x-u) - b^2 x(y-v) at `q=(x,y)`, because it
is zero if and only `q` is a point of normal intersection
Once a subinterval is detected (either for upper or lower portion) where the sign changes, it can be refined to get the desired accuracy. If only modest accuracy is needed, there may be no need to use faster root finding methods, but even if they are needed, having a short subinterval that isolates a root (or root pair in the fourth quadrant) will be useful.
** more to come comparing convergence of various methods **
I had to solve a problem similar to this, for GPS initialization. The question is: what is the latitude of a point interior to the Earth, especially near the center, and is it single-valued? There are lots of methods for converting ECEF cartesian coordinates to geodetic latitude, longitude and altitude (look up "ECEF to Geodetic"). We use a fast one with only one divide and sqrt per iteration, instead of several trig evaluations like most methods, but since I can't find it in the wild, I can't give it to you here. I would start with Lin and Wang's method, since it only uses divisions in its iterations. Here is a plot of the ellipsoid surface normals to points within 100 km of Earth's center (North is up in the diagram, which is really ECEF Z, not Y):
The star-shaped "caustic" in the figure center traces the center of curvature of the WGS-84 ellipsoid as latitude is varied from pole to equator. Note that the center of curvature at the poles is on the opposite side of the equator, due to polar flattening, and that the center of curvature at the equator is nearer to the surface than the axis of rotation.
Wherever lines cross, there is more than one latitude for that cartesian position. The green circle shows where our algorithm was struggling. If you consider that I cut off these normal vectors where they reach the axis, you would have even more normals for a given position for the problem considered in this SO thread. You would have 4 latitudes / normals inside the caustic, and 2 outside.
The problem can be expressed as the solution of a cubic equation which
gives 1, 2, or 3 real roots. For the derivation and closed form
solution see Appendix B of Geodesics on an ellipsoid of revolution. The boundary between 1 and 3 solutions is an astroid.
I'm not sure if this is the right place to ask, but here goes...
Short version: I'm trying to compute the orientation of a triangle on a plane, formed by the intersection of 3 edges, without explicitly computing the intersection points.
Long version: I need to triangulate a PSLG on a triangle in 3D. The vertices of the PSLG are defined by the intersections of line segments with the plane through the triangle, and are guaranteed to lie within the triangle. Assuming I had the intersection points, I could project to 2D and use a point-line-side (or triangle signed area) test to determine the orientation of a triangle between any 3 intersection points.
The problem is I can't explicitly compute the intersection points because of the floating-point error that accumulates when I find the line-plane intersection. To figure out if the line segments strike the triangle in the first place, I'm using some freely available robust geometric predicates, which give the sign of the volume of a tetrahedron, or equivalently which side of a plane a point lies on. I can determine if the line segment endpoints are on opposite sides of the plane through the triangle, then form tetrahedra between the line segment and each edge of the triangle to determine whether the intersection point lies within the triangle.
Since I can't explicitly compute the intersection points, I'm wondering if there is a way to express the same 2D orient calculation in 3D using only the original points. If there are 3 edges striking the triangle that gives me 9 points in total to play with. Assuming what I'm asking is even possible (using only the 3D orient tests), then I'm guessing that I'll need to form some subset of all the possible tetrahedra between those 9 points. I'm having difficultly even visualizing this, let alone distilling it into a formula or code. I can't even google this because I don't know what the industry standard terminology might be for this type of problem.
Any ideas how to proceed with this? Thanks. Perhaps I should ask MathOverflow as well...
EDIT: After reading some of the comments, one thing that occurs to me... Perhaps if I could fit non-overlapping tetrahedra between the 3 line segments, then the orientation of any one of those that crossed the plane would be the answer I'm looking for. Other than when the edges enclose a simple triangular prism, I'm not sure this sub-problem is solvable either.
EDIT: The requested image.
I am answering this on both MO & SO, expanding the comments I made on MO.
My sense is that no computational trick with signed tetrahedra volumes will avoid the precision issues that are your main concern. This is because, if you have tightly twisted segments, the orientation of the triangle depends on the precise positioning of the cutting plane.
[image removed; see below]
In the above example, the upper plane crosses the segments in the order (a,b,c) [ccw from above]: (red,blue,green), while the lower plane crosses in the reverse order (c,b,a): (green,blue,red). The height
of the cutting plane could be determined by your last bit of precision.
Consequently, I think it makes sense to just go ahead and compute the points of intersection in
the cutting plane, using enough precision to make the computation exact. If your segment endpoints coordinates and plane coefficients have L bits of precision, then there is just a small constant-factor increase needed. Although I am not certain of precisely what that factor is, it is small--perhaps 4. You will not need e.g., L2 bits, because the computation is solving linear equations.
So there will not be an explosion in the precision required to compute this exactly.
Good luck!
(I was prevented from posting the clarifying image because I don't have the reputation. See
the MO answer instead.)
Edit: Do see the MO answer, but here's the image:
I would write symbolic vector equations, you know, with dot and cross products, to find the normal of the intersection triangle. Then, the sign of the dot product of this normal with the initial triangle one gives the orientation. So finally you can express this in a form sign(F(p1,...,p9)), where p1 to p9 are your points and F() is an ugly formula including dot and cross products of differences (pi-pj). Don't know if this can be done simpler, but this general approach does the job.
As I understand it, you have three lines intersecting the plane, and you want to calculate the orientation of the triangle formed by the intersection points, without calculating the intersection points themselves?
If so: you have a plane
N·(x - x0) = 0
and six points...
l1a, l1b, l2a, l2b, l3a, l3b
...forming three lines
l1 = l1a + t(l1b - l1a)
l2 = l2a + u(l2b - l2a)
l3 = l3a + v(l3b - l3a)
The intersection points of these lines to the plane occur at specific values of t, u, v, which I'll call ti, ui, vi
N·(l1a + ti(l1b - l1a) - x0) = 0
N·(x0 - l1a)
ti = ----------------
N·(l1b - l1a)
(similarly for ui, vi)
Then the specific points of intersection are
intersect1 = l1a + ti(l1b - l1a)
intersect2 = l2a + ui(l2b - l2a)
intersect3 = l3a + vi(l3b - l3a)
Finally, the orientation of your triangle is
orientation = direction of (intersect2 - intersect1)x(intersect3 - intersect1)
(x is cross-product) Work backwards plugging the values, and you'll have an equation for orientation based only on N, x0, and your six points.
Let's call your triangle vertices T[0], T[1], T[2], and the first line segment's endpoints are L[0] and L[1], the second is L[2] and L[3], and the third is L[4] and L[5]. I imagine you want a function
int Orient(Pt3 T[3], Pt3 L[6]); // index L by L[2*i+j], i=0..2, j=0..1
which returns 1 if the intersections have the same orientation as the triangle, and -1 otherwise.
The result should be symmetric under interchange of j values, antisymmetric under interchange of i values and T indices. As long as you can compute a quantity with these symmetries, that's all you need.
Let's try
Sign(Product( Orient3D(T[i],T[i+1],L[2*i+0],L[2*i+1]) * -Orient3D(T[i],T[i+1],L[2*i+1],L[2*i+0]) ), i=0..2))
where the product should be taken over cyclic permutations of the indices (modulo 3). I believe this has all the symmetry properties required. Orient3D is Shewchuk's 4-point plane orientation test, which I assume you're using.
Say I have an arbitrary set of latitude and longitude pairs representing points on some simple, closed curve. In Cartesian space I could easily calculate the area enclosed by such a curve using Green's Theorem. What is the analogous approach to calculating the area on the surface of a sphere? I guess what I am after is (even some approximation of) the algorithm behind Matlab's areaint function.
There several ways to do this.
1) Integrate the contributions from latitudinal strips. Here the area of each strip will be (Rcos(A)(B1-B0))(RdA), where A is the latitude, B1 and B0 are the starting and ending longitudes, and all angles are in radians.
2) Break the surface into spherical triangles, and calculate the area using Girard's Theorem, and add these up.
3) As suggested here by James Schek, in GIS work they use an area preserving projection onto a flat space and calculate the area in there.
From the description of your data, in sounds like the first method might be the easiest. (Of course, there may be other easier methods I don't know of.)
Edit – comparing these two methods:
On first inspection, it may seem that the spherical triangle approach is easiest, but, in general, this is not the case. The problem is that one not only needs to break the region up into triangles, but into spherical triangles, that is, triangles whose sides are great circle arcs. For example, latitudinal boundaries don't qualify, so these boundaries need to be broken up into edges that better approximate great circle arcs. And this becomes more difficult to do for arbitrary edges where the great circles require specific combinations of spherical angles. Consider, for example, how one would break up a middle band around a sphere, say all the area between lat 0 and 45deg into spherical triangles.
In the end, if one is to do this properly with similar errors for each method, method 2 will give fewer triangles, but they will be harder to determine. Method 1 gives more strips, but they are trivial to determine. Therefore, I suggest method 1 as the better approach.
I rewrote the MATLAB's "areaint" function in java, which has exactly the same result.
"areaint" calculates the "suface per unit", so I multiplied the answer by Earth's Surface Area (5.10072e14 sq m).
private double area(ArrayList<Double> lats,ArrayList<Double> lons)
{
double sum=0;
double prevcolat=0;
double prevaz=0;
double colat0=0;
double az0=0;
for (int i=0;i<lats.size();i++)
{
double colat=2*Math.atan2(Math.sqrt(Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)+ Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)),Math.sqrt(1- Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)- Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)));
double az=0;
if (lats.get(i)>=90)
{
az=0;
}
else if (lats.get(i)<=-90)
{
az=Math.PI;
}
else
{
az=Math.atan2(Math.cos(lats.get(i)*Math.PI/180) * Math.sin(lons.get(i)*Math.PI/180),Math.sin(lats.get(i)*Math.PI/180))% (2*Math.PI);
}
if(i==0)
{
colat0=colat;
az0=az;
}
if(i>0 && i<lats.size())
{
sum=sum+(1-Math.cos(prevcolat + (colat-prevcolat)/2))*Math.PI*((Math.abs(az-prevaz)/Math.PI)-2*Math.ceil(((Math.abs(az-prevaz)/Math.PI)-1)/2))* Math.signum(az-prevaz);
}
prevcolat=colat;
prevaz=az;
}
sum=sum+(1-Math.cos(prevcolat + (colat0-prevcolat)/2))*(az0-prevaz);
return 5.10072E14* Math.min(Math.abs(sum)/4/Math.PI,1-Math.abs(sum)/4/Math.PI);
}
You mention "geography" in one of your tags so I can only assume you are after the area of a polygon on the surface of a geoid. Normally, this is done using a projected coordinate system rather than a geographic coordinate system (i.e. lon/lat). If you were to do it in lon/lat, then I would assume the unit-of-measure returned would be percent of sphere surface.
If you want to do this with a more "GIS" flavor, then you need to select an unit-of-measure for your area and find an appropriate projection that preserves area (not all do). Since you are talking about calculating an arbitrary polygon, I would use something like a Lambert Azimuthal Equal Area projection. Set the origin/center of the projection to be the center of your polygon, project the polygon to the new coordinate system, then calculate the area using standard planar techniques.
If you needed to do many polygons in a geographic area, there are likely other projections that will work (or will be close enough). UTM, for example, is an excellent approximation if all of your polygons are clustered around a single meridian.
I am not sure if any of this has anything to do with how Matlab's areaint function works.
I don't know anything about Matlab's function, but here we go. Consider splitting your spherical polygon into spherical triangles, say by drawing diagonals from a vertex. The surface area of a spherical triangle is given by
R^2 * ( A + B + C - \pi)
where R is the radius of the sphere, and A, B, and C are the interior angles of the triangle (in radians). The quantity in the parentheses is known as the "spherical excess".
Your n-sided polygon will be split into n-2 triangles. Summing over all the triangles, extracting the common factor of R^2, and bringing all of the \pi together, the area of your polygon is
R^2 * ( S - (n-2)\pi )
where S is the angle sum of your polygon. The quantity in parentheses is again the spherical excess of the polygon.
[edit] This is true whether or not the polygon is convex. All that matters is that it can be dissected into triangles.
You can determine the angles from a bit of vector math. Suppose you have three vertices A,B,C and are interested in the angle at B. We must therefore find two tangent vectors (their magnitudes are irrelevant) to the sphere from point B along the great circle segments (the polygon edges). Let's work it out for BA. The great circle lies in the plane defined by OA and OB, where O is the center of the sphere, so it should be perpendicular to the normal vector OA x OB. It should also be perpendicular to OB since it's tangent there. Such a vector is therefore given by OB x (OA x OB). You can use the right-hand rule to verify that this is in the appropriate direction. Note also that this simplifies to OA * (OB.OB) - OB * (OB.OA) = OA * |OB| - OB * (OB.OA).
You can then use the good ol' dot product to find the angle between sides: BA'.BC' = |BA'|*|BC'|*cos(B), where BA' and BC' are the tangent vectors from B along sides to A and C.
[edited to be clear that these are tangent vectors, not literal between the points]
Here is a Python 3 implementation, loosely inspired by the above answers:
def polygon_area(lats, lons, algorithm = 0, radius = 6378137):
"""
Computes area of spherical polygon, assuming spherical Earth.
Returns result in ratio of the sphere's area if the radius is specified.
Otherwise, in the units of provided radius.
lats and lons are in degrees.
"""
from numpy import arctan2, cos, sin, sqrt, pi, power, append, diff, deg2rad
lats = np.deg2rad(lats)
lons = np.deg2rad(lons)
# Line integral based on Green's Theorem, assumes spherical Earth
#close polygon
if lats[0]!=lats[-1]:
lats = append(lats, lats[0])
lons = append(lons, lons[0])
#colatitudes relative to (0,0)
a = sin(lats/2)**2 + cos(lats)* sin(lons/2)**2
colat = 2*arctan2( sqrt(a), sqrt(1-a) )
#azimuths relative to (0,0)
az = arctan2(cos(lats) * sin(lons), sin(lats)) % (2*pi)
# Calculate diffs
# daz = diff(az) % (2*pi)
daz = diff(az)
daz = (daz + pi) % (2 * pi) - pi
deltas=diff(colat)/2
colat=colat[0:-1]+deltas
# Perform integral
integrands = (1-cos(colat)) * daz
# Integrate
area = abs(sum(integrands))/(4*pi)
area = min(area,1-area)
if radius is not None: #return in units of radius
return area * 4*pi*radius**2
else: #return in ratio of sphere total area
return area
Please find a somewhat more explicit version (and with many more references and TODOs...) here.
You could also have a look at this code of the spherical_geometry package: Here and here. It does provide two different methods for calculating the area of a spherical polygon.