aboutsummaryrefslogtreecommitdiff
path: root/julia/CrystFEL/src/image.jl
blob: 33af3252915aa66e779555dd9fa0f4ff6c10bf4d (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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
module Images

using Printf

import ..CrystFEL: libcrystfel
import ..CrystFEL.DataTemplates: DataTemplate, InternalDataTemplate
import ..CrystFEL.DetGeoms: DetGeom
import ..CrystFEL.PeakLists: PeakList, InternalPeakList
import ..CrystFEL.Crystals: Crystal, InternalCrystal
import ..CrystFEL.RefLists: RefList, InternalRefList, UnmergedReflection
import ..CrystFEL.Symmetry: SymOpList
export Image

const HEADER_CACHE_SIZE = 128

mutable struct CrystalRefListPair
    crystal::Ptr{InternalCrystal}
    reflist::Ptr{InternalRefList}
    owns_crystal::Cint
    owns_reflist::Cint
end

mutable struct InternalImage
    dp::Ptr{Ptr{Cfloat}}
    bad::Ptr{Ptr{Cint}}
    sat::Ptr{Ptr{Cfloat}}
    hit::Cint
    crystals::Ptr{CrystalRefListPair}
    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}
    crystals
    reflists
end


function makecrystallist(image, listptr, n)

    crystals = []

    if listptr == C_NULL
        return crystals
    end

    for i in 1:n
        pairptr = unsafe_load(listptr, i)

        # Re-use old Crystal if possible
        n = findfirst(getfield(image, :crystals)) do x
            x.internalptr == pairptr.crystal
        end

        if n !== nothing
            cr = getfield(image, :crystals)[n]
        else
            cr = Crystal(pairptr.crystal, nothing)
        end

        if pairptr.reflist == C_NULL
            reflist = nothing
        else
            reflist = RefList{UnmergedReflection}(pairptr.reflist, SymOpList("1"))
            pairptr.owns_reflist = 0
        end
        push!(crystals, (crystal=cr, reflections=reflist))
        pairptr.owns_crystal = 0
        unsafe_store!(listptr, pairptr, i)
        # We are now responsible for freeing the Crystal and RefList
    end

    image.crystals = map(x->x.crystal, crystals)
    image.reflists = map(x->x.reflections, crystals)
    return crystals

end


function getpeaklist(image)
    idata = unsafe_load(image.internalptr)
    if (getfield(image, :peaklist) === nothing) ||
        (idata.peaklist != getfield(image, :peaklist).internalptr)
        if idata.peaklist != C_NULL
            setfield!(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
            setfield!(image, :peaklist, nothing)
        end
    end
    return getfield(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(image,
                                   getfield(idata, :crystals),
                                   getfield(idata, :n_crystals))

        else
            getfield(idata, name)
        end
    end
end


strdup(str) = @ccall libcrystfel.cfstrdup(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 == 1) && (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)
    setfield!(image, :peaklist, new_pl)

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)

        elseif name === :crystals
            return setfield!(image, :crystals, val)

        elseif name === :reflists
            return setfield!(image, :reflists, 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)
    @ccall libcrystfel.image_add_crystal(image.internalptr::Ptr{InternalImage},
                                         cr.internalptr::Ptr{InternalCrystal})::Cvoid

    idata = unsafe_load(image.internalptr)
    ncryst = idata.n_crystals
    pairptr = unsafe_load(idata.crystal_refls, ncryst)
    pairptr.owns_crystal = 0
    unsafe_store!(idata.crystal_refls, pairptr, ncryst)
    push!(getfield(image, :crystals), cr)
end


function Base.push!(image::Image, cr::Crystal, reflections::RefList{UnmergedReflection})
    @ccall libcrystfel.image_add_crystal_refls(image.internalptr::Ptr{InternalImage},
                                               cr.internalptr::Ptr{InternalCrystal},
                                               reflections.internalptr::Ptr{InternalRefList})::Cvoid
    idata = unsafe_load(image.internalptr)
    ncryst = idata.n_crystals
    pairptr = unsafe_load(idata.crystals, ncryst)
    pairptr.owns_crystal = 0
    pairptr.owns_reflist = 0
    unsafe_store!(idata.crystals, pairptr, ncryst)
    push!(getfield(image, :crystals), cr)
    push!(getfield(image, :reflists), reflections)
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


function Base.show(io::IO, mime::MIME"text/plain", image::Image)

    idata = unsafe_load(image.internalptr)
    @printf(io, "CrystFEL.Image(%p):\n\n", image.internalptr)

    println(io, "                    Serial number: ", idata.serial)
    write(io, "                         Filename: ")
    if idata.filename == C_NULL
        write(io, "<not set>")
    else
        write(io, unsafe_string(idata.filename))
    end
    write(io, "\n")

    write(io, "                         Frame ID: ")
    if idata.ev == C_NULL
        write(io, "<not set>")
    else
        write(io, unsafe_string(idata.ev))
    end
    write(io, "\n")

    write(io, "\n")
    println(io, "                       Wavelength: ", idata.lambda*1e10, " Å")
    println(io, "                        Bandwidth: ", idata.bw*100, " %")
    println(io, "                       Divergence: ", idata.div*1e3, " mrad")

    write(io, "\n")
    if idata.peaklist != C_NULL
        let npk = @ccall libcrystfel.image_feature_count(idata.peaklist::Ptr{InternalPeakList})::Cint
            println(io, "                  Number of peaks: ", npk)
        end
    else
        println(io, "                  Number of peaks: 0 (no peak list)")
    end

    println(io, "             Estimated resolution: ", 1e10/idata.peak_resolution, " Å")
    write(io, "                         Hit flag: ")
    if idata.hit != 0
        write(io, "set")
    else
        write(io, "not set")
    end
    write(io, "\n")

    write(io, "\n")
    println(io, "               Number of crystals: ", idata.n_crystals)
    println(io, " Number of indexing attempts made: ", idata.n_crystals)
    println(io, "             Indexed by algorithm: ", idata.indexed_by)
end


function Base.show(io::IO, image::Image)
    @printf(io, "CrystFEL.Image(%p)", image.internalptr)
end


end  # of module