0

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

firion
  • 296
  • 3
  • 12

0 Answers0