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

Switch to unified view

a b/slideslicer/cocohacks.py
1
import numpy as np
2
import json
3
from pycocotools.mask import encode, decode
4
from warnings import warn
5
from .slideutils import convert_contour2mask
6
7
8
def remove_upper_channel(lo, hi):
9
    """
10
    take difference between two channels:
11
    # rule:
12
    lo )  0 0 1 1
13
    up )  0 1 0 1
14
     ->   0 0 1 0
15
    """
16
    lo = lo.astype(bool)
17
    hi = hi.astype(bool)
18
    return (lo ^ (lo & hi).astype(bool)).astype(bool)
19
20
21
def convert_cocorle2onehotmask(rois, tissuedict):
22
    """constructs a dense mask given a list of `rois`
23
    and a dictionary mapping roi names to channel
24
    numbers in tissuedict are expected to start at one
25
    as the default class is constructed
26
    and assigned to zeroth channel
27
28
    Inputs
29
        rois       : an MS-COCO formated list with RLE 'counts'
30
        tissuedict : a mapping from roi names to integers, 
31
            can come in 2 possible formats:
32
            + dictionary : {'tissue_1': 1, 'tissue_2': 2, ...}
33
            + list       : ['tissue_1', 'tissue_2', ... ]
34
35
    Calls `pycocotools.mask.decode`
36
    """
37
    if isinstance(tissuedict, list):
38
        tissuedict = {xx: ii+1 for ii, xx in enumerate(tissuedict)}
39
40
    nchannels = 1+max(tissuedict.values())
41
    maskarr = np.zeros(rois[-1]["size"] + [nchannels], dtype=bool)
42
    
43
    for roi_ in rois:
44
        mask = decode(roi_)
45
        name = roi_["name"]
46
        if name in tissuedict:
47
            channel = tissuedict[name]
48
            maskarr[..., channel] |= mask.astype(bool)
49
 
50
    for nn in range(maskarr.shape[-1]-2, 0, -1):
51
        maskarr[..., nn] =  remove_upper_channel(
52
                                                maskarr[..., nn],
53
                                                maskarr[...,nn+1:].any(-1)
54
                                                )
55
    maskarr[..., 0] = ~maskarr[...,1:].any(-1)
56
57
    if not  maskarr.sum(-1).max() == 1:
58
        print("maskarr.sum(-1).max()", maskarr.sum(-1).max())
59
        raise ValueError()
60
61
    return maskarr
62
63
64
def convert_cocorle2intmask(rois, tissuedict):
65
    """constructs an interger mask given a list of `rois`
66
    and a dictionary mapping roi names to channel
67
    numbers in tissuedict are expected to start at one
68
    as the default class is constructed
69
    and assigned to zeroth channel
70
    
71
    Inputs
72
        rois       : an MS-COCO formated list with RLE 'counts'
73
        tissuedict : a mapping from roi names to integers, 
74
            can come in 2 possible formats:
75
            + dictionary : {'tissue_1': 1, 'tissue_2': 2, ...}
76
            + list       : ['tissue_1', 'tissue_2', ... ]
77
78
    Calls `pycocotools.mask.decode`
79
    """
80
    if isinstance(tissuedict, list):
81
        tissuedict = {xx: ii+1 for ii, xx in enumerate(tissuedict)}
82
83
    nchannels = 1+max(tissuedict.values())
84
    maskarr = np.zeros(rois[-1]["size"], dtype=bool)
85
    
86
    for roi_ in rois:
87
        mask = decode(roi_)
88
        name = roi_["name"]
89
        if name in tissuedict:
90
            channel = tissuedict[name]
91
            maskarr = np.maximum(maskarr, channel*mask.astype(np.uint8))
92
    return maskarr
93
94
95
def convert_contour2cocorle(verts, w, h, format=None):
96
    mask = convert_contour2mask(verts, w,h, order='F')[...,np.newaxis]
97
    entry = encode(mask)[0]
98
    if format is str:
99
        entry['counts'] = entry['counts'].decode('ascii')
100
    return entry
101
102
103
def construct_sparse_mask(*args):
104
    warn('use convert_vertices2intmask instead of construct_sparse_mask',
105
        DeprecationWarning)
106
    return construct_sparse_mask(*args)
107
108
def construct_dense_mask(rois, tissuedict):
109
    warn('use convert_cocorle2onehotmask instead of construct_dense_mask',
110
        DeprecationWarning)
111
    return convert_cocorle2onehotmask(rois, tissuedict)
112
113
def dense_to_sparse(maskarr):
114
    return (np.arange(maskarr.shape[-1]).reshape([1,1,-1]) *
115
            maskarr).sum(-1)
116
117
118
def read_roi_to_sparse(jsonfile, roidict):
119
    with open(jsonfile) as fh:
120
        rois = json.load(fh)
121
    return construct_sparse_mask(rois, roidict)
122
123
124
def read_roi_to_dense(jsonfile, roidict):
125
    with open(jsonfile) as fh:
126
        rois = json.load(fh)
127
    return construct_dense_mask(rois, roidict)