nms python代码_角点检测(1) - Moravec算子 - 理论与Python代码

1977 - H.P. Moravec 特征(兴趣点)——在各个方向上灰度变化很大的像素点,利用灰度图像的自相关函数提取角点。

1)计算待检测点在四个方向的灰度变化方差

计算某个像素点沿水平、垂直、对角线、反对角线八个方向的灰度方差

  • 目标像素(c,r)
  • 窗口大小 w*w
  • 活动半径 k=int(w/2)

dcd0a6c8479738369ef5c37143c99c3e.png

2)选取灰度方差最小值为此监测点的响应函数=兴趣值CRF,

兴趣值CRF和阈值比较,判断是否为角点

(阈值选择以候选点包含所需要的角点,而又不包含过多的假角点为原则。 )

#选择方差最小的为当前像素的CRF,所有点的CRF构成keymap
c = min(v1, v2, v3, v4, v5, v6, v7, v8)
keymap[i, j] = c

ece271dc459eb05e7ef982c96e7e3869.png
#thCRF:在对CRF进行筛选时使用的阈值,-1表示自动计算平均值作为阈值
    if thCRF == -1:
        # CRF的平均值作为筛选阈值
        mean_c = np.mean(keymap)
        print ('=>auto threshold for score value:', mean_c)
#thCRF:阈值需要手动设置
    else:
        mean_c = thCRF
        print ('=>threshold for score value:', mean_c)
    keymap = np.where(keymap < mean_c, 0, keymap)

3)兴趣值CRF使用非极大值抑制NMS减少角点

在一定大小的窗口内(可与兴趣值计算之窗口大小不同,如 3×3,5×5,7×7),选择角点响应函数值最大的候选角点为最终角点。

#非极大值抑制
    for i in range(safe_range, img_h - safe_range):
        for j in range(safe_range, img_w - safe_range):
            win, stx, enx, sty, eny = getWindowWithRange(keymap, i, j, nonMax_size)
            nonMax_win, row, col = nonMaximumSupression(win)
            keymap[stx:enx, sty:eny] = nonMax_win
    cv2.imwrite("keymap_nonMax.jpg", keymap)
#把使用阈值筛选过的窗口放入NMS函数中

def nonMaximumSupression(mat, nonMaxValue=0):
    mask = np.zeros(mat.shape, mat.dtype) + nonMaxValue 
    max_value = np.max(mat)
    loc = np.where(mat == max_value)
    row = loc[0]
    col = loc[1]
    mask[row, col] = max_value
    return mask, row, col

缺点:没有旋转不变性

Python代码

# coding=utf-8
import cv2
import numpy as np
from matplotlib import pyplot as plt

def calcV(window1, window2):
    # 用于计算窗口间的差异
    win1 = np.int32(window1)
    win2 = np.int32(window2)
    diff = win1 - win2
    diff = diff * diff
    return np.sum(diff)


def getWindow(img, i, j, win_size):
    # 获得指定范围、大小的窗口内容
    if win_size % 2 == 0:
        win = None
        return win
    half_size = int(win_size / 2)
    start_x = i - half_size
    start_y = j - half_size
    end_x = i + half_size + 1
    end_y = j + half_size + 1
    win = img[start_x:end_x, start_y:end_y]
    return win


def getWindowWithRange(img, i, j, win_size):
    # 获取指定范围、大小的窗口内容以及坐标
    if win_size % 2 == 0:
        win = None
        return win
    half_size = int(win_size / 2)
    start_x = i - half_size
    start_y = j - half_size
    end_x = i + half_size + 1
    end_y = j + half_size + 1
    win = img[start_x:end_x, start_y:end_y]
    return win, start_x, end_x, start_y, end_y


def get8directionWindow(img, i, j, win_size, win_offset):
    # 获取8个方向的不同窗口内容
    half_size = int(win_size / 2)
    win_tl = img[i - win_offset - half_size:i - win_offset + half_size + 1,j - win_offset - half_size:j - win_offset + half_size + 1]
    win_t = img[i - win_offset - half_size:i - win_offset + half_size + 1,j - half_size:j + half_size + 1]
    win_tr = img[i - win_offset - half_size:i - win_offset + half_size + 1,j + win_offset - half_size:j + win_offset + half_size + 1]
    win_l = img[i - half_size:i + half_size + 1,j - win_offset - half_size:j - win_offset + half_size + 1]
    win_r = img[i - half_size:i + half_size + 1,j + win_offset - half_size:j + win_offset + half_size + 1]
    win_bl = img[i + win_offset - half_size:i + win_offset + half_size + 1,j - win_offset - half_size:j - win_offset + half_size + 1]
    win_b = img[i + win_offset - half_size:i + win_offset + half_size + 1,j - half_size:j + half_size + 1]
    win_br = img[i + win_offset - half_size:i + win_offset + half_size + 1,j + win_offset - half_size:j + win_offset + half_size + 1]
    return win_tl, win_t, win_tr, win_l, win_r, win_bl, win_b, win_br


