ransac.py
Go to the documentation of this file.
00001 ## Copyright (c) 2004-2007, Andrew D. Straw. All rights reserved.
00002 
00003 ## Redistribution and use in source and binary forms, with or without
00004 ## modification, are permitted provided that the following conditions are
00005 ## met:
00006 
00007 ##     * Redistributions of source code must retain the above copyright
00008 ##       notice, this list of conditions and the following disclaimer.
00009 
00010 ##     * Redistributions in binary form must reproduce the above
00011 ##       copyright notice, this list of conditions and the following
00012 ##       disclaimer in the documentation and/or other materials provided
00013 ##       with the distribution.
00014 
00015 ##     * Neither the name of the Andrew D. Straw nor the names of its
00016 ##       contributors may be used to endorse or promote products derived
00017 ##       from this software without specific prior written permission.
00018 
00019 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021 ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00022 ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00023 ## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00024 ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00025 ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00026 ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00027 ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00028 ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00029 ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 
00031 
00032 ##Original code was modified, PlaneLeastSquaresModel() added:
00033 #  \author Martin Schuster (Healthcare Robotics Lab, Georgia Tech.)
00034 
00035 
00036 import numpy as np
00037 import scipy # use numpy if scipy unavailable
00038 import scipy.linalg # use numpy if scipy unavailable
00039 
00040 ### Optional imports below
00041 #    import processor    [Both only for testing obsolete dataset, testPlanePointcloud() ]
00042 #    import configuration  
00043 #    import pylab;  import sys
00044 
00045 
00046 TEST_FOLDER ='/home/jokerman/svn/robot1_data/usr/martin/laser_camera_segmentation/labeling'
00047 
00048 def ransac(data,model,n,k,t,d,debug=False,return_all=False):
00049     print 'INFO: running RANSAC for k=',k,'iterations'
00050     """fit model parameters to data using the RANSAC algorithm
00051     
00052 This implementation written from pseudocode found at
00053 http://en.wikipedia.org/w/index.php?title=RANSAC&oldid=116358182
00054 
00055 {{{
00056 Given:
00057     data - a set of observed data points
00058     model - a model that can be fitted to data points
00059     n - the minimum number of data values required to fit the model
00060     k - the maximum number of iterations allowed in the algorithm
00061     t - a threshold value for determining when a data point fits a model
00062     d - the number of close data values required to assert that a model fits well to data
00063 Return:
00064     bestfit - model parameters which best fit the data (or nil if no good model is found)
00065 iterations = 0
00066 bestfit = nil
00067 besterr = something really large
00068 while iterations < k {
00069     maybeinliers = n randomly selected values from data
00070     maybemodel = model parameters fitted to maybeinliers
00071     alsoinliers = empty set
00072     for every point in data not in maybeinliers {
00073         if point fits maybemodel with an error smaller than t
00074              add point to alsoinliers
00075     }
00076     if the number of elements in alsoinliers is > d {
00077         % this implies that we may have found a good model
00078         % now test how good it is
00079         bettermodel = model parameters fitted to all points in maybeinliers and alsoinliers
00080         thiserr = a measure of how well model fits these points
00081         if thiserr < besterr {
00082             bestfit = bettermodel
00083             besterr = thiserr
00084         }
00085     }
00086     increment iterations
00087 }
00088 return bestfit
00089 }}}
00090 """
00091 #    iterations = 0
00092 #    bestfit = None
00093 #    besterr = np.inf
00094 #    best_inlier_idxs = None
00095 #    while iterations < k:
00096 #        maybe_idxs, test_idxs = random_partition(n,data.shape[0])
00097 #        print n
00098 #        maybeinliers = data[maybe_idxs,:]
00099 #        #print 'z',maybeinliers
00100 #        test_points = data[test_idxs]
00101 #        maybemodel = model.fit(maybeinliers)
00102 #        test_err = model.get_error( test_points, maybemodel)
00103 #        also_idxs = test_idxs[test_err < t] # select indices of rows with accepted points
00104 #        alsoinliers = data[also_idxs,:]
00105 #        if debug:
00106 #            print 'test_err.min()',test_err.min()
00107 #            print 'test_err.max()',test_err.max()
00108 #            print 'np.mean(test_err)',np.mean(test_err)
00109 #            print 'iteration %d:len(alsoinliers) = %d'%(
00110 #                iterations,len(alsoinliers))
00111 #        if len(alsoinliers) > d:
00112 #            print np.asmatrix(maybeinliers), np.asmatrix(alsoinliers)
00113 #            betterdata = np.concatenate( (maybeinliers, np.asmatrix(alsoinliers)) )
00114 #            bettermodel = model.fit(np.asarray(betterdata))
00115 #            better_errs = model.get_error( betterdata, bettermodel)
00116 #            thiserr = np.mean( better_errs )
00117 #            if thiserr < besterr:
00118 #                bestfit = bettermodel
00119 #                besterr = thiserr
00120 #                print maybe_idxs, also_idxs
00121 #                best_inlier_idxs = np.concatenate( (maybe_idxs, [also_idxs]) )
00122 #        iterations+=1
00123 #    if bestfit is None:
00124 #        raise ValueError("did not meet fit acceptance criteria")
00125 #    if return_all:
00126 #        return bestfit, {'inliers':best_inlier_idxs}
00127 #    else:
00128 #        return bestfit
00129     iterations = 0
00130     bestfit = None
00131     besterr = np.inf
00132     best_inlier_idxs = None
00133     while iterations < k:
00134         #print data
00135         maybe_idxs, test_idxs = random_partition(n,data.shape[0])
00136         maybeinliers = data[maybe_idxs,:]
00137         test_points = data[test_idxs]
00138         maybemodel = model.fit(maybeinliers)
00139         test_err = model.get_error( test_points, maybemodel)
00140         also_idxs = test_idxs[test_err < t] # select indices of rows with accepted points
00141         alsoinliers = data[also_idxs,:]
00142         if debug:
00143             print 'test_err.min()',test_err.min()
00144             print 'test_err.max()',test_err.max()
00145             print 'np.mean(test_err)',np.mean(test_err)
00146             print 'iteration %d:len(alsoinliers) = %d'%(
00147                 iterations,len(alsoinliers))
00148         if len(alsoinliers) > d:
00149             betterdata = np.concatenate( (maybeinliers, alsoinliers) )
00150             bettermodel = model.fit(betterdata)
00151             better_errs = model.get_error( betterdata, bettermodel)
00152             thiserr = np.mean( better_errs )
00153             if thiserr < besterr:
00154                 bestfit = bettermodel
00155                 besterr = thiserr
00156                 best_inlier_idxs = np.concatenate( (maybe_idxs, also_idxs) )
00157         iterations+=1
00158     if bestfit is None:
00159         print "\n\n[ransac.py - line 152]"
00160         print "Ransac plane fitting did not meet fit accaptance criteria at current settings."
00161         print "Consider editing Ransac Parameters to be more generous or trying scan again."  
00162         print "This error often happens when no table is present in front of the robot.\n\n"
00163         import sys; 
00164         sys.exit()
00165         #Lets NOT raise an error. raise ValueError("did not meet fit acceptance criteria")
00166     if return_all:
00167         return bestfit, {'inliers':best_inlier_idxs}
00168     else:
00169         return bestfit
00170 
00171 
00172 def random_partition(n,n_data):
00173     """return n random rows of data (and also the other len(data)-n rows)"""
00174     all_idxs = np.arange( n_data )
00175     np.random.shuffle(all_idxs)
00176     idxs1 = all_idxs[:n]
00177     idxs2 = all_idxs[n:]
00178     return idxs1, idxs2
00179 
00180 class LinearLeastSquaresModel:
00181     """linear system solved using linear least squares
00182 
00183     This class serves as an example that fulfills the model interface
00184     needed by the ransac() function.
00185     
00186     """
00187     def __init__(self,input_columns,output_columns,debug=False):
00188         self.input_columns = input_columns
00189         self.output_columns = output_columns
00190         self.debug = debug
00191     def fit(self, data):
00192         A = np.vstack([data[:,i] for i in self.input_columns]).T
00193         B = np.vstack([data[:,i] for i in self.output_columns]).T
00194         x,resids,rank,s = scipy.linalg.lstsq(A,B)
00195         return x
00196     def get_error( self, data, model):
00197         A = np.vstack([data[:,i] for i in self.input_columns]).T
00198         B = np.vstack([data[:,i] for i in self.output_columns]).T
00199         B_fit = scipy.dot(A,model)
00200         err_per_point = np.sum((B-B_fit)**2,axis=1) # sum squared error per row
00201         print err_per_point
00202         return err_per_point
00203     
00204 class PlaneLeastSquaresModel:
00205  
00206     def __init__(self, debug=False):
00207         self.debug = debug
00208     def fit(self, data):
00209         #print 'fit',data
00210         model = [data[0],np.cross(data[1] - data[0], data[2] - data[1])] #point, normal
00211         model[1] = model[1] / np.linalg.norm(model[1]) #normalize
00212         return model
00213     def get_error( self, data, model):
00214        
00215         #reject model if it's not horizontal
00216         max_angle = 30.0 * np.pi / 180.0
00217         angle = np.arccos(scipy.dot(np.array([0,0,1]),model[1].T)) #normal is normalized
00218         #print 'angle', angle / np.pi * 180.0
00219         
00220         if abs(angle) > max_angle:
00221             return np.ones(np.shape(data)[0]) * 999999999999999999999999999999
00222         #http://de.wikipedia.org/wiki/Hessesche_Normalform
00223         #print model[0], model[1]
00224         d = scipy.dot(model[0],model[1].T)
00225         #print 'd',d
00226         s = scipy.dot(data, model[1].T) - d
00227         #print 'dmds',data, model, d, 's',s
00228         #err_per_point = np.sum(np.asarray(s)**2, axis=1) # sum squared error per row
00229         #return err_per_point   
00230         return abs(s)
00231         
00232 def test():
00233     # generate perfect input data
00234 
00235     n_samples = 500
00236     n_inputs = 1
00237     n_outputs = 1
00238     A_exact = 20*np.random.random((n_samples,n_inputs) )
00239     perfect_fit = 60*np.random.normal(size=(n_inputs,n_outputs) ) # the model
00240     B_exact = scipy.dot(A_exact,perfect_fit)
00241     assert B_exact.shape == (n_samples,n_outputs)
00242 
00243     # add a little gaussian noise (linear least squares alone should handle this well)
00244     A_noisy = A_exact + np.random.normal(size=A_exact.shape )
00245     B_noisy = B_exact + np.random.normal(size=B_exact.shape )
00246 
00247     if 1:
00248         # add some outliers
00249         n_outliers = 100
00250         all_idxs = np.arange( A_noisy.shape[0] )
00251         np.random.shuffle(all_idxs)
00252         outlier_idxs = all_idxs[:n_outliers]
00253         non_outlier_idxs = all_idxs[n_outliers:]
00254         A_noisy[outlier_idxs] =  20*np.random.random((n_outliers,n_inputs) )
00255         B_noisy[outlier_idxs] = 50*np.random.normal(size=(n_outliers,n_outputs) )
00256 
00257     # setup model
00258 
00259     all_data = np.hstack( (A_noisy,B_noisy) )
00260     input_columns = range(n_inputs) # the first columns of the array
00261     output_columns = [n_inputs+i for i in range(n_outputs)] # the last columns of the array
00262     debug = False
00263     model = LinearLeastSquaresModel(input_columns,output_columns,debug=debug)
00264 
00265     linear_fit,resids,rank,s = scipy.linalg.lstsq(all_data[:,input_columns],
00266                                                   all_data[:,output_columns])
00267 
00268     # run RANSAC algorithm
00269     ransac_fit, ransac_data = ransac(all_data,model,
00270                                      50, 1000, 7e3, 300, # misc. parameters
00271                                      debug=debug,return_all=True)
00272     if 1:
00273         import pylab
00274 
00275         sort_idxs = np.argsort(A_exact[:,0])
00276         A_col0_sorted = A_exact[sort_idxs] # maintain as rank-2 array
00277 
00278         if 1:
00279             pylab.plot( A_noisy[:,0], B_noisy[:,0], 'k.', label='data' )
00280             pylab.plot( A_noisy[ransac_data['inliers'],0], B_noisy[ransac_data['inliers'],0], 'bx', label='RANSAC data' )
00281         else:
00282             pylab.plot( A_noisy[non_outlier_idxs,0], B_noisy[non_outlier_idxs,0], 'k.', label='noisy data' )
00283             pylab.plot( A_noisy[outlier_idxs,0], B_noisy[outlier_idxs,0], 'r.', label='outlier data' )
00284         pylab.plot( A_col0_sorted[:,0],
00285                     np.dot(A_col0_sorted,ransac_fit)[:,0],
00286                     label='RANSAC fit' )
00287         pylab.plot( A_col0_sorted[:,0],
00288                     np.dot(A_col0_sorted,perfect_fit)[:,0],
00289                     label='exact system' )
00290         pylab.plot( A_col0_sorted[:,0],
00291                     np.dot(A_col0_sorted,linear_fit)[:,0],
00292                     label='linear fit' )
00293         pylab.legend()
00294         pylab.show()
00295         
00296         
00297 def testPlane():
00298 
00299 
00300 
00301     debug = True
00302     model = PlaneLeastSquaresModel(debug)
00303     data = np.array([[0,0,0],[0,1,0],[0.1,12,0.1],[0,0,12],[1,0,0],[1,2,13]])
00304     # run RANSAC algorithm
00305     ransac_fit, ransac_data = ransac(data,model,
00306                                      3, 1000, 1, 2, # misc. parameters
00307                                      debug=debug,return_all=True)
00308     print ransac_fit
00309     print ransac_data
00310     
00311 def testPlanePointcloud():
00312     import processor
00313     import configuration    
00314 
00315     cfg = configuration.configuration(TEST_FOLDER)
00316     #sc = scanner.scanner(cfg)
00317     pc = processor.processor(cfg)
00318     #pc.load_data('2009Oct30_162400')
00319     pc.load_data('2009Nov04_141226')
00320     pc.process_raw_data()
00321     
00322     
00323     debug = False
00324     model = PlaneLeastSquaresModel(debug)
00325     data = np.asarray(pc.pts3d_bound).T
00326     # run RANSAC algorithm
00327     ransac_fit, ransac_data = ransac(data,model,
00328                                      3, 1000, 0.02, 300, # misc. parameters
00329                                      debug=debug,return_all=True)
00330     print ransac_fit
00331     print ransac_data    
00332     print 'len inlier',len(ransac_data['inliers']),'shape pts',np.shape(pc.pts3d_bound)
00333     pc.pts3d_bound = pc.pts3d_bound[:,ransac_data['inliers']]
00334     pc.display_3d('height')
00335 
00336 if __name__=='__main__':
00337     #testPlane()
00338     testPlanePointcloud()


clutter_segmentation
Author(s): Jason Okerman, Martin Schuster, Advisors: Prof. Charlie Kemp and Jim Regh, Lab: Healthcare Robotics Lab at Georgia Tech
autogenerated on Wed Nov 27 2013 12:07:15