def calculate_features(self): # This is a funny definition of aspect ratio... self.aspect_ratio = float(self.ncols) / self.average_thickness # Line fit to center points: y = mx + b (q is the gamma fit confidence) center_points = [run.center for run in self.runs] if len(center_points) > 2: center_points = center_points[1:-1] self.m, self.b, self.q = structural.least_squares_fit(center_points) # Angle within a single quadrant self.angle = (abs(atan(self.m)) / (pi / 2)) * 90.0 start_y=[run.ul_y for run in self.runs] start_y.sort() self.ul_y = start_y[len(start_y)/2] end_y=[run.lr_y for run in self.runs] end_y.sort() self.lr_y = end_y[len(end_y)/2]
def recurse(direction, staffline, last_points): # The "projected line direction" is a combination of the current # line segment and the "history" of line segments we've seen # up until now. This prevents little line segments with very # off-skew angles from overly affecting the tracing process if direction: # Right stack = staffline.right_connections[:] if len(staffline.runs) > num_points: points = [x.center for x in staffline.runs[-num_points:]] else: points = last_points[-(num_points - len(staffline.runs)):] + [x.center for x in staffline.runs] else: stack = staffline.left_connections[:] if len(staffline.runs) > num_points: points = [x.center for x in staffline.runs[:num_points]] else: points = [x.center for x in staffline.runs] + last_points[:(num_points - len(staffline.runs))] # This is the projected line m, b, q = structural.least_squares_fit(points) # Search A: Try things that are directly connected in the LAG # first. # Perform a breadth-first search in the current direction visited = {} while len(stack): # Sort the potential line segments by vertical distance # from the current line segment so the best match will be # found first stack.sort(lambda x, y: cmp( abs(staffline.center_y - x.center_y), abs(staffline.center_y - y.center_y))) section = stack.pop(0) if not visited.has_key(section): visited[section] = None if (section.average_thickness <= line_match_threshold and abs(section.angle - staffline.angle) <= angle_threshold): if section.avg_distance_from_line(m, b) < line_match_threshold: line_groups.join(staffline, section) if not section.is_staffline: section.is_staffline = True recurse(direction, section, points) return if not section.is_staffline: if direction: stack.extend(section.right_connections) else: stack.extend(section.left_connections) # Search B: If Search A fails, search for line segments across small # gaps (less than horizontal_gaps wide) for section in sections: if not section.is_staffline: if section.average_thickness <= line_match_threshold: if ((direction and section.ul_x > staffline.lr_x and section.ul_x < staffline.lr_x + horizontal_gaps) or (not direction and section.lr_x < staffline.ul_x and section.lr_x > staffline.ul_x - horizontal_gaps)): if section.avg_distance_from_line(m, b) < line_match_threshold: line_groups.join(staffline, section) if not section.is_staffline: section.is_staffline = True recurse(direction, section, points) return
def melt(self, other): """Melts to overlapping segments together using the overlapping part of the segment that fits better into the environmental line. Returns a skeleton of the melted part.""" # Remove all links to the segment to melt # Add links to the current segment instead for up_link in other.up_links: up_link.down_links.remove(other) if up_link.down_links.count(self) == 0: up_link.down_links.append(self) if self.up_links.count(up_link) == 0: self.up_links.append(up_link) other.up_links = [] for down_link in other.down_links: down_link.up_links.remove(other) if down_link.up_links.count(self) == 0: down_link.up_links.append(self) if self.down_links.count(down_link) == 0: self.down_links.append(down_link) other.down_links = [] # Calculate the least squares fit of the non-overlapping # parts of the segments points = [] col_overlap = -1 len_overlap = 0 for col in range(min(self.col_start, other.col_start), \ max(self.col_end, other.col_end) + 1): in_self = col >= self.col_start and col <= self.col_end in_other = col >= other.col_start and col <= other.col_end if in_self and not in_other: points.append( \ CorePoint(col, \ self.skeleton[1][col - self.col_start])) elif in_other and not in_self: points.append( \ CorePoint(col, \ other.skeleton[1][col - other.col_start])) elif in_self and in_other: if col_overlap == -1: col_overlap = col len_overlap = len_overlap + 1 else: raise RuntimeError, \ "Trying to melt non-overlapping segments!" if len(points) < 2: return # Do the least square fit (m, b, q) = least_squares_fit(points) # Calculate cumulative errors of overlapping parts self_error = 0 other_error = 0 for col in range(col_overlap, col_overlap + len_overlap): lsf_row = long(m * col + b + 0.5) self_row = self.skeleton[1][col - self.col_start] other_row = other.skeleton[1][col - other.col_start] self_error = self_error + abs(self_row - lsf_row) other_error = other_error + abs(other_row - lsf_row) skel = [] melt = [] for col in range(min(self.col_start, other.col_start), \ max(self.col_end, other.col_end) + 1): in_self = col >= self.col_start and col <= self.col_end in_other = col >= other.col_start and col <= other.col_end if in_self and not in_other: skel.append(self.skeleton[1][col - self.col_start]) elif not in_self and in_other: skel.append(other.skeleton[1][col - other.col_start]) elif in_other and in_self: if (self_error <= other_error): skel.append(self.skeleton[1][col - self.col_start]) melt.append(long(self.skeleton[1][col - self.col_start])) else: skel.append(other.skeleton[1][col - other.col_start]) melt.append(long(other.skeleton[1][col - other.col_start])) else: raise RuntimeError, \ "Trying to melt non-overlapping segments!" del self.skeleton self.skeleton = [min(self.col_start, other.col_start), skel] self.row_start = self.skeleton[1][0] self.row_end = self.skeleton[1][-1] self.col_start = self.skeleton[0] self.col_end = self.col_start + len(self.skeleton[1]) - 1 return [long(col_overlap), melt]