Proportional scaling of polygon outlines

 

Polygons (contour points) are expanded at equal distances
1. Need to install a python package If you
install pyclipper python, directly pip install pyclipper
address: https://pypi.org/project/pyclipper/
Chinese document: https://www.cnblogs. com/zhigu/p/11943118.html

2. Equidistant expansion of contour points
def equidistant_zoom_contour(contour, margin):
    """
    equidistant zoom polygon contour points
    : param contour: the contour format of a figure [[[x1, x2]],...], shape is (-1, 1, 2)
    :param margin: the pixel distance of the outline extension. A positive margin is an extension, and a negative number is a reduction
    : return: the outline point after the extension
    """
    pco = pyclipper.PyclipperOffset()
    ## ### Parameter limit, the default is 2 The setting here is larger, mainly used for whether the sharp corners of the polygon should be rounded instead of
    pco.MiterLimit = 10
    contour = contour[:, 0, :]
    pco.AddPath(contour, pyclipper. JT_MITER, pyclipper.ET_CLOSEDPOLYGON)
    solution = pco.Execute(margin)
    solution = np.array(solution).reshape(-1, 1, 2).astype(int)
    return solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Call example:

import pyclipper
import math
from shapely.geometry import LineString, Polygon, MultiLineString, Point, MultiPoint

poly = np.array([[[200, 200]], [[200, 300]], [[400, 350]], [[350, 200]], [[300, 200]], [[200, 100]]])
contour1 = equidistant_zoom_contour(poly, 20)
img = np.zeros((500, 500, 3))
cv2.polylines(img, [poly], True, (0, 0, 255), 3)
cv2.polylines(img, [contour1], True, (0, 255, 0), 3)
1
2
3
4
5
6
7
8
9
结果展示:


3. The contour points are scaled
def perimeter(poly):
    p = 0
    nums = poly.shape[0]
    for i in range(nums):
        p += abs(np.linalg.norm(poly[i% nums]- poly[(i + 1)% nums]))
    return p

def proportional_zoom_contour(contour, ratio):
    """
    Polygon contour points are scaled proportionally
    : param contour: the contour format of a figure [[[x1, x2]],...], shape is (-1, 1, 2 )
    :param ratio: zoom ratio, if greater than 1, zoom in and less than 1, zoom out
    : return:
    """
    poly = contour[:, 0, :]
    area_poly = abs(pyclipper.Area(poly))
    perimeter_poly = perimeter(poly )
    poly_s = []
    pco = pyclipper.PyclipperOffset()
    pco.MiterLimit = 10
    if perimeter_poly:
        d = area_poly * (1-ratio * ratio) / perimeter_poly
        pco.AddPath(poly, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON)
        poly_s = pco .Execute(-d)
    poly_s = np.array(poly_s).reshape(-1, 1, 2).astype(int)

    return poly_s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Call the demonstration:

import pyclipper
import math
from shapely.geometry import LineString, Polygon, MultiLineString, Point, MultiPoint
poly = np.array([[[200, 200]], [[200, 300]], [[400, 350]], [ [350, 200]], [[300, 200]], [[200, 100]]])
contour1 = proportional_zoom_contour(poly, 1.5)
img = np.zeros((500, 500, 3))
cv2.polylines( IMG, [contour1], True, (0, 255, 0),. 3)
cv2.polylines (IMG, [poly], True, (0, 0, 255),. 3)
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
wherein pco .MiterLimit = 10 This parameter is 2 by default. If it is the default value, the result graph is the first, and if it is changed to 10, the result graph is the second, which is a sharp difference.

