示例#1
0
def alter_basis_label(config_path, camera, index=None, image_paths=None):
    data_path = os.path.join(
        read_config(config_path)['data_path'], 'labels.pickle')
    with open(data_path, 'rb') as infile:
        basis_labels = pickle.load(infile)

    if image_paths is None:
        camera_images = detect_cage_calibration_images(config_path)
示例#2
0
def detect_2d_coords(config_path, suffix='filtered.h5', bonvideos=False):
    '''
    This function detects and returns the state of deeplabcut-triangulated coordinate h5 files (can be changed)

    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    suffix : string
        The suffix in the DeepLabCut 3D project triangualtion result storage files

    Example
    -------

    '''

    cfg = read_config(config_path)
    dlc3d_cfgs = get_dlc3d_configs(config_path)

    results_path = Path(cfg['results_path'])
    triangulated = results_path / 'triangulated'
    experiments = glob(str(triangulated / '*/'))
    if experiments == []:
        msg = 'Could not find any triangulated coordinates in %s' % triangulated
        raise ValueError(msg)

    coords = {}
    for exp_path in experiments:
        exp_dir_name = os.path.basename(exp_path)
        if bonvideos is True:
            animal, trial, date = exp_dir_name.split('_')
            coords[(animal, trial, date)] = {}
        else:
            coords[exp_dir_name] = {}

        regions_of_interest = {}
        for hdf_path in glob(os.path.join(exp_path, '**/*' + suffix)):
            pair_info = os.path.basename(os.path.dirname(hdf_path)).split('_')
            if len(pair_info) == 2:
                cam1, cam2 = pair_info
            else:
                idx_, cam1, cam2 = pair_info
            pair = (cam1, cam2)

            df = pd.read_hdf(os.path.realpath(hdf_path))
            exp_regions_of_interest = df.columns.levels[0]
            regions_of_interest[pair] = exp_regions_of_interest

            coord = {roi: df[roi].values for roi in exp_regions_of_interest}
            if bonvideos is True:
                coords[(animal, trial, date)][pair] = coord
            else:
                coords[exp_dir_name][pair] = coord

    return coords
示例#3
0
def visualize_triangulation(config_path, undistort=True, decrement=False, save=True):
    dlc3d_cfgs = get_dlc3d_configs(config_path)
    basis_labels = get_labels(config_path)

    cfg = read_config(config_path)
    test_dir = os.path.join(cfg['data_path'], 'test')
    if not os.path.exists(test_dir):
        os.mkdir(test_dir)

    fig = plt.figure(figsize=(14, 10))

    # Get non-corner pairs by splicing
    pairs = tuple(PAIR_IDXS.keys())[::2]
    pair_ax = {}
    for i, pair in enumerate(pairs):
        dlc3d_cfg = dlc3d_cfgs[pair]
        cam1, cam2 = pair

        # Prepare camera plot labels
        cam_labels = get_paired_labels(config_path, pair)['decrement' if decrement is True else 'normal']

        # Triangulate the two sets of labels, and map them to 3D
        trian_dict, trian_coord = triangulate_raw_2d_camera_coords(
            dlc3d_cfg,
            cam1_coords=tuple(cam_labels[cam1].values()),
            cam2_coords=tuple(cam_labels[cam2].values()),
            keys=cam_labels[cam1],
            undistort=undistort
        )

        pair_ax[pair] = fig.add_subplot(2, 2, i+1, projection='3d')
        for label, coord in trian_dict.items():
            pair_ax[pair].scatter(*coord, label=label)

        if CAMERAS[cam1][0][1] == 'left':
            c_origin = trian_coord[0] + (trian_coord[1] - trian_coord[0]) / 2
        else:
            c_origin = trian_coord[1] + (trian_coord[0] - trian_coord[1]) / 2
        pair_ax[pair].scatter(*c_origin, label='computed origin')

        pair_ax[pair].legend()
        angle_origins = vg.angle(trian_dict['origin'], c_origin)
        pair_ax[pair].set_title('%s %s\nInnerAngle(orgin, c_origin): %.2f deg' % (*pair, angle_origins)).set_y(1.005)

    fig.suptitle('Triangulation visualization', fontsize=20)

    if save is True:
        fig.savefig(os.path.join(test_dir, 'visualize_triangulation.png'))

    return fig, pair_ax
示例#4
0
def OE_basis_label(config_path,
                   detect=True,
                   name_pos=0,
                   format='png',
                   image_paths=None):
    '''
    See deepcage.auxiliary.detect.basis_label()
    '''
    import matplotlib.pyplot as plt
    import gc

    from deepcage.auxiliary.detect import detect_cage_calibration_images
    from deepcage.auxiliary.gui import get_title, get_coord

    if image_paths is None:
        camera_images = detect_cage_calibration_images(config_path,
                                                       name_pos=name_pos)

    n = -1
    basis_labels = dict.fromkeys(
        camera_images.keys() if detect is True else CAMERAS.keys())
    for camera in basis_labels.keys():
        cam_img = camera_images[camera]

        basis_labels[camera] = (get_coord(cam_img,
                                          n=n,
                                          title=get_title(
                                              camera, 'x-axis', 'positive',
                                              True)),
                                get_coord(cam_img,
                                          n=n,
                                          title=get_title(
                                              camera, 'y-axis', 'negative',
                                              True)),
                                get_coord(cam_img,
                                          n=n,
                                          title=get_title(
                                              camera, 'z-axis', 'positive',
                                              True)),
                                get_coord(cam_img, n=n, title='Select origin'))

        plt.close()
        gc.collect()

    data_path = os.path.join(
        read_config(config_path)['data_path'], 'labels.pickle')
    with open(data_path, 'wb') as outfile:
        stereo_file = pickle.dump(basis_labels, outfile)

    return basis_labels
