vqdang / hover_net

Simultaneous Nuclear Instance Segmentation and Classification in H&E Histology Images.
MIT License
537 stars 224 forks source link

On forecasting the nucleus and nucleus real center coordinate matching problem #141

Open kiven1213 opened 3 years ago

kiven1213 commented 3 years ago

Hi,@simongraham and @vqdang I recently in the assessing model of time, I found a small problem, is to predict in the nucleus of center coordinates and real label at the center of the nucleus。

111 @ZKJ$%ZN$B4}T~JE~~UOON b54.png)

From the above graph we can see, normally, we get the minimum distance should be 1 4 5 8 9 10 11 12 :, but the program‘s the shortest distance is 1 4 5 8 9 10 12 11 , apparently this is incorrect。

vqdang commented 3 years ago

Hi @kiven1213 , I'm not exactly sure which functionality you are speaking about here. Pairing is only done in metrics code, which is either in PQ https://github.com/vqdang/hover_net/blob/a0f80c7acb9a14964d597d15d7ebd9688a0230cb/metrics/stats_utils.py#L178 or in here https://github.com/vqdang/hover_net/blob/a0f80c7acb9a14964d597d15d7ebd9688a0230cb/metrics/stats_utils.py#L393

PQ pairing is not basing on distance but mask IoU. The latter is basing on the centroid distance. My brief test with the former doesn't indicate any obvious problems.

setA = np.array([
    [0, 0], [1, 1], [2, 2], [3, 3], [4, 4]
])
setB = np.array([
    [0.1, 0.1], [1.1, 1.1], [2.1, 2.1], [3.1, 3.1], [4.2, 4.2]
])
# 4 in setA and 4, 5 in set B should be unpaired
print(pair_coordinates(setA, setB, 0.15))
kiven1213 commented 3 years ago

Hi,@vqdang https://github.com/vqdang/hover_net/blob/a0f80c7acb9a14964d597d15d7ebd9688a0230cb/metrics/stats_utils.py#L417 Is the problem that the function, the function to meet some special value, will cause the wrong matching setA: 0: [241.96872 24.308064] 1: [69.75318 88.25955] 2 :[206.73318 103.90487] 3 : [169.32768 137.7938 ] 4 : [ 51.74554 164.08643] 5 [ 29.06079 190.03941] 6 [184.54932 201.63435] 7 [231.47905 203.78688] setB: 00 [206.54456 6.881188] 01 [241.54782 28.06087] 02 [199.27206 34.45098] 03 [98.0997 65.36254] 04 [72.60095 88.41017] 05 [206.13507 103.97821] 06 [230.151 128.27516] 07 [203.44495 130.74057] 08 [167.69133 138.07654] 09 [ 52.063908 164.16049 ] 10 [ 28.981678 189.89923 ] 11 [240.01443 196.76535] 12 [228.00607 207.59879] radius = 12 You've got to take these two types of center coordinates into
def pair_coordinates(setA, setB, radius): We can get the result: 3G4NP`~6%4D4QO}4GC)82W0 But this is not a correct matching, you can carefully check results will find the right result should be [[0 1],[1 4],[2 5],[3 8],[4 9],[5 10],[7 12]]

vqdang commented 3 years ago

@kiven1213 now i can see. This is peculiar, I think you may want to escalate to the scipy team because thats where the error coming from.

def pair_coordinates(setA, setB, radius):
    # * Euclidean distance as the cost matrix
    pair_distance = scipy.spatial.distance.cdist(setA, setB, metric='euclidean')
    np.set_printoptions(precision=2, linewidth=160, suppress=True)

    # * Munkres pairing with scipy library
    # the algorithm return (row indices, matched column indices)
    # if there is multiple same cost in a row, index of first occurence 
    # is return, thus the unique pairing is ensured

    # artifically inject for early rejection
    pair_distance[pair_distance > radius] = 1.0e4  # artifically inject for early rejection
    indicesA, paired_indicesB = linear_sum_assignment(pair_distance)
    print(indicesA, paired_indicesB)
    # should be [0 1 2 3 4 5 6 7] [ 1  4  5  8  9 10  0 12] (wrong at last index)

    # extract the paired cost and remove instances 
    # outside of designated radius
    pair_cost = pair_distance[indicesA, paired_indicesB]

    pairedA = indicesA[pair_cost <= radius]
    pairedB = paired_indicesB[pair_cost <= radius]

    pairing = np.concatenate([pairedA[:,None], pairedB[:,None]], axis=-1)
    unpairedA = np.delete(np.arange(setA.shape[0]), pairedA)
    unpairedB = np.delete(np.arange(setB.shape[0]), pairedB)
    return pairing, unpairedA, unpairedB

setA = np.array([
    [241.96872, 24.308064],
    [69.75318 ,  88.25955],
    [206.73318, 103.90487],
    [169.32768, 137.7938 ],
    [ 51.74554, 164.08643],
    [ 29.06079, 190.03941],
    [184.54932, 201.63435],
    [231.47905, 203.78688],
])

setB = np.array([
    [206.54456, 6.881188],
    [241.54782, 28.06087],
    [199.27206, 34.45098],
    [98.0997  , 65.36254],
    [72.60095 , 88.41017],
    [206.13507, 103.97821],
    [230.151  , 128.27516],
    [203.44495, 130.74057],
    [167.69133, 138.07654],
    [52.063908, 164.16049],
    [28.981678, 189.89923],
    [240.01443, 196.76535],
    [228.00607, 207.59879],
])
print(pair_coordinates(setA, setB, 12))