Skip to content

deeplabcut.utils.auxfun_multianimal

DeepLabCut2.0 Toolbox (deeplabcut.org) © A. & M. Mathis Labs https://github.com/DeepLabCut/DeepLabCut Please see AUTHORS for contributors.

https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS Licensed under GNU Lesser General Public License v3.0

Functions:

Name Description
IntersectionofIndividualsandOnesGivenbyUser

Returns all individuals when set to 'all', otherwise all bpts that are in the

LoadFullMultiAnimalData

Load predicted data and metadata from pickle files created by predict_videos.py.

SaveFullMultiAnimalData

Save predicted data as h5 file and metadata as pickle file; created by

convert2_maDLC

Convert a single-animal annotation file into a multianimal annotation file.

convert_single2multiplelegacyAM

Convert multi animal to single animal code and vice versa.

filter_unwanted_paf_connections

Get rid of skeleton connections between multi and unique body parts.

getpafgraph

Auxiliary function that turns skeleton (list of connected bodypart pairs) into a

read_inferencecfg

Load inferencecfg or initialize it.

reorder_individuals_in_df

Reorders data of df to match the order given in a list.

returnlabelingdata

Returns a specific labeleing data set -- the user will be asked which one.

IntersectionofIndividualsandOnesGivenbyUser

IntersectionofIndividualsandOnesGivenbyUser(cfg, individuals)

Returns all individuals when set to 'all', otherwise all bpts that are in the intersection of comparisonbodyparts and the actual bodyparts.

Source code in deeplabcut/utils/auxfun_multianimal.py
def IntersectionofIndividualsandOnesGivenbyUser(cfg, individuals):
    """Returns all individuals when set to 'all', otherwise all bpts that are in the
    intersection of comparisonbodyparts and the actual bodyparts.
    """
    if "individuals" not in cfg:  # Not a multi-animal project...
        return [""]
    all_indivs = extractindividualsandbodyparts(cfg)[0]
    if individuals == "all":
        return all_indivs
    else:  # take only items in list that are actually bodyparts...
        return [ind for ind in individuals if ind in all_indivs]

LoadFullMultiAnimalData

LoadFullMultiAnimalData(dataname)

Load predicted data and metadata from pickle files created by predict_videos.py.

Source code in deeplabcut/utils/auxfun_multianimal.py
def LoadFullMultiAnimalData(dataname):
    """Load predicted data and metadata from pickle files created by predict_videos.py."""
    dataname = Path(dataname)
    data_file = dataname.with_name(dataname.stem + "_full.pickle")
    metadata_file = dataname.with_name(dataname.stem + "_meta.pickle")
    try:
        with data_file.open("rb") as handle:
            data = pickle.load(handle)
    except (pickle.UnpicklingError, FileNotFoundError):
        data = shelve.open(data_file, flag="r")
    with metadata_file.open("rb") as handle:
        metadata = pickle.load(handle)
    return data, metadata

SaveFullMultiAnimalData

SaveFullMultiAnimalData(data, metadata, dataname, suffix='_full')

Save predicted data as h5 file and metadata as pickle file; created by predict_videos.py.

Source code in deeplabcut/utils/auxfun_multianimal.py
def SaveFullMultiAnimalData(data, metadata, dataname, suffix="_full"):
    """Save predicted data as h5 file and metadata as pickle file; created by
    predict_videos.py.
    """
    dataname = Path(dataname)
    data_path = dataname.with_name(dataname.stem + suffix + ".pickle")
    metadata_path = dataname.with_name(dataname.stem + "_meta.pickle")

    data_path.parent.mkdir(parents=True, exist_ok=True)
    with data_path.open("wb") as f:
        pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
    with metadata_path.open("wb") as f:
        pickle.dump(metadata, f, pickle.HIGHEST_PROTOCOL)
    return data_path, metadata_path

convert2_maDLC

convert2_maDLC(config: str | Path, userfeedback=True, forceindividual=None)

Convert a single-animal annotation file into a multianimal annotation file.

