Open AN-AN-369 opened 11 months ago
Of course this is a good idea! But since our alpha is calculated based on the boundary length and size, it may need to be converted to surface area and volume in the 3D dataset, or slice the 3D result to calculate the loss, thanks for using and looking forward to your results!
I have modified the Bou as follows, and it can run successfully in 3D tasks, but I don't know if this change is reasonable or conforms to the theory. Could you please help me have a look? class BoundaryDoULoss(nn.Module): def init(self, n_classes): super(BoundaryDoULoss, self).init() self.n_classes = n_classes
def _one_hot_encoder(self, input_tensor):
tensor_list = []
for i in range(self.n_classes):
temp_prob = input_tensor == i
tensor_list.append(temp_prob.unsqueeze(1))
output_tensor = torch.cat(tensor_list, dim=1)
return output_tensor.float()
def _adaptive_size(self, score, target):
kernel = torch.Tensor([[[[0, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 1, 0], [1, 1, 1], [0, 1, 0]],
[[0, 0, 0], [0, 1, 0], [0, 0, 0]]]])
padding_out = torch.zeros((target.shape[0], target.shape[-3] + 2, target.shape[-2] + 2, target.shape[-1] + 2))
padding_out[:, 1:-1, 1:-1, 1:-1] = target
h, w, d = 3, 3, 3
Y = torch.zeros((padding_out.shape[0], padding_out.shape[1] - d + 1, padding_out.shape[2] - h + 1,
padding_out.shape[3] - w + 1)).cuda()
for i in range(Y.shape[0]):
Y[i, :, :,:] = torch.conv3d(target[i].unsqueeze(0),kernel.unsqueeze(0).cuda(), padding=1)
Y = Y * target
Y[Y == 5] = 0
C = torch.count_nonzero(Y)
S = torch.count_nonzero(target)
smooth = 1e-5
alpha = 1 - (C + smooth) / (S + smooth)
alpha = 2 * alpha - 1
intersect = torch.sum(score * target)
y_sum = torch.sum(target * target)
z_sum = torch.sum(score * score)
alpha = min(alpha, 0.8)
loss = (z_sum + y_sum - 2 * intersect + smooth) / (z_sum + y_sum - (1 + alpha) * intersect + smooth)
return loss
def forward(self, inputs, target):
inputs = torch.softmax(inputs, dim=1)
target = self._one_hot_encoder(target)
assert inputs.size() == target.size(), 'predict {} & target {} shape do not match'.format(inputs.size(),
target.size())
loss = 0.0
for i in range(0, self.n_classes):
loss += self._adaptive_size(inputs[:, i], target[:, i])
return loss / self.n_classes
Hi, in theory this code does have a bit of a problem. Calculating the side length and area of an object on a 2D image looks like this:
So in 3D, based on the kernel you are using, you should modify Y[Y == 5] = 0
to Y[Y == 7] = 0
. Since I am not familiar with 3D convolution, you can refer to the above diagram to modify the code further.
Thanks for the advice.Since Bou doesn't perform as well as dice or cross-entropy plus dice in 3D tasks, I'm wondering if there's something wrong with my application. I'll make changes to the code first and come back if I have any questions
Would it be a better way to use the same way of 2D but in 3D take dimensions 2 at a time and average by 3?
I'm not sure. Just give it a try!
Thank you for your contributions in the article! May I use BounDDoULoss for 3D dataset?"