1
+ """
2
+ ReadonlyTiffDiskArray(mappedtype, rawtype, ifds, dims) -> ReadonlyTiffDiskArray
3
+
4
+ A lazy representation of a OMETIFF file. This custom type is needed since TIFF
5
+ files are laid out noncontiguously and nonregularly. It uses an internal index
6
+ to determine the mapping from indices to the locations of data slices on disk.
7
+ These slices are generally XY slices and are usually loaded in all at once so it
8
+ is quickly loaded into an internal cache to speed up the process. Externally,
9
+ this type should behave very similarly to an in-memory array, albeit with a
10
+ higher cost of accessing an element.
11
+
12
+ $(FIELDS)
13
+ """
14
+ mutable struct ReadonlyTiffDiskArray{T <: Gray , R, N1, N2} <: AbstractArray{T, N2}
15
+ """
16
+ A map of dimensions (sans XY) to the corresponding [`IFD`](@ref)
17
+ """
18
+ ifds:: OrderedDict{NTuple{N1, Int}, IFD}
19
+
20
+ """
21
+ The full set of dimensions of the TIFF file, including XY
22
+ """
23
+ dims:: NTuple{N2, Int}
24
+
25
+ """
26
+ An internal cache to fill when reading from disk
27
+ """
28
+ cache:: Array{R, 2}
29
+
30
+ """
31
+ The dimension indices corresponding to the slice currently in the cache
32
+ """
33
+ cache_index:: NTuple{N1, Int}
34
+
35
+ function ReadonlyTiffDiskArray (:: Type{T} , :: Type{R} , ifds:: OrderedDict{NTuple{N1, Int}, IFD} , dims:: NTuple{N2, Int} ) where {T, R, N1, N2}
36
+ if N2 - 2 != N1
37
+ error (" $N2 dimensions given, but the IFDs are indexed on $N1 dimensions instead of " *
38
+ " expected $(N2- 2 ) ." )
39
+ end
40
+ new {T, R, N1, N2} (ifds, dims, Array {R} (undef, dims[1 ], dims[2 ]), (- 1 , - 1 , - 1 , - 1 ))
41
+ end
42
+ end
43
+
44
+ Base. size (A:: ReadonlyTiffDiskArray ) = A. dims
45
+
46
+ function Base. getindex (A:: ReadonlyTiffDiskArray{Gray{T}, R, N1, N2} , i1:: Int , i2:: Int , i:: Vararg{Int, N1} ) where {T, R, N1, N2}
47
+ # check the loaded cache is already the correct slice
48
+ if A. cache_index == i
49
+ return Gray (reinterpret (T, A. cache[i2, i1]))
50
+ end
51
+
52
+ ifd = A. ifds[i]
53
+
54
+ # if the file isn't open, lets open a handle and update it
55
+ if ! isopen (ifd. file. io)
56
+ path = ifd. file. filepath
57
+ ifd. file. io = Stream (format " OMETIFF" , open (path), path)
58
+ end
59
+
60
+ n_strips = length (ifd. strip_offsets)
61
+ strip_len = floor (Int, (size (A. cache, 1 ) * size (A. cache, 2 )) / n_strips)
62
+
63
+ # if the data is striped then we need to change the buffer shape so that we
64
+ # can read into it. This should be replaced with a view of cache in Julia
65
+ # >1.4, see https://github.com/JuliaLang/julia/pull/33046
66
+ if n_strips > 1 && size (tmp) != (strip_len, )
67
+ tmp = Array {R} (undef, strip_len)
68
+ else
69
+ tmp = A. cache
70
+ end
71
+
72
+ _read_ifd_data! (ifd, A. cache, tmp)
73
+
74
+ A. cache_index = i
75
+
76
+ return Gray (reinterpret (T, A. cache[i2, i1]))
77
+ end
78
+
79
+ function Base. setindex! (A:: ReadonlyTiffDiskArray{Gray{T}, R, N1, N2} , X, I... ) where {T, R, N1, N2}
80
+ error (" This array is on disk and is read only. Convert to a mutable in-memory version by running " *
81
+ " `copy(arr)`. \n\n 𝗡𝗼𝘁𝗲: For large files this can be quite expensive. A future PR will add " *
82
+ " support for reading and writing to/from disk." )
83
+ end
0 commit comments