示例#5
0
def visualize_basis_vectors(config_path, undistort=True, normalize=True, decrement=False, stereo_cam_units=None, save=True):
    '''
    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    '''
    if stereo_cam_units is None:
        stereo_cam_units, orig_maps = create_stereo_cam_origmap(config_path, undistort=undistort, normalize=normalize, decrement=decrement, save=False)

    cfg = read_config(config_path)
    dlc3d_cfgs = get_dlc3d_configs(config_path)

    test_dir = os.path.join(cfg['data_path'], 'test')
    if not os.path.exists(test_dir):
        os.mkdir(test_dir)

    pairs = tuple(dlc3d_cfgs.keys())
    pair_num = int(len(pairs) / 2)

    fig = plt.figure(figsize=(14, 8))
    ax_duo = {}
    for i in range(pair_num):
        pair1 = pairs[i]
        reds = iter(plt.cm.Reds(np.linspace(0.38, 0.62, 3)))

        pair2 = pair_cycler(i+4, pairs=pairs)
        blues = iter(plt.cm.Blues(np.linspace(0.38, 0.62, 3)))

        ax_duo[(pair1, pair2)] = fig.add_subplot(2, 2, i+1, projection='3d')
        for pair, color in zip((pair1, pair2), (reds, blues)):
            axes = list(stereo_cam_units[pair].values())[:3]
            initials = pair[0][0] + pair[1][0]
            for i, axis in enumerate(axes):
                ax_duo[(pair1, pair2)].plot(
                    [0, axis[0]], [0, axis[1]], [0, axis[2]],'-',
                    c=next(color), label=f'{initials}: r{i}'
                )
        ax_duo[(pair1, pair2)].legend(loc=2)
        ax_duo[(pair1, pair2)].set_title('%s %s and %s %s' % (*pair1, *pair2)).set_y(1.015)

    if save is True:
        fig.savefig( os.path.join(test_dir, 'visualize_basis_vectors.png') )

    return fig, ax_duo
示例#6
0
def detect_cage_calibration_images(config_path, img_format='png', name_pos=0):
    '''
    Detect images in calibration_images folder, and return a dictionary with their paths

    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    '''
    from .constants import CAMERAS

    camera_names = tuple(CAMERAS.keys())
    image_dir = os.path.realpath(read_config(config_path)['calibration_path'])

    cam_image_paths = {}
    for img in glob(os.path.join(image_dir, '*.' + img_format)):
        img_camera_name = Path(img).stem.split('_')[name_pos]

        if img_camera_name in camera_names:
            cam_image_paths[img_camera_name] = os.path.realpath(img)

    return cam_image_paths
