```import itertools
import numpy as np
import logging

self.cached_sizes = {}

self.cached_unions = {}
self.cached_union_sizes = {}

self.cached_intersections = {}
self.cached_intersection_sizes = {}

@property
def count(self):
return len(self.bbs)

[docs]    def close_sets(self, set_size, max_dist):
return (ms for ms in mask_sets if self.close(ms,  max_dist))

def _idx_key(self, idxs):
return tuple(sorted(set(idxs)))

return None

return union

return union

[docs]    def overlap_fraction(self, idx0, idx1):
union_size = self.union_size([idx0,idx1])
overlap_size = self.intersection_size([idx0,idx1])
return float(overlap_size) / float(union_size)

[docs]    def detect_duplicates(self, overlap_threshold):

for idx0,idx1 in self.close_sets(set_size=2, max_dist=0):
overlap_frac = self.overlap_fraction(idx0, idx1)

if overlap_frac > overlap_threshold:

# does this mask overlap with each element of the set individually?
# i.e. overlap of mask and set element covers most of the set element
if overlap_size < threshold * set_mask_size:
return False

# does this mask cover more than the union of the individual set elements?
set_union = self.union(set_idxs)
overlap_size = overlap.sum()

return overlap_size > self.size(mask_idx) * threshold

[docs]    def detect_unions(self, set_size=2, max_dist=10, threshold=0.7):

continue
elif not self.close([mask_idx] + list(set_idxs), max_dist):
continue

return s

return None

# don't cache the empty ones

return intersection

return intersection

return s

bbs = []

bbs.append([[m[0].min(), m[0].max()],[m[1].min(), m[1].max()]])

return bbs

[docs]def bb_dist(bbs):
num_bbs = len(bbs)

dist = np.zeros((num_bbs, num_bbs))
for i,j in itertools.combinations(range(num_bbs), 2):
bbi = bbs[i]
bbj = bbs[j]

if bbi[0][0] < bbj[0][1]:
distx = bbj[0][0] - bbi[0][1]
else:
distx = bbi[0][0] - bbj[0][1]

if bbi[1][0] < bbj[1][1]:
disty = bbj[1][0] - bbi[1][1]
else:
disty = bbi[1][0] - bbj[1][1]

dist[i,j] = max(distx,disty)
dist[j,i] = dist[i,j]

return dist
```