Introduces an individuals column with either the first individual in individuals list in config.yaml or whatever is passed via "forceindividual".

Parameters:

Name Type Description Default

config

str | Path

Full path of the config.yaml file as a string.

required

userfeedback

bool

If false, all folders are processed without prompting. If true, the user is asked for each folder whether to convert. Use this, e.g. if you have already labeled some folders and want to convert data for new videos only.

True

forceindividual

str | None

If a string is given, that value is used in the individuals column. Defaults to None.

None

Examples:

Convert multianimalbodyparts under the 'first individual' in individuals list in config.yaml and uniquebodyparts under 'single':

deeplabcut.convert2_maDLC("/socialrearing-task/config.yaml")

Convert multianimalbodyparts under the individual label mus17 and uniquebodyparts under 'single':

deeplabcut.convert2_maDLC("/socialrearing-task/config.yaml", forceindividual="mus17")
Source code in deeplabcut/utils/auxfun_multianimal.py
def convert2_maDLC(config: str | Path, userfeedback=True, forceindividual=None):
    """Convert a single-animal annotation file into a multianimal annotation file.

    Introduces an individuals column with either the first individual
    in individuals list in config.yaml or whatever is passed via "forceindividual".

    Args:
        config (str | Path): Full path of the config.yaml file as a string.
        userfeedback (bool, optional): If false, all folders are processed without prompting.
            If true, the user is asked for each folder whether to convert. Use this, e.g. if you have already labeled
            some folders and want to convert data for new videos only.
        forceindividual (str | None, optional): If a string is given, that value is used
            in the individuals column. Defaults to None.

    Examples:
        Convert multianimalbodyparts under the 'first individual' in individuals list in
        `config.yaml` and uniquebodyparts under 'single':

            deeplabcut.convert2_maDLC("/socialrearing-task/config.yaml")

        Convert multianimalbodyparts under the individual label mus17 and uniquebodyparts
        under 'single':

            deeplabcut.convert2_maDLC("/socialrearing-task/config.yaml", forceindividual="mus17")
    """
    cfg = auxiliaryfunctions.read_config(config)
    videos = cfg["video_sets"].keys()
    video_names = [Path(i).stem for i in videos]
    folders = [Path(config).parent / "labeled-data" / Path(i) for i in video_names]

    individuals, uniquebodyparts, multianimalbodyparts = extractindividualsandbodyparts(cfg)

    if forceindividual is None:
        if len(individuals) == 0:
            print("At least one individual should exist...")
            folders = []
            forceindividual = ""
        else:
            forceindividual = individuals[0]  # note that single is added at then end!

        if forceindividual == "single":  # no specific individual ()
            if len(multianimalbodyparts) > 0:  # there should be an individual name...
                print("At least one individual should exist beyond 'single', as there are multianimalbodyparts...")
                folders = []

    for folder in folders:
        if userfeedback:
            print("Do you want to convert the annotation file in folder:", folder, "?")
            askuser = input("yes/no")
        else:
            askuser = "yes"

        if askuser == "y" or askuser == "yes" or askuser == "Ja" or askuser == "ha":  # multilanguage support :)
            fn = folder / ("CollectedData_" + cfg["scorer"])
            Data = pd.read_hdf(fn.with_suffix(".h5"))
            conversioncode.guarantee_multiindex_rows(Data)
            imindex = Data.index

            print("This is a single animal data set, converting to multi...", folder)

            # -> adding (single,bpt) for uniquebodyparts
            for j, bpt in enumerate(uniquebodyparts):
                index = pd.MultiIndex.from_arrays(
                    np.array([2 * [cfg["scorer"]], 2 * ["single"], 2 * [bpt], ["x", "y"]]),
                    names=["scorer", "individuals", "bodyparts", "coords"],
                )

                if bpt in Data[cfg["scorer"]].keys():
                    frame = pd.DataFrame(Data[cfg["scorer"]][bpt].values, columns=index, index=imindex)
                else:
                    frame = pd.DataFrame(
                        np.ones((len(imindex), 2)) * np.nan,
                        columns=index,
                        index=imindex,
                    )

                if j == 0:
                    dataFrame = frame
                else:
                    dataFrame = pd.concat([dataFrame, frame], axis=1)

            if len(uniquebodyparts) == 0:
                dataFrame = None

            # -> adding (individual,bpt) for multianimalbodyparts
            for j, bpt in enumerate(multianimalbodyparts):
                index = pd.MultiIndex.from_arrays(
                    np.array(
                        [
                            2 * [cfg["scorer"]],
                            2 * [str(forceindividual)],
                            2 * [bpt],
                            ["x", "y"],
                        ]
                    ),
                    names=["scorer", "individuals", "bodyparts", "coords"],
                )

                if bpt in Data[cfg["scorer"]].keys():
                    frame = pd.DataFrame(Data[cfg["scorer"]][bpt].values, columns=index, index=imindex)
                else:
                    frame = pd.DataFrame(
                        np.ones((len(imindex), 2)) * np.nan,
                        columns=index,
                        index=imindex,
                    )

                if j == 0 and dataFrame is None:
                    dataFrame = frame
                else:
                    dataFrame = pd.concat([dataFrame, frame], axis=1)

            Data.to_hdf(
                fn.with_name(fn.name + "singleanimal.h5"),
                key="df_with_missing",
            )
            Data.to_csv(fn.with_name(fn.name + "singleanimal.csv"))

            dataFrame.to_hdf(fn.with_suffix(".h5"), key="df_with_missing")
            dataFrame.to_csv(fn.with_suffix(".csv"))

