aboutsummaryrefslogtreecommitdiff
path: root/julia/CrystFEL/src/image.jl
blob: de79a556a27d27458d1cc99be1749f1d1c59ab7f (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
module Images

import ..CrystFEL: libcrystfel
import ..CrystFEL.DataTemplates: DataTemplate, InternalDataTemplate
import ..CrystFEL.DetGeoms: DetGeom
import ..CrystFEL.PeakLists: PeakList, InternalPeakList
import ..CrystFEL.Crystals: Crystal, InternalCrystal
export Image

const HEADER_CACHE_SIZE = 128

mutable struct InternalImage
    dp::Ptr{Ptr{Cfloat}}
    bad::Ptr{Ptr{Cint}}
    sat::Ptr{Ptr{Cfloat}}
    hit::Cint
    crystals::Ptr{Ptr{Cvoid}}
    n_crystals::Cint
    indexed_by::Cint
    n_indexing_tries::Cint
    detgeom::Ptr{DetGeom}
    data_source_type::Cint
    filename::Cstring
    ev::Cstring
    data_block::Ptr{Cvoid}
    data_block_size::Csize_t
    meta_data::Cstring
    header_cache::NTuple{HEADER_CACHE_SIZE, Ptr{Cvoid}}
    n_cached_headers::Cint
    id::Cint
    serial::Cint
    spectrum::Ptr{Cvoid}
    lambda::Cdouble
    div::Cdouble
    bw::Cdouble
    peak_resolution::Cdouble
    peaklist::Ptr{InternalPeakList}
    ida::Ptr{Cvoid}
    owns_peaklist::Cint
end


mutable struct Image
    internalptr::Ptr{InternalImage}
    peaklist::Union{Nothing,PeakList}
end


function makecrystallist(listptr, n)

    crystals = Crystal[]

    if listptr == C_NULL
        return crystals
    end

    for i in 1:n
        crystalptr = unsafe_load(listptr, i)
        push!(crystals, Crystal(crystalptr, true))
    end

    crystals

end


function getpeaklist(image)
    idata = unsafe_load(image.internalptr)
    if (image.peaklist === nothing) || (idata.peaklist != image.peaklist.internalptr)
        if idata.peaklist != C_NULL
            image.peaklist = PeakList(idata.peaklist)
            # From now on, Julia is completely responsible for freeing the peaklist
            idata.owns_peaklist = 0
            unsafe_store!(image.internalptr, idata)
        else
            image.peaklist = nothing
        end
    end
    return image.peaklist
end


function Base.getproperty(image::Image, name::Symbol)
    if name === :internalptr
        getfield(image, :internalptr)
    elseif name === :peaklist
        getpeaklist(image)
    else
        idata = unsafe_load(image.internalptr)

        if name === :crystals
            return makecrystallist(getproperty(idata, :crystals),
                                   getproperty(idata, :n_crystals))

        else
            getproperty(idata, name)
        end
    end
end


strdup(str) = @ccall strdup(str::Cstring)::Cstring


function assert_type(val, type)
    if !(val isa type)
        throw(ArgumentError("Must be a "*string(type)*" (have "*string(typeof(val))*" instead)"))
    end
end


function set_peaklist(image, new_pl)

    assert_type(new_pl, PeakList)

    idata = unsafe_load(image.internalptr)
    if (idata.owns_peaklist == 0) && (idata.peaklist != C_NULL)
        @ccall libcrystfel.image_feature_list_free(idata.peaklist::Ptr{InternalPeakList})::Cvoid
    end
    idata.peaklist = new_pl.internalptr
    idata.owns_peaklist = 0
    unsafe_store!(image.internalptr, idata)

end


function Base.setproperty!(image::Image, name::Symbol, val)
    if name === :internalptr
        setfield!(image, :internalptr, val)
    else

        if name === :peaklist
            return set_peaklist(image, val)

        elseif name === :filename
            assert_type(val, AbstractString)
            val = strdup(val)

        elseif name === :ev
            assert_type(val, AbstractString)
            val = strdup(val)

        end

        idata = unsafe_load(image.internalptr)
        setproperty!(idata, name, val)
        unsafe_store!(image.internalptr, idata)

    end
end


function Base.propertynames(image::Image; private=false)
    if private
        fieldnames(InternalImage)
    else
        tuple(fieldnames(InternalImage)..., :internalptr)
    end
end


function Base.push!(image::Image, cr::Crystal)
    if cr.in_image
        throw(ErrorException("Crystal is already in an image"))
    end
    ccall((:image_add_crystal, libcrystfel),
          Cvoid, (Ptr{InternalImage},Ptr{InternalCrystal}),
          image.internalptr, cr.internalptr)
    cr.in_image = true
end


"""
    Image(dtempl::DataTemplate)

Creates a CrystFEL image structure, not linked to any file or data block,
i.e. for simulation purposes.  This will fail if `dtempl` contains any
references to metadata fields, e.g. `photon_energy = /LCLS/photon_energy eV`.

Corresponds to CrystFEL C API function `image_create_for_simulation()`.
"""
function Image(dtempl::DataTemplate)

    out = ccall((:image_create_for_simulation, libcrystfel),
                Ptr{Image}, (Ref{InternalDataTemplate},), dtempl.internalptr)
    if out == C_NULL
        throw(ArgumentError("Failed to create image"))
    end

    image = Image(out, nothing)

    finalizer(image) do x
        ccall((:image_free, libcrystfel), Cvoid, (Ptr{InternalImage},), x.internalptr)
    end

    return image
end


"""
    Image(dtempl::DataTemplate, filename::AbstractString, event::AbstractString,
          no_image_data=false, no_mask_data=false)

Loads an image from the filesystem.

Corresponds to CrystFEL C API function `image_read()`.
"""
function Image(dtempl::DataTemplate,
               filename::AbstractString,
               event::AbstractString="//",
               no_image_data=false,
               no_mask_data=false)

    out = @ccall libcrystfel.image_read(dtempl.internalptr::Ptr{InternalDataTemplate},
                                        filename::Cstring, event::Cstring,
                                        no_image_data::Cint, no_mask_data::Cint,
                                        C_NULL::Ptr{Cvoid})::Ptr{Image}
    if out == C_NULL
        throw(ArgumentError("Failed to load image"))
    end

    image = Image(out, nothing)

    finalizer(image) do x
        ccall((:image_free, libcrystfel), Cvoid, (Ptr{InternalImage},), x.internalptr)
    end

    return image
end


end  # of module