FUTURE CAPABILITIES (maybe)
these operations are under consideration
Proportional Tapering
The call to running above has three keyword arguments. In the first example, their default values were used. Fully elaborated, that call becomes:
running(sum, width, data; padding=nopadding, atend=false, scale=false)
To scale tapered value by width / current_taper_length,use scale=true.
# using sum with scaled tapering (at start, the default)
(1 * 3/1,), ((1+2) * 3/2), (1+2+3), (2+3+4), (3+4+5), (4+5+6)
(3.0, 4.5, 6.0, 9.0, 12.0, 15.0)
running(sum, width, data; scale=true) == (3.0, 4.5, 6.0, 9.0, 12.0, 15.0)
running(sum, width, data; atend=false, scale=true) == (3.0, 4.5, 6.0, 9.0, 12.0, 15.0)
# using sum with scaled tapering at the end
(1+2+3), (2+3+4), (3+4+5), (4+5+6), ((5+6) * 3/2,), (6 * 3/1,)
(6.0, 9.0, 12.0, 15.0, 16.5, 18.0)
running(sum, width, data; atend=true, scale=true) == (6.0, 9.0, 12.0, 15.0, 16.5, 18.0)Hopping
this is the general case for moving windows
data = [1, 2, 3, 4, 5, 6] # here, a single vector
nitems = n # length(data)
width = w # a window covering `w` indices at a time
slide = s # the window advances in steps of `±s` indices
hop = h # the window skips `±h` indices before sliding
indexfrom = i # the initial window starts at index `i`slide, hop, indexfrom must make sense given nitems and width
constraints
1 <= width <= nitems1 <= indexfrom <= nitems(width-nitems) <= slide <= (nitems-width)(width-nitems) <= hop <= (nitems-width)
orientation
orientation = sign(hop + slide)
- orientation is positive
- the window moves from lower indices to higher indices
indexfrom + width <= nitems
- orientation is negative
- the window moves from higher indices to lower indices
indexfrom - width >= 1
- orientation is zero
- (the moving window does not move)
- the function is applied once, the result is a single value
- it is likely that this is a client error
Varying Window Width
Here is a copy of the initial rolling example
data = [1, 2, 3, 4, 5, 6] # here, a single vector
width = 3 # a window covering 3 indices at a time
slide = 1 # the window advances in steps of 1 index
# the moving window covers
(1,2,3), (2,3,4), (3,4,5), (4,5,6) # windowed data
# using sum
(1+2+3), (2+3+4), (3+4+5), (4+5+6)
(6, 9, 12, 15) # the result
rolling(sum, width, data) == (6, 9, 12, 15) # this packageThere are situations where working without the constraint of a fixed window width is helpful. Specifying successive window widths is supported; the sum of these widths, along with the optional keywords padding and atend, is used to determine the data that is spanned by all of the windows.
rollingwith multiple window widths assumes each window follows the prior, beginning atprior_start+1and finishing atprior_start+current_window_width(inclusive).
data = [1, 2, 3, 4, 5, 6] # here, a single vector
widths = [2, 2, 4] # a window covering 3 indices at a time
slide = 1 # the window advances in steps of 1 index
# the moving windows cover
(1,2), (2,3), (3,4,5,6) # windowed data
# using sum
(1+2), (2+3), (3+4+5+6)
(3, 5, 18) # the result
rolling(sum, widths, data) == (3, 5, 18) # this packagetilingwith multiple window widths assumes the windows are immediately adjacent, beginning atprior_end+1and finishing atprior_end+current_window_width(inclusive).
data = [1, 2, 3, 4, 5, 6] # here, a single vector
widths = [1, 2, 3] # a window covering 3 indices at a time
slide = [1, 2, 3] # the window advances by each width
# the moving windows cover
(1,), (2,3), (4,5,6) # windowed data
# using sum
(1), (2+3), (4+5+6)
(1, 5, 15) # the result
tiling(sum, widths, data) == (1, 5, 15) # this packagerunningwith multiple window widths assumes each window follows the prior, beginning atprior_start+1and finishing atprior_start+current_window_width(inclusive).- use
padding(withatendif needed), it is ignored unless needed
- use
data = [1, 2, 3, 4, 5, 6] # here, a single vector
widths = [2, 2, 3] # a window covering 3 indices at a time
slide = 1 # the window advances in steps of 1 index
atend = false # fill in the first value(s)
# the moving windows cover
(2,3), (3,4), (4,5,6) # windowed data
# tapering covers
(1,), (2,3), (3,4), (4,5,6) # windowed data
# using sum
(1,), (2+3), (3+4), (4+5+6)
(1, 5, 7, 15) # the result
running(sum, widths, data) == (1, 5, 7, 15) # this packageWindowing in 2D
Substantive image processing becomes available with 2D moving windows.
- image [is an image stored as] pixel values within a [rectangular] matrix.
- winsize = (rows spanned, columns spanned) by the window
- winoffset = winsize .- 1
- winmove = advance window by (nrows, ncolumns)
- winstart = (initial row, initial column) for window upper left
- winstop = winstart .+ winoffset
the initial and the next windows
Base.:(+)(start::T, move::T) where {I<:Integer, T<:NTuple{2,I}} =
(start[1]+move[1], start[2]+move[2])
Base.:(-)(start::T, move::T) where {I<:Integer, T<:NTuple{2,I}} =
(start[1]-move[1], start[2]-move[2])
image = reshape(1:5^2, (5,5))
#= 5×5 reshape(::UnitRange{Int64}, 5, 5) with eltype Int64:
1 6 11 16 21
2 7 12 17 22
3 8 13 18 23
4 9 14 19 24
5 10 15 20 25
=#
winstart = (1, 1)
winstop = (3, 3)
winmove = (2, 2)
intial_window_content =
image[CartesianIndex(winstart):CartesianIndex(winstop)]
#= 3×3 Matrix{Int64}:
13 18 23
14 19 24
15 20 25
=#
winstart += winmove
winstop += winmove
next_window_content =
image[CartesianIndex(winstart):CartesianIndex(winstop)]
#= 3×3 Matrix{Int64}:
13 18 23
14 19 24
15 20 25
=#moving through a recursive Z pattern
uses
max indices
# 2D
julia> x=2^16; morton=cartesian2morton([x,x]); match= ([x,x]==morton2cartesian(morton));
julia> (; coords=:xy, x, morton, match)
(coords = :xy, x = 65536, morton = 4294967296, match = true)
# 3D
julia> x=2^10; morton=cartesian3morton([x,x,x]); match= ([x,x,x]==morton3cartesian(morton));
julia> (; coords=:xyz, x, morton, match)
(coords = :xyz, x = 1024, morton = 1073741824, match = true)
refs