def match(cells_ad, labels, match_frac=0.4, split_frac=0.1, output_splits_merges=False):
    """
    This function matches the advected cells of the previous timestep to the newly
    identified ones. A minimal overlap of 40% is required. In case of split of merge,
    the larger cell supersedes the smaller one in naming.
    """
    cells_ov = cells_ad.copy()
    possible_merge_ids = {i: [] for i in np.unique(labels)}
    for ID_a, cell_a in cells_ov.iterrows():
        if cell_a.ID == 0 or np.isnan(cell_a.ID):
            continue
        ID_vec = labels[cell_a.y, cell_a.x]
        IDs = np.unique(ID_vec)
        n_IDs = len(IDs)
        if n_IDs == 1 and IDs[0] == 0:
            cells_ov.loc[ID_a, "t_ID"] = 0
            continue
        IDs = IDs[IDs != 0]
        n_IDs = len(IDs)

        for i in IDs:
            possible_merge_ids[i].append(cell_a.ID)

        N = np.zeros(n_IDs)
        for n in range(n_IDs):
            N[n] = len(np.where(ID_vec == IDs[n])[0])

        if output_splits_merges:
            # Only consider possible split if overlap is large enough
            valid_split_ids = (N / len(ID_vec)) > split_frac
            # splits here
            if sum(valid_split_ids) > 1:
                # Save split information
                cells_ov.loc[ID_a, "splitted"] = True
                cells_ov.loc[ID_a, "split_IDs"] = IDs[valid_split_ids]
                cells_ov.loc[ID_a, "split_fracs"] = N / len(ID_vec)

        m = np.argmax(N)
        ID_match = IDs[m]
        ID_coverage = N[m] / len(ID_vec)
        if ID_coverage >= match_frac:
            cells_ov.loc[ID_a, "t_ID"] = ID_match
        else:
            cells_ov.loc[ID_a, "t_ID"] = 0
        cells_ov.loc[ID_a, "frac"] = ID_coverage
    return cells_ov, labels, possible_merge_ids
This function matches the advected cells of the previous timestep to the newly
identified ones. A minimal overlap of 40% is required. In case of split of merge,
the larger cell supersedes the smaller one in naming.
pysteps/tracking/tdating.py