-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathgenerators.py
111 lines (101 loc) · 3.68 KB
/
generators.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import numpy as np
from numpy import random
import threading
from keras.utils import to_categorical
class BatchIndices(object):
"""
Generates batches of shuffled indices.
# Arguments
n: number of indices
bs: batch size
shuffle: whether to shuffle indices, default False
"""
def __init__(self, n, bs, shuffle=False):
self.n,self.bs,self.shuffle = n,bs,shuffle
self.lock = threading.Lock()
self.reset()
def reset(self):
self.idxs = (np.random.permutation(self.n)
if self.shuffle else np.arange(0, self.n))
self.curr = 0
def __next__(self):
with self.lock:
if self.curr >= self.n: self.reset()
ni = min(self.bs, self.n-self.curr)
res = self.idxs[self.curr:self.curr+ni]
self.curr += ni
return res
class segm_generator(object):
"""
Generates batches of sub-images.
# Arguments
x: array of inputs
y: array of targets
bs: batch size
out_sz: dimension of sub-image
train: If true, will shuffle/randomize sub-images
waldo: If true, allow sub-images to contain targets.
"""
def __init__(self, x, y, bs=64, out_sz=(224,224), train=True, waldo=True):
self.x, self.y, self.bs, self.train = x,y,bs,train
self.waldo = waldo
self.n = x.shape[0]
self.ri, self.ci = [], []
for i in range(self.n):
ri, ci, _ = x[i].shape
self.ri.append(ri), self.ci.append(ci)
self.idx_gen = BatchIndices(self.n, bs, train)
self.ro, self.co = out_sz
self.ych = self.y.shape[-1] if len(y.shape)==4 else 1
def get_slice(self, i,o):
start = random.randint(0, i-o) if self.train else (i-o)
return slice(start, start+o)
def get_item(self, idx):
slice_r = self.get_slice(self.ri[idx], self.ro)
slice_c = self.get_slice(self.ci[idx], self.co)
x = self.x[idx][slice_r, slice_c]
y = self.y[idx][slice_r, slice_c]
if self.train and (random.random()>0.5):
y = y[:,::-1]
x = x[:,::-1]
if not self.waldo and np.sum(y)!=0:
return None
return x, to_categorical(y, num_classes=2)
def __next__(self):
idxs = self.idx_gen.__next__()
items = []
for idx in idxs:
item = self.get_item(idx)
if item is not None:
items.append(item)
if not items:
return None
xs,ys = zip(*tuple(items))
return np.stack(xs), np.stack(ys)
def seg_gen(x, y, bs=64, out_sz=(224,224), train=True, waldo=True):
"""
Generator wrapper on iterators for python 2 compatibility.
"""
sg = segm_generator(x=x, y=y, bs=bs, out_sz = out_sz ,train=train, waldo=waldo)
while True:
yield sg.__next__()
def seg_gen_mix(x1, y1, x2, y2, tot_bs=4, prop=0.75, out_sz=(224,224), train=True):
"""
Mixes generator output. The second generator is set to skip images that contain any positive targets.
# Arguments
x1, y1: input/targets for waldo sub-images
x2, y2: input/targets for whole images
tot_bs: total batch size
prop: proportion of total batch size consisting of first generator output
"""
n1 = int(tot_bs*prop)
n2 = tot_bs - n1
sg1 = segm_generator(x1, y1, n1, out_sz = out_sz ,train=train)
sg2 = segm_generator(x2, y2, n2, out_sz = out_sz ,train=train, waldo=False)
while True:
out1 = sg1.__next__()
out2 = sg2.__next__()
if out2 is None:
yield out1
else:
yield np.concatenate((out1[0], out2[0])), np.concatenate((out1[1], out2[1]))