/MosaicViews.jl

Julia package for lazily viewing a 3D or 4D array as an expanded 2D array in the form of a mosaic of matrix slices

Primary LanguageJuliaOtherNOASSERTION

MosaicViews

Travis-CI CodeCov PkgEval

Motivations

When visualizing images, it is not uncommon to provide a 2D view of different image sources. For example, comparing multiple images of different sizes, getting a preview of machine learning dataset. This package aims to provide an easy-to-use tool for such tasks.

Compare two images

When comparing and showing multiple images, cat/hcat/vcat can be helpful if images sizes and colorants are the same. But if not, you'll need mosaicview for this purpose.

julia> using MosaicViews, ImageShow, TestImages

julia> lena = testimage("lena") # 256*256 RGB image

julia> cameraman = testimage("cameraman") # 512*512 Gray image

julia> mosaicview(lena, cameraman; nrow=1)

compare-images

Get a preview of dataset

Many datasets in machine learning field are stored as 3D/4D array, mosaicview provides some convenient keyword arguments to get a nice looking preview of your dataset.

julia> using MosaicViews, ImageShow, MLDatasets

julia> A = MNIST.convert2image(MNIST.traintensor(1:9))
28×28×9 Array{Gray{Float64},3}:
[...]

julia> mosaicview(A, fillvalue=.5, nrow=2, npad=1, rowmajor=true)
57×144 MosaicViews.MosaicView{Gray{Float64},4,...}:
[...]

dataset-preview

Usage

MosaicViews.jl provides an array decorator type, MosaicView, that creates a matrix-shaped "view" of any three or four dimensional array A. The resulting MosaicView will display the data in A such that it emulates using vcat for all elements in the third dimension of A, and hcat for all elements in the fourth dimension of A.

If performance isn't a priority, mosaicview is a convenience helper function to create a MosaicView.

the mosaicview helper

mosaicview is sufficient for most visualization use cases. It accepts multiple arrays as input:

julia> A1 = fill(1, 3, 1)
3×1 Array{Int64,2}:
 1
 1
 1

julia> A2 = fill(2, 1, 3)
1×3 Array{Int64,2}:
 2  2  2

# A1 and A2 will be padded to the common size and shifted
# to the center, this is a common operation to visualize
# multiple images
julia> mosaicview(A1, A2)
6×3 MosaicView{Int64,4, ...}:
 0  1  0
 0  1  0
 0  1  0
 0  0  0
 2  2  2
 0  0  0

Besides this, mosaicview also allows for a couple of convenience keywords. The following example provides a preview, for more detailed explanation, please refer to the documentation ?mosaicview.

# disable center shift
julia> mosaicview(A1, A2; center=false)
6×3 MosaicView{Int64,4, ...}:
 1  0  0
 1  0  0
 1  0  0
 2  2  2
 0  0  0
 0  0  0

julia> A = [k for i in 1:2, j in 1:3, k in 1:5]
2×3×5 Array{Int64,3}:
[:, :, 1] =
 1  1  1
 1  1  1

[:, :, 2] =
 2  2  2
 2  2  2

[:, :, 3] =
 3  3  3
 3  3  3

[:, :, 4] =
 4  4  4
 4  4  4

[:, :, 5] =
 5  5  5
 5  5  5

# number of tiles in column direction
julia> mosaicview(A, ncol=2)
6×6 MosaicViews.MosaicView{Int64,4,...}:
 1  1  1  4  4  4
 1  1  1  4  4  4
 2  2  2  5  5  5
 2  2  2  5  5  5
 3  3  3  0  0  0
 3  3  3  0  0  0

# number of tiles in row direction
julia> mosaicview(A, nrow=2)
4×9 MosaicViews.MosaicView{Int64,4,...}:
 1  1  1  3  3  3  5  5  5
 1  1  1  3  3  3  5  5  5
 2  2  2  4  4  4  0  0  0
 2  2  2  4  4  4  0  0  0

# take a row-major order, i.e., tile-wise permute
julia> mosaicview(A, nrow=2, rowmajor=true)
4×9 MosaicViews.MosaicView{Int64,4,...}:
 1  1  1  2  2  2  3  3  3
 1  1  1  2  2  2  3  3  3
 4  4  4  5  5  5  0  0  0
 4  4  4  5  5  5  0  0  0

# add empty padding space between adjacent mosaic tiles
julia> mosaicview(A, nrow=2, npad=1, rowmajor=true)
5×11 MosaicViews.MosaicView{Int64,4,...}:
 1  1  1  0  2  2  2  0  3  3  3
 1  1  1  0  2  2  2  0  3  3  3
 0  0  0  0  0  0  0  0  0  0  0
 4  4  4  0  5  5  5  0  0  0  0
 4  4  4  0  5  5  5  0  0  0  0

# fill spaces with -1
julia> mosaicview(A, fillvalue=-1, nrow=2, npad=1, rowmajor=true)
5×11 MosaicViews.MosaicView{Int64,4,...}:
  1   1   1  -1   2   2   2  -1   3   3   3
  1   1   1  -1   2   2   2  -1   3   3   3
 -1  -1  -1  -1  -1  -1  -1  -1  -1  -1  -1
  4   4   4  -1   5   5   5  -1  -1  -1  -1
  4   4   4  -1   5   5   5  -1  -1  -1  -1

The MosaicView Type

If performance is important it is recommended to use MosaicView directly, as mosaicview is not type stable.

Note that the constructor doesn't accept other parameters than the array A itself, it doesn't accept multiple inputs neither. Consequently the layout of the mosaic is encoded in the third (and optionally fourth) dimension. Creating a MosaicView this way is type stable, non-copying, and should in general give a decent performance when accessed with getindex.

Another way to think about this is that MosaicView creates a mosaic of all the individual matrices enumerated in the third (and optionally fourth) dimension of the given 3D or 4D array A. This can be especially useful for creating a single composite image from a set of equally sized images.

Let us look at a couple examples to see the type in action. If size(A) is (2,3,4), then the resulting MosaicView will have the size (2*4,3) which is (8,3).

julia> A = [k for i in 1:2, j in 1:3, k in 1:4]
2×3×4 Array{Int64,3}:
[:, :, 1] =
 1  1  1
 1  1  1

[:, :, 2] =
 2  2  2
 2  2  2

[:, :, 3] =
 3  3  3
 3  3  3

[:, :, 4] =
 4  4  4
 4  4  4

julia> MosaicView(A)
8×3 MosaicViews.MosaicView{Int64,3,Array{Int64,3}}:
 1  1  1
 1  1  1
 2  2  2
 2  2  2
 3  3  3
 3  3  3
 4  4  4
 4  4  4

Alternatively, A is also allowed to have four dimensions. More concretely, if size(A) is (2,3,4,5), then the resulting size will be (2*4,3*5) which is (8,15). For the sake of brevity here is a slightly smaller example:

julia> A = [(k+1)*l-1 for i in 1:2, j in 1:3, k in 1:2, l in 1:2]
2×3×2×2 Array{Int64,4}:
[:, :, 1, 1] =
 1  1  1
 1  1  1

[:, :, 2, 1] =
 2  2  2
 2  2  2

[:, :, 1, 2] =
 3  3  3
 3  3  3

[:, :, 2, 2] =
 5  5  5
 5  5  5

julia> MosaicView(A)
4×6 MosaicViews.MosaicView{Int64,4,Array{Int64,4}}:
 1  1  1  3  3  3
 1  1  1  3  3  3
 2  2  2  5  5  5
 2  2  2  5  5  5