示例#7
0
def detect_triangulation_result(config_path,
                                filter_low_likelihood=True,
                                pcutoff=0.1,
                                undistorted=True,
                                suffix='_DLC_3D.h5',
                                change_basis=False,
                                bonvideos=False):
    '''
    This function detects and returns the state of deeplabcut-triangulated coordinate h5 files (can be changed)

    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    suffix : string
        The suffix in the DeepLabCut 3D project triangualtion result storage files
    change_basis : boolean
        Boolean stating wether the function is within a change basis workflow

    Example
    -------

    '''

    suffix_split = suffix.split('.')
    if len(suffix_split) > 1:
        if suffix_split[-1] != 'h5':
            msg = 'Invalid file extension in suffix: %s' % suffix
            raise ValueError(msg)
    else:
        suffix = suffix + '.h5'

    cfg = read_config(config_path)
    dlc3d_cfgs = get_dlc3d_configs(config_path)

    results_path = Path(cfg['results_path'])
    triangulated = results_path / ('undistorted' if undistorted is True else
                                   'distorted') / 'triangulated'
    experiments = glob(str(triangulated / '*/'))
    if experiments == []:
        msg = 'Could not find any triangulated coordinates in %s' % triangulated
        raise ValueError(msg)

    # Detect triangulation results in related DeepLabCut 3D projects
    # Analyse the number of occurances of hdf across projects
    missing = 0
    status, coords, likelihoods, pairs = {}, {}, {}, {}
    for exp_path in experiments:
        exp_dir_name = os.path.basename(exp_path)
        if bonvideos is True:
            animal, trial, date = exp_dir_name.split('_')
            coords[(animal, trial, date)], likelihoods[(animal, trial,
                                                        date)] = {}, {}
        else:
            coords[exp_dir_name], likelihoods[exp_dir_name] = {}, {}

        regions_of_interest = {}
        for hdf_path in glob(os.path.join(exp_path, '**/*' + suffix)):
            pair_dir = os.path.dirname(hdf_path)
            pair_info = os.path.basename(pair_dir).split('_')
            if len(pair_info) == 2:
                cam1, cam2 = pair_info
            else:
                idx_, cam1, cam2 = pair_info
            pair = (cam1, cam2)

            df = pd.read_hdf(os.path.realpath(hdf_path))['DLC_3D']
            exp_regions_of_interest = df.columns.levels[0]
            regions_of_interest[pair] = exp_regions_of_interest

            if filter_low_likelihood is True:
                try:
                    cam1_prediction_2d = pd.read_hdf(
                        glob(os.path.join(pair_dir,
                                          f'*{cam1}*filtered.h5'))[0])
                    cam2_prediction_2d = pd.read_hdf(
                        glob(os.path.join(pair_dir,
                                          f'*{cam2}*filtered.h5'))[0])
                except FileNotFoundError:
                    print(
                        "No filtered predictions found. Will use the unfiltered predictions."
                    )
                    cam1_prediction_2d = pd.read_hdf(
                        glob(os.path.join(pair_dir, f'*{cam1}*.h5'))[0])
                    cam2_prediction_2d = pd.read_hdf(
                        glob(os.path.join(pair_dir, f'*{cam2}*.h5'))[0])

                coord, roi_likelihood = {}, {}
                for roi in exp_regions_of_interest:
                    cam1_likelihood = cam1_prediction_2d[
                        cam1_prediction_2d.keys()[0]
                        [0]][roi]['likelihood'].values
                    cam2_likelihood = cam2_prediction_2d[
                        cam2_prediction_2d.keys()[0]
                        [0]][roi]['likelihood'].values

                    coord_likelihood = np.dstack(
                        (cam1_likelihood, cam2_likelihood))[0]
                    coord_idxs = np.all(coord_likelihood < pcutoff, axis=1)
                    df[roi].loc[coord_idxs] = np.nan

                    coord[roi] = df[roi].values
                    roi_likelihood[roi] = coord_likelihood
            else:
                coord = {
                    roi: df[roi].values
                    for roi in exp_regions_of_interest
                }

            if bonvideos is True:
                coords[(animal, trial, date)][pair] = coord

                if filter_low_likelihood is True:
                    likelihoods[(animal, trial, date)][pair] = roi_likelihood
            else:
                coords[exp_dir_name][pair] = coord
                if filter_low_likelihood is True:
                    likelihoods[exp_dir_name][pair] = roi_likelihood
        # print(1)
        # print(all([exp_regions_of_interest == rsoi for rsoi in regions_of_interest]))

        # if not all(all([exp_regions_of_interest == rsoi for rsoi in regions_of_interest])):
        #     save_path = os.path.join(exp_path, 'rsoi_incom_%s_%s_%s.xlsx' % (animal, trial, date))
        #     pd.DataFrame.from_dict(regions_of_interest).to_excel(save_path)
        #     print('Inconsistencies in exp %s %s %s were found.\nAn overview was saved:\n%s\n' % (
        #         animal, trial, date, save_path
        #         )
        #     )
        #     missing += 1

    if missing == 0:
        print('Triangulations files detected, and verified')
        if change_basis is True:
            print('Proceeding to changing basis')
        else:
            print('The current DeepCage project is ready for changing basis')
        return coords, likelihoods if filter_low_likelihood is True else coords
    else:
        if missing == 1:
            msg = 'Inconsistencies in regions of interest was found in one experiment'
        else:
            msg = 'Inconsistencies in regions of interest were found in %d experiments' % missing
        raise ValueError(msg)
示例#8
0
def basis_label(config_path,
                decrement=False,
                detect=True,
                name_pos=0,
                format='png',
                image_paths=None):
    '''
    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    image_paths : dict; optional
        Dictionary where the key is name of the camera, and the value is the full path to the image
        of the referance points taken with the camera
    '''
    if image_paths is None:
        camera_images = detect_cage_calibration_images(config_path,
                                                       name_pos=name_pos)

    n = -1
    basis_labels = dict.fromkeys(
        camera_images.keys() if detect is True else CAMERAS.keys())
    for camera in basis_labels.keys():
        cam_img = camera_images[camera]

        if decrement is True:
            basis_labels[camera] = ({
                direction: [
                    get_coord(cam_img,
                              n=n,
                              title=get_title(camera, CAMERAS[camera][0][0],
                                              direction, istip))
                    for istip in (True, False)
                ]
                for direction in ('positive', 'negative')
            }, [
                get_coord(cam_img,
                          n=n,
                          title=get_title(camera, CAMERAS[camera][1][0],
                                          CAMERAS[camera][1][1], istip))
                for istip in (True, False)
            ], [
                get_coord(cam_img,
                          n=n,
                          title=get_title(camera, 'z-axis', 'positive', istip))
                for istip in (True, False)
            ])
        else:
            basis_labels[camera] = ({
                direction:
                get_coord(cam_img,
                          n=n,
                          title=get_title(camera, CAMERAS[camera][0][0], True,
                                          direction))
                for direction in ('positive', 'negative')
            },
                                    get_coord(cam_img,
                                              n=n,
                                              title=get_title(
                                                  camera,
                                                  CAMERAS[camera][1][0],
                                                  CAMERAS[camera][1][1],
                                                  True)),
                                    get_coord(cam_img,
                                              n=n,
                                              title=get_title(
                                                  camera, 'z-axis', 'positive',
                                                  True)),
                                    get_coord(cam_img,
                                              n=n,
                                              title='Select origin'))

        plt.close()
        gc.collect()

    data_path = os.path.join(
        read_config(config_path)['data_path'], 'labels.pickle')
    with open(data_path, 'wb') as outfile:
        stereo_file = pickle.dump(basis_labels, outfile)

    return basis_labels
