First and foremost, if the 4-5 images you’re trying to combine are not discretized, then keep in mind that you’ll get a many-to-one mapping: we humans have only 3 cone photoreceptors, so intensity & color live in a 3-dimensional space. If you have 4-5 continuous-valued arrays you have a 4-5 dimensional space at each pixel, and as a consequence the perceived colors will be ambiguous. (If each channel’s data is either 0 or 1 and not values in between, then of course you can encode a large number of discrete colors, so that case would not be ambiguous.)
That said, Julia & JuliaImages makes it easy to do basically anything you want here. You may have noticed colorview
in the docs:
julia> using Images
julia> a, b, c = rand(5, 5), rand(5, 5), rand(5, 5)
([0.602573 0.0741727 … 0.355561 0.119715; 0.451787 0.563087 … 0.538494 0.564459; … ; 0.967716 0.247382 … 0.377908 0.964664; 0.679923 0.209195 … 0.122144 0.504967], [0.587022 0.945633 … 0.588516 0.207504; 0.508459 0.478767 … 0.290102 0.910498; … ; 0.0567286 0.129025 … 0.721583 0.401006; 0.454971 0.536882 … 0.701881 0.400132], [0.295955 0.402741 … 0.332491 0.712356; 0.165172 0.595745 … 0.711917 0.804955; … ; 0.785742 0.197636 … 0.188005 0.626392; 0.970145 0.637129 … 0.65131 0.42688])
julia> cv = colorview(RGB, a, b, c)
5×5 mappedarray(RGB{Float64}, ImageCore.extractchannels, ::Array{Float64,2}, ::Array{Float64,2}, ::Array{Float64,2}) with eltype RGB{Float64}:
RGB{Float64}(0.602573,0.587022,0.295955) RGB{Float64}(0.0741727,0.945633,0.402741) RGB{Float64}(0.546985,0.992298,0.674326) RGB{Float64}(0.355561,0.588516,0.332491) RGB{Float64}(0.119715,0.207504,0.712356)
RGB{Float64}(0.451787,0.508459,0.165172) RGB{Float64}(0.563087,0.478767,0.595745) RGB{Float64}(0.482917,0.58218,0.769182) RGB{Float64}(0.538494,0.290102,0.711917) RGB{Float64}(0.564459,0.910498,0.804955)
RGB{Float64}(0.478996,0.0149608,0.00117074) RGB{Float64}(0.00735151,0.587855,0.807404) RGB{Float64}(0.753689,0.661367,0.19432) RGB{Float64}(0.751488,0.621405,0.41503) RGB{Float64}(0.608803,0.279269,0.791344)
RGB{Float64}(0.967716,0.0567286,0.785742) RGB{Float64}(0.247382,0.129025,0.197636) RGB{Float64}(0.866418,0.915348,0.683901) RGB{Float64}(0.377908,0.721583,0.188005) RGB{Float64}(0.964664,0.401006,0.626392)
RGB{Float64}(0.679923,0.454971,0.970145) RGB{Float64}(0.209195,0.536882,0.637129) RGB{Float64}(0.604198,0.927303,0.133829) RGB{Float64}(0.122144,0.701881,0.65131) RGB{Float64}(0.504967,0.400132,0.42688)
Notice that the type of cv
is a mappedarray
. So you can easily do this:
julia> d, e = rand(5, 5), rand(5, 5) # make more color channels
([0.378119 0.825132 … 0.93718 0.337192; 0.399923 0.272639 … 0.36042 0.357065; … ; 0.88771 0.140719 … 0.879098 0.884272; 0.141043 0.0173446 … 0.575349 0.630429], [0.231239 0.863472 … 0.750323 0.37466; 0.626438 0.654218 … 0.891385 0.0098046; … ; 0.0813098 0.177314 … 0.163829 0.965542; 0.697322 0.0950159 … 0.658177 0.419483])
julia> rd, gr, bl = RGB(1,0,0), RGB(0,1,0), RGB(0,0,1)
(RGB{N0f8}(1.0,0.0,0.0), RGB{N0f8}(0.0,1.0,0.0), RGB{N0f8}(0.0,0.0,1.0))
julia> using MappedArrays
julia> cv5 = mappedarray((u,v,w,x,y)->(u+w)*rd + y*gr + (v+x)*bl, a, b, c, d, e)
5×5 mappedarray(getfield(Main, Symbol("##13#14"))(), ::Array{Float64,2}, ::Array{Float64,2}, ::Array{Float64,2}, ::Array{Float64,2}, ::Array{Float64,2}) with eltype RGB{Float64}:
RGB{Float64}(0.898528,0.231239,0.965142) RGB{Float64}(0.476914,0.863472,1.77076) RGB{Float64}(1.22131,0.458332,1.16993) RGB{Float64}(0.688053,0.750323,1.5257) RGB{Float64}(0.832072,0.37466,0.544695)
RGB{Float64}(0.616959,0.626438,0.908382) RGB{Float64}(1.15883,0.654218,0.751406) RGB{Float64}(1.2521,0.0795215,1.2919) RGB{Float64}(1.25041,0.891385,0.650522) RGB{Float64}(1.36941,0.0098046,1.26756)
RGB{Float64}(0.480166,0.567315,0.934295) RGB{Float64}(0.814755,0.266489,1.45927) RGB{Float64}(0.948009,0.81312,0.688448) RGB{Float64}(1.16652,0.819015,1.60996) RGB{Float64}(1.40015,0.11565,0.683988)
RGB{Float64}(1.75346,0.0813098,0.944439) RGB{Float64}(0.445018,0.177314,0.269744) RGB{Float64}(1.55032,0.857035,1.23982) RGB{Float64}(0.565913,0.163829,1.60068) RGB{Float64}(1.59106,0.965542,1.28528)
RGB{Float64}(1.65007,0.697322,0.596014) RGB{Float64}(0.846324,0.0950159,0.554226) RGB{Float64}(0.738027,0.452477,0.949546) RGB{Float64}(0.773454,0.658177,1.27723) RGB{Float64}(0.931846,0.419483,1.03056)
Obviously, it’s up to you to figure out how you want to map those 4-5 channels to an RGB color. But once you’ve decided that, it’s a 1-liner.
If your channels are discretized (e.g., 0 or 1), then consider using the 4-5 channels as a bit pattern to make an integer and look up a color from a list, e.g., as returned by distinguishable_colors
:
cv5 = mappedarray((u,v,w,x,y)->colorlist[1 + u + Int(v)<<1 + Int(w)<<2 + Int(x)<<3 + Int(y)<<4], a, b, c, d, e)