【目標檢測】soft-nms的一個大坑+python變量的賦值+函數傳引用機制

soft-nms代碼如下:

def soft_nms(np.ndarray[float, ndim=2] boxes, float sigma=0.5, float Nt=0.3, float threshold=0.001, unsigned int method=0):
    cdef unsigned int N = boxes.shape[0]
    cdef float iw, ih, box_area
    cdef float ua
    cdef int pos = 0
    cdef float maxscore = 0
    cdef int maxpos = 0
    cdef float x1,x2,y1,y2,tx1,tx2,ty1,ty2,ts,area,weight,ov

    for i in range(N):
        maxscore = boxes[i, 4]
        maxpos = i

        tx1 = boxes[i,0]
        ty1 = boxes[i,1]
        tx2 = boxes[i,2]
        ty2 = boxes[i,3]
        ts = boxes[i,4]

        pos = i + 1
        # get max box
        while pos < N:
            if maxscore < boxes[pos, 4]:
                maxscore = boxes[pos, 4]
                maxpos = pos
            pos = pos + 1

        # add max box as a detection 
        boxes[i,0] = boxes[maxpos,0]
        boxes[i,1] = boxes[maxpos,1]
        boxes[i,2] = boxes[maxpos,2]
        boxes[i,3] = boxes[maxpos,3]
        boxes[i,4] = boxes[maxpos,4]

        # swap ith box with position of max box
        boxes[maxpos,0] = tx1
        boxes[maxpos,1] = ty1
        boxes[maxpos,2] = tx2
        boxes[maxpos,3] = ty2
        boxes[maxpos,4] = ts

        tx1 = boxes[i,0]
        ty1 = boxes[i,1]
        tx2 = boxes[i,2]
        ty2 = boxes[i,3]
        ts = boxes[i,4]

        pos = i + 1
        # NMS iterations, note that N changes if detection boxes fall below threshold
        while pos < N:
            x1 = boxes[pos, 0]
            y1 = boxes[pos, 1]
            x2 = boxes[pos, 2]
            y2 = boxes[pos, 3]
            s = boxes[pos, 4]

            area = (x2 - x1 + 1) * (y2 - y1 + 1)
            iw = (min(tx2, x2) - max(tx1, x1) + 1)
            if iw > 0:
                ih = (min(ty2, y2) - max(ty1, y1) + 1)
                if ih > 0:
                    ua = float((tx2 - tx1 + 1) * (ty2 - ty1 + 1) + area - iw * ih)
                    ov = iw * ih / ua #iou between max box and detection box

                    if method == 1: # linear
                        if ov > Nt: 
                            weight = 1 - ov
                        else:
                            weight = 1
                    elif method == 2: # gaussian
                        weight = np.exp(-(ov * ov)/sigma)
                    else: # original NMS
                        if ov > Nt: 
                            weight = 0
                        else:
                            weight = 1

                    boxes[pos, 4] = weight*boxes[pos, 4]
                                
                    # if box score falls below threshold, discard the box by swapping with last box
                    # update N
                    if boxes[pos, 4] < threshold:
                        boxes[pos,0] = boxes[N-1, 0]
                        boxes[pos,1] = boxes[N-1, 1]
                        boxes[pos,2] = boxes[N-1, 2]
                        boxes[pos,3] = boxes[N-1, 3]
                        boxes[pos,4] = boxes[N-1, 4]
                        N = N - 1
                        pos = pos - 1

            pos = pos + 1

    keep = [i for i in range(N)]
    return keep

調用例子爲:
soft_nms(boxes, Nt=0.5, method=2)

當時很迷惑,爲什麼返回時一個index,直接返回boxes不好嗎?
然後當時想,可能因爲傳入的boxes在函數中會被修改,所以就不需要再return回來了

爲了驗證這個想法,我記錄了soft-nms前與soft-nms後的score值

score_before = boxes[:, 4]
soft_nms(boxes, Nt=0.5, method=2)
score_after = boxes[:, 4]

diff = score_before - score_after

然後看他們的差值diff,如果不爲0的話,說明我的boxes的值確實是發生了變化的。但是發現diff居然一直爲零,然後就在soft-nms上耗了一個下午。。。。

先說結論吧:

  1. 我驗證的方式是錯誤的!!!!
  2. 調用soft-nms的時候,boxes確實是有變化的,所以不需要再return
原理

上面兩個結論用到的原理都一樣,就是:在python中,變量賦值的時候是以引用的方式賦值的,例如:

a = [1]
b = a
print(id(a))
print(id(b))

你會發現ab的id是一樣的!因爲他們就是指向的是同一個地址!如果你改變了ab也會跟着變,例如

a.append(2)
print(a)
print(b)

b雖然沒有append,但是結果也會變爲[1,2]

爲什麼驗證的方式是錯誤

這就是了爲什麼我的驗證方法是錯誤的,因爲他們指向的是同一個地址,所以結果兩個scores當然會相同!Orz

爲什麼不用return boxes呢?

因爲boxes在傳入函數後,不是傳一個值進去,而是傳入了boxes的地址,所以在裏面的修改,會對原boxes地址上的值進行直接修改

當我們在後續再讀取boxes的時候,因爲是讀boxes地址的值,而地址上的值已經被改過了,所以我們讀到的就是修改後的值了,所以函數中不需要再return boxes回來

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章