LIVIAETS / boundary-loss

Official code for "Boundary loss for highly unbalanced segmentation", runner-up for best paper award at MIDL 2019. Extended version in MedIA, volume 67, January 2021.
https://doi.org/10.1016/j.media.2020.101851
MIT License
647 stars 97 forks source link

About the calculation of dist_map #51

Closed sd-spf closed 2 years ago

sd-spf commented 2 years ago

According to your suggestion, I read the gt image, but the pixel value of the read image is [0, 255]. When generating the dist_map, the pixel value is [0, 1]. How do I calculate it.

self.dist = dist_map_transform([1, 1], 2)
image = Image.open(self.images[index]).convert('RGB')
# image = np.array(image)
gt = Image.open(self.gts[index]).convert('L')

boundary = self.dist(gt)

Image gt is 24-bit color

sd-spf commented 2 years ago

GT is (1,384,384)

HKervadec commented 2 years ago

Hi,

If I understand correctly, gt ends up having two possible values (0 and 255) whereas dist_map_transform expects a one-hot encoded ground truth (0 or 1 for each class).

You could proceed in two steps: first, remapping the 255 to 1, and then creating the "negative" of the ground truth. Something like this:

gt = np.asarray(Image.open(self.gts[index]).convert('L'), dtype=np.uint8)
assert np.array_equal(np.unique(gt), np.array([0, 255]))
assert gt.shape == (1, 384, 384)

binary_gt = np.copy(gt)[0]
assert binary_gt.shape == (384, 384)
binary_gt[binary_gt == 255] = 1
assert np.array_equal(np.unique(gt), np.array([0, 1]))

oh_gt = np.stack([1 - binary_gt, binary_gt], axis=0)
assert oh_gt.shape == (2, 384, 384)

boundary = self.dist(oh_gt)

Hope that helps,

Hoel

wcyjerry commented 2 years ago

Hi,

If I understand correctly, gt ends up having two possible values (0 and 255) whereas dist_map_transform expects a one-hot encoded ground truth (0 or 1 for each class).

You could proceed in two steps: first, remapping the 255 to 1, and then creating the "negative" of the ground truth. Something like this:

gt = np.asarray(Image.open(self.gts[index]).convert('L'), dtype=np.uint8)
assert np.array_equal(np.unique(gt), np.array([0, 255]))
assert gt.shape == (1, 384, 384)

binary_gt = np.copy(gt)[0]
assert binary_gt.shape == (384, 384)
binary_gt[binary_gt == 255] = 1
assert np.array_equal(np.unique(gt), np.array([0, 1]))

oh_gt = np.stack([1 - binary_gt, binary_gt], axis=0)
assert oh_gt.shape == (2, 384, 384)

boundary = self.dist(oh_gt)

Hope that helps,

Hoel

Is there a little incorrect that now the dist function is already containing a class2onehot function . because I tried ur code it occures a error like: sequence argument must have length equal to input rank

shata-wh commented 2 years ago

I met the same error. So how to process the ignore label(255)? Thank you.

HKervadec commented 2 years ago

I met the same error. So how to process the ignore label(255)?

I personally remap them during pre-processing (I have one example here https://github.com/LIVIAETS/SizeLoss_WSS/blob/master/remap_values.py ), for instance from 255 to 1, and then supervise only class 0 with the idc argument.

While in theory, you don't need to remap, if you did not your code would carry-over for nothing a tensor of size [B,256,W,H], when [B,2,W,H] suffice.

Is there a little incorrect that now the dist function is already containing a class2onehot function .

Hmm, no? https://github.com/LIVIAETS/boundary-loss/blob/master/utils.py#L260

Or are you referring to another distance function/custom transform?

Best,

Hoel