class Simulator(object): def __init__(self, params, scene, initial_pos): self.scene = scene self.params = params self.initial_pos = initial_pos self.status = "none" self.camera = CameraModel(scene, initial_pos, simulate_backlash=self.params.backlash, simulate_noise=self.params.noise) if params.perfect_classification is None: self.perfect_classification = None else: self.perfect_classification = \ params.perfect_classification[scene.filename] def _do_local_search(self, direction, rev_direction): """Perform a local search (incremental hillclimbing in a given direction). The hillclimbing has a tolerance of two steps. i.e., Up to two steps that don't increase the focus value can be taken before we stop climbing.""" while not self.camera.will_hit_edge(direction): prev_fmeasure = self.camera.last_fmeasure() self.camera.move_fine(direction) if self.camera.last_fmeasure() < prev_fmeasure: if (two_step_tolerance and not self.camera.will_hit_edge(direction)): # We've seen a decrease. Consider moving one extra step. prev_fmeasure = self.camera.last_fmeasure() self.camera.move_fine(direction) if (self.camera.last_fmeasure() < prev_fmeasure): # Seen a decrease again, backtrack and stop. self.camera.move_fine(rev_direction, 2) break else: # Backtrack and stop. self.camera.move_fine(rev_direction) break def _go_to_max(self): """Return to the location of the largest focus value seen so far and perform a local search to find the exact location of the peak.""" current_pos = self.camera.last_position() maximum_pos = max(self.camera.visited_positions, key=(lambda pos : self.camera.get_fvalue(pos))) if maximum_pos < current_pos: direction = Direction("left") elif maximum_pos > current_pos: direction = Direction("right") elif current_pos < self.camera.visited_positions[-2]: direction = Direction("left") else: direction = Direction("right") rev_direction = direction.reverse() # Take as many coarse steps as needed to go back to the maximum # without going over it. distance = abs(current_pos - maximum_pos) coarse_steps = distance / 8 self.camera.move_coarse(direction, coarse_steps) # Keep going in fine steps to see if we can find a higher position. start_pos = self.camera.last_position() self._do_local_search(direction, rev_direction) # If we didn't move further, we might want to look in the other # direction too. if start_pos == self.camera.last_position(): self._do_local_search(rev_direction, direction) self.status = "foundmax" def _get_first_direction(self): """Direction in which we should start sweeping initially.""" first, second, third = self.camera.get_fvalues( self.camera.visited_positions[-3:]) norm_lens_pos = float(self.initial_pos) / (self.scene.step_count - 1) evaluator = featuresfirststep.firststep_feature_evaluator( first, second, third, norm_lens_pos) return Direction(evaluatetree.evaluate_tree( self.params.left_right_tree, evaluator)) def _sweep(self, direction): """Sweep the lens in one direction and return a tuple (success state, number of steps taken) along the way. """ initial_position = self.camera.last_position() sweep_fvalues = [ self.camera.last_fmeasure() ] while not self.camera.will_hit_edge(direction): # Move the lens forward. self.camera.move_coarse(direction) sweep_fvalues.append(self.camera.last_fmeasure()) # Take at least two steps before we allow turning back. if len(sweep_fvalues) < 3: continue if self.perfect_classification is None: # Obtain the ML classification at the new lens position. evaluator = featuresturn.action_feature_evaluator( sweep_fvalues, self.scene.step_count) classification = evaluatetree.evaluate_tree( self.params.action_tree, evaluator) else: key = featuresturn.make_key(str(direction), initial_position, self.camera.last_position()) classification = self.perfect_classification[key] if classification != "continue": assert (classification == "turn_peak" or classification == "backtrack") return classification, len(sweep_fvalues) - 1 # We've reached an edge, but the decision tree still does not want # to turn back, so what do we do now? # After thinking a lot about it, I think the best thing to do is to # introduce a condition manually. It's a bit ad-hoc, but we really need # to be able to handle this case robustly, as there are lot of cases # (i.e., landscape shots) where peaks will be at the edge. min_val = min(self.camera.get_fvalues(self.camera.visited_positions)) max_val = max(self.camera.get_fvalues(self.camera.visited_positions)) if float(min_val) / max_val > 0.8: return "backtrack", len(sweep_fvalues) - 1 else: return "turn_peak", len(sweep_fvalues) - 1 def _backtrack(self, previous_direction, step_count): """From the current lens position, go back to the lens position we were at before and look on the other side.""" new_direction = previous_direction.reverse() # Go back to where we started. self.camera.move_coarse(new_direction, step_count) # Sweep again the other way. result, step_count = self._sweep(new_direction) if result == "turn_peak": self._go_to_max() elif result == "backtrack": # If we need to backtrack a second time, we failed. self.status = "failed" else: assert False def evaluate(self): """For every scene and every lens position, run a simulation and store the statistics.""" # Take the first two steps, as to get three focus measures with which # to decide which direction to sweep. self.camera.move_fine(Direction("right"), 2) # Decide initial direction in which to look. direction = self._get_first_direction() # Search in that direction. result, step_count = self._sweep(direction) if result == "turn_peak": self._go_to_max() elif result == "backtrack": self._backtrack(direction, step_count) else: assert False def is_true_positive(self): """Whether a peak was found and the peak is close to a real peak.""" return (self.status == "foundmax" and self.scene.distance_to_closest_peak( self.camera.last_position()) <= 1) def is_false_positive(self): """Whether a peak was found and the peak not close to a real peak.""" return (self.status == "foundmax" and self.scene.distance_to_closest_peak( self.camera.last_position()) > 1) def is_true_negative(self): """Whether we failed to find a peak and we didn't come close to a real peak.""" return (self.status == "failed" and all(self.scene.distance_to_closest_peak(pos) > 1 for pos in self.camera.visited_positions)) def is_false_negative(self): """Whether we failed to find a peak but we did come close to a real peak.""" return (self.status == "failed" and any(self.scene.distance_to_closest_peak(pos) <= 1 for pos in self.camera.visited_positions)) def get_evaluation(self): """Return whether a simulation for this scene starting at the given lens position gave a true/false positive/negative. """ if self.is_true_positive(): return "true positive" if self.is_false_positive(): return "false positive" if self.is_true_negative(): return "true negative" if self.is_false_negative(): return "false negative"
def search_standard(scenes, scene_to_print): print ("Perform a standard hill-climbing search, where coarse steps are\n" "taken until some stopping condition occurs, at which point the\n" "movement is reversed, at which point fine steps are taken to\n" "maximize the focus value. This is the method described in\n" "[He2003] and [Li2005].\n\n" "To visualize the steps taken for simulation of a specific scene,\n" "use the command-line argument --scene-to-print=something.txt") step_size = 8 data_rows = [("filename", "success %", "steps")] # Redirect stdout to a file for printing R script. orig_stdout = sys.stdout file_to_print = open("comparison.R", "w+") sys.stdout = file_to_print total_success = 0 for scene in scenes: success_count = 0 total_step_count = 0 initial_positions = range(0, scene.step_count - step_size) for initial_position in initial_positions: camera = CameraModel(scene, initial_position, simulate_backlash=simulate_backlash, simulate_noise=simulate_noise) first_measure = camera.last_fmeasure() camera.move_coarse(Direction("right")) # Determine whether to start moving left or right. if camera.last_fmeasure() < first_measure: direction = Direction("left") else: direction = Direction("right") # If the first step decreases focus value, switch direction. # This is a simple backtracking, basically. first_measure = camera.last_fmeasure() camera.move_coarse(direction) if camera.last_fmeasure() < first_measure: direction = direction.reverse() # Sweep max_value = camera.last_fmeasure() while not camera.will_hit_edge(direction): camera.move_coarse(direction) max_value = max(max_value, camera.last_fmeasure()) # Have we found a peak? if camera.last_fmeasure() < max_value * 0.9: # Stop searching break # Hillclimb until we're back at the peak. while not camera.will_hit_edge(direction.reverse()): prev_measure = camera.last_fmeasure() camera.move_fine(direction.reverse()) if prev_measure > camera.last_fmeasure(): camera.move_fine(direction) break # Record if we succeeded. if scene.distance_to_closest_peak(camera.last_position()) <= 1: success_count += 1 evaluation = "succeeded" else: evaluation = "failed" if scene.filename == scene_to_print: camera.print_script(evaluation) total_step_count += camera.steps_taken success = float(success_count) / len(initial_positions) * 100 line = (scene.name, "%.1f" % success, "%.1f" % (float(total_step_count) / len(initial_positions))) data_rows.append(line) total_success += success # Restore original stdout sys.stdout = orig_stdout file_to_print.close() print_aligned_data_rows(data_rows) print "average success : %.1f" % (total_success / len(scenes))