朱莉娅

时间:2020-05-27 08:52:37

标签: arrays julia median

它与this question

有关

我想知道如何计算巨大数组上特定维度的中位数,例如大小(20,1920,1080,3)。我不确定是否有任何实际用途,但我只是想检查一下Julia中位数的工作情况。

使用numpy计算(3,1920,1080,3)的中值需要〜0.5秒。它在零点数组上的工作速度非常快(在(120,1920,1080,3)上不到2秒),而在实像上工作得很好(在(120,1920,1080,3)上只有20秒)。 >

Python代码:

import cv2
import sys
import numpy as np
import time

ZEROES=True
N_IMGS=20

print("n_imgs:", N_IMGS)
print("use dummy data:", ZEROES)

imgs_paths = sys.argv[1:]
imgs_paths.sort()
imgs_paths_sparse = imgs_paths[::30]

imgs_paths = imgs_paths_sparse[N_IMGS]

if ZEROES:
    imgs_arr = np.zeros((N_IMGS,1080,1920,3), dtype=np.float32)
else:
    imgs = map(cv2.imread, imgs_paths)
    imgs_arr = np.array(list(imgs), dtype=np.float32)

start = time.time()
imgs_median = np.median(imgs_arr, 0)
end = time.time()
print("time:", end - start)
cv2.imwrite('/tmp/median.png', imgs_median)

在朱莉娅中,我只能计算(3,1920,1080,3)的中位数。之后,由于大量已使用的内存,我的earlyoom进程终止了julia进程。

我尝试的方法类似于我在max上首次尝试的方法:

function median1(imgs_arr)
    a = imgs_arr
    b = reshape(cat(a..., dims=1), tuple(length(a), size(a[1])...))
    imgs_max = Statistics.median(b, dims=1)
    return imgs_max
end

或更简单的情况:

import Statistics
a = zeros(3,1080,1920,3)
@time Statistics.median(a, dims=1)
 10.609627 seconds (102.64 M allocations: 2.511 GiB, 3.37% gc time)
...

因此,花费numpy需要10秒,而花费0.5秒。 我只有4个CPU内核,而不仅仅是并行化。

是否以某种方式或多或少地对其进行了优化?

还是至少要进行切片并在不过度使用内存的情况下对其进行一次计算?

2 个答案:

答案 0 :(得分:3)

尝试JuliennedArrays.jl

julia> a = zeros(3,1080,1920,3);

julia> using JuliennedArrays

julia> @time map(median, Slices(a,1));
  0.822429 seconds (6.22 M allocations: 711.915 MiB, 20.15% gc time)

正如Stefan在下面评论的那样,内置median的功能相同,但是很多

julia> @time median(a, dims=1);
  7.450394 seconds (99.80 M allocations: 2.368 GiB, 4.47% gc time)

至少截至julia> VERSION v"1.5.0-DEV.876"

答案 1 :(得分:3)

很难知道是否单独加载图像是这里问题的关键部分,因为缺少Julia中问题的设置,并且Julia程序员很难遵循Python设置或知道我们需要多少匹配它。您要么需要:

  1. 加载或移动图像数据,以使它们实际上是同一数组的一部分,然后取其中值;

  2. 使一组不同数组中的空间无关值抽象地表现出来,就好像它们是单个数组的一部分一样,然后通过通用的方法来处理该集合的中值。

Fredrik的答案隐式地假定您已经加载了图像数据,因此它们都属于同一连续数组。但是,如果是这种情况,那么您甚至不需要JuliennedArrays,就可以使用median stdlib中的Statistics函数:

julia> a = rand(3, 1080, 1920, 3);

julia> using Statistics

julia> median(a, dims=1)
1×1080×1920×3 Array{Float64,4}:
[:, :, 1, 1] =
 0.63432  0.205958  0.216221  0.571541  …  0.238637  0.285947  0.901014

[:, :, 2, 1] =
 0.821851  0.486859  0.622313  …  0.917329  0.417657  0.724073

如果您可以像这样加载数据,则这是最好的方法-这是迄今为止一堆相同大小图像的最有效表示,并使跨图像的矢量化操作变得简单而高效。第一维是进行操作的最有效的方法,因为Julia是主要列对象,因此第一维(列)是连续存储的。

将图像放入连续内存的最佳方法是预先分配正确类型和尺寸的未初始化数组,然后使用一些就地API将数据读入数组。由于某种原因,您的Julia代码似乎已将图像作为单个数组的向量加载,而您的Python代码似乎已将所有图像加载到单个数组中?

重塑和连接的方法是第二种方法的极端情况,在该方法中,您一次移动所有数据,然后再应用矢量化的中值运算。显然,这涉及到移动大量数据,效率很低。

由于内存的局部性,将数据的单个片段复制到临时数组并计算其中间值可能会更有效。使用数组理解可以很容易地做到这一点:

julia> v_of_a = [rand(1080, 1920, 3) for _ = 1:3]
3-element Array{Array{Float64,3},1}:
 [0.7206652600431633 0.7675119703509619 … 0.7117084561740263 0.8736518021960584; 0.8038479801395197 0.3159392943734012 … 0.976319025405266 0.3278606124069767; … ; 0.7424260315304789 0.4748658164109498 … 0.9942311708400311 0.37048961459068086; 0.7832577306186075 0.13184454935145773 … 0.5895094390350453 0.5470111170897787]

[0.26401298651503025 0.9113932653115289 … 0.5828647778524962 0.752444909740893; 0.5673144007678044 0.8154276504227804 … 0.2667436824684424 0.4895443896447764; … ; 0.2641913584303701 0.16639100493266934 … 0.1860616855126005 0.04922131616483538; 0.4968214514330498 0.994935452055218 … 0.28097239922248685 0.4980189891952156]

julia> [median(a[i,j,k] for a in v_of_a) for i=1:1080, j=1:1920, k=1:3]
1080×1920×3 Array{Float64,3}:
[:, :, 1] =
 0.446895  0.643648  0.694714   …  0.221553   0.711708   0.225268
 0.659251  0.457686  0.672072      0.731218   0.449915   0.129987
 0.573196  0.328747  0.668702      0.355231   0.656686   0.303168
 0.243656  0.702642  0.45708       0.23415    0.400252   0.482792