Ошибка типа: тип данных pts = 8 не поддерживается - PullRequest
0 голосов
/ 06 марта 2020

Привет! Я пытаюсь исправить код, который может показать мне контуры многоугольника изображения. Мне удалось сделать это с ящиками, но не с полигонами.

Вот мой код для получения ящиков:

# post-process predictions
binary_maps, boxes = post_process_probs_typed(predictions['probs'][0])
boxes_resized = [resize_image_coordinates(box, 
                                          probability_map.shape[:2], 
                                          original_image_shape) for box in boxes]

post_process_probs_typed () находится здесь:

# Transform probability maps into binary maps
def post_process_probs_typed(probability_maps):

binary_maps = np.zeros_like(probability_map, np.uint8)
binary_maps = np.delete(binary_maps, 0, 2)

# Text
binary_image = binarization.thresholding(probability_map[:, :, 1], threshold=0.5)

binary_image = binarization.cleaning_binary(binary_image, kernel_size=7)
boxes = boxes_detection.find_boxes(binary_image, mode='rectangle', min_area=0.) #**LINE 1**
bin_map = np.zeros_like(binary_maps)
binary_maps[:, :, 0] = cv2.fillPoly(bin_map, boxes, (255, 0, 0))[:, :, 0] #**LINE 2**

return binary_maps, boxes

boxes.find_boxes () находится здесь:

def find_boxes(boxes_mask: np.ndarray,
           mode: str= 'min_rectangle',
           min_area: float=0.,
           p_arc_length: float=0.01,
           n_max_boxes=math.inf) -> list:
"""
Finds the coordinates of the box in the binary image `boxes_mask`.

:param boxes_mask: Binary image: the mask of the box to find. uint8, 2D array
:param mode: 'min_rectangle' : minimum enclosing rectangle, can be rotated
             'rectangle' : minimum enclosing rectangle, not rotated
             'quadrilateral' : minimum polygon approximated by a quadrilateral
:param min_area: minimum area of the box to be found. A value in percentage of the total area of the image.
:param p_arc_length: used to compute the epsilon value to approximate the polygon with a quadrilateral.
                     Only used when 'quadrilateral' mode is chosen.
:param n_max_boxes: maximum number of boxes that can be found (default inf).
                    This will select n_max_boxes with largest area.
:return: list of length n_max_boxes containing boxes with 4 corners [[x1,y1], ..., [x4,y4]]
"""

assert len(boxes_mask.shape) == 2, \
    'Input mask must be a 2D array ! Mask is now of shape {}'.format(boxes_mask.shape)

contours, _ = cv2.findContours(boxes_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours is None:
    print('No contour found')
    return None
found_boxes = list()

h_img, w_img = boxes_mask.shape[:2]

def validate_box(box: np.array) -> (np.array, float):
    """

    :param box: array of 4 coordinates with format [[x1,y1], ..., [x4,y4]]
    :return: (box, area)
    """
    polygon = geometry.Polygon([point for point in box])
    if polygon.area > min_area * boxes_mask.size:

        # Correct out of range corners
        box = np.maximum(box, 0)
        box = np.stack((np.minimum(box[:, 0], boxes_mask.shape[1]),
                        np.minimum(box[:, 1], boxes_mask.shape[0])), axis=1)

        # return box
        return box, polygon.area

if mode not in ['quadrilateral', 'min_rectangle', 'rectangle']:
    raise NotImplementedError
if mode == 'quadrilateral':
    for c in contours:
        epsilon = p_arc_length * cv2.arcLength(c, True)
        cnt = cv2.approxPolyDP(c, epsilon, True)
        # box = np.vstack(simplify_douglas_peucker(cnt[:, 0, :], 4))

        # Find extreme points in Convex Hull
        hull_points = cv2.convexHull(cnt, returnPoints=True)
        # points = cnt
        points = hull_points
        if len(points) > 4:
            # Find closes points to corner using nearest neighbors
            tree = KDTree(points[:, 0, :])
            _, ul = tree.query((0, 0))
            _, ur = tree.query((w_img, 0))
            _, dl = tree.query((0, h_img))
            _, dr = tree.query((w_img, h_img))
            box = np.vstack([points[ul, 0, :], points[ur, 0, :],
                             points[dr, 0, :], points[dl, 0, :]])
        elif len(hull_points) == 4:
            box = hull_points[:, 0, :]
        else:
                continue
        # Todo : test if it looks like a rectangle (2 sides must be more or less parallel)
        # todo : (otherwise we may end with strange quadrilaterals)
        if len(box) != 4:
            mode = 'min_rectangle'
            print('Quadrilateral has {} points. Switching to minimal rectangle mode'.format(len(box)))
        else:
            # found_box = validate_box(box)
            found_boxes.append(validate_box(box))
if mode == 'min_rectangle':
    for c in contours:
        rect = cv2.minAreaRect(c)
        box = np.int0(cv2.boxPoints(rect))
        found_boxes.append(validate_box(box))
elif mode == 'rectangle':
    for c in contours:
        x, y, w, h = cv2.boundingRect(c)
        box = np.array([[x, y], [x + w, y], [x + w, y + h], [x, y + h]], dtype=int)
        found_boxes.append(validate_box(box))
# sort by area
found_boxes = [fb for fb in found_boxes if fb is not None]
found_boxes = sorted(found_boxes, key=lambda x: x[1], reverse=True)
if n_max_boxes == 1:
    if found_boxes:
        return found_boxes[0][0]
    else:
        return None
else:
    return [fb[0] for i, fb in enumerate(found_boxes) if i <= n_max_boxes]

Теперь, чтобы получить контуры многоугольника вместо прямоугольников, я попытался исправить строку 1 в комментариях к:

polygons = polygon_detection.find_polygonal_regions(binary_image, min_area=0.)

и строка 2 в:

binary_maps[:, :, 0] = cv2.fillPoly(bin_map, polygons, (255, 0, 0))[:, :, 0]

но я получаю сообщение об ошибке:

TypeError: pts data type = 8 is not supported

Это код для find_polygonal_regions ()

def find_polygonal_regions(image_mask: np.ndarray,
                       min_area: float=0.,
                       n_max_polygons: int=math.inf) -> list:
"""
Finds the shapes in a binary mask and returns their coordinates as polygons.

:param image_mask: Uint8 binary 2D array
:param min_area: minimum area the polygon should have in order to be considered as valid
            (value within [0,1] representing a percent of the total size of the image)
:param n_max_polygons: maximum number of boxes that can be found (default inf).
                    This will select n_max_boxes with largest area.
:return: list of length n_max_polygons containing polygon's n coordinates [[x1, y1], ... [xn, yn]]
"""

contours, _ = cv2.findContours(image_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours is None:
    print('No contour found')
    return None
found_polygons = list()

for c in contours:
    if len(c) < 3:  # A polygon cannot have less than 3 points
        continue
    polygon = geometry.Polygon([point[0] for point in c])
    # Check that polygon has area greater than minimal area
    if polygon.area >= min_area*np.prod(image_mask.shape[:2]):
        found_polygons.append(
            (np.array([point for point in polygon.exterior.coords], dtype=np.uint), polygon.area)
        )

# sort by area
found_polygons = [fp for fp in found_polygons if fp is not None]
found_polygons = sorted(found_polygons, key=lambda x: x[1], reverse=True)

if found_polygons:
    return [fp[0] for i, fp in enumerate(found_polygons) if i <= n_max_polygons]
else:
    return None

Любая помощь будет оценили

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...