Support mask
This page explains the mask aspects of the Julia package ImageGeoms.
This page comes from a single Julia file: 2-mask.jl.
You can access the source code for such Julia documentation using the 'Edit on GitHub' link in the top right. You can view the corresponding notebook in nbviewer here: 2-mask.ipynb, or open it in binder here: 2-mask.ipynb.
Setup
Packages needed here.
using ImageGeoms: ImageGeom, MaskCircle, MaskAllButEdge
using ImageGeoms: maskit, embed, embed!, getindex! # +jim +size
using ImageGeoms: mask_outline
using MIRTjim: jim, prompt
using Unitful: mmThe following line is helpful when running this file as a script; this way it will prompt user to hit a key after each figure is displayed.
isinteractive() ? jim(:prompt, true) : prompt(:draw);Mask overview
In tomographic image reconstruction, patients are usually more "round" than "square" so often we only want to estimate the pixels inside some support mask: a Bool array indicating which pixels are to be estimated. (The rest are constrained to be zero.) The ImageGeom struct has an entry to store this mask. The default is Trues(dims) which is a "lazy" Bool AbstractArray from the FillArrays package that is conceptually similar to trues(dims) but requires O(1) storage. So there is essentially no memory penalty to storing this entry in the ImageGeom for users who do not want to think about a mask. For users who do want a mask, fortunately Julia uses a special BitArray type to store Bool arrays, so the storage is 8× less than using bytes in most other languages.
Often we use a "circle inscribed in the square" as a generic support mask, and one of the built-in constructors can generate such a circular mask:
ig = ImageGeom(MaskCircle() ; dims=(40,32), deltas=(1mm,1mm))ImageGeom{2, NTuple{2,Unitful.Quantity{Int64, 𝐋, Unitful.FreeUnits{(mm,), 𝐋, nothing}}}, BitMatrix}
dims::NTuple{2,Int64} (40, 32)
deltas::NTuple{2,Unitful.Quantity{Int64, 𝐋, Unitful.FreeUnits{(mm,), 𝐋, nothing}}} (1 mm, 1 mm)
offsets::NTuple{2,Float32} (0.0f0, 0.0f0)
mask: 40×32 BitMatrix {716 of 1280}
That last line shows that 716 of 1280=40*32 mask pixels are nonzero.
jim(ig)Note that jim displays the axes with the units naturally; see MIRTjim.jl.
A 3D mask can be hard to visualize, so there is a mask_or method that collapses it to 2D:
ig = ImageGeom(MaskAllButEdge() ; dims=(32,32,16))
jim(ig)Mask operations
Often we need to extract the pixel values within a mask:
ig = ImageGeom(MaskAllButEdge() ; dims=(6,4))
x = 1:size(ig,1)
y = 1:size(ig,2)
ramp = x .+ 10*y'
ig.mask
core = ramp[ig.mask]8-element Vector{Int64}:
22
23
24
25
32
33
34
35Or equivalently:
maskit(ramp, ig.mask)8-element Vector{Int64}:
22
23
24
25
32
33
34
35Conversely, we can embed that list of pixels back into an array:
array = embed(core, ig.mask)6×4 Matrix{Int64}:
0 0 0 0
0 22 32 0
0 23 33 0
0 24 34 0
0 25 35 0
0 0 0 0There are in-place versions of these two operations:
core = Array{Float32}(undef, sum(ig.mask))
getindex!(core, ramp, ig.mask)8-element Vector{Float32}:
22.0
23.0
24.0
25.0
32.0
33.0
34.0
35.0array = collect(zeros(Float16, ig))
embed!(array, core, ig.mask)6×4 Matrix{Float16}:
0.0 0.0 0.0 0.0
0.0 22.0 32.0 0.0
0.0 23.0 33.0 0.0
0.0 24.0 34.0 0.0
0.0 25.0 35.0 0.0
0.0 0.0 0.0 0.0Mask outline
Sometimes we need the outline of the mask.
ig = ImageGeom(MaskCircle() ; dims=(40,32))
outline = mask_outline(ig.mask)
jim(
jim(ig.mask, "Mask"; prompt=false),
jim(outline, "Outline"; prompt=false),
)This page was generated using Literate.jl.