def test_generate_points_from_circle_model_center_at_0_0_radius_1(self): center_x = 0 center_y = 0 radius = 1.0 model = CircleModel(center_x, center_y, radius) points = CircleModel.generate_points_from_circle(model) pt: Point for pt in points: distance_from_center = math.sqrt((pt.X - center_x)**2 + (pt.Y - center_y)**2) self.assertAlmostEqual(distance_from_center, radius, 2)
def test_large_circle_50X50_no_noise_2(self): folder_script = os.path.dirname(__file__) filename_input = "NoisyCircle_x_6_y_-30_r_118.162.png" file_noisy_line = os.path.join(folder_script, "./data/", filename_input) np_image = skimage.io.imread(file_noisy_line, as_gray=True) lst_points = Util.create_points_from_numpyimage(np_image) helper = GradientDescentCircleFitting(None, lst_points, learningrate=0.4, iterations=5000) result: CircleModel = helper.FindBestFittingCircle() # #Superimpose the new line over the image # folder_results = os.path.join(folder_script, "../out/") count_of_files = len(os.listdir(folder_results)) filename_results = ("%s.%d.png" % (__name__, count_of_files)) file_result = os.path.join(folder_results, filename_results) new_points = CircleModel.generate_points_from_circle(result) np_superimposed = Util.superimpose_points_on_image( np_image, new_points, 100, 255, 100) skimage.io.imsave(file_result, np_superimposed) delta = 10 self.assertAlmostEquals(result.R, +118.0, delta=delta) self.assertAlmostEquals(result.X, +06.0, delta=delta) self.assertAlmostEquals(result.Y, -30.0, delta=delta) pass
def run_image2matplot(filename): folder_script = os.path.dirname(__file__) absolute_path = os.path.join(folder_script, "./input/", filename) try: np_image = skimage.io.imread(absolute_path, as_gray=True) lst_all_points = Util.create_points_from_numpyimage(np_image) plot_new_points_over_existing_points(lst_all_points, [], "Input data", "Original points", "") lrate = 0.3 iterations = 5000 helper = GradientDescentCircleFitting(None, points=lst_all_points, learningrate=lrate, iterations=iterations) start_time = time.time() model: CircleModel = helper.FindBestFittingCircle() new_points = CircleModel.generate_points_from_circle(model) plot_new_points_over_existing_points( lst_all_points, new_points, "Gradient descent circle fitting", "Original points", "Gradient descent") except Exception as e: tb = traceback.format_exc() print("Error:%s while doing RANSAC on the file: %s , stack=%s" % (str(e), filename, str(tb))) print("------------------------------------------------------------") pass pass
def test_When_get_inliers_and_3_inliers_and_1_outlier_and_no_exclusion_list( self): #arrange p1 = Point(+1.4, 0.0) p2 = Point(+0.0, 1.4) p3 = Point(-1.4, 0.0) p_outlier = Point(-10, 0) list_of_points = list() list_of_points.append(p1) list_of_points.append(p2) list_of_points.append(p3) list_of_points.append(p_outlier) model = CircleModel(0, 0, 1) helper = RansacCircleHelper() helper.threshold_error = 0.5 helper.add_points(list_of_points) #act inliers, model_score = helper.get_inliers(model, []) #assert expected_score = ((0.4 / 1.4) + (0.4 / 1.4) + (0.4 / 1.4)) / 3.0 self.assertAlmostEquals(model_score, expected_score, delta=0.1) self.assertEquals(len(inliers), 3) self.assertTrue(p1 in inliers) self.assertTrue(p2 in inliers) self.assertTrue(p3 in inliers) self.assertFalse(p_outlier in inliers)
def test_large_circle_50X50_no_noise_1(self): folder_script=os.path.dirname(__file__) filename_input="NoisyCircle_x_-10_y_-14.png" file_noisy_line=os.path.join(folder_script,"./data/",filename_input) np_image=skimage.io.imread(file_noisy_line,as_gray=True) lst_points=Util.create_points_from_numpyimage(np_image) helper=BullockCircleFitting(lst_points) result:CircleModel =helper.FindBestFittingCircle() # #Superimpose the new line over the image # folder_results=os.path.join(folder_script,"../out/") count_of_files=len(os.listdir(folder_results)) filename_results=("%s.%d.png" % (__name__,count_of_files) ) file_result=os.path.join(folder_results,filename_results) new_points=CircleModel.generate_points_from_circle(result) np_superimposed=Util.superimpose_points_on_image(np_image,new_points,100,255,100) skimage.io.imsave(file_result,np_superimposed) delta=2 self.assertAlmostEquals(result.R, 48.0, delta=delta); self.assertAlmostEquals(result.X, -10.0, delta=delta); self.assertAlmostEquals(result.Y, -14.0, delta=delta); pass
def test_When_get_inliers_and_all_points_on_circumfrence_and_no_exclusion_list( self): #arrange p1 = Point(+1, 0) p2 = Point(+0, 1) p3 = Point(-1, 0) list_of_points = list() list_of_points.append(p1) list_of_points.append(p2) list_of_points.append(p3) model = CircleModel(0, 0, 1) helper = RansacCircleHelper() helper.threshold_error = 0.2 helper.add_points(list_of_points) #act inliers, model_score = helper.get_inliers(model, []) #assert self.assertAlmostEquals(model_score, 0.0, delta=0.1) self.assertEquals(len(inliers), 3) self.assertTrue(p1 in inliers) self.assertTrue(p2 in inliers) self.assertTrue(p3 in inliers)
def test_generate_model_from_3points_straight_line(self): p_0 = Point(0, 33) p_1 = Point(9, 30) p_2 = Point(12, 29) try: c1 = CircleModel.GenerateModelFrom3Points(p_0, p_1, p_2) self.fail("Expected exception was not thrown") except: #Exception was expected pass
def test_generate_points_from_circle_model_center_at_0_0_radius_10(self): center_x = 0 center_y = 0 radius = 100.0 distance = 5 model = CircleModel(center_x, center_y, radius) points = CircleModel.generate_points_from_circle(model, distance=distance) pt: Point for pt in points: distance_from_center = math.sqrt((pt.X - center_x)**2 + (pt.Y - center_y)**2) self.assertAlmostEqual(distance_from_center, radius, 2) #compare distance for index in range(1, len(points)): point1 = points[index] point2 = points[index - 1] actual_distance = Point.euclidean_distance(point1, point2) self.assertAlmostEqual(distance, actual_distance, delta=2)
def run(filename, threshold, inlier, sampling_fraction=0.25, matplot=False): print("Going to process file:%s" % (filename)) folder_script = os.path.dirname(__file__) file_noisy_circle = os.path.join(folder_script, "./input/", filename) try: np_image = skimage.io.imread(file_noisy_circle, as_gray=True) # #Iterate over all cells of the NUMPY array and convert to array of Point classes # lst_all_points = Util.create_points_from_numpyimage(np_image) # #begin RANSAC # helper = RansacCircleHelper() helper.threshold_error = threshold helper.threshold_inlier_count = inlier helper.add_points(lst_all_points) helper.sampling_fraction = sampling_fraction best_model = helper.run() print("RANSAC-complete") if (best_model == None): print( "ERROR! Could not find a suitable model. Try altering ransac-threshold and min inliner count" ) return # #Generate an output image with the model circle overlayed on top of original image # now = datetime.datetime.now() filename_result = ("%s-%s.png" % (filename, now.strftime("%Y-%m-%d-%H-%M-%S"))) file_result = os.path.join(folder_script, "./out/", filename_result) #Load input image into array np_image_result = skimage.io.imread(file_noisy_circle, as_gray=True) new_points = CircleModel.generate_points_from_circle(best_model) np_superimposed = Util.superimpose_points_on_image( np_image_result, new_points, 100, 255, 100) #Save new image skimage.io.imsave(file_result, np_superimposed) print("Results saved to file:%s" % (file_result)) print("------------------------------------------------------------") if (matplot == True): plot_new_points_over_existing_points( lst_all_points, new_points, "Outcome of RANSAC algorithm", "Original points", "RANSAC") except Exception as e: tb = traceback.format_exc() print("Error:%s while doing RANSAC on the file: %s , stack=%s" % (str(e), filename, str(tb))) print("------------------------------------------------------------") pass
def test_GenerateModelFrom3pmodels_PassingThrough_1_1_And_0_2_minus1_1( self): p_1_0 = Point(1, 1) p_0_0 = Point(0, 2) p_minus1_0 = Point(-1, 1) c1 = CircleModel.GenerateModelFrom3Points(p_0_0, p_1_0, p_minus1_0) #Assert on radius self.assertAlmostEquals(c1.R, 1.0, 1) #Assert on center X,Y self.assertAlmostEquals(c1.X, 0.0, 1) self.assertAlmostEquals(c1.Y, 1.0, 1)
def superimpose_circle_over_original_image(self, original_image_file, circle): np_image = skimage.io.imread(original_image_file, as_gray=True) folder_script = os.path.dirname(__file__) folder_results = os.path.join(folder_script, "../out/") count_of_files = len(os.listdir(folder_results)) filename_results = ("%s.%d.png" % (__name__, count_of_files)) file_result = os.path.join(folder_results, filename_results) new_points = CircleModel.generate_points_from_circle(circle, distance=2) np_superimposed = Util.superimpose_points_on_image( np_image, new_points, 255, 255, 0) skimage.io.imsave(file_result, np_superimposed)
def FindBestFittingCircle(self)->CircleModel: seed_circle:CircleModel=None if (self._modelhint == None): seed_circle=self.GenerateSeedCircle() else: seed_circle=self._modelhint; for i in range(0,self._max_iterations): #mse=self.ComputeMse(seed_circle) derivative_radius,derivative_cx,derivative_cy=self.ComputeDerivativesOfMse(seed_circle) new_cx=-1*self._learningrate*derivative_cx + seed_circle.X new_cy=-1*self._learningrate*derivative_cy + seed_circle.Y new_radius=-1*self._learningrate*derivative_radius + seed_circle.R seed_circle=CircleModel(new_cx,new_cy,new_radius) return seed_circle pass
def GenerateSeedCircle(self)->CircleModel: all_x=list(map(lambda c: c.X, self._points)) all_y=list(map(lambda c: c.Y, self._points)) center_x=sum(all_x)/len(all_x) center_y=sum(all_y)/len(all_y) min_x=min(all_x) max_x=max(all_x) min_y=min(all_y) max_y=max(all_y) radius=abs(max_x-min_x)/2 + abs(max_y-min_y)/2 model=CircleModel(center_x, center_y, radius) return model
def run_image2image(filename): print("Going to fit circle in the file:%s" % (filename)) folder_script = os.path.dirname(__file__) absolute_path = os.path.join(folder_script, "./input/", filename) try: np_image = skimage.io.imread(absolute_path, as_gray=True) lst_all_points = Util.create_points_from_numpyimage(np_image) lrate = 0.3 iterations = 5000 helper = GradientDescentCircleFitting(None, points=lst_all_points, learningrate=lrate, iterations=iterations) start_time = time.time() model: CircleModel = helper.FindBestFittingCircle() print("--- %s seconds for gradient descent algo ---" % (time.time() - start_time)) # #Generate an output image with the model circle overlayed on top of original image # now = datetime.datetime.now() filename_result = ("gradient-descent-%s.png" % (filename)) file_result = os.path.join(folder_script, "./out/", filename_result) #Load input image into array np_image_result = skimage.io.imread(absolute_path, as_gray=True) new_points = CircleModel.generate_points_from_circle(model) np_superimposed = Util.superimpose_points_on_image( np_image_result, new_points, 100, 255, 100) #Save new image skimage.io.imsave(file_result, np_superimposed) print("Results saved to file:%s" % (file_result)) print("------------------------------------------------------------") except Exception as e: tb = traceback.format_exc() print("Error:%s while doing RANSAC on the file: %s , stack=%s" % (str(e), filename, str(tb))) print("------------------------------------------------------------") pass pass
def FindBestFittingCircle(self) -> CircleModel: self.compute_mean() self.shift_all_points() Su = sum(self._shifted_x) Sv = sum(self._shifted_y) Suu = self.compute_suu() Svv = self.compute_svv() Suv = self.compute_suv() Suuu = self.compute_suuu() Svvv = self.compute_svvv() Suvv = self.compute_Suvv() Svuu = self.compute_Svuu() #simulatenous equations # suu*x + suv*y = c # suv*x + svv*y = c # uc=x (center of circle, shifted about mean) # vc=y (center of circle, shifted about mean) # c1 = 1/2 * (Suuu+Suvv) # c2 = 1/2 * (Svvv+Svuu) # a1 = suu # b1 = suv # a2 = suv # b2 = svv C1 = (1 / 2) * (Suuu + Suvv) C2 = (1 / 2) * (Svvv + Svuu) Uc = (C2 * Suv - C1 * Svv) / (Suv * Suv - Suu * Svv) Vc = (C1 * Suv - C2 * Suu) / (Suv * Suv - Suu * Svv) alpha = Uc**2 + Vc**2 + (Suu + Svv) / len(self._points) real_x = self._mean_x + Uc real_y = self._mean_y + Vc radius = alpha**0.5 model = CircleModel(real_x, real_y, radius) return model
def run(self) -> CircleModel: self.validate_hyperparams() # #generate trigrams of points - find some temporary model to hold this model # print("Generating trigrams") trigrams = self.generate_trigam_from_points() print("Generating trigrams complete. Count=%d" % (len(trigrams))) # #for ever triagram find circle model # find the circle that passes through those points # Determine model goodness score # tri: TrigramOfPoints lst_trigram_scores = list() # all_trigram_indices = list(range(0, len(trigrams))) fraction = self.sampling_fraction random_count = int(len(all_trigram_indices) * fraction) random_trigram_indices = random.sample(all_trigram_indices, random_count) #for trig_index in range(0,len(trigrams)): progress_count = 0 count_of_trigrams_with_poor_inliers = 0 #scope for improvement of performance by multithreading #if you use a 200X200 image, with salt peper ration of 0.85 and sample fraction of 0.2 then you can generate ample load to test multi-threading # for trig_index in random_trigram_indices: progress_count += 1 tri = trigrams[trig_index] if (trig_index % 100 == 0): print( "PROGRESS:Processing trigram %d of %d, shortlisted=%d poor inliers=%d" % (progress_count, len(random_trigram_indices), len(lst_trigram_scores), count_of_trigrams_with_poor_inliers)) try: temp_circle = CircleModel.GenerateModelFrom3Points( tri.P1, tri.P2, tri.P3) except Exception as e: print("Could not generate Circle model. Error=%s" % (str(e))) continue inliers, goodness_score = self.get_inliers( temp_circle, [tri.P1, tri.P2, tri.P3]) count_inliers = len(inliers) if (count_inliers < self.threshold_inlier_count): #print("Skipping because of poor inlier count=%d and this is less than threshold=%f)" % (count_inliers, self.threshold_inlier_count)) count_of_trigrams_with_poor_inliers += 1 continue result = (temp_circle, inliers, tri) lst_trigram_scores.append(result) # #Sort trigrams with lowest error # sorted_trigram_inliercount = sorted(lst_trigram_scores, key=lambda x: len(x[1]), reverse=True) if (len(sorted_trigram_inliercount) == 0): print( "Finished building shortlist of trigrams. No trigrams found. Quitting" ) return print( "Finished building shortlist of trigrams. Count=%d, Max inlier count=%d" % (len(sorted_trigram_inliercount), len(sorted_trigram_inliercount[0][1]))) lst_results_gdescent = list() jobs = [] for index in range(0, len(sorted_trigram_inliercount)): t = sorted_trigram_inliercount[index] model = t[0] inliers = t[1] trigram: TrigramOfPoints = t[2] new_points = list() new_points.extend(inliers) new_points.append(trigram.P1) new_points.append(trigram.P2) new_points.append(trigram.P3) new_thread = threading.Thread( target=self.find_model_using_gradient_descent2, args=(model, new_points, lst_results_gdescent)) jobs.append(new_thread) for j in jobs: j.start() #Wait for all threads to finish! for j in jobs: j.join() if (len(lst_results_gdescent) == 0): return None if (len(lst_results_gdescent) == 0): return None lst_results_gdescent_sortedby_inlier_count = sorted( lst_results_gdescent, key=lambda x: len(x[1]), reverse=True) max_inliers = len(lst_results_gdescent_sortedby_inlier_count[0][1]) lst_all_results_with_highest_inlier_count = list( filter(lambda x: len(x[1]) >= max_inliers, lst_results_gdescent_sortedby_inlier_count)) lst_results_best_inlier_best_goodness = sorted( lst_all_results_with_highest_inlier_count, key=lambda x: (x[2]), reverse=False) best_model = lst_results_best_inlier_best_goodness[0][0] return best_model
def test_String_Representation_Must_Have_Center_And_Radius(self): c1 = CircleModel(100.1, 200.2, 300.3) display = str(c1) self.assertTrue(display.find("X=100.1") >= 0) self.assertTrue(display.find("Y=200.2") >= 0) self.assertTrue(display.find("R=300.3") >= 0)
def test_When_Constructed_All_Properties_Must_Be_Initialized(self): c1 = CircleModel(100.1, 200.2, 300.3) self.assertAlmostEquals(100.1, c1.X) self.assertAlmostEquals(200.2, c1.Y) self.assertAlmostEquals(300.3, c1.R)