Diff of /slideslicer/cocohacks.py [000000] .. [5a7589]

Switch to side-by-side view

--- a
+++ b/slideslicer/cocohacks.py
@@ -0,0 +1,127 @@
+import numpy as np
+import json
+from pycocotools.mask import encode, decode
+from warnings import warn
+from .slideutils import convert_contour2mask
+
+
+def remove_upper_channel(lo, hi):
+    """
+    take difference between two channels:
+    # rule:
+    lo )  0 0 1 1
+    up )  0 1 0 1
+     ->   0 0 1 0
+    """
+    lo = lo.astype(bool)
+    hi = hi.astype(bool)
+    return (lo ^ (lo & hi).astype(bool)).astype(bool)
+
+
+def convert_cocorle2onehotmask(rois, tissuedict):
+    """constructs a dense mask given a list of `rois`
+    and a dictionary mapping roi names to channel
+    numbers in tissuedict are expected to start at one
+    as the default class is constructed
+    and assigned to zeroth channel
+
+    Inputs
+        rois       : an MS-COCO formated list with RLE 'counts'
+        tissuedict : a mapping from roi names to integers, 
+            can come in 2 possible formats:
+            + dictionary : {'tissue_1': 1, 'tissue_2': 2, ...}
+            + list       : ['tissue_1', 'tissue_2', ... ]
+
+    Calls `pycocotools.mask.decode`
+    """
+    if isinstance(tissuedict, list):
+        tissuedict = {xx: ii+1 for ii, xx in enumerate(tissuedict)}
+
+    nchannels = 1+max(tissuedict.values())
+    maskarr = np.zeros(rois[-1]["size"] + [nchannels], dtype=bool)
+    
+    for roi_ in rois:
+        mask = decode(roi_)
+        name = roi_["name"]
+        if name in tissuedict:
+            channel = tissuedict[name]
+            maskarr[..., channel] |= mask.astype(bool)
+ 
+    for nn in range(maskarr.shape[-1]-2, 0, -1):
+        maskarr[..., nn] =  remove_upper_channel(
+                                                maskarr[..., nn],
+                                                maskarr[...,nn+1:].any(-1)
+                                                )
+    maskarr[..., 0] = ~maskarr[...,1:].any(-1)
+
+    if not  maskarr.sum(-1).max() == 1:
+        print("maskarr.sum(-1).max()", maskarr.sum(-1).max())
+        raise ValueError()
+
+    return maskarr
+
+
+def convert_cocorle2intmask(rois, tissuedict):
+    """constructs an interger mask given a list of `rois`
+    and a dictionary mapping roi names to channel
+    numbers in tissuedict are expected to start at one
+    as the default class is constructed
+    and assigned to zeroth channel
+    
+    Inputs
+        rois       : an MS-COCO formated list with RLE 'counts'
+        tissuedict : a mapping from roi names to integers, 
+            can come in 2 possible formats:
+            + dictionary : {'tissue_1': 1, 'tissue_2': 2, ...}
+            + list       : ['tissue_1', 'tissue_2', ... ]
+
+    Calls `pycocotools.mask.decode`
+    """
+    if isinstance(tissuedict, list):
+        tissuedict = {xx: ii+1 for ii, xx in enumerate(tissuedict)}
+
+    nchannels = 1+max(tissuedict.values())
+    maskarr = np.zeros(rois[-1]["size"], dtype=bool)
+    
+    for roi_ in rois:
+        mask = decode(roi_)
+        name = roi_["name"]
+        if name in tissuedict:
+            channel = tissuedict[name]
+            maskarr = np.maximum(maskarr, channel*mask.astype(np.uint8))
+    return maskarr
+
+
+def convert_contour2cocorle(verts, w, h, format=None):
+    mask = convert_contour2mask(verts, w,h, order='F')[...,np.newaxis]
+    entry = encode(mask)[0]
+    if format is str:
+        entry['counts'] = entry['counts'].decode('ascii')
+    return entry
+
+
+def construct_sparse_mask(*args):
+    warn('use convert_vertices2intmask instead of construct_sparse_mask',
+        DeprecationWarning)
+    return construct_sparse_mask(*args)
+
+def construct_dense_mask(rois, tissuedict):
+    warn('use convert_cocorle2onehotmask instead of construct_dense_mask',
+        DeprecationWarning)
+    return convert_cocorle2onehotmask(rois, tissuedict)
+
+def dense_to_sparse(maskarr):
+    return (np.arange(maskarr.shape[-1]).reshape([1,1,-1]) *
+            maskarr).sum(-1)
+
+
+def read_roi_to_sparse(jsonfile, roidict):
+    with open(jsonfile) as fh:
+        rois = json.load(fh)
+    return construct_sparse_mask(rois, roidict)
+
+
+def read_roi_to_dense(jsonfile, roidict):
+    with open(jsonfile) as fh:
+        rois = json.load(fh)
+    return construct_dense_mask(rois, roidict)