{ "cells": [ { "cell_type": "markdown", "source": [ "This demonstration shows how [alpha compositing](https://en.wikipedia.org/wiki/Alpha_compositing)\n", "can be done in 10 lines of code using\n", "[OffsetArrays](https://github.com/JuliaArrays/OffsetArrays.jl) and\n", "[PaddedViews](https://github.com/JuliaArrays/PaddedViews.jl)" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using Images\n", "using OffsetArrays # provide `OffsetArray`" ], "metadata": {}, "execution_count": 1 }, { "cell_type": "markdown", "source": [ "## 1. basic offset and pad operation" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Assume that we have two objects with transparent color and we want to place them on a canvas\n", "with partially overlapping." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "32×84 MosaicView{RGB{Float64},4,Base.ReshapedArray{RGB{Float64},4,PaddedView{RGB{Float64},3,Tuple{Base.OneTo{Int64},Base.OneTo{Int64},Base.OneTo{Int64}},MappedArrays.MappedArray{RGB{Float64},3,Array{RGBA{Float64},3},MappedArrays.var\"#2#4\"{RGB{Float64}},MappedArrays.var\"#3#5\"{RGBA{Float64}}}},Tuple{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64}}}}:\n RGB{Float64}(1.0,1.0,1.0) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n ⋮ ⋱ \n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,1.0,1.0) RGB{Float64}(0.0,1.0,0.0)", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKgAAABAAgMAAADzMtTIAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACVBMVEX///8A/wD/AAAKrVo8AAAAAWJLR0QAiAUdSAAAAC5JREFUSMdjYMAJQtEAw6hSkpSuQgKjSkeVjiodVTqqdFTpqNKRqRQXGFVKgVIAhvLSn8meTRUAAAAASUVORK5CYII=" }, "metadata": {}, "execution_count": 2 } ], "cell_type": "code", "source": [ "red_patch = fill(RGBA(1., 0., 0., 1), 24, 24)\n", "green_patch = fill(RGBA(0., 1., 0., 1), 32, 32)\n", "mosaicview(red_patch, green_patch; npad=20, nrow=1, fillvalue=colorant\"white\")" ], "metadata": {}, "execution_count": 2 }, { "cell_type": "markdown", "source": [ "Assume the canvas axes starts from `(1, 1)`, here we keep the red patch unshifted,\n", "and shift the green patch 6 pixels downward and 16 pixels rightward, and then\n", "pad them to a common axes" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "38×116 MosaicView{RGB{Float64},4,Base.ReshapedArray{RGB{Float64},4,PaddedView{RGB{Float64},3,Tuple{Base.OneTo{Int64},Base.OneTo{Int64},Base.OneTo{Int64}},MappedArrays.MappedArray{RGB{Float64},3,Array{RGBA{Float64},3},MappedArrays.var\"#2#4\"{RGB{Float64}},MappedArrays.var\"#3#5\"{RGBA{Float64}}}},Tuple{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64}}}}:\n RGB{Float64}(1.0,0.0,0.0) … RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.0,0.0,0.0) … RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.0,0.0,0.0) RGB{Float64}(0.0,1.0,0.0)\n ⋮ ⋱ ⋮\n RGB{Float64}(0.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOgAAABMAgMAAAAaShMTAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX/AAAzMzP///8A/wDlBMZ2AAAAAWJLR0QCZgt8ZAAAADdJREFUWMPty7EJACAMAMEsmSVdMnYSBUsL4b7+i2hla6zyUqDoZ7SOUBRFURRFa9tRFEVR9CGd/6OdKZmztC0AAAAASUVORK5CYII=" }, "metadata": {}, "execution_count": 3 } ], "cell_type": "code", "source": [ "green_o = OffsetArray(green_patch, 6, 16)\n", "r, g = paddedviews(Gray(0.2), red_patch, green_o)\n", "mosaicview(r, g; npad=20, nrow=1, fillvalue=colorant\"white\")" ], "metadata": {}, "execution_count": 3 }, { "cell_type": "markdown", "source": [ "Note that their axes and sizes are changed after shifting and padding:" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "before shifting -- size: (32, 32) axes: (Base.OneTo(32), Base.OneTo(32))\n", "after shifting -- size: (32, 32) axes: (OffsetArrays.IdOffsetRange(7:38), OffsetArrays.IdOffsetRange(17:48))\n", "after padding -- size: (38, 48) axes: (1:38, 1:48)\n" ] } ], "cell_type": "code", "source": [ "# Regardless of implementaion details, `Base.OneTo(32)` is mostly equivalent to `1:32`\n", "println(\"before shifting -- size: \", size(green_patch), \" axes: \", axes(green_patch))\n", "println(\"after shifting -- size: \", size(green_o), \" axes: \", axes(green_o))\n", "println(\"after padding -- size: \", size(g), \" axes: \", axes(g))" ], "metadata": {}, "execution_count": 4 }, { "cell_type": "markdown", "source": [ "Axes are preserved after padding, which means you can easily get original image from\n", "padded results using" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "32×32 OffsetArray(::Array{RGBA{Float64},2}, 7:38, 17:48) with eltype RGBA{Float64} with indices 7:38×17:48:\n RGBA{Float64}(0.0,1.0,0.0,1.0) … RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) … RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n ⋮ ⋱ \n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) … RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) … RGBA{Float64}(0.0,1.0,0.0,1.0)\n RGBA{Float64}(0.0,1.0,0.0,1.0) RGBA{Float64}(0.0,1.0,0.0,1.0)", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUA/wD///9vvVhRAAAAAWJLR0QB/wIt3gAAABlJREFUSMdjYBgFo2AUjIJRMApGwSigLwAACIAAAW4rHVQAAAAASUVORK5CYII=" }, "metadata": {}, "execution_count": 5 } ], "cell_type": "code", "source": [ "r[axes(red_patch)...]\n", "g[axes(green_o)...]" ], "metadata": {}, "execution_count": 5 }, { "cell_type": "markdown", "source": [ "As described [here](https://en.wikipedia.org/wiki/Alpha_compositing), there are several compositing\n", "methods:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "38×252 MosaicView{RGB{Float64},4,Base.ReshapedArray{RGB{Float64},4,PaddedView{RGB{Float64},3,Tuple{Base.OneTo{Int64},Base.OneTo{Int64},Base.OneTo{Int64}},MappedArrays.MappedArray{RGB{Float64},3,Array{RGBA{Float64},3},MappedArrays.var\"#2#4\"{RGB{Float64}},MappedArrays.var\"#3#5\"{RGBA{Float64}}}},Tuple{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64}}}}:\n RGB{Float64}(1.2,0.2,0.2) … RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.2,0.2,0.2) … RGB{Float64}(0.2,0.2,0.2)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(1.2,0.2,0.2) RGB{Float64}(0.0,1.0,0.0)\n ⋮ ⋱ \n RGB{Float64}(0.4,0.4,0.4) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) … RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) RGB{Float64}(0.0,1.0,0.0)\n RGB{Float64}(0.4,0.4,0.4) RGB{Float64}(0.0,1.0,0.0)", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfgAAABMBAMAAABzKribAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAHlBMVEX/MzNmZmb/////AAAzMzP//wAz/zMAAAAAMwAA/wA13Wc+AAAAAWJLR0QCZgt8ZAAAAKdJREFUeNrt2kENgDAQRcFawAIWsIAFLGABC7WAWy6cmm5aSJr0ME/A3869KQUtQWulLWgPmmUjwcPDw8PDw8PDw8/x8CH44+0M6jl6NfqykYN6Nu5G8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8CPw5Yejs1HtaPnR5w++3MiNejbg4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4SfGP9NOqGudOCoeAAAAAElFTkSuQmCC" }, "metadata": {}, "execution_count": 6 } ], "cell_type": "code", "source": [ "# add operation\n", "out_add = r .+ g\n", "\n", "# clear operation\n", "out_clear = copy(r)\n", "out_clear[axes(green_o)...] .= colorant\"black\"\n", "\n", "# multiply operation\n", "out_mul = copy(r)\n", "# channel-wise multiplication\n", "channelview(out_mul)[:, axes(green_o)...] .*= channelview(green_o)\n", "\n", "# overlap operation\n", "out_over = copy(r)\n", "out_over[axes(green_o)...] .= green_o\n", "\n", "# display the results of these operation\n", "mosaicview(out_add, out_clear, out_mul, out_over;\n", " npad=20, nrow=1, fillvalue=colorant\"white\")" ], "metadata": {}, "execution_count": 6 }, { "cell_type": "markdown", "source": [ "## 2. build the three-primary color panel" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Now, let's use the same trick to build something more meaningful.\n", "First we create three circles with colors red, green and blue" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "256×768 MosaicView{RGB{Float64},4,Base.ReshapedArray{RGB{Float64},4,PaddedView{RGB{Float64},3,Tuple{Base.OneTo{Int64},Base.OneTo{Int64},Base.OneTo{Int64}},MappedArrays.MappedArray{RGB{Float64},3,Array{ARGB{Float64},3},MappedArrays.var\"#2#4\"{RGB{Float64}},MappedArrays.var\"#3#5\"{ARGB{Float64}}}},Tuple{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64}}}}:\n RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n ⋮ ⋱ \n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)\n RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0)", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAwAAAAEABAMAAAD2MAyUAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAD1BMVEUAAAD/AAAA/wAAAP////+B57DtAAAAAWJLR0QEj2jZUQAABKxJREFUeNrtne2N4lAQBBdIwGSAHIEl8s/thO5jtVqMZ+Z1v9EeVQm0NKXu9wv88QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+Y6ydLR/76ya0j//7J1hB//coyO3/9ym12/v0r2+T463eWmfnrd24z8+/f2SbGn65PmZZ/Xp8yLf9yf8q0/J37TzOwc/9pBnbuP83AdZ9lRv66z21G/n2fbUb+9dprYF17DdzvvQZOLwX4V+j8UoB/hS4vBfhX6OD+dgMH97cbOLi/3cDh/c0GDu9vNnB4f7eB4/t7n4Hj+3ufgeP7e5+BwP2tBgL3txoI3N9qIDBA1hEKDJB1hAID5B2h2P19FYjd31eB2P19FQje32YgeH+bgeD9bQaCA2QboeAA2UYoOEC+EYrf31OB+P09FYjf31OBRAEsFUgUwFKBRAE8Fcjc31GBzP0dFcjc31GBVAEMFUgVwFCBVAEcFcjdX1+B3P31FcjdX1+BZAHkFUgWQF6BZAH0FcjeX12B7P3VFcjeX14BBPQKSC+QeIPSCyTeoPQCqTcof39tBfL311Ygf39tBQoFkFagUABpBQoF0FYAAc0CKvdXblDl/soNqtxfuUGlAggrUCqAsAKlAigrgIBmAbX76zaodn/dBtXuL9wgBPQKKC6QbIOKCyTboOIC6Taoen9VBar3V1Wgen9ZBRDQK6C8QKINKi+QaIPKC6TaIAQg4L0F1O+veQTq99c8AvX7ix4BBPQKGFggyQYNLJBkgwYWSLNBCEDAewsYub/iERi5v+IRGLm/5BFAAAIQgIA+AachAeOv8HlIwPgrfBkSIHiFEYAABCCgUcDY/cdf4bH7j7/CY/cXvMIIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAf+ngNOYgOH885iA4fzLmIDhfAQgAAEI6BTA7wNG2BCAAAQg4GcLOI0IEOSfRwQI8i8jAgT5CEDAmwvg31J6nwAEtAsY2CBJ/sAGSfIHNkiSjwAEvLkA/je09wlAQLuA8gaJ8ssbJMovb5Aon39Pby4AAtoFFDdIll/cIFl+cYNk+QjoFsBXlHoXiO+IlSogzEdAtwC+Jdm7QHxNtVABaT7fE24uAALaBaQ3SJyf3iBxfnqDxPnpCizq/N4CpCuwyQUkKyDPT1ZAnp+sgDw/WYFFn99bgGQFNoOAVAUM+akKGPJTFTDkpyqwOPJ7C5CqwGYRkKiAJT9RAUt+ogKW/EQFFk9+bwESFdhMAqIGFld+7/3DBjZXfnSEbPnBEbLlB0fIlh+swOLL7y1AsAKbLz9kYHHm994/ZGBz5kdGyJofGCFrfmCErPkBA+b8QwPm/EMD5vxDA/b8c+v9Dw3Y8w+egcWf3/cA/KbxAfhD7/1fGrjNyO++/4sVmhO/v0KT8ndXaFL+roFp+efW++8amJb/8XSGlpn5XfPzl775+Ufn+R90nv9B8/kfdJ7/Qef5HzSfHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4H/hFxciBGWchSdNAAAAAElFTkSuQmCC" }, "metadata": {}, "execution_count": 7 } ], "cell_type": "code", "source": [ "using ImageDraw\n", "function make_circle(sz, c::T) where T\n", " # fill with transparent color to avoid black region\n", " fillvalue = ARGB(c)\n", " img = fill(ARGB{eltype(T)}(0., 0., 0., 0.), sz...)\n", " origin = sz .÷ 2\n", " r = sz .÷ 4\n", " draw!(img, Ellipse(origin..., r...), fillvalue)\n", " img\n", "end\n", "\n", "# create three circles with color red, green and blue\n", "red_c = make_circle((256, 256), ARGB(1., 0., 0., 1.))\n", "green_c = make_circle((256, 256), ARGB(0., 1., 0., 1.))\n", "blue_c = make_circle((256, 256), ARGB(0., 0., 1., 1.))\n", "\n", "mosaicview(red_c, green_c, blue_c; nrow=1)" ], "metadata": {}, "execution_count": 7 }, { "cell_type": "markdown", "source": [ "Then, shift these circles to appropriate positions, pad them to common axes, and finally composite\n", "using the add operation:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "256×256 Array{ARGB{Float64},2} with eltype ARGB{Float64}:\n ARGB{Float64}(0.0,0.0,0.0,0.0) … ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) … ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ⋮ ⋱ ⋮\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) … ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) ARGB{Float64}(0.0,0.0,0.0,0.0)\n ARGB{Float64}(0.0,0.0,0.0,0.0) … ARGB{Float64}(0.0,0.0,0.0,0.0)", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEABAMAAACuXLVVAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAGFBMVEUAAAAA/wAA/////wAAAP//////AAD/AP/ToJ54AAAAAXRSTlMAQObYZgAAAAFiS0dEBfhv6ccAAAQCSURBVHja7ZdBbttADEUdQMg6Tk5gATlAYEBrQyDQAxSCt13NDVpfv6njSLI0HHI4pMYo+JeaxX94nxvtdh6Px+PxeDwej8fj8Xg84uynvNStr4GwX2dLhKd9NLX7NyPY43mp3L8JwVMSwH4Fot+cgOy3JqD7bc+A0W9KwBjAdgRev50CZr8ZAXMAuxH4/TYKMgTYKMjpt1CQJcBCQV6/voJMAfoKcvvVFdQGyF5Ae4P8fl0FAgG6CqoDSPo1NxAJ0FRQHUDWr7hBbQDhAnobSPvVFNQGEC+gtYEDyPuVjqA2QMECOhs4QEm/yhE4QG2ApyIAhSt0gOoAZf0KV+gADuAADuAADwTQTjnEuo5TPgwA2vscEvVzBC2Adp1Don5C0AF4baMZ69+O0agBIP0jAdJ/JdAAaPEcEP3TDAoAbZsmOKbyUQ7wmgRocf9fKQZo0v3te7r/2JX2A9E/DARAKAMAAmAYhp8EwK+yfuiJfoIghBKChgB4vwIMBEDBCEAAfPUnFYQSBUAADANNEAoImisAfoXvIwA+QncFEI4ABMDUjyu4AYgUNATATACu4AYgUgAEwLwfVfANIFDwLQC7wjsBqIIQxAqAALjvxxSMANkKGgJgIQBTEMQKYApHAKKgC2IFtQGaGUDPWSC+QQjSDYAAWPdHFcwBshQ0BEBEQFRBkCq4AwAxQCcGAAIg1h/Z4B4gY4N7AesNogIiCkIQKlgAgBCgEwMAARDvX22wBOBvsAToZQBBCtAQAMgCqw2WAOwNANIbYP0LBV2QKlgD9BKAIAVoCAB0gcUGawDmBhEAEAB0qgB9PkAQAwCkFeD98yOICWAeQW2AJgrQcxaYbxCCdIM4AGQCdOoAfR5AkAMApBWk+scjQASwjgAD6HMAggEAZAB0JgA9HyAUADSQVpC8wdsVogI4V5gAACZAor8QoOcBBDMAYAF0ZQAAaYJ0/+cVJvsZV5gG6GmAYAoAPQUQjAHgdxrgjznAJdl/3gAgRXC+bAFwQVf4fNsEACO4bAYQneF82RJghXC+fd4O4A7hPH7cEiAaB3AAB3AAB/gPABoC4EcagOgv/TFxgC0Ayq6w/AYd4AEAiq5Q4QYdoOgINE7gAQAKNlBZwAEKjkDnBB4AQLyB0gJiBVoCHgBAuIHaAvUBZBvoLSBUoCigPoBkA80FRApUBQgU6Ap4AIDsDZQXyFagLSBbgbqATAX6AjIVGAjIUmAhIEuBiYAMBTYC+ARm/dwRjAZgK7ATwCMw7eeMYDgAi8C4nyTYPRv3E2dw2u0MD4AmOP17t+5PrHB7R1fQqccJxvdn4/74DKf5u51+DOG0fDeuXyCcYu/G9R6Px+PxeDwej8fj8Xg8VvkLDqzMGxZlpigAAAAASUVORK5CYII=" }, "metadata": {}, "execution_count": 8 } ], "cell_type": "code", "source": [ "r = size(red_c, 1) ÷ 8\n", "red_o = OffsetArray(red_c, r, r)\n", "green_o = OffsetArray(green_c, -r, 0)\n", "blue_o = OffsetArray(blue_c, r, -r)\n", "\n", "color_panel = sum(paddedviews(zero(eltype(red_o)), red_o, green_o, blue_o))\n", "color_panel = color_panel[axes(red_c)...] # crop empty region" ], "metadata": {}, "execution_count": 8 }, { "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.5.1" }, "kernelspec": { "name": "julia-1.5", "display_name": "Julia 1.5.1", "language": "julia" } }, "nbformat": 4 }