-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
base.jl
131 lines (97 loc) · 3.65 KB
/
base.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
abstract type ProjectiveTransform <: Transform
Abstract supertype for projective transformations. See
[Projective transformations](../docs/projective/interface.md).
"""
abstract type ProjectiveTransform <: Transform end
"""
getprojection(tfm, bounds; randstate)
Create a projection for an item with spatial bounds `bounds`.
The projection should be a `CoordinateTransformations.Transformation`.
See [CoordinateTransformations.jl](https://github.com/JuliaGeometry/CoordinateTransformations.jl)
"""
function getprojection end
struct Bounds{N}
rs::NTuple{N, UnitRange{Int}}
end
function Base.show(io::IO, bounds::Bounds{N}) where N
print(io, "Bounds(")
for (i, r) in enumerate(bounds.rs)
print(io, first(r), ':', last(r))
i == N || print(io, '×')
end
print(io, ")")
end
Bounds(sz::NTuple{N, <:Integer}) where N = Bounds(Tuple(1:n for n in sz))
Bounds(axes::NTuple{N, <:Base.OneTo}) where N = Bounds(convert.(UnitRange{Int}, axes))
Base.:(==)(bs1::Bounds, bs2::Bounds) = false
function Base.:(==)(bs1::Bounds{N}, bs2::Bounds{N}) where N
c1 = all((first(bs1.rs[i]) == first(bs2.rs[i])) for i in 1:N)
c2 = all((last(bs1.rs[i]) == last(bs2.rs[i])) for i in 1:N)
return c1 && c2
end
"""
transformbounds(bounds, P)
Apply `CoordinateTransformations.Transformation` to `bounds`.
"""
function transformbounds(bounds::Bounds, P::CoordinateTransformations.Transformation)
return Bounds(ImageTransformations.autorange(CartesianIndices(bounds.rs), P))
end
"""
getbounds(item)
Return the spatial bounds of `item`. For a 2D-image (`Image{2}`)
the bounds are the 4 corners of the bounding rectangle. In general,
for an N-dimensional item, the bounds are a vector of the N^2 corners
of the N-dimensional hypercube bounding the data.
"""
function getbounds end
getbounds(wrapper::ItemWrapper) = getbounds(getwrapped(wrapper))
"""
project(P, item, indices)
Project `item` using projection `P` and crop to `indices` if given.
"""
function project end
"""
project!(bufitem, P, item, indices)
Project `item` using projection `P` and crop to `indices` if given.
Store result in `bufitem`. Inplace version of [`project`](#).
Default implementation falls back to `project`.
"""
function project!(bufitem, P, item, indices)
titem = project(P, item, indices)
copyitemdata!(bufitem, titem)
return bufitem
end
"""
projectionbounds(tfm, P, bounds, randstate)
"""
function projectionbounds(tfm, P, bounds; randstate = getrandstate(tfm))
return transformbounds(bounds, P)
end
# With the interface defined, any `ProjectiveTransform` can be `apply`ed
# like this:
function apply(tfm::ProjectiveTransform, item::Item; randstate = getrandstate(tfm))
bounds = getbounds(item)
P = getprojection(tfm, bounds; randstate = randstate)
bounds_ = projectionbounds(tfm, P, bounds; randstate = randstate)
return P isa IdentityTransformation ? item : project(P, item, bounds_)
end
# For the buffered version, `project!` is used. Of course the size
# of the data produced by `tfm` needs to be the same every time it
# is applied for this to work.
function apply!(
bufitem::AbstractItem,
tfm::ProjectiveTransform,
item::AbstractItem;
randstate = getrandstate(tfm))
bounds = getbounds(item)
P = getprojection(tfm, bounds; randstate = randstate)
bounds_ = projectionbounds(tfm, P, bounds; randstate = randstate)
res = project!(bufitem, P, item, bounds_)
return res
end
# The simplest `ProjectiveTransform` is the static `Project`.
struct Project{T<:CoordinateTransformations.Transformation} <: ProjectiveTransform
P::T
end
getprojection(tfm::Project, bounds; randstate = nothing) = tfm.P