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