convert_single2multiplelegacyAM

convert_single2multiplelegacyAM(config, userfeedback=True, target=None)

Convert multi animal to single animal code and vice versa.

Note that by providing target='single'/'multi' this will be target!

Source code in deeplabcut/utils/auxfun_multianimal.py
def convert_single2multiplelegacyAM(config, userfeedback=True, target=None):
    """Convert multi animal to single animal code and vice versa.

    Note that by providing target='single'/'multi' this will be target!
    """
    cfg = auxiliaryfunctions.read_config(config)
    videos = cfg["video_sets"].keys()
    video_names = [Path(i).stem for i in videos]
    folders = [Path(config).parent / "labeled-data" / Path(i) for i in video_names]

    prefixes, uniquebodyparts, multianimalbodyparts = extractindividualsandbodyparts(cfg)
    for folder in folders:
        if userfeedback:
            print("Do you want to convert the annotation file in folder:", folder, "?")
            askuser = input("yes/no")
        else:
            askuser = "yes"

        if askuser == "y" or askuser == "yes" or askuser == "Ja" or askuser == "ha":  # multilanguage support :)
            fn = folder / ("CollectedData_" + cfg["scorer"])
            Data = pd.read_hdf(fn.with_suffix(".h5"))
            conversioncode.guarantee_multiindex_rows(Data)
            imindex = Data.index

            if "individuals" in Data.columns.names and (target is None or target == "single"):
                print("This is a multianimal data set, converting to single...", folder)
                for prfxindex, prefix in enumerate(prefixes):
                    if prefix == "single":
                        for j, bpt in enumerate(uniquebodyparts):
                            index = pd.MultiIndex.from_product(
                                [[cfg["scorer"]], [bpt], ["x", "y"]],
                                names=["scorer", "bodyparts", "coords"],
                            )
                            frame = pd.DataFrame(
                                Data[cfg["scorer"]][prefix][bpt].values,
                                columns=index,
                                index=imindex,
                            )
                            if j == 0:
                                dataFrame = frame
                            else:
                                dataFrame = pd.concat([dataFrame, frame], axis=1)
                    else:
                        for j, bpt in enumerate(multianimalbodyparts):
                            index = pd.MultiIndex.from_product(
                                [[cfg["scorer"]], [prefix + bpt], ["x", "y"]],
                                names=["scorer", "bodyparts", "coords"],
                            )
                            frame = pd.DataFrame(
                                Data[cfg["scorer"]][prefix][bpt].values,
                                columns=index,
                                index=imindex,
                            )
                            if j == 0:
                                dataFrame = frame
                            else:
                                dataFrame = pd.concat([dataFrame, frame], axis=1)
                    if prfxindex == 0:
                        DataFrame = dataFrame
                    else:
                        DataFrame = pd.concat([DataFrame, dataFrame], axis=1)

                Data.to_hdf(
                    fn.with_name(fn.name + "multianimal.h5"),
                    key="df_with_missing",
                )
                Data.to_csv(fn.with_name(fn.name + "multianimal.csv"))

                DataFrame.to_hdf(
                    fn.with_suffix(".h5"),
                    key="df_with_missing",
                )
                DataFrame.to_csv(fn.with_suffix(".csv"))
            elif target is None or target == "multi":
                print("This is a single animal data set, converting to multi...", folder)
                for prfxindex, prefix in enumerate(prefixes):
                    if prefix == "single":
                        if cfg["uniquebodyparts"] != [None]:
                            for j, bpt in enumerate(uniquebodyparts):
                                index = pd.MultiIndex.from_arrays(
                                    np.array(
                                        [
                                            2 * [cfg["scorer"]],
                                            2 * [prefix],
                                            2 * [bpt],
                                            ["x", "y"],
                                        ]
                                    ),
                                    names=[
                                        "scorer",
                                        "individuals",
                                        "bodyparts",
                                        "coords",
                                    ],
                                )
                                if bpt in Data[cfg["scorer"]].keys():
                                    frame = pd.DataFrame(
                                        Data[cfg["scorer"]][bpt].values,
                                        columns=index,
                                        index=imindex,
                                    )
                                else:  # fill with nans...
                                    frame = pd.DataFrame(
                                        np.ones((len(imindex), 2)) * np.nan,
                                        columns=index,
                                        index=imindex,
                                    )

                                if j == 0:
                                    dataFrame = frame
                                else:
                                    dataFrame = pd.concat([dataFrame, frame], axis=1)
                        else:
                            dataFrame = None
                    else:
                        for j, bpt in enumerate(multianimalbodyparts):
                            index = pd.MultiIndex.from_arrays(
                                np.array(
                                    [
                                        2 * [cfg["scorer"]],
                                        2 * [prefix],
                                        2 * [bpt],
                                        ["x", "y"],
                                    ]
                                ),
                                names=["scorer", "individuals", "bodyparts", "coords"],
                            )
                            if prefix + "_" + bpt in Data[cfg["scorer"]].keys():
                                frame = pd.DataFrame(
                                    Data[cfg["scorer"]][prefix + "_" + bpt].values,
                                    columns=index,
                                    index=imindex,
                                )
                            else:
                                frame = pd.DataFrame(
                                    np.ones((len(imindex), 2)) * np.nan,
                                    columns=index,
                                    index=imindex,
                                )

                            if j == 0:
                                dataFrame = frame
                            else:
                                dataFrame = pd.concat([dataFrame, frame], axis=1)
                    if prfxindex == 0:
                        DataFrame = dataFrame
                    else:
                        DataFrame = pd.concat([DataFrame, dataFrame], axis=1)

                Data.to_hdf(
                    fn.with_name(fn.name + "singleanimal.h5"),
                    key="df_with_missing",
                )
                Data.to_csv(fn.with_name(fn.name + "singleanimal.csv"))

                DataFrame.to_hdf(
                    fn.with_suffix(".h5"),
                    key="df_with_missing",
                )
                DataFrame.to_csv(fn.with_suffix(".csv"))

