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 <= nitems
1 <= 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 package
There 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.
rolling
with multiple window widths assumes each window follows the prior, beginning atprior_start+1
and 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 package
tiling
with multiple window widths assumes the windows are immediately adjacent, beginning atprior_end+1
and 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 package
running
with multiple window widths assumes each window follows the prior, beginning atprior_start+1
and finishing atprior_start+current_window_width
(inclusive).- use
padding
(withatend
if 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 package
Windowing 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