diff options
Diffstat (limited to 'julia/CrystFEL/src/image.jl')
-rw-r--r-- | julia/CrystFEL/src/image.jl | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/julia/CrystFEL/src/image.jl b/julia/CrystFEL/src/image.jl new file mode 100644 index 00000000..4090dc61 --- /dev/null +++ b/julia/CrystFEL/src/image.jl @@ -0,0 +1,356 @@ +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 + finalizer(reflist) do x + @ccall libcrystfel.reflist_free(x.internalptr::Ptr{InternalRefList})::Cvoid + end + end + push!(crystals, (crystal=cr, reflections=reflist)) + pairptr.owns_crystal = 0 + unsafe_store!(listptr, pairptr, i) + finalizer(cr) do x + @ccall libcrystfel.crystal_free(x.internalptr::Ptr{InternalCrystal})::Cvoid + end + 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 |