Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add an introduction demo for indexed image #166

Merged
merged 4 commits into from
Dec 7, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ImageMorphology = "787d08f9-d448-5407-9aad-5290dd7ab264"
ImageSegmentation = "80713f31-8817-5129-9cf8-209ff8fb23e1"
ImageShow = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
IndirectArrays = "9b13fd28-a010-5f03-acff-a1bbcff69959"
MappedArrays = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
MosaicViews = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
Expand Down Expand Up @@ -53,6 +54,7 @@ ImageMorphology = "0.2"
ImageSegmentation = "1"
ImageShow = "0"
Images = "0.23"
IndirectArrays = "0"
MappedArrays = "0"
MosaicViews = "0"
OffsetArrays = "1.0"
Expand Down
86 changes: 86 additions & 0 deletions docs/examples/color_channels/indexed_image.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# ---
# cover: assets/indexed_image.png
# title: Indexed image in 5 minutes
# author: Johnny Chen
# date: 2020-11-23
# ---

# This demonstration shows you how to work with [indexed
# image](https://en.wikipedia.org/wiki/Indexed_color) using
# [IndirectArrays.jl](https://github.com/JuliaArrays/IndirectArrays.jl)

# An indexed image consists of two parts: the indices and the palatte. The palatte keeps track of
# all possible pixel values of an image, while the `indices` consists of the palatte index of each
# pixel.

using Images

# The following is a normally used representation of image, i.e., an image as an array of
# `Colorant`s.

img = [
RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0)
RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0)
RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0)
RGB(0.0, 0.0, 1.0) RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0)
RGB(1.0, 1.0, 1.0) RGB(0.0, 0.0, 0.0) RGB(1.0, 0.0, 0.0) RGB(0.0, 1.0, 0.0) RGB(0.0, 0.0, 1.0)]

# Alternatively, we could save it in the indexed image format:

indices = [1 2 3 4 5; 2 3 4 5 1; 3 4 5 1 2; 4 5 1 2 3; 5 1 2 3 4] # `i` records the pixel value `palatte[i]`
palatte = [RGB(0.0, 0.0, 0.0), RGB(1.0, 0.0, 0.0), RGB(0.0, 1.0, 0.0), RGB(0.0, 0.0, 1.0), RGB(1.0, 1.0, 1.0)]

palatte[indices] == img

# This is doable because it follows the Julia [indexing
# rules](https://docs.julialang.org/en/v1/manual/arrays/#man-supported-index-types) that `indices`
# is an array of scalar indices. For example, the following equivalence holds:
timholy marked this conversation as resolved.
Show resolved Hide resolved

palatte[[1, 2]] == [palatte[1], palatte[2]]
palatte[[1 2; 2 1]] == [palatte[1] palatte[2]; palatte[2] palatte[1]]
#md nothing #hide

# Now, let's do some math on it. For this example, we are storing the data using type `Float64`,
timholy marked this conversation as resolved.
Show resolved Hide resolved
# where each `Float64` number requires 8 bytes (1 byte=8bits). Hence the normal representation
# format requires `5*5*3*8 = 600` bytes storage in total. As a comparison, we only need `5*5*8 +
# 5*3*8 = 320` bytes storage if we use the indexed image format. This is why indexed image requires
# less memories.
timholy marked this conversation as resolved.
Show resolved Hide resolved
#
# Although it does compress the data in this example, in real world applications, it can be quite
# complicated and unclear whether you should or should not use indexed image format. There are two
timholy marked this conversation as resolved.
Show resolved Hide resolved
# main drawbacks of it:
#
# - indexed images can require more memories if `length(unique(img))` is too large.
timholy marked this conversation as resolved.
Show resolved Hide resolved
# - indexing into an indexed image requires two `getindex` operations, so using it can be
# theoratically slower than a normal image representation. This is a typical [space-time
timholy marked this conversation as resolved.
Show resolved Hide resolved
# tradeoff](https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff) case.
#
# Benchmarks are always recommended before you choose to use the indexed image format.

# Using indexed image format with two seperate arrays can be inconvinient, hence
# [`IndirectArrays.jl`](https://github.com/JuliaArrays/IndirectArrays.jl) provides an array
# abstraction to union these two data:
using IndirectArrays

indexed_img = IndirectArray(indices, palatte)
img == indexed_img

# Under the hook, it is just a simple struct that subtypes `AbstractArray`:
#
# ```julia
# # no need to run this
# struct IndirectArray{T,N,A,V} <: AbstractArray{T,N}
# index::A
# values::V
# end
# ```

indexed_img.index === indices, indexed_img.values === palatte

# Since `IndirectArray` is just an array, common image operations are applicable to this type, for
# example:

imresize(indexed_img; ratio=2) # no longer an IndirectArray

# --- save covers --- #src
save("assets/indexed_image.png", repeat(indexed_img, inner=(20, 20))) #src