I have a python function for alpha shape computation that I found here. It emplys the Scipy Delaunay triangulation method. By running tracemalloc it seems that I have a memory leak. How can I fix it?
import numpy as np
import tracemalloc
from random import random
from scipy.spatial import Delaunay
import time
from tqdm import tqdm
def alpha_shape(points, alpha, only_outer=True):
"""
Compute the alpha shape (concave hull) of a set of points.
:param points: np.array of shape (n,2) points.
:param alpha: alpha value.
:param only_outer: boolean value to specify if we keep only the outer border
or also inner edges.
:return: set of (i,j) pairs representing edges of the alpha-shape. (i,j) are
the indices in the points array.
"""
if points.shape[0] < 4:
return None
def add_edge(edges, i, j):
"""
Add a line between the i-th and j-th points,
if not in the list already
"""
if (i, j) in edges or (j, i) in edges:
# already added
assert (j, i) in edges, "Can't go twice over same directed edge right?"
if only_outer:
# if both neighboring triangles are in shape, it is not a boundary edge
edges.remove((j, i))
return
edges.add((i, j))
tri = Delaunay(points)
edges = set()
# Loop over triangles:
# ia, ib, ic = indices of corner points of the triangle
for ia, ib, ic in tri.simplices:
pa = points[ia]
pb = points[ib]
pc = points[ic]
# Computing radius of triangle circumcircle
# www.mathalino.com/reviewer/derivation-of-formulas/derivation-of-formula-for-radius-of-circumcircle
a = np.sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)
b = np.sqrt((pb[0] - pc[0]) ** 2 + (pb[1] - pc[1]) ** 2)
c = np.sqrt((pc[0] - pa[0]) ** 2 + (pc[1] - pa[1]) ** 2)
s = (a + b + c) / 2.0
area = np.sqrt(s * (s - a) * (s - b) * (s - c))
circum_r = a * b * c / (4.0 * area)
if circum_r < alpha:
add_edge(edges, ia, ib)
add_edge(edges, ib, ic)
add_edge(edges, ic, ia)
# prevent thread to overload cpu
time.sleep(0.01)
return edges
if __name__ == '__main__':
tracemalloc.start()
snap1 = tracemalloc.take_snapshot()
for i in tqdm(range(50)):
# randomly generate new points each time
points = []
for j in range(20):
points.append([random(), random()])
res = alpha_shape(np.array(points), 0.5)
snap2 = tracemalloc.take_snapshot()
top_stats = snap2.compare_to(snap1, 'lineno')
for stat in top_stats[:50]:
print(stat)
Output:
<frozen importlib._bootstrap>:219: size=49.3 KiB (+49.3 KiB), count=503 (+503), average=100 B
<frozen importlib._bootstrap_external>:487: size=43.5 KiB (+43.5 KiB), count=541 (+541), average=82 B
test_memory.py:39: size=18.4 KiB (+18.4 KiB), count=65 (+65), average=289 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:499: size=3816 B (+3816 B), count=53 (+53), average=72 B
test_memory.py:68: size=2632 B (+2632 B), count=4 (+4), average=658 B
/usr/lib/python3.6/tempfile.py:289: size=2600 B (+2600 B), count=2 (+2), average=1300 B
/usr/lib/python3.6/threading.py:846: size=2312 B (+2312 B), count=2 (+2), average=1156 B
/usr/lib/python3.6/multiprocessing/util.py:280: size=2208 B (+2208 B), count=1 (+1), average=2208 B
test_memory.py:71: size=2136 B (+2136 B), count=70 (+70), average=31 B
/usr/lib/python3.6/multiprocessing/synchronize.py:210: size=2116 B (+2116 B), count=10 (+10), average=212 B
/usr/lib/python3.6/multiprocessing/util.py:347: size=2108 B (+2108 B), count=11 (+11), average=192 B
test_memory.py:30: size=2048 B (+2048 B), count=32 (+32), average=64 B
test_memory.py:37: size=2048 B (+2048 B), count=1 (+1), average=2048 B
/usr/lib/python3.6/multiprocessing/util.py:147: size=1982 B (+1982 B), count=12 (+12), average=165 B
/usr/lib/python3.6/multiprocessing/synchronize.py:371: size=1868 B (+1868 B), count=9 (+9), average=208 B
/usr/lib/python3.6/multiprocessing/synchronize.py:332: size=1836 B (+1836 B), count=10 (+10), average=184 B
/usr/lib/python3.6/multiprocessing/synchronize.py:123: size=1836 B (+1836 B), count=7 (+7), average=262 B
/usr/lib/python3.6/multiprocessing/util.py:330: size=1780 B (+1780 B), count=9 (+9), average=198 B
/usr/lib/python3.6/multiprocessing/synchronize.py:46: size=1756 B (+1756 B), count=6 (+6), average=293 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:1150: size=1640 B (+1640 B), count=3 (+3), average=547 B
/usr/lib/python3.6/multiprocessing/synchronize.py:142: size=1620 B (+1620 B), count=7 (+7), average=231 B
/usr/lib/python3.6/multiprocessing/synchronize.py:159: size=1444 B (+1444 B), count=6 (+6), average=241 B
/usr/lib/python3.6/multiprocessing/synchronize.py:184: size=1332 B (+1332 B), count=6 (+6), average=222 B
/usr/lib/python3.6/threading.py:551: size=1216 B (+1216 B), count=2 (+2), average=608 B /usr/local/lib/python3.6/dist-packages/tqdm/std.py:1081: size=1112 B (+1112 B), count=1 (+1), average=1112 B
/usr/lib/python3.6/multiprocessing/synchronize.py:40: size=1112 B (+1112 B), count=1 (+1), average=1112 B
/usr/lib/python3.6/tempfile.py:299: size=992 B (+992 B), count=2 (+2), average=496 B
/usr/lib/python3.6/weakref.py:168: size=952 B (+952 B), count=2 (+2), average=476 B
test_memory.py:73: size=928 B (+928 B), count=2 (+2), average=464 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:1494: size=928 B (+928 B), count=2 (+2), average=464 B
/usr/lib/python3.6/threading.py:790: size=928 B (+928 B), count=2 (+2), average=464 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:1458: size=880 B (+880 B), count=2 (+2), average=440 B
/usr/lib/python3.6/threading.py:884: size=840 B (+840 B), count=1 (+1), average=840 B
/usr/lib/python3.6/threading.py:798: size=744 B (+744 B), count=2 (+2), average=372 B
/usr/lib/python3.6/threading.py:237: size=736 B (+736 B), count=3 (+3), average=245 B
<frozen importlib._bootstrap_external>:60: size=729 B (+729 B), count=7 (+7), average=104 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:1031: size=664 B (+664 B), count=1 (+1), average=664 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:1195: size=656 B (+656 B), count=1 (+1), average=656 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:343: size=632 B (+632 B), count=1 (+1), average=632 B
<frozen importlib._bootstrap>:371: size=616 B (+616 B), count=8 (+8), average=77 B /usr/local/lib/python3.6/dist-packages/tqdm/std.py:1274: size=608 B (+608 B), count=1 (+1), average=608 B
/usr/lib/python3.6/multiprocessing/util.py:17: size=608 B (+608 B), count=1 (+1), average=608 B
/usr/lib/python3.6/multiprocessing/synchronize.py:187: size=600 B (+600 B), count=1 (+1), average=600 B
/usr/local/lib/python3.6/dist-packages/tqdm/_monitor.py:39: size=592 B (+592 B), count=1 (+1), average=592 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:539: size=576 B (+576 B), count=1 (+1), average=576 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:536: size=568 B (+568 B), count=2 (+2), average=284 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:982: size=552 B (+552 B), count=2 (+2), average=276 B
/usr/local/lib/python3.6/dist-packages/tqdm/utils.py:217: size=552 B (+552 B), count=1 (+1), average=552 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:113: size=544 B (+544 B), count=2 (+2), average=272 B
/usr/local/lib/python3.6/dist-packages/tqdm/std.py:163: size=536 B (+536 B), count=1 (+1), average=536 B