Python机器视觉(2)——特征检测

角点检测

       角点检测可以提取点的邻域特征,一般是根据梯度大小和方向判断。Harris自相关检测提供了一种角点检测思路。

       它希望用一个窗函数(可以是中值滤波算子,也可以是高斯算子)来扫描整幅图像,并加一定的偏移量(u,v)计算邻域像素点的差值(平方是避免差值为负数),最后求窗函数所包含的像素点的加权值,然后移动窗函数,继续计算。只要找到了一个极小值点,那么它应该为角点。

       但是,这样扫描会十分耗时,Harris用数学的方法,将上方公式进行泰勒二阶展开:

       整理可得:

       M包含了像素点在x和y方向的导数,并与窗函数求加权值。由于M实对称矩阵的特性,可以对其求特征值\lambda _{1},\lambda _{2},来表达平坦区域、边缘区域和角点:

       为了更好表达,采用角点响应函数

R=det(M)-k*trace(M)^{2}=\lambda _{1}\lambda _{2}-k*(\lambda _{1}+\lambda _{2})^{2}=(I_{x}^{2}I_{y}^{2}-(I_{x}I_{y})^{2})-k*(I_{x}^{2}+I_{y}^{2})^{2}

       R为大数值负数时,是边缘区;R为小数值时,是平坦区;R为大数值正数时,是角点。

       由于R的计算会出现很多不合理的点,需要进行非极大值抑制。

       由于R的值会十分大,需要进行拉伸变换到[0,255]区间。

       效果如下:

       代码如下,后期会补一下注释,滤波器已经在Python机器视觉(1)中解释过。

import cv2
import numpy as np
import math
import matplotlib.pyplot as plt

def Filter(img,direction):
    core_size = 3
    # 零填充
    pad = core_size//2
    if direction == 'gaussian':
        filter = np.zeros((img.shape[0]+2*pad,img.shape[1]+2*pad,3),dtype=np.uint8)
        filter[pad:pad+img.shape[0],pad:pad+img.shape[1],:] = img.astype(np.uint8)
    else:
        filter = np.zeros((img.shape[0]+2*pad,img.shape[1]+2*pad),dtype=np.uint8)
        filter[pad:pad+img.shape[0],pad:pad+img.shape[1]] = img.astype(np.uint8)
    #滤波计算
    tmp = filter.copy()
    if direction == 'x':
        core = np.array([[1,0,-1],[2,0,-2],[1,0,-1]])/9.0
    elif direction == 'y':
        core = np.array([[1,2,1],[0,0,0],[-1,-2,-1]])/9.0
    elif direction == 'gaussian':
        core = np.zeros((3,3))
        kesai = 1
        for dx in range(-1,2):
            for dy in range(-1,2):
                core[dx+1,dy+1] = (1/(2*kesai*math.pi))*math.exp(-((dx**2+dy**2)/(2*kesai**2)))
        core = core/np.sum(core)
    else:
        print('error: please input the right string of direction')
    for y in range(img.shape[0]):
        for x in range(img.shape[1]):
            if direction == 'gaussian':
                for channel in range(3):
                    filter[pad+y,pad+x,channel] = np.sum(tmp[y:y+core_size,x:x+core_size,channel]*core)
            else:
                filter[pad+y,pad+x] = np.sum(tmp[y:y+core_size,x:x+core_size]*core)
    filter = filter[pad:pad+img.shape[0],pad:pad+img.shape[1]].astype(np.uint8)
    return filter

def NMS(img):
    for row in range(2,img.shape[0]-2):
        for col in range(2,img.shape[1]-2):
            for i in range(row-2,row+3):
                for j in range(col-2,col+3):
                    if i!=row and j!=col and img[i,j]<=img[row,col] and img[i,j]>0:
                        img[row,col]=0
    return img

def plt_cornor(img,array):
    plt.imshow(img)
    for row in range(array.shape[0]):
        for col in range(array.shape[1]):
            if array[row,col]>=200:
                plt.plot(col,row,color='r',marker='o',markersize=0.1)
            else:
                continue
    plt.show()


image = cv2.imread("image.png")
img_gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
filterx = Filter(img_gray,'x')
filtery = Filter(img_gray,'y')