filter_unwanted_paf_connections

filter_unwanted_paf_connections(cfg, paf_graph)

Get rid of skeleton connections between multi and unique body parts.

Source code in deeplabcut/utils/auxfun_multianimal.py
def filter_unwanted_paf_connections(cfg, paf_graph):
    """Get rid of skeleton connections between multi and unique body parts."""
    multi = extractindividualsandbodyparts(cfg)[2]
    desired = list(combinations(range(len(multi)), 2))
    return [i for i, edge in enumerate(paf_graph) if tuple(edge) not in desired]

getpafgraph

getpafgraph(cfg, printnames=True)

Auxiliary function that turns skeleton (list of connected bodypart pairs) into a list of corresponding indices (with regard to the stacked multianimal/uniquebodyparts)

Convention: multianimalbodyparts go first!

Source code in deeplabcut/utils/auxfun_multianimal.py
def getpafgraph(cfg, printnames=True):
    """Auxiliary function that turns skeleton (list of connected bodypart pairs) into a
    list of corresponding indices (with regard to the stacked
    multianimal/uniquebodyparts)

    Convention: multianimalbodyparts go first!
    """
    individuals, uniquebodyparts, multianimalbodyparts = extractindividualsandbodyparts(cfg)
    # Attention this order has to be consistent (for training set creation, training, inference etc.)

    bodypartnames = multianimalbodyparts + uniquebodyparts
    lookupdict = {bodypartnames[j]: j for j in range(len(bodypartnames))}

    if cfg["skeleton"] is None:
        cfg["skeleton"] = []

    connected = set()
    partaffinityfield_graph = []
    for link in cfg["skeleton"]:
        if link[0] in bodypartnames and link[1] in bodypartnames:
            bp1 = int(lookupdict[link[0]])
            bp2 = int(lookupdict[link[1]])
            connected.add(bp1)
            connected.add(bp2)
            partaffinityfield_graph.append([bp1, bp2])
        else:
            print("Attention, parts do not exist!", link)

    if printnames:
        graph2names(cfg, partaffinityfield_graph)

    return partaffinityfield_graph

