|
| 1 | +# --- |
| 2 | +# title: Alpha Compositing |
| 3 | +# cover: assets/alpha_compositing.png |
| 4 | +# --- |
| 5 | + |
| 6 | +# This demonstration shows how [alpha compositing](https://en.wikipedia.org/wiki/Alpha_compositing) |
| 7 | +# can be done in 10 lines of code using |
| 8 | +# [OffsetArrays](https://github.com/JuliaArrays/OffsetArrays.jl) and |
| 9 | +# [PaddedViews](https://github.com/JuliaArrays/PaddedViews.jl) |
| 10 | + |
| 11 | +using Images, MosaicViews |
| 12 | +using OffsetArrays, PaddedViews # provide `OffsetArray` and `paddedviews` |
| 13 | + |
| 14 | +# ## 1. basic offset and pad operation |
| 15 | + |
| 16 | +# Assume that we have two objects with transparent color and we want to place them on a canvas |
| 17 | +# with partially overlapping. |
| 18 | + |
| 19 | +red_patch = fill(RGBA(1., 0., 0., 1), 24, 24) |
| 20 | +green_patch = fill(RGBA(0., 1., 0., 1), 32, 32) |
| 21 | +mosaicview(red_patch, green_patch; npad=20, nrow=1, fillvalue=colorant"white") |
| 22 | + |
| 23 | +# Assume the canvas axes starts from `(1, 1)`, here we keep the red patch unshifted, |
| 24 | +# and shift the green patch 6 pixels downward and 16 pixels rightward, and then |
| 25 | +# pad them to a common axes |
| 26 | +green_o = OffsetArray(green_patch, 6, 16) |
| 27 | +r, g = paddedviews(Gray(0.2), red_patch, green_o) |
| 28 | +mosaicview(r, g; npad=20, nrow=1, fillvalue=colorant"white") |
| 29 | + |
| 30 | +# Note that their axes and sizes are changed after shifting and padding: |
| 31 | + |
| 32 | +## Regardless of implementaion details, `Base.OneTo(32)` is mostly equivalent to `1:32` |
| 33 | +println("before shifting -- size: ", size(green_patch), " axes: ", axes(green_patch)) |
| 34 | +println("after shifting -- size: ", size(green_o), " axes: ", axes(green_o)) |
| 35 | +println("after padding -- size: ", size(g), " axes: ", axes(g)) |
| 36 | + |
| 37 | +# Axes are preserved after padding, which means you can easily get original image from |
| 38 | +# padded results using |
| 39 | + |
| 40 | +r[axes(red_patch)...] |
| 41 | +g[axes(green_o)...] |
| 42 | +#md nothing #hide |
| 43 | + |
| 44 | +# As described [here](https://en.wikipedia.org/wiki/Alpha_compositing), there are several compositing |
| 45 | +# methods: |
| 46 | + |
| 47 | +## add operation |
| 48 | +out_add = r .+ g |
| 49 | + |
| 50 | +## clear operation |
| 51 | +out_clear = copy(r) |
| 52 | +out_clear[axes(green_o)...] .= colorant"black" |
| 53 | + |
| 54 | +## multiply operation |
| 55 | +out_mul = copy(r) |
| 56 | +## channel-wise multiplication |
| 57 | +channelview(out_mul)[:, axes(green_o)...] .*= channelview(green_o) |
| 58 | + |
| 59 | +## overlap operation |
| 60 | +out_over = copy(r) |
| 61 | +out_over[axes(green_o)...] .= green_o |
| 62 | + |
| 63 | +## display the results of these operation |
| 64 | +mosaicview(out_add, out_clear, out_mul, out_over; |
| 65 | + npad=20, nrow=1, fillvalue=colorant"white") |
| 66 | + |
| 67 | +# ## 2. build the three-primary color panel |
| 68 | + |
| 69 | +# Now, let's use the same trick to build something more meaningful. |
| 70 | +# First we create three circles with colors red, green and blue |
| 71 | + |
| 72 | +using ImageDraw |
| 73 | +function make_circle(sz, c::T) where T |
| 74 | + ## fill with transparent color to avoid black region |
| 75 | + fillvalue = ARGB(c) |
| 76 | + img = fill(ARGB{eltype(T)}(0., 0., 0., 0.), sz...) |
| 77 | + origin = sz .÷ 2 |
| 78 | + r = sz .÷ 4 |
| 79 | + draw!(img, Ellipse(origin..., r...), fillvalue) |
| 80 | + img |
| 81 | +end |
| 82 | + |
| 83 | +## create three circles with color red, green and blue |
| 84 | +red_c = make_circle((256, 256), ARGB(1., 0., 0., 1.)) |
| 85 | +green_c = make_circle((256, 256), ARGB(0., 1., 0., 1.)) |
| 86 | +blue_c = make_circle((256, 256), ARGB(0., 0., 1., 1.)) |
| 87 | + |
| 88 | +mosaicview(red_c, green_c, blue_c; nrow=1) |
| 89 | + |
| 90 | +# Then, shift these circles to appropriate positions, pad them to common axes, and finally composite |
| 91 | +# using the add operation: |
| 92 | + |
| 93 | +r = size(red_c, 1) ÷ 8 |
| 94 | +red_o = OffsetArray(red_c, r, r) |
| 95 | +green_o = OffsetArray(green_c, -r, 0) |
| 96 | +blue_o = OffsetArray(blue_c, r, -r) |
| 97 | + |
| 98 | +color_panel = sum(paddedviews(zero(eltype(red_o)), red_o, green_o, blue_o)) |
| 99 | +color_panel = color_panel[axes(red_c)...] # crop empty region |
| 100 | + |
| 101 | +# --- save covers --- #src |
| 102 | +using FileIO #src |
| 103 | +save("assets/alpha_compositing.png", color_panel) #src |
0 commit comments