python implementation of rotating caliper algorithm
Rotating caliper algorithm is used to find a rectangle that fits a convex hull. Below is a python implementation that finds all rotated rectangles for a given convex hull points. It is up to the user to select which rectangle to use since it returns all possible rotating caliper rectangles.
import numpy as np
def get\_rotating\_caliper\_bbox\_list(hull\_points\_2d):
"""
hull\_points\_2d: array of hull points. each element should have \[x,y\] format
"""
# Compute edges (x2-x1,y2-y1)
edges = np.zeros( (len(hull\_points\_2d)-1,2) ) # empty 2 column array
for i in range( len(edges) ):
edge\_x = hull\_points\_2d\[i+1,0\] - hull\_points\_2d\[i,0\]
edge\_y = hull\_points\_2d\[i+1,1\] - hull\_points\_2d\[i,1\]
edges\[i\] = \[edge\_x,edge\_y\]
# Calculate edge angles atan2(y/x)
edge\_angles = np.zeros( (len(edges)) ) # empty 1 column array
for i in range( len(edge\_angles) ):
edge\_angles\[i\] = np.arctan2( edges\[i,1\], edges\[i,0\] )
# Check for angles in 1st quadrant
for i in range( len(edge\_angles) ):
edge\_angles\[i\] = np.abs( edge\_angles\[i\] % (np.pi/2) ) # want strictly positive answers
#print "Edge angles in 1st Quadrant: \\n", edge\_angles
# Remove duplicate angles
edge\_angles = np.unique(edge\_angles)
#print "Unique edge angles: \\n", edge\_angles
bbox\_list=\[\]
for i in range( len(edge\_angles) ):
# Create rotation matrix to shift points to baseline
# R = \[ cos(theta) , cos(theta-PI/2)
# cos(theta+PI/2) , cos(theta) \]
R = np.array(\[ \[ np.cos(edge\_angles\[i\]), np.cos(edge\_angles\[i\]-(np.pi/2)) \], \[ np.cos(edge\_angles\[i\]+(np.pi/2)), np.cos(edge\_angles\[i\]) \] \])
# Apply this rotation to convex hull points
rot\_points = np.dot(R, np.transpose(hull\_points\_2d) ) # 2x2 \* 2xn
# Find min/max x,y points
min\_x = np.nanmin(rot\_points\[0\], axis=0)
max\_x = np.nanmax(rot\_points\[0\], axis=0)
min\_y = np.nanmin(rot\_points\[1\], axis=0)
max\_y = np.nanmax(rot\_points\[1\], axis=0)
# Calculate height/width/area of this bounding rectangle
width = max\_x - min\_x
height = max\_y - min\_y
area = width\*height
# Calculate center point and restore to original coordinate system
center\_x = (min\_x + max\_x)/2
center\_y = (min\_y + max\_y)/2
center\_point = np.dot( \[ center\_x, center\_y \], R )
# Calculate corner points and restore to original coordinate system
corner\_points = np.zeros( (4,2) ) # empty 2 column array
corner\_points\[0\] = np.dot( \[ max\_x, min\_y \], R )
corner\_points\[1\] = np.dot( \[ min\_x, min\_y \], R )
corner\_points\[2\] = np.dot( \[ min\_x, max\_y \], R )
corner\_points\[3\] = np.dot( \[ max\_x, max\_y \], R )
bbox\_info = \[edge\_angles\[i\], area, width, height, min\_x, max\_x, min\_y, max\_y, corner\_points, center\_point\]
bbox\_list.append(bbox\_info)
return bbox\_list
this code was based on code from here. I only updated the code to return all possible rectangles instead of the one with minimum area since the user may be interested in other rectangles depending on their needs. Also I’ve updated it to use explicit functions from numpy.