def nonMaximumSupression(mat, nonMaxValue=0):
    mask = np.zeros(mat.shape, mat.dtype) + nonMaxValue
    max_value = np.max(mat)
    loc = np.where(mat == max_value)
    row = loc[0]
    col = loc[1]
    mask[row, col] = max_value
    return mask, row, col


def getScore(item):
    return item[2]


def getKeypoints(keymap, nonMaxValue, nFeature=-1):
    # 用于获取角点的坐标以及对角点进行排序筛选
    loc = np.where(keymap != nonMaxValue)
    xs = loc[1]
    ys = loc[0]
    print (xs.__len__(), 'keypoints were found.')
    kps = []
    for x, y in zip(xs, ys):
        kps.append([x, y, keymap[y, x]])

    if nFeature != -1:
        kps.sort(key=getScore)
        kps = kps[:nFeature]
        print (kps.__len__(), 'keypoints were selected.')
    return kps


def drawKeypoints(img, kps):
    for kp in kps:
        pt = (kp[0], kp[1])
        cv2.circle(img, pt, 3, [0, 0, 255], 1, cv2.LINE_AA)
    return img


def getMoravecKps(img_path, win_size=3, win_offset=1, nonMax_size=5, nonMaxValue=0, nFeature=-1, thCRF=-1):
    """
    将上面的步骤整合为一个函数,方便调用

    :param img_path: 影像的路径
    :param win_size: 滑动窗口的大小
    :param win_offset: 窗口偏移的距离
    :param nonMax_size: 非极大值抑制的滑动窗口大小
    :param nonMaxValue: 非极大值抑制时,非极大值被赋的值
    :param nFeature: 打算提取的角点个数,-1表示自动
    :param thCRF: 在对CRF进行筛选时使用的阈值,-1表示自动计算平均值作为阈值
    :return:
    """
    print ("step 1:read image")
    img_rgb = cv2.imread(img_path)
    img = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
    img_h = img.shape[0]
    img_w = img.shape[1]
    print ("=>image size:", img_h, '*', img_w)

    keymap = np.zeros([img_h, img_w], np.int32)

    print ("step 2:calculate score value using sliding window")
    safe_range = win_offset + win_size
    for i in range(safe_range, img_h - safe_range):
        for j in range(safe_range, img_w - safe_range):
            win = getWindow(img, i, j, win_size)
            win_tl, win_t, win_tr, win_l, win_r, win_bl, win_b, win_br = get8directionWindow(img, i, j, win_size,win_offset)
            v1 = calcV(win, win_tl)
            v2 = calcV(win, win_t)
            v3 = calcV(win, win_tr)
            v4 = calcV(win, win_l)
            v5 = calcV(win, win_r)
            v6 = calcV(win, win_bl)
            v7 = calcV(win, win_b)
            v8 = calcV(win, win_br)
            c = min(v1, v2, v3, v4, v5, v6, v7, v8)
            keymap[i, j] = c

    if thCRF == -1:
        # CRF的平均值作为筛选阈值
        mean_c = np.mean(keymap)
        print ('=>auto threshold for score value:', mean_c)
    else:
        mean_c = thCRF
        print ('=>threshold for score value:', mean_c)

    print ("step 3:filter keypoints using threshold...")
    cv2.imwrite("keymap.jpg", keymap)
    keymap = np.where(keymap < mean_c, 0, keymap)
    cv2.imwrite("keymap_th.jpg", keymap)

    print ("step 4:non maximum supression...")
    for i in range(safe_range, img_h - safe_range):
        for j in range(safe_range, img_w - safe_range):
            win, stx, enx, sty, eny = getWindowWithRange(keymap, i, j, nonMax_size)
            nonMax_win, row, col = nonMaximumSupression(win)
            keymap[stx:enx, sty:eny] = nonMax_win
    cv2.imwrite("keymap_nonMax.jpg", keymap)

    print ("step 5:get keypoint location and draw points.")
    kps = getKeypoints(keymap, nonMaxValue=nonMaxValue, nFeature=nFeature)
    img_kps = drawKeypoints(img_rgb, kps)
    return kps, img_kps


if __name__ == '__main__':
    kps, img = getMoravecKps("img.jpg", nFeature=300)
    cv2.imwrite("moravec.jpg", img)
    cv2.imshow("img", img)
    cv2.waitKey(0)

结果:

f1c9f5391f42663efcefa9104f1551be.png

0b04f4f95851e5a43afcd31beacc6542.png
原图

239a0d2d4ef311c9cfb8fe14dc458c3c.png
keymap

a9a7c0f4d238d3bf32dba579643b1a26.png
kepmap_th

cb8aa78a20f0dd4d3418b3873f11376b.png
moravec

42ecf7fc622213db8ae003b5dc352622.png
keymap_nms
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值