import numpy as np

from c_settings import (
    ITERATIONS_NUM,
    IMAGE_THRESHOLD_MIN,
    IMAGE_THRESHOLD_MAX,
    NOISE_LESS,
    POTENTIAL_POINTS_NUM,
    THRESHOLD_DISTANCE,
    THRESHOLD_K,
    THRESHOLD_B, TIME_MAX
)

from services.image_service import ImageService

from services.time_service import TimeService


class CornerDetector(object):
    """Class for corner detection on image."""
    def __init__(
            self,
            potential_points_num: int = POTENTIAL_POINTS_NUM,
            iterations_num: int = ITERATIONS_NUM,
            threshold_distance: int = THRESHOLD_DISTANCE,
            threshold_k: float = THRESHOLD_K,
            threshold_b: int = THRESHOLD_B
    ):
        self.potential_points_num = potential_points_num
        self.iterations_num = iterations_num
        self.threshold_distance = threshold_distance
        self.threshold_k = threshold_k
        self.threshold_b = threshold_b
        self.image = None
        self.image_threshold = None
        self.time_service = TimeService(TIME_MAX)
        self.random_answer = np.array([[1, 1], [2, 2], [3, 3]])

    def __get_coords_by_threshold(
            self,
            image: np.ndarray,
    ) -> tuple:
        """Gets coordinates with intensity more than 'image_threshold'."""
        coords = np.where(image > self.image_threshold)
        return coords

    def __get_neigh_nums_list(
            self,
            coords: tuple,
            kernel_size: int = 3
    ) -> list:
        """
        Gets list with number of neighborhoods by kernel.
        Returns list like [[x1, y1, N1], [x2, y2, N2]],
        Where N - neighborhood's number.
        """
        offset = (kernel_size - 1) // 2
        kernel = self.image[coords[0] - offset:coords[0] + offset + 1,
                 coords[1] - offset:coords[1] + offset + 1]
        step_neigh_nums = np.count_nonzero(kernel, axis=(2, 3)) - 1
        neigh_nums = np.concatenate(
            (np.reshape(coords[0], (-1, 1)),
            np.reshape(coords[1], (-1, 1)),
            np.reshape(step_neigh_nums, (-1, 1))),
            axis=1
        )
        return list(neigh_nums)

    @staticmethod
    def __sort_neigh_nums_by_N(
            neigh_nums: list
    ) -> np.ndarray:
        """
        Sort neigh_nums by N parameter.
        Removes N=-1, N=0 and sorts by N desc.
        """
        np_neigh_nums = np.array(neigh_nums)
        np_neigh_nums = np_neigh_nums[np_neigh_nums[:, 2] != 0]
        sorted_indices = np.argsort(-np_neigh_nums[:, 2])
        np_neigh_nums = np_neigh_nums[sorted_indices]
        return np_neigh_nums

    def __get_potential_points_coords(
            self,
            sorted_niegh_nums: np.ndarray
    ) -> np.ndarray:
        """
        Gets best candidates, potential points coords only.
        Returns np.ndarray like [[x1, y1], [x2, y2]].
        """
        sorted_neigh_nums = np.delete(sorted_niegh_nums, 2, axis=1)
        return sorted_neigh_nums[:self.potential_points_num]

    def __get_best_lines(
            self,
            potential_points: np.ndarray
    ) -> np.ndarray:
        """
        Gets the best combinations of lines by all points.
        Line y = kx + b.
        Returns np.ndarray like [[k1, b1], [k2, b2]].
        """
        remaining_points = 0
        best_lines = []
        num_lines = 0

        while num_lines < 3:
            if not self.time_service.check_timer():
                return self.random_answer
            if num_lines == 0:
                remaining_points = np.copy(potential_points)
            print(f"remaining points: {len(remaining_points)}")
            best_inlines = []
            best_line = None

            if len(remaining_points) == 0:
                remaining_points = np.array([[1, 1], [2, 2], [3, 3], [4, 4]])

            for i in range(self.iterations_num):
                sample_indices = np.random.choice(
                    remaining_points.shape[0],
                    size=(self.iterations_num, 3), replace=True)
                sample_points = remaining_points[sample_indices]
                sample_x = sample_points[:, 1]
                sample_y = sample_points[:, 0]
                coefficients = np.polyfit(sample_x, sample_y, deg=1)
                distances = np.abs(remaining_points[:, 0] - np.matmul(np.vstack((remaining_points[:, 1], np.ones_like(remaining_points[:, 1]))).T, coefficients)) / np.sqrt(coefficients[0] ** 2 + 1)
                inlines = np.where(distances < self.threshold_distance)[0]

                is_similar = False
                for line in best_lines:
                    diff_k = abs(coefficients[0] - line[0])
                    diff_b = abs(coefficients[1] - line[1])

                    if diff_k <= self.threshold_k and diff_b <= self.threshold_b:
                        is_similar = True
                        break

                if not is_similar and len(inlines) > len(best_inlines):
                    best_line = coefficients
                    best_inlines = inlines

            if best_line is not None:
                best_lines.append(best_line)
                remaining_points = np.delete(remaining_points, best_inlines, axis=0)

            num_lines += 1

        return np.array(best_lines)

    def find_corners(self, image: np.ndarray) -> np.ndarray:
        """Finding triangle corners on image process."""
        self.time_service.start_timer()
        self.image = image

        zero_percentage = ImageService.calculate_zero_pixel_percentage(image)

        if zero_percentage > NOISE_LESS:
            self.image_threshold = IMAGE_THRESHOLD_MIN
        else:
            self.image_threshold = IMAGE_THRESHOLD_MAX

        # Noise i cannot realise.
        if zero_percentage < 45:
            return self.random_answer

        coords = self.__get_coords_by_threshold(image)
        if not self.time_service.check_timer():
            return self.random_answer

        neigh_nums = self.__get_neigh_nums_list(coords)
        if not self.time_service.check_timer():
            return self.random_answer

        sorted_neigh_nums = self.__sort_neigh_nums_by_N(neigh_nums)
        if not self.time_service.check_timer():
            return self.random_answer

        potential_points = self.__get_potential_points_coords(sorted_neigh_nums)
        if not self.time_service.check_timer():
            return self.random_answer

        best_lines = self.__get_best_lines(potential_points)
        if not self.time_service.check_timer():
            return self.random_answer

        intersection_points = np.linalg.solve(
            np.array([[best_lines[0][0], -1], [best_lines[1][0], -1]]),
            [-best_lines[0][1], -best_lines[1][1]])
        intersection_points = np.vstack((intersection_points, np.linalg.solve(
            np.array([[best_lines[0][0], -1], [best_lines[2][0], -1]]),
            [-best_lines[0][1], -best_lines[2][1]])))
        intersection_points = np.vstack((intersection_points, np.linalg.solve(
            np.array([[best_lines[1][0], -1], [best_lines[2][0], -1]]),
            [-best_lines[1][1], -best_lines[2][1]])))

        return intersection_points


ПОЛУЧАЮ ОШИБКУ:
ERROR: only integer scalar arrays can be converted to a scalar index; Traceback: File "/3divi/estimators/test_estimator.py", line 72, in estimator; result = solver.solve(image); File "/tmp/tmpc9w_4bqy/solver.py", line 11, in solve; result = self.alg.find_corners(image_gray); File "/tmp/tmpc9w_4bqy/corner_detector/corner_detector.py", line 171, in find_corners; neigh_nums = self.__get_neigh_nums_list(coords); File "/tmp/tmpc9w_4bqy/corner_detector/corner_detector.py", line 58, in __get_neigh_nums_list; kernel = self.image[coords[0] - offset:coords[0] + offset + 1,

В чем дело?!!?