|
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) |