1111import matplotlib .colors as colors
1212import matplotlib .pyplot as plt
1313import numpy as np
14- from opensfm import io , multiview , feature_loader
14+ from opensfm import io , multiview , feature_loader , pymap
1515from opensfm .dataset import DataSet , DataSetBase
1616
1717RESIDUAL_PIXEL_CUTOFF = 4
@@ -98,9 +98,10 @@ def gcp_errors(data: DataSetBase, reconstructions):
9898
9999def _compute_errors (reconstructions , tracks_manager ):
100100 @lru_cache (10 )
101- def _compute_errors_cached (index , normalized ):
101+ def _compute_errors_cached (index , error_type ):
102102 return reconstructions [index ].map .compute_reprojection_errors (
103- tracks_manager , normalized
103+ tracks_manager ,
104+ error_type ,
104105 )
105106
106107 return _compute_errors_cached
@@ -115,38 +116,52 @@ def _get_valid_observations_cached(index):
115116
116117
117118def _projection_error (tracks_manager , reconstructions ):
118- all_errors_normalized , all_errors_pixels = [], []
119- average_error_normalized , average_error_pixels = 0 , 0
119+ all_errors_normalized , all_errors_pixels , all_errors_angular = [], [], []
120+ average_error_normalized , average_error_pixels , average_error_angular = 0 , 0 , 0
120121 for i in range (len (reconstructions )):
121- errors_normalized = _compute_errors (reconstructions , tracks_manager )(i , True )
122- errors_unnormalized = _compute_errors (reconstructions , tracks_manager )(i , False )
122+ errors_normalized = _compute_errors (reconstructions , tracks_manager )(
123+ i , pymap .ErrorType .Normalized
124+ )
125+ errors_unnormalized = _compute_errors (reconstructions , tracks_manager )(
126+ i , pymap .ErrorType .Pixel
127+ )
128+ errors_angular = _compute_errors (reconstructions , tracks_manager )(
129+ i , pymap .ErrorType .Angular
130+ )
123131
124132 for shot_id , shot_errors_normalized in errors_normalized .items ():
125133 shot = reconstructions [i ].get_shot (shot_id )
126134 normalizer = max (shot .camera .width , shot .camera .height )
127135
128- for error_normalized , error_unnormalized in zip (
129- shot_errors_normalized .values (), errors_unnormalized [shot_id ].values ()
136+ for error_normalized , error_unnormalized , error_angular in zip (
137+ shot_errors_normalized .values (),
138+ errors_unnormalized [shot_id ].values (),
139+ errors_angular [shot_id ].values (),
130140 ):
131141 norm_pixels = _norm2d (error_unnormalized * normalizer )
132142 norm_normalized = _norm2d (error_normalized )
143+ norm_angle = error_angular [0 ]
133144 if norm_pixels > RESIDUAL_PIXEL_CUTOFF :
134145 continue
135146 average_error_normalized += norm_normalized
136147 average_error_pixels += norm_pixels
148+ average_error_angular += norm_angle
137149 all_errors_normalized .append (norm_normalized )
138150 all_errors_pixels .append (norm_pixels )
151+ all_errors_angular .append (norm_angle )
139152
140153 error_count = len (all_errors_normalized )
141154 if error_count == 0 :
142- return (- 1.0 , - 1.0 , ([], []), ([], []))
155+ return (- 1.0 , - 1.0 , - 1.0 , ([], []), ([], []), ([], []))
143156
144157 bins = 30
145158 return (
146159 average_error_normalized / error_count ,
147160 average_error_pixels / error_count ,
161+ average_error_angular / error_count ,
148162 np .histogram (all_errors_normalized , bins ),
149163 np .histogram (all_errors_pixels , bins ),
164+ np .histogram (all_errors_angular , bins ),
150165 )
151166
152167
@@ -203,11 +218,14 @@ def reconstruction_statistics(data: DataSetBase, tracks_manager, reconstructions
203218 (
204219 avg_normalized ,
205220 avg_pixels ,
221+ avg_angular ,
206222 (hist_normalized , bins_normalized ),
207223 (hist_pixels , bins_pixels ),
224+ (hist_angular , bins_angular ),
208225 ) = _projection_error (tracks_manager , reconstructions )
209226 stats ["reprojection_error_normalized" ] = avg_normalized
210227 stats ["reprojection_error_pixels" ] = avg_pixels
228+ stats ["reprojection_error_angular" ] = avg_angular
211229 stats ["reprojection_histogram_normalized" ] = (
212230 list (map (float , hist_normalized )),
213231 list (map (float , bins_normalized )),
@@ -216,6 +234,10 @@ def reconstruction_statistics(data: DataSetBase, tracks_manager, reconstructions
216234 list (map (float , hist_pixels )),
217235 list (map (float , bins_pixels )),
218236 )
237+ stats ["reprojection_histogram_angular" ] = (
238+ list (map (float , hist_angular )),
239+ list (map (float , bins_angular )),
240+ )
219241
220242 return stats
221243
@@ -475,7 +497,7 @@ def save_residual_histogram(
475497 io_handler ,
476498):
477499 backup = dict (mpl .rcParams )
478- fig , axs = plt .subplots (1 , 2 , tight_layout = True , figsize = (15 , 3 ))
500+ fig , axs = plt .subplots (1 , 3 , tight_layout = True , figsize = (15 , 3 ))
479501
480502 h_norm , b_norm = stats ["reconstruction_statistics" ][
481503 "reprojection_histogram_normalized"
@@ -493,8 +515,19 @@ def save_residual_histogram(
493515 for i in range (len (p_pixel )):
494516 p_pixel [i ].set_facecolor (plt .cm .viridis (n [i ] / max (n )))
495517
518+ h_angular , b_angular = stats ["reconstruction_statistics" ][
519+ "reprojection_histogram_angular"
520+ ]
521+ n , _ , p_angular , = axs [
522+ 2
523+ ].hist (b_angular [:- 1 ], b_angular , weights = h_angular )
524+ n = n .astype ("int" )
525+ for i in range (len (p_angular )):
526+ p_angular [i ].set_facecolor (plt .cm .viridis (n [i ] / max (n )))
527+
496528 axs [0 ].set_title ("Normalized Residual" )
497529 axs [1 ].set_title ("Pixel Residual" )
530+ axs [2 ].set_title ("Angular Residual" )
498531
499532 with io_handler .open (
500533 os .path .join (output_path , "residual_histogram.png" ), "wb"
@@ -768,8 +801,8 @@ def save_residual_grids(
768801
769802 for i in range (len (reconstructions )):
770803 valid_observations = _get_valid_observations (reconstructions , tracks_manager )(i )
771- errors_scaled = _compute_errors (reconstructions , tracks_manager )(i , True )
772- errors_unscaled = _compute_errors (reconstructions , tracks_manager )(i , False )
804+ errors_scaled = _compute_errors (reconstructions , tracks_manager )(i , pymap . ErrorType . Normalized )
805+ errors_unscaled = _compute_errors (reconstructions , tracks_manager )(i , pymap . ErrorType . Pixel )
773806
774807 for shot_id , shot_errors in errors_scaled .items ():
775808 shot = reconstructions [i ].get_shot (shot_id )
0 commit comments