-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AstroNet mesh frontend metrics #295
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall. My main concern is that why we compute reprojection errors only on points which are inliers from epipolar constraint.
|
||
with Client(cluster) as client, performance_report(filename="dask-report.html"): | ||
# Scatter surface mesh across all nodes to preserve computation time and memory. | ||
gt_scene_trimesh_future = client.scatter(self.loader.gt_scene_trimesh, broadcast=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is interesting. Do you have any experiments/rough approximations on the savings obtained by scatter?
If you recommend, we can use this scatter at other places too (like NN models)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, the main reason I did it is because Dask told me to, but I can definitely say it saves a significant amount of computation time (although I don't have any exact numbers).
The documentation is a little spotty, but from my understanding, it preallocates memory for the object on each worker and then passes around the pointer to the worker threads.
It definitely may be useful for other large objects in the pipeline.
num_inliers_gt_model = np.count_nonzero(v_corr_idxs_inlier_mask_gt) | ||
inlier_ratio_gt_model = ( | ||
np.count_nonzero(v_corr_idxs_inlier_mask_gt) / v_corr_idxs.shape[0] if len(v_corr_idxs) > 0 else 0.0 | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should just depend on v_corr_idxs_inlier_mask_gt
not being None
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm getting a lot of mypy errors for a lot of variables because many of them are labeled as Optional
. I think this should be part of a larger effort where we reassess whether certain variables are, in fact, optional.
return is_inlier, reproj_err | ||
|
||
|
||
def compute_keypoint_intersections( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I live putting it in the new file. This is a function which can potentially be used in other components too (in the future).
The reprojection errors are computed for all keypoints, not just inliers. |
if report.reproj_error_gt_model is not None and report.v_corr_idxs_inlier_mask_gt is not None | ||
else None, | ||
"outlier_avg_reproj_error_gt_model": round( | ||
np.nanmean(report.reproj_error_gt_model[np.logical_not(report.v_corr_idxs_inlier_mask_gt)]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@travisdriver can't we just use report.inlier_avg_reproj_error_gt_model
here? why do we need to compute the mean over again? I think we already compute it in the TwoViewEstimator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I think it's cleaner to just compute it here when everything else is computed. I'll remove it here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I'm going to open a different PR to cleaner up the TwoViewEstimationReport
a little
rot3_angular_errors.append(report.R_error_deg) | ||
trans_angular_errors.append(report.U_error_deg) | ||
if report.R_error_deg is not None: | ||
rot3_angular_errors.append(report.R_error_deg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@travisdriver we shouldn't need this is not None
check here, since we are just filling the array with None, and then casting it to float which converts them to Nan, and then they are ignored in the np.nanmean
call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if keeps the types simpler, maybe it's ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was trying to fix some of the mypy errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, sure.
gt_scene_mesh, | ||
dist_threshold, | ||
) | ||
elif gt_wTi1 is not None and gt_wTi2 is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't think we need this check here to see if the ground truth poses are provided since we already check it in the TwoViewEstimator:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think the double nesting here makes it harder to read. I prefer just checking to see if the mesh is there. or you can exit immediately with None,None.
We discuss this a but in Contributing.md, but returning early is always preferred to nesting.
# Compute ground truth metrics. | ||
if v_corr_idxs_inlier_mask_gt is not None and reproj_error_gt_model is not None: | ||
num_inliers_gt_model = np.count_nonzero(v_corr_idxs_inlier_mask_gt) | ||
inlier_ratio_gt_model = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@travisdriver this is actually a different definition of the metric than the one we have been using. Before we were using the # of putatives as the denominator
The way we had it before follows Heinly12eccv:
https://www.cs.unc.edu/~jheinly/publications/eccv2012-heinly.pdf
If we try to compute inlier_ratio_gt_model
here, we don't have access to the # of putatives (corr_idxs
instead of v_corr_idxs
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe that metric should be moved to the matcher, actually, since it's a matcher-based metric, and has nothing to do with the verifier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the fraction here is (#verified w.r.t. GT model) / (# verified w.r.t. estimated model), which I don't think is what we want for inlier ratio, since it could be greater than 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it looks like we were computing it this way before, since we were passing in v_corr_idxs
to compute_correspondence_metrics()
, not corr_idxs
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it looks like the inlier_ratio_gt_model
here is actually
inlier_ratio_gt_model = (# verified correspondences that were right w.r.t. GT model) / # verrified correspondences
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@akshay-krishnan I think this explains the bug we saw in #306, actually
This PR adds a front-end evaluation based on a provided ground truth surface mesh of the scene. High-fidelity ground truth shape models are available for small bodies explored by past missions, allowing for highly accurate evaluation of the correspondences computes by the front-end.
Through testing, the current Sampson distance (SD) method seems to consistently over-estimate the precision of the front end, at least for the AstroNet examples I have run. For example, on a 14-image sequence from Dawn @ 4 Vesta, the Sampson distance method estimated an overall precision of .9998 while the mesh-based method computed a precision of .9388.
Moreover, I ran GTSfM using the ground truth information to verify correspondences using both the SD and mesh-based methods as a sanity check. When correspondences were verfied using the ground truth mesh, the final reconstruction was perfect. However, the result using the SD method was far from perfect.
I still think the Sampson distance is a useful metric for when a ground truth mesh is not present, but I think the mesh-based front-end evaluation provides a more accurate estimate of the precision
Mesh-based method
mean_inlier_ratio_wrt_gt_model: 0.9998
Metrics for pair (0, 8)
Final reconstruction with ground truth verified correspondences
Sampson distance method
mean_inlier_ratio_wrt_gt_model: 0.9388
Metrics for pair (0, 8)
Final reconstruction with ground truth verified correspondences