示例#9
0
def dlc3d_create_labeled_video(config_path, fps=20, undistort=True, video_root=None, video_dir_hierarchy=False,
                               remove_origin=False):
    '''
    Augmented function from https://github.com/AlexEMG/DeepLabCut

    Create pairwise videos
    
    '''
    from deepcage.auxiliary.detect import detect_videos_in_hierarchy

    start_path = os.getcwd()

    cfg = read_config(config_path)
    result_path = Path(cfg['results_path'])
    triangulate_path = result_path / ("undistorted" if undistort is True else "distorted") / 'triangulated'

    if not os.path.exists(triangulate_path) or 0 == len(glob(os.path.join(triangulate_path, '*'))):
        msg = f'Could not detect triangulated coordinates in {triangulate_path}'
        raise ValueError(msg)

    if remove_origin is True:
        basis_result_path = os.path.join(cfg['data_path'], 'cb_result.pickle')
        try:
            with open(basis_result_path, 'rb') as infile:
                stereo_cam_units, orig_maps = pickle.load(infile)
        except FileNotFoundError:
            msg = f'Could not detect results from deepcage.compute.generate_linear_map() in:\n{basis_result_path}'
            raise FileNotFoundError(msg)

    skipped = []
    dlc3d_cfgs = get_dlc3d_configs(config_path)
    futures = {}
    # with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
    for pair, dlc3d_cfg_path in dlc3d_cfgs.items():
        dlc3d_cfg = read_config(dlc3d_cfg_path)
        pcutoff = dlc3d_cfg['pcutoff']
        markerSize = dlc3d_cfg['dotsize']
        alphaValue = dlc3d_cfg['alphaValue']
        cmap = dlc3d_cfg['colormap']
        skeleton_color = dlc3d_cfg['skeleton_color']
        scorer_3d = dlc3d_cfg['scorername_3d']

        bodyparts2connect = dlc3d_cfg['skeleton']
        bodyparts2plot = list(np.unique([val for sublist in bodyparts2connect for val in sublist]))
        color = plt.cm.get_cmap(cmap, len(bodyparts2plot))

        cam1, cam2 = pair

        if video_dir_hierarchy is True:
            hierarchy, _ = detect_videos_in_hierarchy(
                video_root, deep_dict=True
            )
            for exp_id, pairs in hierarchy.items():
                for pair_info, cams in pairs.items():
                    pair_idx, cam1, cam2 = pair_info.split('_')
                    pair = (cam1, cam2)
                    cam1_video, cam2_video = cams.values()
                    info = exp_id
                    futures[create_video(
                        # Paths
                        (triangulate_path / exp_id / pair_info), cam1_video, cam2_video,
                        # ID
                        info, pair,
                        # Config
                        dlc3d_cfg, pcutoff, markerSize, alphaValue, cmap, skeleton_color, scorer_3d,
                        bodyparts2plot, bodyparts2connect, color,
                        # Style
                        origin_to_remove=orig_maps[pair]['origin'] if remove_origin is True else None,
                        new_path=True
                    )] = (*info, pair)

        else:
            if video_root is None:
                video_root = os.path.join(os.path.dirname(dlc3d_cfg_path), 'videos')
            else:
                video_root = os.path.realpath(video_root)

            cam1_videos = glob(os.path.join(video_root, (f'*{cam1}*')))
            cam2_videos = glob(os.path.join(video_root, (f'*{cam2}*')))

            with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
                for i, v_path in enumerate(cam1_videos):
                    _, video_name = os.path.split(v_path)
                    cam1_video, cam2_video = cam1_videos[i], cam2_videos[i]
                    info = video_name.replace('.avi', '').split('_')
                    futures[executor.submit(create_video,
                        # Paths
                        triangulate_path, cam1_video, cam2_video,
                        # ID
                        info, pair,
                        # Config
                        dlc3d_cfg, pcutoff, markerSize, alphaValue, cmap, skeleton_color, scorer_3d,
                        bodyparts2plot, bodyparts2connect, color,
                        # Style
                        origin_to_remove=orig_maps[pair]['origin'] if remove_origin is True else None,
                        new_path=True, fps=fps
                    )] = (*info, pair)

    for future in concurrent.futures.as_completed(futures):
        video_id = futures[future]
        try:
            result = future.result()
        except Exception as exc:
            print('%s generated an exception: %s' % (video_id, exc))
        else:
            print('%s = %s' % (video_id, result))

    os.chdir(start_path)
