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)
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
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
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
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
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
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)
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
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)
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)
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
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
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
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
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