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