diff --git a/skimage/measure/fit.py b/skimage/measure/fit.py index 7de90291..e800eb89 100644 --- a/skimage/measure/fit.py +++ b/skimage/measure/fit.py @@ -502,7 +502,7 @@ def _dynamic_max_trials(n_inliers, n_samples, min_samples, probability): def ransac(data, model_class, min_samples, residual_threshold, is_data_valid=None, is_model_valid=None, max_trials=100, stop_sample_num=np.inf, stop_residuals_sum=0, - stop_probability=1): + stop_probability=1, exceptions=Exception): """Fit a model to data with the RANSAC (random sample consensus) algorithm. RANSAC is an iterative algorithm for the robust estimation of parameters @@ -573,6 +573,10 @@ def ransac(data, model_class, min_samples, residual_threshold, where the probability (confidence) is typically set to a high value such as 0.99, and e is the current fraction of inliers w.r.t. the total number of samples. + exceptions : exception class or tuple of exception classes + A list of exceptions that are ignored when estimating the model from a + random subset. By default all exceptions derived from the built-in + exception class `Exception` are ignored. Returns ------- @@ -688,7 +692,14 @@ def ransac(data, model_class, min_samples, residual_threshold, # estimate model for current random sample set sample_model = model_class() - sample_model.estimate(*samples) + + if exceptions: + try: + sample_model.estimate(*samples) + except exceptions: + continue + else: + sample_model.estimate(*samples) # check if estimated model is valid if is_model_valid is not None and not is_model_valid(sample_model, diff --git a/skimage/measure/tests/test_fit.py b/skimage/measure/tests/test_fit.py index c2a71f71..c8e789fb 100644 --- a/skimage/measure/tests/test_fit.py +++ b/skimage/measure/tests/test_fit.py @@ -239,16 +239,32 @@ def test_ransac_dynamic_max_trials(): def test_ransac_invalid_input(): - assert_raises(ValueError, ransac, np.zeros((10, 2)), None, min_samples=-1, - residual_threshold=0) assert_raises(ValueError, ransac, np.zeros((10, 2)), None, min_samples=2, - residual_threshold=0, max_trials=-1) + residual_threshold=0, max_trials=-1) assert_raises(ValueError, ransac, np.zeros((10, 2)), None, min_samples=2, - residual_threshold=0, - stop_probability=-1) + residual_threshold=0, stop_probability=-1) assert_raises(ValueError, ransac, np.zeros((10, 2)), None, min_samples=2, - residual_threshold=0, - stop_probability=1.01) + residual_threshold=0, stop_probability=1.01) + + +def test_ransac_exceptions(): + class Estimator(object): + def estimate(self, x): + raise AttributeError + + def residuals(self, x): + return x + + assert_raises(AttributeError, ransac, (np.zeros((10,)),), Estimator, + min_samples=2, residual_threshold=0, exceptions=None) + assert_raises(AttributeError, ransac, (np.zeros((10,)),), Estimator, + min_samples=2, residual_threshold=0, exceptions=tuple()) + + ransac((np.zeros((10,)),), Estimator, min_samples=2, residual_threshold=0) + ransac((np.zeros((10,)),), Estimator, min_samples=2, + residual_threshold=0, exceptions=AttributeError) + ransac((np.zeros((10,)),), Estimator, min_samples=2, + residual_threshold=0, exceptions=(AttributeError,)) def test_deprecated_params_attribute():