Examples
This example uses some real on-sky data taken at high frame rates (~40 Hz) from the Subaru telescope.
Setup
To run these examples, you will need to install the following packages
using Pkg
Pkg.add(["FITSIO", "HCIToolbox", "LuckyImaging", "Plots", "SubpixelRegistration"])using FITSIO
using LuckyImaging
using Plots
# set up plotting function
function imshow(arr; kwargs...)
scaled_arr = log10.(arr' .- minimum(arr))
# get 100x100 window around peak
idx = argmax(scaled_arr)
ranges = range.(idx.I .- 50, idx.I .+ 50)
varr = @view scaled_arr[ranges...]
xaxis = axes(arr, 1)[ranges[1]]
yaxis = axes(arr, 2)[ranges[2]]
# plot
heatmap(xaxis, yaxis, transpose(varr);
xlims=extrema(xaxis), ylims=extrema(yaxis),
aspect_ratio=1, c=:magma, size=(450, 400),
cbar=true, kwargs...)
# add black "+" on center of frame (on cell)
scatter!([128.5], [128.5], c=:black, marker=:+, lab="")
endlet's look at the first few frames from the cube
# You may be prompted (download is ~100 MB)
filename = testcube()
cube = read(FITS(filename)[1])
imshow(cube[:, :, 1], title="frame 1")
imshow(cube[:, :, 2], title="frame 2")
imshow(cube[:, :, 3], title="frame 3")
we can see some really exceptional cases of poor seeing, with cases where there are two bright copies of the PSF (frame 1)!
Long exposure
To get a benchmark, let's see what a long exposure would look like by averaging the frames as they are
using Statistics
long_expo = mean(cube, dims=3)[:, :, 1]
imshow(long_expo, title="long exposure")
Shift-and-add
The simplest way to improve our image is to co-align the frames and add them together. This is also called "shift-and-add". To do the registration, we will use SubpixelRegistration.jl for efficient FFT-based registration. To improve performance, we should choose a reference frame that is peak quality as possible. We can use the peak flux in each frame as a quality metric to aid our selection.
peaks = map(maximum, eachslice(cube, dims=3))
refidx = argmax(peaks)
imshow(cube[:, :, refidx], title="frame $refidx")
and, just for fun, the worst frame
worstidx = argmin(peaks)
imshow(cube[:, :, worstidx], title="frame $worstidx")
using HCIToolbox
using SubpixelRegistration
# shift reference to center
ctr = (128.5, 128.5)
refshift = ctr .- argmax(cube[:, :, refidx]).I(2.5, -1.5)# shift entire cube according to refshift
shift_frame!(cube, refshift)
# coregister cube now that reference frame is centered
registered = coregister(cube; dims=3, refidx, upsample_factor=10)
shift_added = mean(registered, dims=3)[:, :, 1]
imshow(shift_added, title="shift and add")
this entire process is equivalent to classic lucky imaging with a selection quantile of 0
lucky_classic_0 = lucky_image(cube; dims=3, q=0, upsample_factor=10)
# note we can use our already registered cube, too!
lucky_classic_0 = lucky_image(registered; dims=3, q=0, register=nothing)
imshow(lucky_classic_0, title="classic (0%)")
Classic lucky imaging
Classic lucky imaging uses some metric to select or discard entire frames from the cube. lucky_image describes these metrics in more detail. We need to specify a selection quantile, in other words, one minus the selection fraction. The selected frames will be shift-and-added. By default, the frames will be coregistered using SubpixelRegistration.jl.
lucky_classic_50 = lucky_image(registered; dims=3, q=0.5, register=nothing)
imshow(lucky_classic_50, title="classic (50%)")
lucky_classic_90 = lucky_image(registered; dims=3, q=0.9, register=nothing)
imshow(lucky_classic_90, title="classic (90%)")
lucky_classic_99 = lucky_image(registered; dims=3, q=0.99, register=nothing)
imshow(lucky_classic_99, title="classic (99%)")
We can see that the higher our selection quantile (i.e., a lower selection fraction), the better our angular resolution is. However, the lower total number of frames combined lowers the signal-to-noise ratio.