예제 #1
0
    def __init__(self,
                 corners: geometry_utils.Polygon,
                 horizontal_cells: int,
                 vertical_cells: int,
                 image: np.ndarray,
                 save_path: typing.Optional[pathlib.PurePath] = None):
        """Initiate a new Grid. Corners should be clockwise starting from the
        top left - if not, the grid will have unexpected behavior.

        If `save_path` is provided, will save the resulting image to this location
        as "grid.jpg". Used for debugging purposes."""
        self.corners = corners
        self.horizontal_cells = horizontal_cells
        self.vertical_cells = vertical_cells
        self._to_grid_basis, self._from_grid_basis = geometry_utils.create_change_of_basis(
            corners[0], corners[3], corners[2])

        self.horizontal_cell_size = 1 / self.horizontal_cells
        self.vertical_cell_size = 1 / self.vertical_cells

        self.image = image

        if save_path:
            image_utils.save_image(save_path / "grid.jpg", self.draw_grid())
예제 #2
0
def find_corner_marks(image: np.ndarray,
                      save_path: typing.Optional[pathlib.PurePath] = None
                      ) -> geometry_utils.Polygon:

    all_polygons: typing.List[
        geometry_utils.Polygon] = image_utils.find_polygons(
            image, save_path=save_path)

    # Even though the LMark and SquareMark classes check length, it's faster to
    # filter out the shapes of incorrect length despite the increased time
    # complexity.
    hexagons: typing.List[geometry_utils.Polygon] = []
    quadrilaterals: typing.List[geometry_utils.Polygon] = []
    for poly in all_polygons:
        if len(poly) == 6:
            hexagons.append(poly)
        elif len(poly) == 4:
            quadrilaterals.append(poly)

    for hexagon in hexagons:
        try:
            l_mark = LMark(hexagon)
        except WrongShapeError:
            continue

        to_new_basis, _ = geometry_utils.create_change_of_basis(
            l_mark.polygon[0], l_mark.polygon[5], l_mark.polygon[4])
        nominal_to_right_side = 50 - 0.5
        nominal_to_bottom = ((64 - 0.5) / 2)
        tolerance = 0.1 * nominal_to_right_side

        top_right_squares = []
        bottom_left_squares = []
        bottom_right_squares = []

        for quadrilateral in quadrilaterals:
            try:
                square = SquareMark(quadrilateral, l_mark.unit_length)
            except WrongShapeError:
                continue
            centroid = geometry_utils.guess_centroid(square.polygon)
            centroid_new_basis = to_new_basis(centroid)

            if math_utils.is_within_tolerance(
                    centroid_new_basis.x, nominal_to_right_side,
                    tolerance) and math_utils.is_within_tolerance(
                        centroid_new_basis.y, 0.5, tolerance):
                top_right_squares.append(square)
            elif math_utils.is_within_tolerance(
                    centroid_new_basis.x, 0.5,
                    tolerance) and math_utils.is_within_tolerance(
                        centroid_new_basis.y, nominal_to_bottom, tolerance):
                bottom_left_squares.append(square)
            elif math_utils.is_within_tolerance(
                    centroid_new_basis.x, nominal_to_right_side,
                    tolerance) and math_utils.is_within_tolerance(
                        centroid_new_basis.y, nominal_to_bottom, tolerance):
                bottom_right_squares.append(square)

        if len(top_right_squares) == 0 or len(bottom_left_squares) == 0 or len(
                bottom_right_squares) == 0:
            continue

        # TODO: When multiple, either progressively decrease tolerance or
        # choose closest to centroid

        top_left_corner = l_mark.polygon[0]
        top_right_corner = geometry_utils.get_corner(
            top_right_squares[0].polygon, geometry_utils.Corner.TR)
        bottom_right_corner = geometry_utils.get_corner(
            bottom_right_squares[0].polygon, geometry_utils.Corner.BR)
        bottom_left_corner = geometry_utils.get_corner(
            bottom_left_squares[0].polygon, geometry_utils.Corner.BL)

        return [
            top_left_corner, top_right_corner, bottom_right_corner,
            bottom_left_corner
        ]
    raise RuntimeError("Couldn't find document corners.")