From 39eef3195bbeb75b61742b614efd76e1661884c7 Mon Sep 17 00:00:00 2001 From: Lorenzo Bertoni <34957815+bertoni9@users.noreply.github.com> Date: Mon, 7 Oct 2019 11:28:54 +0200 Subject: [PATCH] Disparity calculation (#12) * add filter for z < 40 * change figure name * stereo results in figure * stereo figure * pylint * pylint(2) * pylint updated --- monoloco/eval/eval_kitti.py | 6 +- monoloco/eval/stereo_baselines.py | 5 +- monoloco/run.py | 2 +- monoloco/train/trainer.py | 2 +- monoloco/utils/kitti.py | 3 +- monoloco/utils/misc.py | 6 +- monoloco/visuals/figures.py | 110 +++++++++++++++++------------- 7 files changed, 77 insertions(+), 57 deletions(-) diff --git a/monoloco/eval/eval_kitti.py b/monoloco/eval/eval_kitti.py index 2368881..885eb2c 100644 --- a/monoloco/eval/eval_kitti.py +++ b/monoloco/eval/eval_kitti.py @@ -113,7 +113,7 @@ class EvalKitti: def printer(self, show, save): if save or show: - show_results(self.dic_stats, show, save) + show_results(self.dic_stats, show, save, stereo=self.stereo) show_spread(self.dic_stats, show, save) show_task_error(show, save) @@ -178,7 +178,7 @@ class EvalKitti: self.update_uncertainty(stds_ale[idx], stds_epi[idx], dds[idx], dds_gt[idx_gt], cat) dd_task_error = dds_gt[idx_gt] + (get_task_error(dds_gt[idx_gt]))**2 self.update_errors(dd_task_error, dds_gt[idx_gt], cat, self.errors['task_error']) - dd_pixel_error = get_pixel_error(dds_gt[idx_gt], zzs_gt[idx_gt]) + dd_pixel_error = dds_gt[idx_gt] + get_pixel_error(zzs_gt[idx_gt]) self.update_errors(dd_pixel_error, dds_gt[idx_gt], cat, self.errors['pixel_error']) def _compare_error(self, out_gt, methods_out): @@ -211,7 +211,7 @@ class EvalKitti: self.update_errors(dd_monoloco, dd_gt, cat, self.errors['monoloco_merged']) self.update_errors(dd_geometric, dd_gt, cat, self.errors['geometric_merged']) self.update_errors(dd_gt + get_task_error(dd_gt), dd_gt, cat, self.errors['task_error_merged']) - dd_pixel = get_pixel_error(dd_gt, zzs_gt[idx_gt]) + dd_pixel = dd_gt + get_pixel_error(zzs_gt[idx_gt]) self.update_errors(dd_pixel, dd_gt, cat, self.errors['pixel_error_merged']) for key in self.methods: diff --git a/monoloco/eval/stereo_baselines.py b/monoloco/eval/stereo_baselines.py index 3a27abd..ced4834 100644 --- a/monoloco/eval/stereo_baselines.py +++ b/monoloco/eval/stereo_baselines.py @@ -20,7 +20,7 @@ def baselines_association(baselines, zzs, keypoints, keypoints_right, reid_featu keypoints, keypoints_right, baselines, reid_features) # count maximum possible associations - cnt_stereo['max'] = min(keypoints.shape[0], keypoints_r.shape[0]) + cnt_stereo['max'] = min(keypoints.shape[0], keypoints_r.shape[0]) # pylint: disable=E1136 # Filter joints disparity and calculate avg disparity avg_disparities, disparities_x, disparities_y = mask_joint_disparity(keypoints, keypoints_r) @@ -161,7 +161,8 @@ def verify_stereo(zz_stereo, zz_mono, disparity_x, disparity_y): if abs(zz_stereo - zz_mono) < z_max_difference and \ avg_disparity_y < y_max_difference and \ - cov < COV_MIN: + cov < COV_MIN\ + and 4 < zz_stereo < 40: return True # if not np.isnan(zz_stereo): # return True diff --git a/monoloco/run.py b/monoloco/run.py index 725c4fb..bcc137c 100644 --- a/monoloco/run.py +++ b/monoloco/run.py @@ -128,7 +128,7 @@ def main(): elif args.command == 'eval': if args.geometric: - assert args.joints, "joints argument not provided" + assert args.joints, "joints argument not provided" from .eval import geometric_baseline geometric_baseline(args.joints) diff --git a/monoloco/train/trainer.py b/monoloco/train/trainer.py index e3918aa..481c7af 100644 --- a/monoloco/train/trainer.py +++ b/monoloco/train/trainer.py @@ -231,7 +231,7 @@ class Trainer: # Debug plot for input-output distributions if debug: debug_plots(inputs, labels) - exit() + sys.exit() # Forward pass outputs = self.model(inputs) diff --git a/monoloco/utils/kitti.py b/monoloco/utils/kitti.py index 0542a71..4a9493e 100644 --- a/monoloco/utils/kitti.py +++ b/monoloco/utils/kitti.py @@ -93,8 +93,9 @@ def check_conditions(line, category, method, thresh=0.3): check = True else: + zz = float(line[13]) conf = float(line[15]) - if conf >= thresh: + if conf >= thresh and 0.5 < zz < 70: check = True return check diff --git a/monoloco/utils/misc.py b/monoloco/utils/misc.py index 06ca582..e0fc39a 100644 --- a/monoloco/utils/misc.py +++ b/monoloco/utils/misc.py @@ -30,12 +30,12 @@ def get_task_error(dd): return dd * mm -def get_pixel_error(dd_gt, zz_gt): +def get_pixel_error(zz_gt): """calculate error in stereo distance due to 1 pixel mismatch (function of depth)""" disp = 0.54 * 721 / zz_gt - delta_z = zz_gt - 0.54 * 721 / (disp - 1) - return dd_gt + delta_z + error = abs(zz_gt - 0.54 * 721 / (disp - 1)) + return error def open_annotations(path_ann): diff --git a/monoloco/visuals/figures.py b/monoloco/visuals/figures.py index c762885..5eae2c2 100644 --- a/monoloco/visuals/figures.py +++ b/monoloco/visuals/figures.py @@ -1,4 +1,3 @@ - # pylint: disable=R0915 import math @@ -9,11 +8,10 @@ import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Ellipse -from ..utils import get_task_error +from ..utils import get_task_error, get_pixel_error -def show_results(dic_stats, show=False, save=False): - +def show_results(dic_stats, show=False, save=False, stereo=False): """ Visualize error as function of the distance and compare it with target errors based on human height analyses """ @@ -22,40 +20,41 @@ def show_results(dic_stats, show=False, save=False): phase = 'test' x_min = 0 x_max = 38 + y_min = 0 + y_max = 4.7 xx = np.linspace(0, 60, 100) excl_clusters = ['all', '50', '>50', 'easy', 'moderate', 'hard'] clusters = tuple([clst for clst in dic_stats[phase]['monoloco'] if clst not in excl_clusters]) yy_gender = get_task_error(xx) - plt.figure(0) - plt.grid(linewidth=0.2) - plt.xlabel("Ground-truth distance [m]") - plt.ylabel("Average localization error [m]") - plt.xlim(x_min, x_max) - labels = ['Mono3D', 'Geometric Baseline', 'MonoDepth', 'Our MonoLoco', '3DOP (stereo)'] - mks = ['*', '^', 'p', 's', 'o'] - mksizes = [6, 6, 6, 6, 6] - lws = [1.5, 1.5, 1.5, 2.2, 1.6] - colors = ['r', 'deepskyblue', 'grey', 'b', 'darkorange'] - lstyles = ['solid', 'solid', 'solid', 'solid', 'dashdot'] + styles = printing_styles(stereo) + for idx_style, (key, style) in enumerate(styles.items()): + plt.figure(idx_style) + plt.grid(linewidth=0.2) + plt.xlim(x_min, x_max) + plt.ylim(y_min, y_max) + plt.xlabel("Ground-truth distance [m]") + plt.ylabel("Average localization error [m]") + for idx, method in enumerate(style['methods']): + errs = [dic_stats[phase][method][clst]['mean'] for clst in clusters] + assert errs, "method %s empty" % method + xxs = get_distances(clusters) - for idx, method in enumerate(['m3d_merged', 'geometric_merged', 'monodepth_merged', 'monoloco_merged', - '3dop_merged']): - errs = [dic_stats[phase][method][clst]['mean'] for clst in clusters] - assert errs, "method %s empty" % method - xxs = get_distances(clusters) + plt.plot(xxs, errs, marker=style['mks'][idx], markersize=style['mksizes'][idx], linewidth=style['lws'][idx], + label=style['labels'][idx], linestyle=style['lstyles'][idx], color=style['colors'][idx]) + plt.plot(xx, yy_gender, '--', label="Task error", color='lightgreen', linewidth=2.5) + if key == 'stereo': + yy_stereo = get_pixel_error(xx) + plt.plot(xx, yy_stereo, linewidth=1.7, color='k', label='Pixel error') - plt.plot(xxs, errs, marker=mks[idx], markersize=mksizes[idx], linewidth=lws[idx], label=labels[idx], - linestyle=lstyles[idx], color=colors[idx]) - plt.plot(xx, yy_gender, '--', label="Task error", color='lightgreen', linewidth=2.5) - plt.legend(loc='upper left') - if save: - path_fig = os.path.join(dir_out, 'results.png') - plt.savefig(path_fig) - print("Figure of results saved in {}".format(path_fig)) - if show: - plt.show() - plt.close() + plt.legend(loc='upper left') + if save: + path_fig = os.path.join(dir_out, 'results_' + key + '.png') + plt.savefig(path_fig) + print("Figure of results " + key + " saved in {}".format(path_fig)) + if show: + plt.show() + plt.close() def show_spread(dic_stats, show=False, save=False): @@ -66,7 +65,7 @@ def show_spread(dic_stats, show=False, save=False): excl_clusters = ['all', '50', '>50', 'easy', 'moderate', 'hard'] clusters = tuple([clst for clst in dic_stats[phase]['our'] if clst not in excl_clusters]) - plt.figure(1) + plt.figure(2) fig, ax = plt.subplots(2, sharex=True) plt.xlabel("Distance [m]") plt.ylabel("Aleatoric uncertainty [m]") @@ -80,10 +79,10 @@ def show_spread(dic_stats, show=False, save=False): yys = get_task_error(np.array(xxs)) ax[1].plot(xxs, bbs, marker='s', color='b', label="Spread b") ax[1].plot(xxs, yys, '--', color='lightgreen', label="Task error", linewidth=2.5) - yys_up = [rec_c + ar/2 * scale * yy for yy in yys] - bbs_up = [rec_c + ar/2 * scale * bb for bb in bbs] - yys_down = [rec_c - ar/2 * scale * yy for yy in yys] - bbs_down = [rec_c - ar/2 * scale * bb for bb in bbs] + yys_up = [rec_c + ar / 2 * scale * yy for yy in yys] + bbs_up = [rec_c + ar / 2 * scale * bb for bb in bbs] + yys_down = [rec_c - ar / 2 * scale * yy for yy in yys] + bbs_down = [rec_c - ar / 2 * scale * bb for bb in bbs] if plots_line: ax[0].plot(xxs, yys_up, '--', color='lightgreen', markersize=5, linewidth=1.4) @@ -92,8 +91,8 @@ def show_spread(dic_stats, show=False, save=False): ax[0].plot(xxs, bbs_down, marker='s', color='b', markersize=5, linewidth=0.7) for idx, xx in enumerate(xxs): - te = Ellipse((xx, rec_c), width=yys[idx]*ar*scale, height=scale, angle=90, color='lightgreen', fill=True) - bi = Ellipse((xx, rec_c), width=bbs[idx]*ar*scale, height=scale, angle=90, color='b', linewidth=1.8, + te = Ellipse((xx, rec_c), width=yys[idx] * ar * scale, height=scale, angle=90, color='lightgreen', fill=True) + bi = Ellipse((xx, rec_c), width=bbs[idx] * ar * scale, height=scale, angle=90, color='b', linewidth=1.8, fill=False) ax[0].add_patch(te) @@ -113,9 +112,9 @@ def show_spread(dic_stats, show=False, save=False): def show_task_error(show, save): """Task error figure""" - plt.figure(2) + plt.figure(3) dir_out = 'docs' - xx = np.linspace(0, 40, 100) + xx = np.linspace(0.1, 50, 100) mu_men = 178 mu_women = 165 mu_child_m = 164 @@ -128,12 +127,14 @@ def show_task_error(show, save): yy_young_male = target_error(xx, mm_young_male) yy_young_female = target_error(xx, mm_young_female) yy_gender = target_error(xx, mm_gmm) + yy_stereo = get_pixel_error(xx) plt.grid(linewidth=0.3) plt.plot(xx, yy_young_male, linestyle='dotted', linewidth=2.1, color='b', label='Adult/young male') plt.plot(xx, yy_young_female, linestyle='dotted', linewidth=2.1, color='darkorange', label='Adult/young female') plt.plot(xx, yy_gender, '--', color='lightgreen', linewidth=2.8, label='Generic adult (task error)') plt.plot(xx, yy_female, '-.', linewidth=1.7, color='darkorange', label='Adult female') plt.plot(xx, yy_male, '-.', linewidth=1.7, color='b', label='Adult male') + plt.plot(xx, yy_stereo, linewidth=1.7, color='k', label='Pixel error') plt.xlim(np.min(xx), np.max(xx)) plt.xlabel("Ground-truth distance from the camera $d_{gt}$ [m]") plt.ylabel("Localization error $\hat{e}$ due to human height variation [m]") # pylint: disable=W1401 @@ -193,7 +194,6 @@ def calculate_gmm(): def get_confidence(xx, zz, std): - theta = math.atan2(zz, xx) delta_x = std * math.cos(theta) @@ -215,11 +215,9 @@ def get_distances(clusters): def get_confidence_points(confidences, distances, errors): - confidence_points = [] distance_points = [] for idx, dd in enumerate(distances): - conf_perc = confidences[idx] confidence_points.append(errors[idx] + conf_perc) confidence_points.append(errors[idx] - conf_perc) @@ -230,7 +228,6 @@ def get_confidence_points(confidences, distances, errors): def height_distributions(): - mu_men = 178 std_men = 7 mu_women = 165 @@ -256,7 +253,7 @@ def expandgrid(*itrs): def plot_dist(dist_gmm, dist_men, dist_women): try: - import seaborn as sns + import seaborn as sns # pylint: disable=C0415 sns.distplot(dist_men, hist=False, rug=False, label="Men") sns.distplot(dist_women, hist=False, rug=False, label="Women") sns.distplot(dist_gmm, hist=False, rug=False, label="GMM") @@ -273,9 +270,30 @@ def get_percentile(dist_gmm): dd_gt = 1000 mu_gmm = np.mean(dist_gmm) dist_d = dd_gt * mu_gmm / dist_gmm - perc_d, _ = np.nanpercentile(dist_d, [18.5, 81.5]) # Laplace bi => 63% + perc_d, _ = np.nanpercentile(dist_d, [18.5, 81.5]) # Laplace bi => 63% perc_d2, _ = np.nanpercentile(dist_d, [23, 77]) mu_d = np.mean(dist_d) # mm_bi = (mu_d - perc_d) / mu_d # mm_test = (mu_d - perc_d2) / mu_d # mad_d = np.mean(np.abs(dist_d - mu_d)) + + +def printing_styles(stereo): + style = {'mono': {"labels": ['Mono3D', 'Geometric Baseline', 'MonoDepth', 'Our MonoLoco', '3DOP (stereo)'], + "methods": ['m3d_merged', 'geometric_merged', 'monodepth_merged', 'monoloco_merged', + '3dop_merged'], + "mks": ['*', '^', 'p', 's', 'o'], + "mksizes": [6, 6, 6, 6, 6], "lws": [1.5, 1.5, 1.5, 2.2, 1.6], + "colors": ['r', 'deepskyblue', 'grey', 'b', 'darkorange'], + "lstyles": ['solid', 'solid', 'solid', 'solid', 'dashdot']}} + if stereo: + style['stereo'] = {"labels": ['3DOP', 'Pose Baseline', 'ReiD Baseline', 'Our MonoLoco (monocular)', + 'Our Stereo Baseline'], + "methods": ['3dop_merged', 'pose_merged', 'reid_merged', 'monoloco_merged', + 'ml_stereo_merged'], + "mks": ['o', '^', 'p', 's', 's'], + "mksizes": [6, 6, 6, 4, 6], "lws": [1.5, 1.5, 1.5, 1.2, 1.5], + "colors": ['darkorange', 'lightblue', 'red', 'b', 'b'], + "lstyles": ['solid', 'solid', 'solid', 'dashed', 'solid']} + + return style