def simplify_anchor_points(line: Line,
                           max_distance: int = 25,
                           min_distance: int = 10,
                           min_degree_to_keep_points: float = 0.2):
    new_line = []

    def distance(p1: Point, p2: Point):
        return p2.x - p1.x

    prev_point: Point = None
    for point_ind, point in enumerate(line):
        if prev_point is not None:
            point_distance = distance(prev_point, point)
        else:
            point_distance = min_distance + 1
        if point_distance > max_distance:
            new_line.append(
                Point(prev_point.x + (point.x - prev_point.x) / 2, point.y)
            )  # [point[0], prev_point[1] + (point[1] - prev_point[1])/2])
        if point_distance < min_distance:
            if angle_difference_of_points(
                    prev_point, point
            ) > min_degree_to_keep_points or point_ind == len(line) - 1:
                new_line.append(point)
        else:
            new_line.append(point)
        prev_point = point
    return Line(new_line)
def polyline_simplification(
        staff_list: List[System],
        algorithm: LineSimplificationAlgorithm = LineSimplificationAlgorithm.
    VISVALINGAM_WHYATT,
        max_points_vw: int = 30,
        ramer_dougler_dist: float = 0.5) -> List[System]:
    new_staff_list = []
    for system in staff_list:
        new_system = []
        for line in system:
            x, y = line.get_xy()
            line_list = list(zip(x, y))
            line_array = np.asarray(line_list, dtype=np.float64)
            simplified = line_list

            if algorithm is LineSimplificationAlgorithm.VISVALINGAM_WHYATT:
                simplifier = VWSimplifier(line_array)
                simplified = simplifier.from_number(max_points_vw)
            elif algorithm is LineSimplificationAlgorithm.RAMER_DOUGLER_PEUCKLER:
                simplified = ramerdouglas(line_list, dist=ramer_dougler_dist)

            simplified = [Point(x, y) for x, y in simplified]
            new_system.append(Line(simplified))
        new_staff_list.append(System(new_system))
    return new_staff_list
    def smooth_lines_advanced(self, staff_lines: List[System]) -> List[System]:
        new_staff_lines = []
        for system in staff_lines:
            new_system = []
            for line in system:
                x, y = line.get_xy()
                if len(x) < 2:
                    continue

                x, y = interpolate_sequence(x, y)
                append_start = [y[0] for x in range(10)]
                append_end = [y[-1] for x in range(10)]
                m_y = append_start + y + append_end
                remove_hill(m_y, self.settings.smooth_value_adv)
                line = Line([Point(x, y) for x, y in zip(x, m_y[10:-10])])

                new_system.append(line)
            new_staff_lines.append(System(new_system))

        if self.settings.smooth_lines_adv_debug:
            f, ax = plt.subplots(1, 2, True, True)
            cmap = plt.get_cmap('jet')
            colors = cmap(np.linspace(0, 1.0, len(new_staff_lines)))
            for system, color in zip(staff_lines, colors):
                for staff in system:
                    x, y = staff.get_xy()
                    ax[0].plot(x, y, color=color)
            for system, color in zip(new_staff_lines, colors):
                for staff in system:
                    x, y = staff.get_xy()
                    ax[1].plot(x, y, color=color)
            plt.show()
        return new_staff_lines
 def prune_small_lines(line_list: List[Line],
                       staff_space_height: int) -> List[Line]:
     line_list = [
         l for l in line_list if l.get_end_point().x -
         l.get_start_point().x > staff_space_height * 3
     ]
     mean_line_height_list = [
         line.get_average_line_height() for line in line_list
     ]
     line_list_copy = line_list.copy()
     while True:
         prev_line_height = 0
         for line_ind, line_height in enumerate(mean_line_height_list):
             if (abs(prev_line_height - line_height) <
                     staff_space_height / 3.0) and prev_line_height != 0:
                 p1a = line_list_copy[line_ind - 1].get_start_point()
                 p1e = line_list_copy[line_ind - 1].get_end_point()
                 p2a = line_list_copy[line_ind].get_start_point()
                 p2e = line_list_copy[line_ind].get_end_point()
                 if p2e.x >= p1e.x and p2a.x <= p1a.x:
                     del line_list_copy[line_ind - 1]
                     del mean_line_height_list[line_ind - 1]
                     break
                 if p2e.x <= p1e.x and p2a.x >= p1a.x:
                     del line_list_copy[line_ind]
                     del mean_line_height_list[line_ind]
                     break
                 if p2e.x >= p1e.x and p2a.x >= p1a.x:
                     line_list_copy[line_ind -
                                    1] = Line(line_list_copy[line_ind - 1] +
                                              line_list_copy[line_ind])
                     del line_list_copy[line_ind]
                     del mean_line_height_list[line_ind]
                     break
                 if p2e.x <= p1e.x and p1a.x >= p2e.x:
                     line_list_copy[line_ind -
                                    1] = Line(line_list_copy[line_ind] +
                                              line_list_copy[line_ind - 1])
                     del line_list_copy[line_ind]
                     del mean_line_height_list[line_ind]
                     break
             prev_line_height = line_height
         else:
             break
     return line_list_copy
 def extract_ccs(img: np.ndarray) -> List[Line]:
     cc_list = extract_connected_components(img)
     cc_list = normalize_connected_components(cc_list)
     cc_list_new = []
     for cc in cc_list:
         cc_new = []
         for y, x in cc:
             cc_new.append(Point(x, y))
         cc_list_new.append(Line(cc_new))
     return cc_list_new
