Skip to content
Snippets Groups Projects

Label reverse mapping

Merged François LAURENT requested to merge label-reverse-mapping into main
3 files
+ 157
1
Compare changes
  • Side-by-side
  • Inline
Files
3
+ 154
0
 
#!/usr/bin/env bash
 
#=
 
if realpath --version &> /dev/null; then
 
macos_realpath=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd -P)
 
PROJECT_DIR=$(dirname $macos_realpath)
 
else
 
PROJECT_DIR=$(dirname $(dirname $(realpath "${BASH_SOURCE[0]}")))
 
fi
 
if [ -z "$JULIA" ]; then JULIA=julia; fi
 
exec $JULIA --project="$PROJECT_DIR" --color=yes --startup-file=no "${BASH_SOURCE[0]}" "$@"
 
=#
 
 
using PlanarLarvae.LarvaBase, PlanarLarvae.Datasets, PlanarLarvae.Formats
 
using JSON3
 
 
"""
 
revert_label_mapping(output_label_file, unmapped_label_file, edited_label_file, clfconfig_file)
 
 
Reverse-map the labels while keeping the manual editions made on top of mapped labels.
 
 
All input arguments are file paths.
 
 
This is useful for example with taggers `20230311-0` and `20230311` (same predictions, with
 
labels mapped from 12 labels to 7 labels):
 
 
* `edited_label_file` saved after manually editing a file generated by the `20230311` tagger
 
(mapped labels);
 
* `unmapped_label_file` generated by `20230311-0` (original labels prior to label mapping and
 
editing); can be a *trx.mat* file instead;
 
* `clfconfig_file`: *clf_config.json* file for tagger `20230311` (specifies label mapping);
 
* `output_label_file`: resulting json *.label* file.
 
"""
 
function revert_label_mapping(output_label_file, unmapped_label_file, edited_label_file, clfconfig_file)
 
clfconfig = JSON3.read(clfconfig_file)
 
legal_labels = collect(clfconfig["original_behavior_labels"])
 
mapping = zip(legal_labels, clfconfig["behavior_labels"])
 
reverse_mapping = mergewith(vcat, (Dict(v=>[k]) for (k, v) in mapping)...)
 
original_needed = Dict{String, Vector{String}}()
 
for (k, v) in pairs(reverse_mapping)
 
if 1 < length(v)
 
original_needed[k] = pop!(reverse_mapping, k)
 
end
 
end
 
reverse_mapping = Dict(k=>v[1] for (k, v) in pairs(reverse_mapping))
 
#
 
input_labels = load(edited_label_file)
 
original_labels = preload(unmapped_label_file)
 
if !isempty(original_needed)
 
if original_labels isa Formats.Trxmat
 
Formats.drop_spines!(original_labels)
 
Formats.drop_outlines!(original_labels)
 
end
 
original_labels = Formats.getnativerepr(original_labels)
 
if original_labels isa Run
 
decodelabels!(original_labels)
 
end
 
end
 
run = decodelabels!(getrun(input_labels))
 
#
 
for track in values(run)
 
labels = track[:labels]
 
for (i, labels′) in enumerate(labels)
 
label = if labels′ isa Vector
 
@assert length(labels′) == 2 && labels′[2] == "edited"
 
labels′[1]
 
else
 
@assert labels′ isa AbstractString
 
labels′
 
end
 
if label in keys(reverse_mapping)
 
label = reverse_mapping[label]
 
else
 
original = if original_labels isa Run
 
labels″ = original_labels[track.id][:labels][i]
 
labels″ isa Vector ? labels″ : [labels″]
 
elseif original_labels isa LarvaBase.Larvae
 
_, labels″ = original_labels[track.id][i]
 
convert(Vector{String}, labels″[:tags])
 
end
 
for label′ in original
 
if label′ in legal_labels
 
if label′ original_needed[label]
 
@warn "Restored label qualitatively differs" label label′ track.id step=i
 
end
 
label = label′
 
break
 
end
 
end
 
end
 
@assert label in legal_labels
 
if labels′ isa Vector
 
labels′[1] = label
 
else
 
labels′ = label
 
end
 
labels[i] = labels′
 
end
 
end
 
#
 
if run.attributes[:labels] isa AbstractDict
 
run.attributes[:labels][:names] = legal_labels
 
else
 
run.attributes[:labels] = legal_labels
 
end
 
secondary_labels = Datasets.getsecondarylabels(run)
 
if !isnothing(secondary_labels)
 
foreach(secondary_labels) do label
 
push!(legal_labels, label)
 
end
 
end
 
encodelabels!(run; labels=legal_labels, storelabels=false)
 
#
 
if :metadata in keys(run.attributes)
 
metadata = run.attributes[:metadata]
 
pop!(metadata, :software, nothing)
 
if isempty(metadata)
 
pop!(run.attributes, :metadata)
 
end
 
end
 
#
 
Datasets.to_json_file(output_label_file, run)
 
end
 
 
usage = """revert_label_mapping.jl
 
 
Usage:
 
revert_label_mapping.jl <output-file> <unmapped-labels> <edited-labels> <clfconfig-file>
 
 
Options:
 
-h, --help Show this message.
 
 
Arguments:
 
<output-file> Output .label file path with reverse-mapped labels and manual editions.
 
<unmapped-labels> Unmapped .label file path (e.g. from tagger 20230311-0).
 
<edited-labels> .label file path with mapped labels (e.g. from tagger 20230311) and
 
manually edited afterwards.
 
<clfconfig-file> Tagger's clf_config.json file path with label mapping specification
 
(e.g. 20230311's).
 
"""
 
 
function main(args=ARGS)
 
if args isa AbstractString
 
args = split(args)
 
end
 
if length(args) != 4 || "-h" in args || "--help" in args
 
print(usage)
 
else
 
revert_label_mapping(args...)
 
end
 
end
 
 
if abspath(PROGRAM_FILE) == @__FILE__
 
main()
 
end
Loading