4. Rotation of the contour points of the figure
# Get the centroid of a shape
def get_centroid(coord):
    coord = np.array(coord)
    shape = coord.shape
    if len(shape) == 1 and len(coord) == 2: # point
        return coord
    if len(shape) == 1 and len(coord) == 4: # bounding box
        return tuple([(coord[0] + coord[2]) // 2, (coord[1] + coord[ 3]) // 2])
    elif len(shape) == 2 and shape[-1] == 2:
        if shape[0] == 2: # If it is a straight line
            cen = LineString(coord).centroid
        else:
            cen = Polygon(coord).centroid
        return tuple(map(int, [cen.x, cen.y]))
    elif len(shape) == 3 and shape[1:] == (1, 2): # contour
        cen = Polygon(coord.squeeze()).centroid
        return tuple(map(int, [cen.x, cen.y]))
    else:
        raise Exception('coordinate error, must be bbox or contour shape:{}'.format(coord))


def point_Srotate(im_w, im_h, angle, spin_point, origin_point):
    """
    :param im_w: the width of the picture where the original point is located
    : param im_h: the height of the picture where the original point is located
    : param angle: the angle of rotation
    : param spin_point: Rotated point
    : param origin_point: reference point
    : return: the rotated point
    """
    row, col = im_h, im_w
    # P(x1, y1), around a certain pixel point Q(x2, y2)
    x1, y1 = spin_point
    x2, y2 = origin_point
    y1 = row-y1
    y2 = row-y2
    x = (x1-x2) * math.cos(math.pi / 180.0 * angle)-(y1-y2) * math.sin(math.pi / 180.0 * angle) + x2
    y = (x1-x2) * math.sin(math.pi / 180.0 * angle) + (y1-y2) * math.cos(math.pi / 180.0 * angle) + y2
    x = x
    y = row - y

    return [x, y]

. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
20 is
21 is
22 is
23 is
24
25
26 is
27
28
29
30
31 is
32
33 is
34 is
35
36
37 [
38 is
39
40
41 is
42 is
43 is
invoked Model

import pyclipper
import math
from shapely.geometry import LineString, Polygon, MultiLineString, Point, MultiPoint
# Rotate with the centroid of the polygon contour as the reference point
poly = np.array([[[200, 200]], [[200, 300] ], [[400, 350]], [[350, 200]], [[300, 200]], [[200, 100]]])

origin_point = get_centroid(poly)
spin_list = []
for con in poly:
    print('con', con)
    new = point_Srotate(500, 500, 50, con[0], origin_point)
    spin_list.append(new)
spin_con = np.array(spin_list).reshape(-1, 1, 2).astype(int)
img = np.zeros((500, 500, 3))
cv2.polylines(img, [spin_con], True, (0, 255, 0), 3)
cv2.polylines(img, [poly], True, (0, 0, 255), 3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
结果图:


5. Other extended functions
def extend_contour2(contour, margin):
    # Each point is extended a certain distance
    """ relative to the center of mass
    : param contour: contour point collection
    : param margin: extended distance
    : return: outer The expanded contour point set
    """
    #### Find the center of mass of the contour####
    gravity_point = get_centroid(contour)
    #### Get the lowest left point####
    # min_x = np.minimum(contour)
    #### Calculate the vector composed of all contour points and centroids, calculate the modulus of the vector
    vector_arr = contour-np.array(
    gravity_point ) vector_length = np.linalg.norm(vector_arr, axis=2)
    #### Calculate all How many times does the point need to be magnified for the externally expanded pixels
    ratio = 1 + margin / vector_length
    ratio = np.concatenate([ratio, ratio], axis=1)
    #### Scale coordinates
    contour_ext = (vector_arr[:, 0, :] * ratio + np.array(gravity_point)).reshape(-1, 1, 2)
    contour_ext = contour_ext.astype(int)
    return contour_ext

def coordinate_conversion(reference_point, contour, ratio):
    # Useful for convex polygons, easy to deform for concave polygons, proportionally zoom the contour
    """
    :param reference_point: the coordinates of the reference point
    : param contour: the contour point of the image
    : param ratio: zoom Ratio
    : return: get the contour point coordinates after scaling with the reference point unchanged
    """
    contour_trans_array = (contour-np.array(reference_point)) * ratio + np.array(reference_point)
    contour_trans_array = contour_trans_array.astype( int)
    return contour_trans_array

————————————————
Copyright statement: This article is the original article of the CSDN blogger "Cecilia_lu", and it follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this statement for reprinting. .
Original link: https://blog.csdn.net/weixin_43624833/article/details/112919141

Guess you like

Origin blog.csdn.net/jacke121/article/details/115223529