Skip to content
Snippets Groups Projects
Commit 87e36bbc authored by François  LAURENT's avatar François LAURENT
Browse files

Merge branch 'label-reverse-mapping' into 'main'

Label reverse mapping

See merge request !4
parents d129ed83 0e52422f
No related branches found
No related tags found
1 merge request!4Label reverse mapping
[deps] [deps]
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3" LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3"
PlanarLarvae = "c2615984-ef14-4d40-b148-916c85b43307"
[tool.poetry] [tool.poetry]
name = "MaggotUBA-adapter" name = "MaggotUBA-adapter"
version = "0.16.1" version = "0.16.2"
description = "Interface between MaggotUBA and the Nyx tagging UI" description = "Interface between MaggotUBA and the Nyx tagging UI"
authors = ["François Laurent"] authors = ["François Laurent"]
license = "MIT" license = "MIT"
......
#!/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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment