Skip to content

Commit b465d96

Browse files
committed
correct soft/hard dice and IoU
1 parent c251491 commit b465d96

File tree

1 file changed

+69
-54
lines changed

1 file changed

+69
-54
lines changed

tensorlayer/cost.py

Lines changed: 69 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ def normalized_mean_square_error(output, target):
121121
return nmse
122122

123123

124-
def dice_coe(output, target, loss_type='jaccard', epsilon=1e-10):
124+
125+
def dice_coe(output, target, loss_type='jaccard', axis=[1,2,3], epsilon=1e-5):
125126
"""Soft dice (Sørensen or Jaccard) coefficient for comparing the similarity
126127
of two distributions, usually be used for binary image segmentation
127128
i.e. labels are binary. The coefficient in [0, 1], 1 means totally match.
@@ -134,39 +135,54 @@ def dice_coe(output, target, loss_type='jaccard', epsilon=1e-10):
134135
A distribution with shape: [batch_size, ....], (any dimensions).
135136
loss_type : string
136137
``jaccard`` or ``sorensen``, default is ``jaccard``.
138+
axis : list of integer
139+
All dimensions are reduced, default ``[1,2,3]``.
137140
epsilon : float
138141
An small value be added to the numerator and denominator.
139142
140143
Examples
141144
---------
142-
>>> outputs = tf.nn.sigmoid(network.outputs)
145+
>>> outputs = tl.act.pixel_wise_softmax(network.outputs)
143146
>>> dice_loss = 1 - tl.cost.dice_coe(outputs, y_)
144147
145148
References
146149
-----------
147150
- `Wiki-Dice <https://en.wikipedia.org/wiki/Sørensen–Dice_coefficient>`_
148151
"""
149-
# inse = tf.reduce_sum( tf.mul(output, target) )
150-
# l = tf.reduce_sum( tf.mul(output, output) )
151-
# r = tf.reduce_sum( tf.mul(target, target) )
152-
inse = tf.reduce_sum( output * target )
152+
# # inse = tf.reduce_sum( tf.mul(output, target) )
153+
# # l = tf.reduce_sum( tf.mul(output, output) )
154+
# # r = tf.reduce_sum( tf.mul(target, target) )
155+
# inse = tf.reduce_sum( output * target )
156+
# if loss_type == 'jaccard':
157+
# l = tf.reduce_sum( output * output )
158+
# r = tf.reduce_sum( target * target )
159+
# elif loss_type == 'sorensen':
160+
# l = tf.reduce_sum( output )
161+
# r = tf.reduce_sum( target )
162+
# else:
163+
# raise Exception("Unknow loss_type")
164+
# dice = (2. * inse + epsilon) / (l + r + epsilon) # if both output and target are empty, dice is 1
165+
# return dice
166+
# # dice = 2 * (inse) / (l + r)
167+
# # if epsilon == 0:
168+
# # return dice
169+
# # else:
170+
# # return tf.clip_by_value(dice, 0, 1.0-epsilon)
171+
inse = tf.reduce_sum(output * target, axis=axis)
153172
if loss_type == 'jaccard':
154-
l = tf.reduce_sum( output * output )
155-
r = tf.reduce_sum( target * target )
173+
l = tf.reduce_sum(output * output, axis=axis)
174+
r = tf.reduce_sum(target * target, axis=axis)
156175
elif loss_type == 'sorensen':
157-
l = tf.reduce_sum( output )
158-
r = tf.reduce_sum( target )
176+
l = tf.reduce_sum(output, axis=axis)
177+
r = tf.reduce_sum(target, axis=axis)
159178
else:
160179
raise Exception("Unknow loss_type")
161-
dice = (2.*inse + epsilon) / (l + r + epsilon) # if both output and target are empty, dice is 1
180+
dice = (2. * inse + epsilon) / (l + r + epsilon) # if both output and target are empty, dice is 1
181+
dice = tf.reduce_mean(dice)
162182
return dice
163-
# dice = 2 * (inse) / (l + r)
164-
# if epsilon == 0:
165-
# return dice
166-
# else:
167-
# return tf.clip_by_value(dice, 0, 1.0-epsilon)
168183

169-
def dice_hard_coe(output, target, threshold=0.5, epsilon=1e-10):
184+
185+
def dice_hard_coe(output, target, threshold=0.5, axis=[1,2,3], epsilon=1e-5):
170186
"""Non-differentiable Sørensen–Dice coefficient for comparing the similarity of two distributions,
171187
usually be used for binary image segmentation i.e. labels are binary.
172188
The coefficient = [0, 1], 1 if totally match.
@@ -179,29 +195,41 @@ def dice_hard_coe(output, target, threshold=0.5, epsilon=1e-10):
179195
A distribution with shape: [batch_size, ....], (any dimensions).
180196
threshold : float
181197
The threshold value to be true.
198+
axis : list of integer
199+
All dimensions are reduced, default ``[1,2,3]``.
182200
epsilon : float
183201
An small value be added to the numerator and denominator.
184202
185203
References
186204
-----------
187205
- `Wiki-Dice <https://en.wikipedia.org/wiki/Sørensen–Dice_coefficient>`_
188206
"""
207+
# output = tf.cast(output > threshold, dtype=tf.float32)
208+
# target = tf.cast(target > threshold, dtype=tf.float32)
209+
# inse = tf.reduce_sum( output * target )
210+
# l = tf.reduce_sum( output )
211+
# r = tf.reduce_sum( target )
212+
# # l = tf.reduce_sum( output * output )
213+
# # r = tf.reduce_sum( target * target )
214+
# dice = (2. * inse + epsilon) / (l + r + epsilon) # if both output and target are empty, it is 1
215+
# return dice
216+
# # dice = 2 * (inse) / (l + r)
217+
# # if epsilon == 0:
218+
# # return dice
219+
# # else:
220+
# # return tf.clip_by_value(dice, 0, 1.0-epsilon)
189221
output = tf.cast(output > threshold, dtype=tf.float32)
190222
target = tf.cast(target > threshold, dtype=tf.float32)
191-
inse = tf.reduce_sum( output * target )
192-
l = tf.reduce_sum( output )
193-
r = tf.reduce_sum( target )
194-
# l = tf.reduce_sum( output * output )
195-
# r = tf.reduce_sum( target * target )
196-
dice = (2. * inse + epsilon) / (l + r + epsilon) # if both output and target are empty, it is 1
197-
return dice
198-
# dice = 2 * (inse) / (l + r)
199-
# if epsilon == 0:
200-
# return dice
201-
# else:
202-
# return tf.clip_by_value(dice, 0, 1.0-epsilon)
223+
inse = tf.reduce_sum( output * target, axis=axis)
224+
l = tf.reduce_sum(output, axis=axis)
225+
r = tf.reduce_sum(target, axis=axis)
226+
# hard_dice = (2. * inse ) / (l + r + epsilon)
227+
hard_dice = (2. * inse + epsilon) / (l + r + epsilon)
228+
hard_dice = tf.reduce_mean(hard_dice)
229+
return hard_dice
230+
203231

204-
def iou_coe(output, target, threshold=0.5, epsilon=1e-10):
232+
def iou_coe(output, target, threshold=0.5, axis=[1,2,3], epsilon=1e-5):
205233
"""Non-differentiable Intersection over Union, usually be used for
206234
evaluating binary image segmentation.
207235
The coefficient = [0, 1], 1 means totally match.
@@ -214,39 +242,26 @@ def iou_coe(output, target, threshold=0.5, epsilon=1e-10):
214242
A distribution with shape: [batch_size, ....], (any dimensions).
215243
threshold : float
216244
The threshold value to be true.
245+
axis : list of integer
246+
All dimensions are reduced, default ``[1,2,3]``.
217247
epsilon : float
218248
An small value be added to the numerator and denominator.
219249
220-
Examples
221-
---------
222-
>>> outputs = tf.nn.sigmoid(network.outputs)
223-
>>> iou = tl.cost.iou_coe(outputs, y_)
224-
225250
Notes
226251
------
227252
- IOU cannot be used as training loss, people usually use dice coefficient for training, and IOU for evaluating.
228253
"""
229254
pre = tf.cast(output > threshold, dtype=tf.float32)
230255
truth = tf.cast(target > threshold, dtype=tf.float32)
231-
intersection = tf.reduce_sum(pre * truth)
232-
union = tf.reduce_sum(tf.cast((pre + truth) > threshold, dtype=tf.float32))
233-
iou = (tf.reduce_sum(intersection) + epsilon) / (tf.reduce_sum(union) + epsilon)
234-
return iou
235-
# return tf.reduce_sum(intersection) / (tf.reduce_sum(union) + epsilon)
236-
237-
# ## test soft/hard dice and iou
238-
# import numpy as np
239-
# y = np.zeros((100,100,1))
240-
# y[0:10,0:10]=1
241-
# o = np.zeros((100,100,1))
242-
# # o[0:10,0:10]=1
243-
# o[5:15,5:15]=1
244-
# d = dice_coe(y, o)#, epsilon=1)
245-
# hd = dice_hard_coe(y, o)#, epsilon=1)
246-
# i = iou_coe(y, o)#, epsilon=1)
247-
# sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True))
248-
# print(sess.run([d,hd,i]))
249-
# exit()
256+
inse = tf.reduce_sum(tf.multiply(pre, truth), axis=axis) # AND
257+
union = tf.reduce_sum(tf.cast(tf.add(pre,truth)>= 1, dtype=tf.float32), axis=axis) # OR
258+
# iou = (tf.reduce_sum(inse) + epsilon) / (tf.reduce_sum(union) + epsilon)
259+
# it is incorrect as. if one empty, not inse==0 and union==0, it cannt be 1
260+
# iou = tf.reduce_sum(inse) / (tf.reduce_sum(union) + epsilon)
261+
# iou = inse / (union + epsilon) # bug: if one empty, iou=0 (correct), but if all empty, iou=0 (incorrect, it should be 1)
262+
batch_iou = (inse + epsilon) / (union + epsilon)
263+
iou = tf.reduce_mean(batch_iou)
264+
return iou#, pre, truth, inse, union
250265

251266

252267
def cross_entropy_seq(logits, target_seqs, batch_size=None):#, batch_size=1, num_steps=None):

0 commit comments

Comments
 (0)