Adrian's FWHM V1.1 code documentation


This document describes the workings of the ImageJ plugin "Adrian's FWHM", written by Adrian Martin during August-October 2007.  V1.1 was released Feb 2008.


Adrian Martin is at Sensor Sciences, 3333 Vincent Road Suite 103, Pleasant Hill, CA 94523, and can be reached at


Installation instructions.

1. Update ImageJ to the most current version


2. To install the plug in, just copy the file Adrian_FWHM.jar (source) into the "plugins" directory, within the "ImageJ" directory.  Nothing else.  On PCs this directory is under "Program Files" and on a Mac it is under "Applications".  Both require certain privileges to do this.  If you don’t think this step has been successful, don’t forget to read step 3.



3. Run ImageJ again.  The plugin is already installed under "Analyze/Tools/Adrian's FWHM", however it is convenient to assign a shortcut to it if it is to be run often (you don't have to do this);


4. "Plugin/Shortcut/Create Shortcut" and select "Adrian's FWHM" from the list, and a key (F1 to F12) to run it.


Purpose and history.


The plugin was written to analyse data taken using the Sensor Sciences microchannel based UV photon counting detectors, and came out of the need to analyse both many individual points and to do so quickly and repeatably for the purposes of calculating detector resolution.  The Sensor Sciences data is a grid of pinhole images (it is made by placing a thin foil, with tiny holes in it spaced in a square grid 0.5, 1 or 1mm apart directly in front of the detector), with each pinhole image typically containing anywhere from 0 to 1000 photons on a background of nothing.  The image below shows an example, which is saved as a FITS image file (ImageJ can directly handle FITS files).


A typical pinhole image


Before the invention of this code, the job of determining the size of each pinhole image (and hence the resolving power of the detector) was done manually by taking an image slice through a randomly selected pinhole or two and estimating the FWHM (full width at half maximum) by eye.  Obviously this had drawbacks; the selection of the pinholes was not random and the halfway point was sometimes open to interpretation.  Using an automated routine was considered to be better because it selects all pinholes, always uses the same fit method and can do so quickly.


Operation of the routine


Details given hereafter concern the workings of the routine and operation of the plugin.  They may require a certain degree of concentration to understand that is often lacking in lab-based researchers.


There are two main and separate parts to the routine - the search to locate the pinholes, and the fitting of Gaussian curves to each pinhole found.


The search for pinholes was adapted from an IDL version of a similar application probably written mostly by Joe Stock and Pat Jelinsky at the Space Sciences Lab, University of California, Berkeley.  The region of interest is thresholded at a particular level (the "pinhole threshold"), and any pixel containing more events than that threshold is "flagged" as a pinhole.  Flagged pixels are passed as starting guesses to the fit routine, and once completed, those within a certain distance of the eventual fit are cancelled.  At the moment the pinhole threshold is set to a level such that 65% of all events (the original SSL number) fall under it and 35% above it, but it is possible to change this number in the "advanced" settings menu – see later.  The flag cancelling distance is set to twice the FWHM in both X and Y and is currently unchangeable.  A possible problem could exist where an erroneously large fit disqualifies many nearby true pinholes, however, the user should really try to refrain from including such dubious data in the region of interest.


Simple n-order polynominal fits cannot be used to fit the non-linear Gaussian curve, so a more complicated but versatile form of fit is implemented.  However, prior to the fit, a more simple moment calculation is done over the region selected in the "search options" window around the flagged pixel.  As well as giving a surprisingly accurate figure for the (x, y) centroid of the pinhole itself for the real fitting routine to start with, it calculates the total number of events in the pinhole.  


The Gaussian fitting routine, a Levenburg-Marquardt implementation, is described on wikipedia as 'robust', and rarely fails to converge.  It is however slower than simpler methods and of course quite complicated.  The implementation used here was been adapted from "" and uses the JAMA library (which must be supplied with the routine).  It is left to the reader to discover the intricacies of this routine.


The Gaussian function is fitted in 4 dimensions; offset, amplitude, position and width, though amplitude is not reported, and offset only used optionally.  The width is fitted as standard deviation (s), but is converted to FWHM using the linear conversion FWHM = 2.355s. After the routine has fitted each pinhole it reports to the results window the data together with a number expressing the quality of the fit.  The fit quality is a measure of the error of the fit, so a small number is better.  Experience says that anything below 1e-6 is really quite adequate.  Data can be saved directly from this window (using File…) or cut and pasted elsewhere.


Additionally, a graph is plotted of the FWHMs vs. centroids in whichever axis contains more pixels.  This feature is not ideal as it sometimes plots an unexpected axis, especially one that has particularly small bins, but raw data is always available in the "Results" window for plotting separately in case of panic.


“Adrian's FWHM” is usually distributed with another ImageJ plugin that allows export of data in Excel format (Excel writer).  The author has had no connection with this plugin and it is included for convenience only.


The "Results" window


The Routine



The "Search options" main window.


The "search options" menu is always displayed when the routine is run.  The 3 parameters are remembered so long as ImageJ is not quit, in which case they revert to their default values (above). 


The "pinhole spacing" in X and Y should be the distance in pixels between the pinholes for at least most of the image.  It is important that stray pixels belonging to neighbouring pinholes do not creep into this box, which will upset the fit.  However, it is also important that all events associated with a pinhole are included.  The "single pinhole?" option is supposed to be a way to check that the fits are working as expected for the type of image being analysed.  If there are any events from neighbouring pinholes within the region specified, then the fit position is unlikely to change much, but the event counts will do so, and so will the FWHM (see later for a discussion of this). 


The "Height of results plot" box ensures that the end product of the fits - a graph of either X (or Y) FWHM vs. X (or Y) is always the same height so similar results can be fairly compared.


The "Single pinhole?" function offers more information on the fit, but because of the quantity of data produced, only works for one pinhole.  The pinhole search is omitted, and the region of interest is used as the "pinhole spacing" in single pinhole mode.  The fit is identical other than it plots a zoomed in version of the pinhole in a separate window, and a graph of an X cut together with the fit superimposed over it.  It also does the same for Y.


The "Zoom" box


The "Results" window from the single pinhole fit


The "X histogram" window, showing both data and fit in one dimension.


No checking of data is done to ensure that a pinhole is actually inside the region of interest so this option is best suited to testing the routine for suitability to a particular application.  When the user is pleased with the configuration of the routine, the "single pinhole?" option can be turned off.


Advanced settings


The "Advanced settings" box


There are several additional options included on the "advanced settings" menu that might be of use to someone familiar with what they are doing, but they should not be played with to 'see what will happen' because they might produce unjustifiably good results, or cause frequent convergence failures.


The "Fit X and Y offset" fit Gaussians using the offset as a variable.  Before selecting this option, the user should have a reason to do so (such as a significant background), not just that selecting it gives a better (ie tighter) fit.  By default this option is off in both axes.  When enabled, the results table shows an additional column or columns for the offset.  Otherwise the offset is fixed at 0.


The "search threshold" is the "pinhole threshold" described above.  Depending on the distribution of events it might be necessary to change this number if the routine is having trouble finding all of the pinholes.  This is especially true when there are very few pinholes; if there are only two pinholes and the first one grabs most of the events, then the second one won't be flagged.  Lowering this number in that case will help to find it.  Conversely if areas of noise or background are flagged as pinholes incorrectly, raising the pinhole threshold may help.  However, the routine finds most of the pinholes most of the time, and all of the pinholes some of the time…


The final two options "Show zoom" and "Show fits" should be self-explanatory.


Problems and Future ideas.


Very little error checking is done in the routine, so if the fit fails for whatever reason, it often brings down ImageJ with it.  Please don't try to confuse it - for example by not giving it an image.  The region of interest can be any shape, but that function has not been thoroughly tested.


Currently a single pixel at the edge of the fit box can have an anomalously large effect on the fit, especially the width.  This is because is the pixel is highly leveraged, being so far away from the centroid.  It is possible to weight the data such that events far away from the fit center count for less, but that has not been implemented at present, and the user should be aware of this, and experiment with the inclusion/exclusion of individual pixels.


ImageJ has a spatial calibration function, but it has not been implemented in this routine.


Sometimes a fit will pause at one tricky pinhole.


Revision History


V1.1    1 Feb 2008      Convert all images to 16 bit unsigned pixels when loaded.  This is done automatically on all images.  Pixels should be integer bins, not scaled in some way otherwise the histogramming function will not know how to deal with each pixel in its pinhole search.  This should not be a problem when dealing with images taken by photon counting systems, but might be if an image has been processed in some way externally then analysed removing the photon counts from each pixel. 


Alter ROI checking method so it is possible to select the whole image if no ROI is selected.  Also prevent the fit from looking outside the edge of the image if the ROI goes right up to the edge of it.


V1.0    30 Oct 2007    Initial release


I have used several tracts of code that have been derived from other programs.  All have been substantially modified (except ImageJ) so I feel somewhat justified in doing so, but I could not have got the code to where it is now without them.  They are;