示例#10
0
def plot_3d_trajectories(
    config_path, undistort=True, cm_is_real_idx=True, cols=2, dfs=None, remap=True,
    normalize=True, use_saved_origmap=True, save=True
):
    '''
    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    cm_is_real_idx : bool
        The trajectories are color-mapped with basis on their position in the array, rainbow (red->green->blue)
    '''
    from matplotlib import cm, colors
    from math import ceil

    from deepcage.auxiliary.detect import detect_triangulation_result
    from .basis import map_experiment    

    cfg = read_config(config_path)
    data_path = cfg['data_path']
    tracjetory_dir = Path(data_path) / 'test' / 'trajectories'
    if not os.path.exists(tracjetory_dir):
        os.makedirs(tracjetory_dir)

    if dfs is None:
        if remap is True:
            dfs = map_experiment(
                config_path, undistort=undistort, save=False,
                use_saved_origmap=use_saved_origmap,normalize=normalize
            )
        else:
            dfs = detect_triangulation_result(config_path, undistorted=undistort)

    # Experimen DFs
    for exp_info, df in dfs.items():
        print(f'Plotting the trajectories of experiment "{exp_info}"')
        # Region of interest DFs
        exp_info = exp_info if remap else f'unmapped_{exp_info}'
        exp_dir = tracjetory_dir / exp_info
        if not os.path.exists(exp_dir):
            os.mkdir(exp_dir)

        roi_groups = df.groupby(level=0, axis=1)
        rows_all = ceil(len(roi_groups) / cols)


        fig_all = plt.figure(figsize=(16, 8))
        fig_all.suptitle(exp_info)

        ax_all, ax_sep = {}, {}
        for roi_idx, (roi, df_roi) in enumerate(roi_groups):
            pair_groups = df_roi[roi].groupby(level=0, axis=1)
            num_pairs = len(pair_groups)
            rows_sep = ceil(num_pairs / cols)

            # Plot related chores
            ax_all[roi] = fig_all.add_subplot(rows_all, cols, roi_idx+1, projection='3d')
            ax_all[roi].set_title(roi)

            ax_sep[roi] = {}

            # Create color map
            # All plot figure
            cmap_all = plt.cm.rainbow(np.linspace(0, 1, num_pairs))
            # Separate plot figures
            frame_ids = df_roi[roi].index.values
            if cm_is_real_idx is True:
                frames = frame_ids[-1]
                cmap_sep = plt.cm.rainbow(np.linspace(0, 1, frames))
            
            # Compute number of rows_sep that will be allocated to respective figure
            fig_sep = plt.figure(figsize=(16, 8))       # sep -> separate
            fig_sep.suptitle(f'{exp_info}: {roi}')

            # Pair DFs
            for pair_idx, (pair, df_pair) in enumerate(pair_groups):
                coords = df_pair.values
                if cm_is_real_idx is False:
                    cmap_sep = plt.cm.rainbow(np.linspace(0, 1, len(coords.shape[0])))
                ax_sep[roi][pair] = fig_sep.add_subplot(rows_sep, cols, pair_idx+1,  projection='3d')
                ax_sep[roi][pair].set_title(pair)

                # Get trajectories
                not_nan_locs = np.logical_not(np.any(np.isnan(coords), axis=1))
                # trajectory_start_locs = np.all(np.dstack((not_nan_locs[:-1], not_nan_locs[1:]))[0], axis=1)
                incremental_delta_trajectory_start_locs = {}
                for delta in range(1, 5+1):
                    # Check if potential point has an end node at <delta> number of increments from it, [i, delta]
                    incremental_delta_trajectory_start_locs[delta] = np.append(
                        np.all(np.dstack( (not_nan_locs[:-delta], not_nan_locs[delta:]) )[0], axis=1),
                        [False] * delta     # Mark the last indeces <delta> as False, because they are either end points or invalid end points (can't be start points)
                    )
                # Only use points that have a valid end node, using "incremental_delta_trajectory_start_locs"
                valid_start_locs = np.any(np.dstack(tuple(incremental_delta_trajectory_start_locs.values()))[0], axis=1)
                # valid_coords = coords[valid_start_locs]

                trajectory_start_idxs = np.where(valid_start_locs == True)[0]

                # meta_idx is for "cm_is_real_idx is True"
                for meta_idx, idx in enumerate(trajectory_start_idxs):
                    delta = 1
                    while incremental_delta_trajectory_start_locs[delta][trajectory_start_idxs[meta_idx]] is False:
                        delta += 1
                    ax_all[roi].plot(
                        (coords[idx][0], coords[idx+delta][0]),
                        (coords[idx][1], coords[idx+delta][1]),
                        (coords[idx][2], coords[idx+delta][2]),
                        'x-', c=cmap_all[pair_idx], alpha=0.25
                    )
                    ax_sep[roi][pair].plot(
                        (coords[idx][0], coords[idx+delta][0]),
                        (coords[idx][1], coords[idx+delta][1]),
                        (coords[idx][2], coords[idx+delta][2]),
                        'x-', c=cmap_sep[frame_ids[idx if cm_is_real_idx is True else meta_idx]],
                        alpha=0.25
                    )

            if save is True:
                plt.tight_layout()
                fig_sep.savefig(str( exp_dir / f'{roi}.png' ))

        if save is True:
            fig_all.savefig(str( exp_dir / 'all.png'))

    if save is False:
        plt.tight_layout()

    return (fig_all, ax_all), (fig_sep, ax_sep)
