{ "cells": [ { "cell_type": "markdown", "source": [ "# Support mask\n", "\n", "This page explains the `mask` aspects of the Julia package\n", "[`ImageGeoms`](https://github.com/JuliaImageRecon/ImageGeoms.jl)." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Setup" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Packages needed here." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using ImageGeoms: ImageGeom, MaskCircle, MaskAllButEdge\n", "using ImageGeoms: maskit, embed, embed!, getindex! # +jim +size\n", "using ImageGeoms: mask_outline\n", "using MIRTjim: jim, prompt\n", "using Unitful: mm" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "The following line is helpful when running this file as a script;\n", "this way it will prompt user to hit a key after each figure is displayed." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "isinteractive() ? jim(:prompt, true) : prompt(:draw);" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "## Mask overview\n", "\n", "In tomographic image reconstruction, patients are usually more \"round\"\n", "than \"square\" so often we only want to estimate the pixels inside some\n", "support `mask`: a `Bool` array indicating which pixels are to be estimated.\n", "(The rest are constrained to be zero.)\n", "The `ImageGeom` struct has an entry to store this `mask`.\n", "The default is `Trues(dims)` which is a \"lazy\" Bool `AbstractArray`\n", "from the `FillArrays` package that is conceptually similar to `trues(dims)`\n", "but requires `O(1)` storage. So there is essentially no memory penalty\n", "to storing this entry in the `ImageGeom` for users who do not want\n", "to think about a `mask`.\n", "For users who do want a `mask`, fortunately Julia uses a special `BitArray`\n", "type to store `Bool` arrays, so the storage is 8× less than using bytes\n", "in most other languages.\n", "\n", "Often we use a \"circle inscribed in the square\" as a generic support mask,\n", "and one of the built-in constructors can generate such a circular mask:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "ig = ImageGeom(MaskCircle() ; dims=(40,32), deltas=(1mm,1mm))" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "That last line shows that 716 of 1280=40*32 mask pixels are nonzero." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "jim(ig)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Note that `jim` displays the axes with the units naturally;\n", "see [MIRTjim.jl](https://github.com/JeffFessler/MIRTjim.jl).\n", "\n", "A 3D mask can be hard to visualize, so there is a `mask_or` method\n", "that collapses it to 2D:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "ig = ImageGeom(MaskAllButEdge() ; dims=(32,32,16))\n", "\n", "jim(ig)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "## Mask operations" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Often we need to extract the pixel values within a mask:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "ig = ImageGeom(MaskAllButEdge() ; dims=(6,4))\n", "x = 1:size(ig,1)\n", "y = 1:size(ig,2)\n", "ramp = x .+ 10*y'\n", "\n", "ig.mask\n", "\n", "core = ramp[ig.mask]" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Or equivalently:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "maskit(ramp, ig.mask)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Conversely, we can `embed` that list of pixels back into an array:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "array = embed(core, ig.mask)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "There are in-place versions of these two operations:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "core = Array{Float32}(undef, sum(ig.mask))\n", "getindex!(core, ramp, ig.mask)" ], "metadata": {}, "execution_count": null }, { "outputs": [], "cell_type": "code", "source": [ "array = collect(zeros(Float16, ig))\n", "embed!(array, core, ig.mask)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "## Mask outline\n", "Sometimes we need the outline of the mask." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "ig = ImageGeom(MaskCircle() ; dims=(40,32))\n", "outline = mask_outline(ig.mask)\n", "jim(\n", " jim(ig.mask, \"Mask\"; prompt=false),\n", " jim(outline, \"Outline\"; prompt=false),\n", ")" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "---\n", "\n", "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*" ], "metadata": {} } ], "nbformat_minor": 3, "metadata": { "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.11.1" }, "kernelspec": { "name": "julia-1.11", "display_name": "Julia 1.11.1", "language": "julia" } }, "nbformat": 4 }