Compressed Sensing Example

In this example we will go through a simple example from the field of Compressed Sensing. In addtion to RegularizedLeastSquares.jl, we will need the packages LinearOperatorCollection.jl, ImagePhantoms.jl, ImageGeoms.jl and Random.jl, as well as CairoMakie.jl for visualization. We can install them the same way we did RegularizedLeastSquares.jl.

Preparing the Inverse Problem

To get started, let us generate a simple phantom using the ImagePhantoms and ImageGeom packages:

using ImagePhantoms, ImageGeoms
N = 256
image = shepp_logan(N, SheppLoganToft())
size(image)
(256, 256)

This produces a 256x256 image of a Shepp-Logan phantom.

In this example, we consider a problem in which we randomly sample a third of the pixels in the image. Such a problem and the corresponding measurement can be constructed with the packages LinearOperatorCollection and Random:

We first randomly shuffle the indices of the image and then select the first third of the indices to sample.

using Random, LinearOperatorCollection
randomIndices = shuffle(eachindex(image))
sampledIndices = sort(randomIndices[1:div(end, 3)])
21845-element Vector{Int64}:
     2
     3
     4
     5
     6
     7
    13
    19
    22
    27
     ⋮
 65510
 65513
 65514
 65516
 65519
 65523
 65524
 65530
 65531

Afterwards we build a sampling operator which samples the image at the selected indices.

A = SamplingOp(eltype(image), pattern = sampledIndices , shape = size(image));

Then we apply the sampling operator to the vectorized image to obtain the sampled measurement vector

b = A*vec(image);

To visualize our image we can use CairoMakie:

using CairoMakie
function plot_image(figPos, img; title = "", width = 150, height = 150)
  ax = CairoMakie.Axis(figPos; yreversed=true, title, width, height)
  hidedecorations!(ax)
  heatmap!(ax, img)
end
fig = Figure()
plot_image(fig[1,1], image, title = "Image")
samplingMask = fill(false, size(image))
samplingMask[sampledIndices] .= true
plot_image(fig[1,2], image .* samplingMask, title = "Sampled Image")
resize_to_layout!(fig)
fig
Example block output

As we can see in the right image, only a third of the pixels are sampled. The goal of the inverse problem is to recover the original image from this measurement vector.

Solving the Inverse Problem

To recover the image from the measurement vector, we solve the TV-regularized least squares problem:

\[\begin{equation} \underset{\mathbf{x}}{argmin} \frac{1}{2}\vert\vert \mathbf{A}\mathbf{x}-\mathbf{b} \vert\vert_2^2 + \lambda\vert\vert\mathbf{x}\vert\vert_{\text{TV}} . \end{equation}\]

For this purpose we build a TV regularizer with regularization parameter $λ=0.01$

using RegularizedLeastSquares
reg = TVRegularization(0.01; shape=size(image));

We will use the Fast Iterative Shrinkage-Thresholding Algorithm (FISTA) to solve our inverse problem. Thus, we build the corresponding solver

solver = createLinearSolver(FISTA, A; reg=reg, iterations=20);

and apply it to our measurement vector

img_approx = solve!(solver,b)
65536-element Vector{Float32}:
 7.5375277f-19
 1.4254111f-18
 3.4523988f-18
 8.367589f-18
 1.9255124f-17
 4.2910904f-17
 9.2002786f-17
 1.8753667f-16
 3.830775f-16
 8.49308f-16
 ⋮
 2.435243f-16
 1.2161483f-16
 5.90744f-17
 2.7788742f-17
 1.2509881f-17
 5.4832657f-18
 2.4100902f-18
 1.1232092f-18
 6.593036f-19

To visualize the reconstructed image, we need to reshape the result vector to the correct shape. Afterwards we can use CairoMakie again:

img_approx = reshape(img_approx,size(image));
plot_image(fig[1,3], img_approx, title = "Reconstructed Image")
resize_to_layout!(fig)
fig
Example block output

This page was generated using Literate.jl.