示例#11
0
def visualize_workflow(config_path, undistort=True, normalize=True, decrement=False, save=True):
    '''
    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    '''
    import matplotlib.image as image

    dlc3d_cfgs = get_dlc3d_configs(config_path)
    basis_labels = get_labels(config_path)

    cfg = read_config(config_path)
    test_dir = os.path.join(cfg['data_path'], 'test')
    figure_dir = os.path.join(test_dir, 'visualize_workflow')
    if not os.path.exists(figure_dir):
        os.makedirs(figure_dir)

    n = np.linspace(-1, 5, 100)
    pairs = tuple(dlc3d_cfgs.keys())
    for pair in pairs:
        cam1, cam2 = pair

        fig = plt.figure(figsize=(12, 10))
        ax = {
            'trian': fig.add_subplot(221, projection='3d'),
            'basis': fig.add_subplot(222, projection='3d'),
            'cam1': fig.add_subplot(223),
            'cam2': fig.add_subplot(224)
        }
        
        # Get camera plot labels
        cam_labels = get_paired_labels(config_path, pair)['decrement' if decrement is True else 'normal']

        # Plot manual labels
        for cam, cax in zip(pair, (ax['cam1'], ax['cam2'])):
            # Add respective calibration image as background
            img_cam = image.imread(glob(os.path.join(cfg['calibration_path'], cam+'*'))[0])
            cax.imshow(img_cam)

            cmap = iter(plt.cm.rainbow(np.linspace( 0, 1, len(cam_labels[cam]) )))
            for (label, coord), color in zip(cam_labels[cam].items(), cmap):
                cax.set_title((cam, 'labels')).set_y(1.005)
                cax.scatter(*coord, c=color, label=label)
                cax.legend()

        # Triangulate the two sets of labels, and map them to 3D
        dlc3d_cfg = dlc3d_cfgs[pair]
        trian_dict, trian = triangulate_basis_labels(
            dlc3d_cfg, cam_labels, pair, undistort=undistort, decrement=decrement, keys=True
        )

        cmap = iter(plt.cm.rainbow(np.linspace( 0, 1, len(trian_dict)+1) ))
        for (label, coord), color in zip(trian_dict.items(), cmap):
            ax['trian'].scatter(*(coord - trian_dict['origin']), c=color, label=label)

        if CAMERAS[cam1][0][1] == 'left':
            c_origin = trian[0] + (trian[1] - trian[0]) / 2
        else:
            c_origin = trian[1] + (trian[0] - trian[1]) / 2
        c_origin -= trian_dict['origin']
        ax['trian'].scatter(*c_origin, c=next(cmap), label='computed origin')

        ax['trian'].set_title('Triangualted').set_y(1.005)
        ax['trian'].legend()

        ax['trian'].set_xlabel('X', fontsize=10)
        ax['trian'].set_ylabel('Y', fontsize=10)
        ax['trian'].set_zlabel('Z', fontsize=10)

        _, orig_map = compute_basis_vectors(trian, pair, normalize=normalize, decrement=decrement)

        r = []
        for axis in orig_map['map'].T:
            r.append(n * axis[np.newaxis, :].T)

        r_1, r_2, r_3 = r
        ax['basis'].plot(*r_1, label='r1/x')
        ax['basis'].plot(*r_2, label='r2/y')
        ax['basis'].plot(*r_3, label='r3/z')

        # Angles
        i, ii, iii = orig_map['map'].T
        i_ii = vg.angle(i, ii)
        i_iii = vg.angle(i, iii)
        ii_iii = vg.angle(ii, iii)

        title_text2 = 'r1-r2: %3f r1-r3: %3f\nr2-r3: %3f' % (i_ii, i_iii, ii_iii)

        ax['basis'].set_title(title_text2).set_y(1.005)
        ax['basis'].legend()

        ax['basis'].set_xticklabels([])
        ax['basis'].set_yticklabels([])
        ax['basis'].set_zticklabels([])
        ax['basis'].set_xlabel('X', fontsize=10)
        ax['basis'].set_ylabel('Y', fontsize=10)
        ax['basis'].set_zlabel('Z', fontsize=10)

        if save is True:
            fig.savefig( os.path.join(figure_dir, '%d_%s_%s.png' % (PAIR_IDXS[pair], *pair)) )

    return fig, ax
示例#12
0
def visualize_basis_vectors_single(config_path, undistort=True, normalize=True, decrement=False, stereo_cam_units=None, save=True):
    '''
    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    '''
    if stereo_cam_units is None:
        stereo_cam_units, orig_maps = create_stereo_cam_origmap(
            config_path, undistort=undistort, normalize=normalize, decrement=False, save=False
        )

    dlc3d_cfgs = get_dlc3d_configs(config_path)

    cfg = read_config(config_path)
    test_dir = os.path.join(cfg['data_path'], 'test')
    if not os.path.exists(test_dir):
        os.mkdir(test_dir)

    pairs = tuple(dlc3d_cfgs.keys())
    cmap = iter(plt.cm.rainbow(np.linspace(0, 1, 2*len(pairs)-2)))

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    c_spacing = 1 / (len(pairs) - 2)
    for i, pair in enumerate(pairs):
        cam1, cam2 = pair

        rem_space = c_spacing * i
        cmap = plt.cm.rainbow(np.linspace(rem_space, rem_space+0.12, 3))

        ax.plot(
            [0, stereo_cam_units[pair]['x-axis'][0]],
            [0, stereo_cam_units[pair]['x-axis'][1]],
            [0, stereo_cam_units[pair]['x-axis'][2]],
            label='%s %s r1/x' % pair, c=cmap[0]
        )
        # ax.text(*t_1, label='r1', c=cmap[0])

        ax.plot(
            [0, stereo_cam_units[pair]['y-axis'][0]],
            [0, stereo_cam_units[pair]['y-axis'][1]],
            [0, stereo_cam_units[pair]['y-axis'][2]],
            label='%s %s r2/y' % pair, c=cmap[1]
        )
        # ax.text(*t_2, label='r2', c=cmap[1])

        ax.plot(
            [0, stereo_cam_units[pair]['z-axis'][0]],
            [0, stereo_cam_units[pair]['z-axis'][1]],
            [0, stereo_cam_units[pair]['z-axis'][2]],
            label='%s %s r3/z' % pair, c=cmap[2]
        )
        # ax.text(*t_3, label='r3', c=cmap[2])

    ax.set_title('Basis comparison', fontsize=20).set_y(1.005)
    ax.legend(loc=2)

    if save is True:
        fig.savefig( os.path.join(test_dir, 'visualize_basis_vectors.png') )

    return fig, ax
