TITLE: A method to crop hemispherical images to a field of view
DATE: 2018-09-12
AUTHOR: John L. Godlee
====================================================================


For hemispherical photography of forest canopies, sometimes it's
necessary to crop the circular image to exclude a certain field of
view below the zenith angle. This might be because the lens
distorts the image too much below a given angle. Additionally,
because the calculation for LAI assumes random leaf orientation, an
assumption which is often broken, lower angles where leaf
orientation has a large effect on the relationship between gap
fraction and LAI can be excluded, typically below 60 degrees.

Cropping an image to a given field of view, where the image has
been projected onto a flat surface isn't that easy however.

Here is a function I wrote in R to calculate the number of pixels
of the radius of a circle equal to a given number of degrees field
of view, given the relationship between lens curvature and sensor
size.

   fov_px <- function(theta, circle_diam_px, focal_length_mm,
theta_max){
       library(NISTunits)

       rads_theta <- NISTdegTOradian(theta - 1)

       circle_radius_px <- circle_diam_px / 2

       R <- ((2*focal_length_mm) * sin(rads_theta / 2))

       max_rads_theta <-  NISTdegTOradian(theta_max)

       sensor_circle_radius_mm <- 2 * focal_length_mm *
sin(max_rads_theta / 2)

       sensor_px_per_mm_flat <- circle_radius_px /
sensor_circle_radius_mm

       pixels_per_theta <- R * sensor_px_per_mm_flat

       print(pixels_per_theta)
   }

The first thing the function does is convert the desired degrees
field of view to radians. Then it converts the pixel diameter of
the circular projected image into a radius. Then it uses an
equation for projecting equisolid images onto a flat plane, i.e.
the image sensor. This equation gives the number of mm from the
centre of the sensor an object will appear on the sensor and
therefore the flat image, given the focal length. The next step
calculates R for the maximum theta of the lens, in most cases 90
degrees for a full hemispheric image. This maximum R value can be
related to the pixel length of the full image to create a value of
pixel circle radius per theta degree value.

Here is a diagram which roughly describes the various values used
in the function, though the focal length is normally just taken
from the given focal length of the lens:

 ![Lens curvature
diagram](https://johngodlee.xyz/img_full/fov_function/diagram.png)

Then, it's easy enough to take the value given by fov_px and plug
it into this macro in ImageJ to crop the image to the desired pixel
radius:

   circle_radius = <ADJUST_TO_fov_px()_OUTPUT>

   makeOval(
       (getWidth/2) - (0.5 * circle_radius),
       (getHeight/2) - (0.5 * circle_radius),
       circle_radius,
       circle_radius))

   run("Crop");

   // Creates an elliptical selection, where (x,y) define the
upper left corner of the bounding rectangle of the ellipse.

   // In this case, the ellipse is a circle and is centred on the
image