python实现Moravec角点检测器

1980年Moravec等人提出了Moravec角点检测算法,该算法作为较早的一批角点检测算法。是一种非常适用于简单场景的角点检测算法。

如图为Moravec角点检测算法的原理图:

Moravec角点检测算法以检测的像素点为中心,建立大小为3*3的检测窗口,对不同方向(通常为0°,45°,90°,135°的4个方向)进行平移,对检测窗口中的像素计算灰度方差,选取其中的最小值作为角点响应值,如果该响应值大于所设定的阈值,则认为该点为角点。其角点响应值的计算公式为:

E(x,y)=\sum_{u,v}\omega (u,v)[p(x+u,y+v)-p(x,y)]

式中:(x,y)为检测的像素点,p(x,y)为该点灰度值;\omega(u,v)为检测窗口

Moravec角点检测算法计算量小,能够对二值图像快速检测角点,但检测窗口的大小,窗口的移动方向等变换因素会导致鲁棒性较低,且容易出现伪角点和错点,在实际图像较为复杂的情况下,一个区域中可能会出现多个检测点,因此有必要对检测角点进行进一步检测。

援引一个知名大佬某语言之父的话:别BB,直接给我看代码

import cv2
import numpy as np

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 = 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[int(start_x):int(end_x), int(start_y):int(end_y)]
    return win


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

def get8directionWindow(img, i, j, win_size, win_offset):
    # 获取8个方向的不同窗口内容
    half_size = win_size / 2
    win_tl = img[int(i - win_offset - half_size):int(i - win_offset + half_size + 1),
             int(j - win_offset - half_size):int(j - win_offset + half_size + 1)]
    win_t = img[int(i - win_offset - half_size):int(i - win_offset + half_size + 1),
             int(j - win_offset - half_size):int(j - win_offset + half_size + 1)]
    win_tr = img[int(i - win_offset - half_size):int(i - win_offset + half_size + 1),
             int(j - win_offset - half_size):int(j - win_offset + half_size + 1)]
    win_l = img[int(i - half_size):int(i + half_size + 1),
            int(j - win_offset - half_size):int(j - win_offset + half_size + 1)]
    win_r = img[int(i - half_size):int(i + half_size + 1),
            int(j + win_offset - half_size):int(j + win_offset + half_size + 1)]
    win_bl = img[int(i + win_offset - half_size):int(i + win_offset + half_size + 1),
             int(j - win_offset - half_size):int(j - win_offset + half_size + 1)]
    win_b = img[int(i + win_offset - half_size):int(i + win_offset + half_size + 1),
            int(j - half_size):int(j + half_size + 1)]
    win_br = img[int(i + win_offset - half_size):int(i + win_offset + half_size + 1),
             int(j + win_offset - half_size):int(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表示自动计算平均值作为阈值
    '''
    print('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(img_h, '*', img_w)

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

    print('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('filter keypoints using threshold')
    cv2.imwrite("keymap.jpg", keymap)
    keymap = np.where(keymap < mean_c, 0, keymap)
    cv2.imwrite("keymap_th.jpg", keymap)

    print('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[int(stx):int(enx), int(sty):int(eny)] = nonMax_win
    cv2.imwrite("keymap_nonMax.jpg", keymap)

    print('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, img1 = getMoravecKps("D:/hou/xiaohou.jpg", nFeature=300)
    img = cv2.resize(img1,(500,500))
    cv2.imwrite('xiaohou.jpg', img)
    cv2.imshow('img', img)
    cv2.waitKey(0)
    cv2.destroyWindow()

我用的图片为我一个朋友照片,经过本人授权,显示结果:

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值