示例#13
0
def map_experiment(config_path,
                   undistort=True,
                   percentiles=(5, 95),
                   use_saved_origmap=True,
                   normalize=True,
                   suffix='_DLC_3D.h5',
                   bonvideos=False,
                   save=True,
                   paralell=False,
                   label_getter=None,
                   **kwargs):
    '''
    This function changes the basis of deeplabcut-triangulated that are 3D.

    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    linear_maps : {string: numpy.array}
        (3, 3) array that stores the linear map for changing basis
    suffix : string
        The suffix in the DeepLabCut 3D project triangualtion result storage files
    cbv_kwargs : dictionary
        Keyword arguments for compute_basis_vectors()

    Example
    -------

    '''
    coords, likelihoods = detect_triangulation_result(config_path,
                                                      undistorted=undistort,
                                                      suffix=suffix,
                                                      change_basis=True,
                                                      bonvideos=bonvideos)
    print(likelihoods)
    if coords is False:
        print(
            'According to the DeepCage triangulated coordinates detection algorithm this project is not ready for changing basis'
        )
        return False

    cfg = read_config(config_path)
    data_path = os.path.realpath(cfg['data_path'])
    result_path = cfg['results_path']

    dlc3d_cfgs = get_dlc3d_configs(config_path)

    if use_saved_origmap is True:
        basis_result_path = os.path.join(data_path, 'cb_result.pickle')
        try:
            with open(basis_result_path, 'rb') as infile:
                stereo_cam_units, orig_maps = pickle.load(infile)
        except FileNotFoundError:
            msg = 'Could not detect results from deepcage.compute.generate_linear_map() in:\n' \
                  + basis_result_path
            raise FileNotFoundError(msg)
    else:
        stereo_cam_units, orig_maps = create_stereo_cam_origmap(
            config_path,
            undistort=undistort,
            decrement=False,
            save=False,
            normalize=normalize,
            **kwargs)

    dfs = {}
    cpu_cores = cpu_count(logical=False)
    if paralell is False or cpu_cores < 2:
        for info, pair_roi_df in coords.items():
            # info = (animal, trial, date)
            dfs[info] = sort_coords_in_df(pair_roi_df, orig_maps, percentiles)

    else:
        submissions = {}
        workers = 4 if cpu_cores < 8 else 8
        with concurrent.futures.ProcessPoolExecutor(
                max_workers=workers) as executor:
            for info, pair_roi_df in coords.items():
                # info = (animal, trial, date)
                submissions[executor.submit(sort_coords_in_df, pair_roi_df,
                                            orig_maps)] = info

            for future in submissions:
                info = submissions[future]
                if info not in dfs:
                    dfs[info] = {}
                try:
                    dfs[info][(roi, pair)] = future.result()
                except Exception as exc:
                    print('%s generated an exception: %s' %
                          (submissions[future], exc))

    if save is True:
        # print('Attempting to save new coordinates to result folder:\n%s' % result_path)
        for info, df in dfs.items():
            df_name = 'mapped'
            for i in info.split('_'):
                df_name += '_' + i

            file_path = os.path.join(result_path, df_name)

            df.to_hdf(file_path + '.h5',
                      key=df_name if bonvideos is False else 'a%st%sd%s' %
                      info)
            df.to_csv(file_path + '.csv')
            df.to_excel(file_path + '.xlsx')

            print('The mapped coordinates of %s have been saved to\n%s\n' %
                  (info, file_path))

    print('DONE: Basis changed')
    return dfs
示例#14
0
def create_stereo_cam_origmap(config_path,
                              undistort=True,
                              decrement=False,
                              save=True,
                              labels_are2d=True,
                              labels_getter=get_paired_labels,
                              basis_computer=compute_basis_vectors,
                              **cbv_kwargs):
    '''
    Parameters
    ----------
    config_path : string
        Absolute path of the project config.yaml file.
    cbv_kwargs : dictionary
        Keyword arguments for compute_basis_vectors()
    '''

    cfg = read_config(config_path)
    dlc3d_cfgs = get_dlc3d_configs(config_path)
    data_path = os.path.realpath(cfg['data_path'])

    if save is True:
        basis_result_path = os.path.join(data_path, 'cb_result.pickle')
        dataframe_path = os.path.join(data_path, 'basis_vectors.xlsx')
        if os.path.exists(basis_result_path) and os.path.exists(
                dataframe_path):
            msg = f'Please remove old analysis files before proceeding. File paths:\n{basis_result_path}\n{dataframe_path}'
        elif os.path.exists(basis_result_path):
            msg = f'Please remove old analysis file before proceeding. File paths:\n{basis_result_path}'
        elif os.path.exists(dataframe_path):
            msg = f'Please remove old analysis file before proceeding. File paths:\n{dataframe_path}\n'

    pairs = tuple(dlc3d_cfgs.keys())
    stereo_cam_units, orig_maps = {}, {}
    for pair in pairs:
        cam1, cam2 = pair
        dlc3d_cfg = dlc3d_cfgs[pair]

        basis_labels = labels_getter(config_path, pair)
        if labels_getter is get_paired_labels:
            basis_labels = basis_labels[
                'decrement' if decrement is True else 'normal']

        if labels_are2d is True:
            trian = triangulate_basis_labels(dlc3d_cfg,
                                             basis_labels,
                                             pair,
                                             undistort=undistort,
                                             decrement=decrement)
        else:
            trian = basis_labels

        stereo_cam_units[pair], orig_maps[pair] = basis_computer(
            trian, pair, decrement=decrement, **cbv_kwargs)

    if save is True:
        with open(basis_result_path, 'wb') as outfile:
            pickle.dump((stereo_cam_units, orig_maps), outfile)
            print('Saved linear map to:\n{}'.format(basis_result_path))

        pd.DataFrame.from_dict(stereo_cam_units).to_excel(dataframe_path)
        print('Saved excel file containing the computed basis vectors to:\n{}'.
              format(dataframe_path))

    print('Returning dictionary containing the computed linear maps')

    return stereo_cam_units, orig_maps