def get_blackness_of_line(line: Line, image: np.ndarray) -> int:
    x_list, y_list = line.get_xy()
    func = interpolate.interp1d(x_list, y_list)
    x_start, x_end = int(x_list[0]), int(x_list[-1])
    x_list_new = np.arange(x_start, x_end - 1)
    y_new = func(x_list_new)
    y_new[y_new > image.shape[0] - 1] = image.shape[0] - 1
    y_new_int = np.floor(y_new + 0.5).astype(int)
    indexes = (np.array(y_new_int), np.array(x_list_new))

    blackness = np.mean(image[indexes])
    return blackness
    def smooth_lines(self, staff_lines: List[System]) -> List[System]:
        new_staff_lines = []
        for system in staff_lines:
            new_system = []
            for line in system:
                x, y = line.get_xy()
                y = smooth_array(list(y), self.settings.smooth_value_low_pass)

                line = Line([Point(x, y) for x, y in zip(x, y)])
                new_system.append(line)
            new_staff_lines.append(System(new_system))

        return new_staff_lines
def approximate_blackness_of_line(line: Line, image: np.ndarray) -> int:
    image = image
    x_list, y_list = line.get_xy()
    func = interpolate.interp1d(x_list, y_list)
    x_start, x_end = x_list[0], x_list[-1]
    spaced_numbers = np.linspace(x_start,
                                 x_end,
                                 num=int(abs(x_list[0] - x_list[-1]) * 1 / 5),
                                 endpoint=True)
    y_new = func(spaced_numbers)
    blackness = 0
    for ind, number in enumerate(y_new):
        if image[int(number)][int(spaced_numbers[ind])] == 255:
            blackness += 1
    return blackness
def best_line_fit(img: np.array,
                  line: Line,
                  line_thickness: int = 3,
                  max_iterations: int = 30,
                  scale: float = 1.0,
                  skip_startend_points: bool = False) -> Line:
    current_blackness = get_blackness_of_line(line, img)
    best_line = line.__copy__()

    change = True
    iterations = 0

    while change:
        if iterations > max_iterations:
            break
        change = False
        for point_ind, point in enumerate(best_line):
            if skip_startend_points:
                if point_ind == 0 or point_ind == len(best_line):
                    continue
            y, x = point.y, point.x
            scaled_line_thickness = line_thickness * np.ceil(scale).astype(int)
            for i in range(1, scaled_line_thickness + 1):
                if y + i < line[point_ind].y + scaled_line_thickness:
                    test_line = best_line.__copy__()
                    test_line[point_ind] = Point(x, y + i)
                    blackness = get_blackness_of_line(test_line, img)

                    if blackness < current_blackness:
                        change = True
                        current_blackness = blackness
                        best_line[point_ind] = Point(x, y + i)
                if y - i > line[point_ind].y - scaled_line_thickness:

                    test_line[point_ind] = Point(x, y - i)
                    blackness = get_blackness_of_line(test_line, img)

                    if blackness < current_blackness:
                        change = True
                        current_blackness = blackness
                        best_line[point_ind] = Point(x, y - i)

        iterations += 1
    return best_line
    def post_process_staff_systems(self, staffs_lines: List[System], line_height: int, image: np.ndarray)\
            -> List[System]:
        post_processed_staff_systems = []
        h = image.shape[0]
        l2 = math.ceil(line_height / 2)
        l2 = max(l2, 2)
        for system in staffs_lines:
            processed_system = []
            for staff in system:
                x, y = staff.get_xy()
                x_new, y_new = interpolate_sequence(x, y)
                dict_count = defaultdict(list)
                for i_ind, i in enumerate(y_new):
                    st_point = min(i, image.shape[0] - 1)

                    max_l2 = min(abs(st_point - image.shape[0]), l2 + 1)
                    if image[st_point][x_new[i_ind]] != 0:
                        for z in range(1, max_l2):
                            if image[st_point - z][x_new[i_ind]] == 0:
                                st_point = st_point - z
                                break
                            if image[st_point + z][x_new[i_ind]] == 0:
                                st_point = st_point + z
                                break
                    yt = st_point
                    yb = st_point

                    if image[yt][x_new[i_ind]] == 0:
                        dict_count[x_new[i_ind]].append(yt)
                        while yt < h - 1:
                            yt += 1
                            if image[yt][x_new[i_ind]] == 0:
                                dict_count[x_new[i_ind]].append(yt)
                            else:
                                break
                        while yb > 0:
                            yb -= 1
                            if image[yb][x_new[i_ind]] == 0:
                                dict_count[x_new[i_ind]].append(yb)
                            else:
                                break
                processed_staff = []
                for key in dict_count.keys():
                    if len(dict_count[key]) <= line_height:
                        processed_staff.append(
                            Point(y=np.mean(dict_count[key]), x=key))
                processed_system.append(Line(processed_staff))
            post_processed_staff_systems.append(System(processed_system))

        for system_ind, system in enumerate(post_processed_staff_systems):
            post_processed_staff_systems[system_ind] = [
                lin for lin in system if lin
            ]
        post_processed_staff_systems = [
            sys for sys in post_processed_staff_systems if sys
        ]

        if self.settings.post_process_debug:
            f, ax = plt.subplots(1, 2, True, True)
            ax[0].imshow(image, cmap='gray')
            ax[1].imshow(image, cmap='gray')
            cmap = plt.get_cmap('jet')
            colors = cmap(np.linspace(0, 1.0, len(staffs_lines)))
            for system, color in zip(staffs_lines, colors):
                for staff in system:
                    x, y = staff.get_xy()
                    ax[0].plot(x, y, color=color)
            for system, color in zip(post_processed_staff_systems, colors):
                for staff in system:
                    x, y = staff.get_xy()
                    ax[1].plot(x, y, color=color)
            plt.show()
        return post_processed_staff_systems