read_inferencecfg

read_inferencecfg(path_inference_config, cfg)

Load inferencecfg or initialize it.

Source code in deeplabcut/utils/auxfun_multianimal.py
def read_inferencecfg(path_inference_config, cfg):
    """Load inferencecfg or initialize it."""
    try:
        inferencecfg = auxiliaryfunctions.read_plainconfig(path_inference_config)
    except FileNotFoundError:
        inferencecfg = form_default_inferencecfg(cfg)
        auxiliaryfunctions.write_plainconfig(str(path_inference_config), dict(inferencecfg))
    return inferencecfg

reorder_individuals_in_df

reorder_individuals_in_df(df: DataFrame, order: list) -> pd.DataFrame

Reorders data of df to match the order given in a list.

Parameters:

Name Type Description Default

df

DataFrame

Data from tracked .h5 file.

required

order

list of str

Desired order of individuals.

required

Returns:

Type Description
DataFrame

pd.DataFrame: Reordered DataFrame.

Source code in deeplabcut/utils/auxfun_multianimal.py
def reorder_individuals_in_df(df: pd.DataFrame, order: list) -> pd.DataFrame:
    """Reorders data of df to match the order given in a list.

    Args:
        df (pd.DataFrame): Data from tracked .h5 file.
        order (list of str): Desired order of individuals.

    Returns:
        pd.DataFrame: Reordered DataFrame.
    """
    columns = df.columns
    inds = df.index

    data = df.loc(axis=1)[:, order].to_numpy()
    df = pd.DataFrame(data, columns=columns, index=inds)

    return df

returnlabelingdata

returnlabelingdata(config)

Returns a specific labeleing data set -- the user will be asked which one.

Source code in deeplabcut/utils/auxfun_multianimal.py
def returnlabelingdata(config):
    """Returns a specific labeleing data set -- the user will be asked which one."""
    cfg = auxiliaryfunctions.read_config(config)
    videos = cfg["video_sets"].keys()
    video_names = [Path(i).stem for i in videos]
    folders = [Path(config).parent / "labeled-data" / Path(i) for i in video_names]
    for folder in folders:
        print("Do you want to get the data for folder:", folder, "?")
        askuser = input("yes/no")
        if askuser == "y" or askuser == "yes" or askuser == "Ja" or askuser == "ha":  # multilanguage support :)
            fn = folder / ("CollectedData_" + cfg["scorer"] + ".h5")
            Data = pd.read_hdf(fn)
            return Data