示例#15
0
def triangulate_raw_2d_camera_coords(dlc3d_cfg,
                                     cam1_coords=None,
                                     cam2_coords=None,
                                     cam1_image=None,
                                     cam2_image=None,
                                     keys=None,
                                     undistort=True):
    """
    Augmented deeplabcut.triangulate() for DeepCage workflow

    This function triangulates user-defined coordinates from the two camera views using the camera matrices (derived from calibration) to calculate 3D predictions.
    Optionally, the user can define the coordiantes from images.

    Used for changing basis operations.

    Note: cam1 is the first camera on the 'camera_names' list located in the project 'config.yaml' file; cam2 is the second camera on the same list

    Parameters
    ----------
    dlc3d_cfg : string
        Absolute path of the config.yaml file as a string.
    cam1_image : string; default None
        Absolute path of the image of camera 1 as a string.
    cam2_image : string; default None
        Absolute path of the image of camera 2 as a string.
    cam1_coords : numpy.array-like; default None
        List of vectors that are coordinates in the camera 1 image
    cam2_coords : numpy.array-like; default None
        List of vectors that are coordinates in the camera 2 image
    keys : list-like; default None
        List of names or dictionary keys that can be associated with the 3d-coordinate with the identical index

    Example
    -------
    To analyze a set of coordinates:
    >>> deeplabcut.triangulate_raw_2d_camera_coords(dlc3d_cfg, cam1_coords=((1, 2), (20, 50), ...), cam2_coords=((3, 5), (14, 2), ...) )

    Linux/MacOS
    To analyze a set of images in a directory:
    >>> deeplabcut.triangulate_raw_2d_camera_coords(dlc3d_cfg, cam1_image='/image_directory/cam1.png', cam2_image='/image_directory/cam2.png')

    Windows
    To analyze a set of images in a directory:
    >>> deeplabcut.triangulate_raw_2d_camera_coords(dlc3d_cfg, cam1_image='<drive_letter>:\\<image_directory>\\cam1.png', cam2_image='\\image_directory\\cam2.png')

    """
    # if ((cam1_coords is None and cam2_coords is None) and (cam1_image is None and cam2_image is None)) or (
    #     (cam1_coords is not None and cam2_coords is not None) and (cam1_image is not None and cam2_image is not None)):
    #     msg = 'Must include a set of camera images or 2d-coordinates'
    #     raise ValueError(msg)

    if cam1_coords is not None and cam2_coords is not None:
        coords_defined = True

    if cam1_image is not None and cam2_image is not None:
        if coords_defined is True:
            msg = 'Must include a set of camera images or 2d-coordinates'
            raise ValueError(msg)
        cam1_coords = get_coord(cam1_image, n=-1)
        cam2_coords = get_coord(cam2_image, n=-1)

        if len(cam1_coords) != len(cam2_coords):
            msg = 'Each image must have the same number of selections'
            raise ValueError(msg)

    cam1_coords = np.array(cam1_coords, dtype=np.float64)
    cam2_coords = np.array(cam2_coords, dtype=np.float64)

    if cam1_coords.shape != cam2_coords.shape:
        msg = "Camera coordinate arrays have different dimensions"
        raise ValueError(msg)

    if not cam1_coords[0].shape == (1, 2):
        if cam1_coords[0].shape == (2, ):
            print(
                "Attempting to fix coordinate-array by np.expand_dims(<array>, axis=1)"
            )
            cam1_coords = np.expand_dims(cam1_coords, axis=1)
            cam2_coords = np.expand_dims(cam2_coords, axis=1)
        else:
            msg = "Coordinate-array has an invalid format"
            raise ValueError(msg)

    cfg_3d = read_config(dlc3d_cfg)
    img_path, path_corners, path_camera_matrix, path_undistort = auxiliaryfunctions_3d.Foldernames3Dproject(
        cfg_3d)

    cam_names = cfg_3d['camera_names']
    camera_pair_key = cam_names[0] + '-' + cam_names[1]

    # Create an empty dataFrame to store the undistorted 2d coordinates and likelihood
    stereo_path = os.path.join(path_camera_matrix, 'stereo_params.pickle')
    with open(stereo_path, 'rb') as infile:
        stereo_file = pickle.load(infile)

    mtx_l = stereo_file[camera_pair_key]['cameraMatrix1']
    dist_l = stereo_file[camera_pair_key]['distCoeffs1']

    mtx_r = stereo_file[camera_pair_key]['cameraMatrix2']
    dist_r = stereo_file[camera_pair_key]['distCoeffs2']

    R1 = stereo_file[camera_pair_key]['R1']
    P1 = stereo_file[camera_pair_key]['P1']

    R2 = stereo_file[camera_pair_key]['R2']
    P2 = stereo_file[camera_pair_key]['P2']

    if undistort is True:
        cam1_coords = cv2.undistortPoints(src=cam1_coords,
                                          cameraMatrix=mtx_l,
                                          distCoeffs=dist_l,
                                          P=P1,
                                          R=R1)
        cam2_coords = cv2.undistortPoints(src=cam2_coords,
                                          cameraMatrix=mtx_r,
                                          distCoeffs=dist_r,
                                          P=P2,
                                          R=R2)

    homogenous_coords = auxiliaryfunctions_3d.triangulatePoints(
        P1, P2, cam1_coords, cam2_coords)
    triangulated_coords = np.array(
        (homogenous_coords[0], homogenous_coords[1], homogenous_coords[2])).T

    if keys is not None:
        return {
            label: coord
            for label, coord in zip(keys, triangulated_coords)
        }, triangulated_coords
    else:
        return triangulated_coords