Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving Mask to Polygon compression compression method #15

Open
valvgab-bh opened this issue Mar 25, 2024 · 0 comments
Open

Improving Mask to Polygon compression compression method #15

valvgab-bh opened this issue Mar 25, 2024 · 0 comments

Comments

@valvgab-bh
Copy link
Collaborator

Is your feature request related to a problem? Please describe.
The current implementation creates compression artefacts due to interpolation. We can do something more effective using shapely

Describe the solution you'd like
There should not be compression artefacts due to the reduction of the number of polygon coordinates.

Describe alternatives you've considered
Using shapely and rewriting the function as:

from shapely.geometry import Polygon
import numpy as np
from typing import List

def mask_to_polygon(
        mask: np.ndarray,
        simplify_tolerance: float = 1.0,
        **kwargs
) -> List:
    """
    Converts segmentation mask to a list of polygons.

    To reduce the number of vertices in the obtained polygon, try to increase
    the value of `polygon_simplify_tolerance`.

    Args:
        mask: numpy array containing multiple segmentation masks. Each mask must
            be associated with a different number, where 0 is for background,
            and other numbers related to different objects. For example, objects
            of a class A may be associated with a value of 1, class B to values
            10, class C to 255, and so on.
        simplify_tolerance: a tolerance value used to remove redundant
            vertexes for the polygons extracted from the mask.
        **kwargs: extra parameters are ignored.

    Returns:
        List of polygons segmentation masks.
    """
    polygons = []
    classes = list(np.unique(mask))
    if 0 in classes:
        classes.remove(0)

    # iterate over the different segmentations (classes) inside the mask:
    for cls in classes:
        # pick the class-related segmentation and convert it to binary:
        binary = np.zeros_like(mask)
        binary[mask == cls] = 1

        # compute mask contours and then convert them to polygons:
        binary = binary.astype(np.uint8)
        contours, hierarchy = cv2.findContours(binary, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)

        # simplify contours
        contours = [cnt.reshape((-1, 2)) for cnt in contours]

        for cnt in contours:
            sqz_cnt = cnt
            if cnt.shape[1] == 1:
                sqz_cnt = np.squeeze(sqz_cnt, axis=1)

            if len(sqz_cnt) >= 3:  # a polygon must contain at least 3 points
                polygon = Polygon(sqz_cnt)
                polygon = polygon.simplify(tolerance=simplify_tolerance, preserve_topology=True)
                polygon = polygon.exterior.coords

                # get coordinates
                x_coords = [c[0] for c in polygon]
                y_coords = [c[1] for c in polygon]
                sgm = []
                for x, y in zip(x_coords, y_coords):
                    sgm.extend([float(x), float(y)])
                polygons.append(sgm)

    return polygons

Additional context
Add any other context or screenshots about the feature request here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant