首页 > 解决方案 > 更快地调整图像大小

问题描述

我有一堆图像(3D 数组),我想提高它们的分辨率(上采样)。我运行以下代码片段,我发现它有点慢......

有什么办法可以提高这段代码的速度吗?(不使用多处理)

using BenchmarkTools
using Interpolations

function doInterpol(arr::Array{Int, 2}, h, w)
   A = interpolate(arr, BSpline(Linear()))
   return A[1:2/(h-1)/2:2, 1:2/(w-1)/2:2]
end

function applyResize!(arr3D_hd::Array, arr3D_ld::Array, t::Int, h::Int, w::Int)
    for i = 1:1:t
         @inbounds arr3D_hd[i, :, :] = doInterpol(arr3D_ld[i, :, :], h, w)
    end
end

t, h, w = 502, 65, 47
h_target, w_target = 518, 412

arr3D_ld = reshape(collect(1:t*h*w), (t, h, w))
arr3D_hd = Array{Float32}(undef, t, h_target, w_target)
applyResize!(arr3D_hd, arr3D_ld, t, h_target, w_target)

当我对以下内容进行基准测试时:

@btime applyResize!(arr3D_hd, arr3D_ld, t, h_target, w_target)

我有 :

2.334 s (68774 allocations: 858.01 MiB)

我多次运行它,结果在 [1.8s - 2.8s] 间隔内。

标签: imageimage-processingmultidimensional-arrayjuliainterpolation

解决方案


Julia 以列优先顺序存储数组。这意味着类似切片的arr[i, : ,:]性能比arr[:,:,i](在内存中连续)要差得多。因此,获得一些速度的一种方法是使用(h,w,t)而不是索引您的数组(t, w, h)

第二个问题是像arr[i,:,:]复制数据一样获取切片。它在这里的影响似乎可以忽略不计,但是如果可以的话,养成使用数组视图而不是切片的习惯可能会很好。视图是一个小型包装对象,其行为方式与较大数组的切片相同,但不保存数据的副本:它直接访问父数组的数据(请参阅下面的示例以更好地理解一个观点是)。

请注意, Julia 性能提示中提到了这两个问题;阅读此页面中的其余建议可能会很有用。

综上所述,您的示例可以重写为:

function applyResize2!(arr3D_hd::Array, arr3D_ld::Array, h::Int, w::Int, t)
    @inbounds for i = 1:1:t
        A = interpolate(@view(arr3D_ld[:, :, i]), BSpline(Linear()))
        arr3D_hd[:, :, i] .= A(1:2/(h-1)/2:2, 1:2/(w-1)/2:2)
    end
end

它与存储的数组一起使用,与您的情况略有不同:

       # Note the order of indices
julia> arr3D_ld = reshape(collect(1:t*h*w), (h, w, t));
julia> arr3D_hd = Array{Float32}(undef, h_target, w_target, t);

       # Don't forget to escape arguments with a $ when using btime
       # (not really an issue here, but could have been one)
julia> @btime applyResize2!($arr3D_hd, $arr3D_ld, h_target, w_target, t)
  506.449 ms (6024 allocations: 840.11 MiB)

这大约是原始代码的 3.4 倍的加速,在我的机器上进行了这样的基准测试:

julia> arr3D_ld = reshape(collect(1:t*h*w), (t, h, w));
julia> arr3D_hd = Array{Float32}(undef, t, h_target, w_target);
julia> @btime applyResize!($arr3D_hd, $arr3D_ld, t, h_target, w_target)
  1.733 s (50200 allocations: 857.30 MiB)


注意:您的原始代码使用类似A[x, y]获取插值的语法。这似乎已被弃用,取而代之的是A(x, y). 我可能没有Interpolations和你一样的版本,虽然......




说明视图行为的示例

julia> a = rand(3,3)
3×3 Array{Float64,2}:
 0.042097  0.767261  0.0433798
 0.791878  0.764044  0.605218
 0.332268  0.197196  0.722173

julia> v = @view(a[:,2]) # creates a view instead of a slice
3-element view(::Array{Float64,2}, :, 2) with eltype Float64:
 0.7672610491393876
 0.7640443797187411
 0.19719581867637093

julia> v[3] = 42  # equivalent to a[3,2] = 42
42

推荐阅读