Kermit
Kermit

Reputation: 6002

Julia iteratively make 3D array from 2D arrays

I am trying to make a 3D array from many 2D arrays.

Image Files

Each image becomes a 2D array.

https://drive.google.com/drive/folders/1xBucvqhKFjAfbRIhq5wjr40kSjNor_0t?usp=sharing

using Images, Colors

paths = readdir(
    "/Users/me/Downloads/ct_scans"
    , join = true
)

images_3D = []

for p = paths
    img = load(p)
    gray = Gray.(img)
    arr = convert(Array{Float64}, gray) # <----- 2D array
    append!(images_3d, arr)
end


>>> size(images_3d) 
(1536000) # <--- 1D view? 

>>> 1536000 == 80*160*120 
true

>>> reshaped_3d = reshape(images_3d, (80,160,120))

>>> Gray.(reshaped_3d[1,:,:])
# 160x120 scrambled mess of pixels not rearranged as expected

Part of the reason for posting this is to see how Julia programmers approach multi-dimensional arrays.

Upvotes: 1

Views: 966

Answers (2)

Jiaweikodomo
Jiaweikodomo

Reputation: 23

Just add to the accepted answer, looping over the first index will be even faster. Consider the following two functions, test1() is faster to run because the loop is in the first index.

aa_stack1 = zeros(3, 10000, 10000);
aa_stack3 = zeros(10000, 10000, 3);

function test1()
    for ii = 1:3
        aa_stack1[ii, :, :] = rand(10000, 10000)
    end
end

function test2()
    for ii = 1:3
        aa_stack3[:, :, ii] = rand(10000, 10000)
    end
end

@time test1()
@time test2()

The first way "maximizes memory locality and reduces cache misses" because "when you iterate over the first dimension, the values of the other two dimensions are kept in cache, which means that accessing them takes less time" (according to ChatGPT).

Upvotes: 0

MarcMush
MarcMush

Reputation: 1488

There's multiple ways to do this, you'll have to tty and test which one is the best in your case.

with append! and resize

Arrays in Julia should start iterating with the first index, which the number of images is the last index. If 80 is the amount of images, the reshape should be reshape(images_3d, (160,120,80)) (maybe exchange 120 and 160, not sure about this one).

And then to get the first image, it's reshaped_3d[:,:,1]

with push!

push!ing the matrices and then creating the 3d array with cat would work too :

julia> A = [rand(3,4) for i in 1:2];

julia> cat(A..., dims=3)
3×4×2 Array{Float64, 3}:
[:, :, 1] =
 0.372747  0.17654   0.398272  0.231992
 0.514789  0.342374  0.399816  0.277959
 0.908909  0.864676  0.9788    0.585375

[:, :, 2] =
 0.358169  0.816448  0.0558052  0.404178
 0.747453  0.80815   0.384903   0.447053
 0.314895  0.46264   0.947465   0.170982

initialize the 3D Array (probably the best one)

and fill it up progressively

julia> A = Array{Float64}(undef,3,4,2);

julia> for i in 1:2
           A[:,:,i] = rand(3,4)
       end

julia> A
3×4×2 Array{Float64, 3}:
[:, :, 1] =
 0.478106  0.829818  0.526572  0.644238
 0.714812  0.781246  0.93239   0.759864
 0.523958  0.955136  0.70079   0.193489

[:, :, 2] =
 0.481405   0.561407  0.184557    0.449584
 0.547769   0.170311  0.371797    0.538843
 0.0285712  0.731686  0.00126473  0.452273

Upvotes: 3

Related Questions