M = np.zeros((img_gray.shape[0],img_gray.shape[1],3),dtype=np.float32)
for row in range(img_gray.shape[0]):
    for col in range(img_gray.shape[1]):  
        M[row,col,0] = filterx[row,col]*filtery[row,col]    #Ix*Ix
        M[row,col,1] = filterx[row,col]*filtery[row,col]    #Ix*Iy 
        M[row,col,2] = filtery[row,col]*filtery[row,col]    #Iy*Iy 
gaussian = Filter(M,'gaussian')
k = 0.04
R = np.zeros((gaussian.shape[0],gaussian.shape[1]),dtype=np.float32)
for row in range(gaussian.shape[0]):
    for col in range(gaussian.shape[1]):       
        A = gaussian[row,col,0];  #Ix*Ix            
        C = gaussian[row,col,1];  #Ix*Iy            
        B = gaussian[row,col,2];  #Iy*Iy            
        #响应值计算公式            
        R[row,col] =(A*B-C*C)-(k*(A+B)*(A+B)); 
R = NMS(R)
R = (R/np.max(R))*255
R = R.astype(np.uint8)
print(R)
plt_cornor(image,R)
'''cv2.imshow("filter",R)
cv2.waitKey(0)
cv2.destroyAllWindows()'''



边缘检测

       可以直接用sobel、scharr求图片一阶导数,即为边缘,但准确度低(scharr优于sobel);laplace求二阶导可以检测边缘,且具有旋转不变性的优点,但对噪声更加敏感,降噪方法不好的话,很影响边缘检测效果;canny检测效果很好,但计算量大,实现复杂,非最大值抑制需要检测邻域中沿梯度方向的最大值。 下面主要介绍canny边缘检测。

       首先在x和y方向上进行高斯差分滤波

       计算梯度方向和幅值gradientMagnitude=\sqrt{G_{x}^{2}+G_{y}^{2}},theta=atan2(G_{x},G_{y})

       进行非极大值抑制,

       滞后阈值法进行边缘连接,即定义高低阈值,使用高阈值检测边缘,使用低阈值继续检测边缘,分出强弱边缘,再从强边缘像素开始的“跟随”边缘,如果弱边缘和强边缘连通,则认为弱边缘是实际边缘的一部分。

直线检测

       hough变换即可。本质是笛卡尔系转参数系,将极坐标下表达直线的(角度、距离)作为新坐标系的(x,y)轴,可绘出正弦波,求这些正弦波最多的交点,该交点就是最佳拟合的直线。不过,异常点对检测结果影响很大,可以通过ransac随机抽样检测,但对更高级的圆、椭圆检测,ransac也达不到预期效果。

       hough变换起初对直线的表达是y=mx+b,但将其从笛卡尔系(图像空间)转到参数系(霍夫空间)后,可能会出现无限多的交点(代价太大了),而且无法检测垂直直线的检测。

       所以,用极坐标系表达直线,伪代码如下:

       除此之外,还有一些广义霍夫变换,不过参数更多,难度也更大。

import numpy as np 
import cv2 
from matplotlib import pyplot as plt 

def abline (slope, intercept):
    axes = plt.gca()
    x_vals = np.array (axes.get_xlim())
    y_vals = intercept + slope * x_vals
    plt.plot (x_vals, y_vals, '--')

# 读取图像 
img = cv2.imread('2.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,50,150,apertureSize=3)  

lens = np.ceil(np.sqrt(edges.shape[0]**2 + edges.shape[1]**2))

acc_count = np.zeros((int(2*lens),180),dtype = np.uint64)

for row in range(edges.shape[0]):
    for col in range(edges.shape[1]):
        if edges[row,col] == 255:
            for theta in range(0,180,10):
                d = round(col*np.cos(np.deg2rad(theta))+row*np.sin(np.deg2rad(theta)))+lens
                if isinstance(d,int):
                    acc_count[d,theta] += 1
                else:
                    acc_count[d.astype(int),theta] += 1

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for row in range(acc_count.shape[0]):
    for col in range(acc_count.shape[1]):
        if acc_count[row,col] > 100:
            k = -np.cos(np.deg2rad(col))/np.sin(np.deg2rad(col))
            b = (row-lens)/np.sin(np.deg2rad(col))
            plt.imshow(img)
            abline(k,b)
plt.show()

  • 14
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值