ImageMetadata.jl
ImageMetadata (a package incorporated into Images) allows you to add metadata to images: for example, the date and time at which it was collected, identifiers for the location or subject, etc. This metadata is stored as a dictionary, and the ImageMeta
type combines properties of arrays and Dict
.
Introduction
You typically create an ImageMeta
using keyword arguments:
julia> using Colors, ImageMetadata, Dates
julia> img = ImageMeta(fill(RGB(1,0,0), 3, 2), date=Date(2016, 7, 31), time="high noon")
RGB ImageMeta with:
data: 3×2 Array{RGB{N0f8},2} with eltype RGB{N0f8}
properties:
date: 2016-07-31
time: high noon
You can then index elements of img
like this:
julia> img[1,2]
RGB{N0f8}(1.0,0.0,0.0)
and access and set properties like this:
julia> img.time
"high noon"
julia> img.time = "evening"
"evening"
julia> img
RGB ImageMeta with:
data: 3×2 Array{RGB{N0f8},2} with eltype RGB{N0f8}
properties:
date: 2016-07-31
time: evening
You can extract the data matrix with arraydata(img)
:
julia> arraydata(img)
3×2 Array{RGB{N0f8},2} with eltype RGB{FixedPointNumbers.N0f8}:
RGB{N0f8}(1.0,0.0,0.0) RGB{N0f8}(1.0,0.0,0.0)
RGB{N0f8}(1.0,0.0,0.0) RGB{N0f8}(1.0,0.0,0.0)
RGB{N0f8}(1.0,0.0,0.0) RGB{N0f8}(1.0,0.0,0.0)
and the properties dictionary with properties
:
julia> properties(img)
Dict{Symbol, Any} with 2 entries:
:date => Date("2016-07-31")
:time => "high noon"
Properties are not accessed or modified by most of Images' algorithms–-the traits that most affect processing are encoded through Julia's type system. However, functions that receive an ImageMeta
should return an ImageMeta
when appropriate. Naturally, in your own code it's fine to use properties to your advantage for custom tasks.
If you index a scalar location (a single pixel), img[i,j,...]
will return just the value of that pixel. But if you index a range, you get another ImageMeta
:
julia> c = img[1:2, 1:2]
RGB ImageMeta with:
data: 2×2 Array{RGB{N0f8},2} with eltype RGB{N0f8}
properties:
date: 2016-07-31
time: high noon
This copies both the data (just the relevant portions) and the properties dictionary. In contrast,
julia> v = view(img, 1:2, 1:2)
RGB ImageMeta with:
data: 2×2 view(::Array{RGB{N0f8},2}, 1:2, 1:2) with eltype RGB{N0f8}
properties:
date: 2016-07-31
time: high noon
shares both the data and the properties with the original image img
. Modifying values or properties in c
has no impact on img
, but modifying values or properties in v
does.
copyproperties/shareproperties
Two convenient ways to construct a new image with the "same" properties are copyproperties
(makes a copy of the properties dictionary) and shareproperties
(shares the properties dictionary).
Incidentally, similar
makes a copy of the properties dictionary.
spatialproperties
Occasionally you may have a property that is linked to the spatial axes of the image. In such cases, one source for potential confusion is permutedims
, which swaps the order of the dimensions in the array: if the order is not also swapped in the appropriate properties, chaos could result.
You can declare that certain properties are coupled to spatial axes using "spatialproperties"
:
julia> using ImageMetadata
julia> A = reshape(1:15, 3, 5)
3×5 reshape(::UnitRange{Int64}, 3, 5) with eltype Int64:
1 4 7 10 13
2 5 8 11 14
3 6 9 12 15
julia> img = ImageMeta(A, spatialproperties=Set([:maxsum]), maxsum=[maximum(sum(A,dims=1)), maximum(sum(A,dims=2))])
Int64 ImageMeta with:
data: 3×5 reshape(::UnitRange{Int64}, 3, 5) with eltype Int64
properties:
maxsum: [42, 45]
spatialproperties: Set([:maxsum])
julia> imgp = permutedims(img, (2,1))
Int64 ImageMeta with:
data: 5×3 Matrix{Int64}
properties:
maxsum: [45, 42]
spatialproperties: Set([:maxsum])
julia> maximum(sum(imgp, dims=1))
45
It's not possible to anticipate all the possible transformations that might be necessary, but at least simple swaps are handled automatically.