Add matrix dimension with map()

Viewed 131

I have a 2D array of colours in Julia

using Images

white = RGB{Float32}(1, 1, 1)
green = RGB{Float32}(0.1, 1, 0.1)
blue = RGB{Float32}(0, 0.1, 1)

A = [white white;
     green blue;
     blue blue]

I want to turn each RGB colour into a Array{Float32, 3} in the higher dimension. This is what I tried:

B = map(A) do a
  [a.r, a.g, a.b]
end

size(B) == (3, 2, 3)   # (rows, cols, channels)
# => false

Instead, B is a 2D-matrix of 1D-arrays.

Does Julia have a map-like method for expanding the dimensions of a matrix?

2 Answers

You should use ImageCore's channelview instead:

julia> Av = channelview(A)
3×3×2 reinterpret(reshape, Float32, ::Array{RGB{Float32},2}) with eltype Float32:
[:, :, 1] =
 1.0  0.1  0.0
 1.0  1.0  0.1
 1.0  0.1  1.0

[:, :, 2] =
 1.0  0.0  0.0
 1.0  0.1  0.1
 1.0  1.0  1.0

The color channel is the first dimension (the fastest dimension). You can check that by setting some values and seeing the impact on the original, since Av is a view of A:

julia> Av[1,2,1] = -5
-5

julia> Av[1,2,2] = -10
-10

julia> A
3×2 Array{RGB{Float32},2} with eltype RGB{Float32}:
 RGB{Float32}(1.0,1.0,1.0)   RGB{Float32}(1.0,1.0,1.0)
 RGB{Float32}(-5.0,1.0,0.1)  RGB{Float32}(-10.0,0.1,1.0)
 RGB{Float32}(0.0,0.1,1.0)   RGB{Float32}(0.0,0.1,1.0)

In both cases we tweaked the red channel, because of using 1 as the first index.

map doesn't work because it is elementwise, so it can only allocate an output with the same size as the input. It's not difficult to allocate your output array in this case:

function RGBtoT_loop(x::Array{RGB{T}, N}) where {T,N}
  # allocate output array without any (valid) instances
  result = Array{T, N+1}(undef, size(x)..., 3)
  # write RGB values into output array
  for i in CartesianIndices(x)
    result[i, 1] = x[i].r
    result[i, 2] = x[i].g
    result[i, 3] = x[i].b
  end
  result
end

EDIT: I figured out how to do the same thing by broadcasting the getfield method where getfield(RGBvalue, 1) is equivalent to RGBvalue.r. Interestingly, ndims(A) and thus the Tuple of ones is calculated at compile-time from A's type parameters, so this method ends up type-stable and only allocates 1 thing at run-time: the result array.

RGBtoT(x) = getfield.(x, reshape(1:3, ntuple(i->1, ndims(x))..., 3) )
Related