图像梯度计算

像素点的梯度计算

引文来自:https://blog.csdn.net/huanhuanq1209/article/details/79845061
Sobel算子依然是一种过滤器,只是其是带有方向的。在OpenCV-Python中,使用Sobel的算子的函数原型如下:

dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])  

参数介绍

  • src:需要处理的图片,一般是由*cv2.imread()*得到的img。
  • 第二个参数是图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度。
  • dx和dy表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。这里我们仅需用到1阶即可。

中括号后面的内容是可选参数,其中:

  • dst输出与src具有相同大小和相同通道数的图像。
  • ksize是Sobel算子的大小,必须为1、3、5、7。
  • scale是缩放导数的比例常数,默认情况下没有伸缩系数;
  • delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
  • borderType是判断图像边界的模式。这个参数默认值cv2.BORDER_DEFAULT。

补充说明
在Sobel函数的第二个参数这里使用了cv2.CV_16S。因为OpenCV文档中对Sobel算子的介绍中有这么一句:“in the case of 8-bit input images it will result in truncated derivatives”。即Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用64位有符号的数据类型,即cv2.CV_64F。
关于图片的位数可参考博客:图像位数的去别
在经过处理后,别忘了用convertScaleAbs()函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。convertScaleAbs()的原型为:

dst = cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])

其中可选参数alpha是伸缩系数,beta是加到结果上的一个值。结果返回uint8类型的图片。
由于Sobel算子是在两个方向计算的,最后还需要用*cv2.addWeighted(…)*函数将其组合起来。其函数原型为:

dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])

其中alpha是第一幅图片中元素的权重,beta是第二个的权重,gamma是加到最后结果上的一个值。
这里是原代码:


#######################################
#create by Ming 2020.1.15
#Sobel test
#to figure out how to deal with Sobel
#######################################
def Sobelmake(img):
    x=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=1)
    y=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=1)

    absX=cv2.convertScaleAbs(x)#convertScaleAbs()该函数可以缩放,取绝对值,转为uint8,这里仅转换格式
    absY=cv2.convertScaleAbs(y)
    dst = cv2.addWeighted(absX,0.5,absY,0.5,0)
    cv2.imshow("raw",img)
    cv2.imshow("absX",absX)
    cv2.imshow("absY",absY)
    cv2.imshow("Result",dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

图像分析:
原图像为:
结果


Cell的梯度计算

在计算完每个像素点的梯度大小和方向后,我们需要将cell中所有像素点的梯度大小和方向做一个直方图统计。

在Hog中,一个图像首先根据需求划分出一个block,可以想象为从一个风景照中,用block框住待识别的人。cell则是更小的单元,一般一个cell的大小为8*8像素,即cell_size=8。之后根据cell中每一个像素的梯度大小和方向,将每个像素的梯度放入统计直方图的bin中。这里读者可能就要问bin是什么了。我用一个例子来说明。首先大家知道一个圆是360度,即全方向为360,而我们将其分为9个不同的统计单元,即分为了9个bin,第一个bin是0~40度,第二个bin是40 ~60度…以此类推。

bin
序号123456789
梯度大小
角度0~4040 ~8080 ~ 120120 ~160160 ~200200~240240~280280~320320~360

你可以将bin想象为上图

例:像素点的梯度为35,角度为45度
很明显,这个点的梯度落在了序号为1,和2的bin之间,那么它的梯度大小35则需要被分散统计到1号bin和2号bin中去。具体如何操作见代码:

#######################################
#create by Ming 2020.1.15
#CaculateGradient 计算梯度
#函数作用:计算每个像素的梯度
#######################################
def caculateGrandient(img,cell_size,bin_size):
    height,width=img.shape
    dst,angle=Sobelmake(img)#获取图片的梯度大小和方向
    print("dst:",dst.shape)
    angle_unit=360/bin_size#360度分为bin_size个直方图的刻度
    absDst=abs(dst)#求梯度长度的绝对值
    print("absDst:",absDst.shape)
    cell_gradient_vector=np.zeros((int(height/cell_size),int(width/cell_size),bin_size),dtype=float)
    print ("初始化的cell_gradient_vector",cell_gradient_vector.shape)
    for i in range(cell_gradient_vector.shape[0]):#  i 遍历height方向上的cell
        for j in range(cell_gradient_vector.shape[1]):#  j 遍历 width方向的cell
            cell_magnitude = absDst[i * cell_size:(i + 1) * cell_size,j * cell_size:(j + 1) * cell_size]#举例:i=0,j=1时,cell_magnitude=absDst[0:8,8:16]
            cell_angle = angle[i * cell_size:(i + 1) * cell_size,j * cell_size:(j + 1) * cell_size]
            #print (cell_angle.max())
            cell_gradient_vector[i][j] = cell_gradient(cell_magnitude, cell_angle,bin_size,angle_unit)
    return cell_gradient_vector

函数返回的 cell_gradient_vector 类型为: ( h e i g h t c e l l _ s i z e , w i d t h c e l l _ s i z e , b i n _ s i z e ) \left( \frac{height}{cell\_size}, \frac{width}{cell\_size} ,bin\_size \right) (cell_sizeheight,cell_sizewidth,bin_size)


重新构造可视化Cell直方图

上述统计结束后,我们将每个cell的梯度直方图绘出,得到原图像的特征图。具体代码如下:

######################################
#create by Ming 2020.2.16
#rebuildImage
#重新构造可视化cell直方图
######################################
def rebuildImage(img,cell_gradient_vector,cell_size):
    cell_gradient=cell_gradient_vector#直方图向量
    height,width=img.shape
    hog_image=np.zeros([height,width])#初始化图片长宽高
    cell_width=cell_size/2#宽度是cell_size的一半
    max_mag=np.array(cell_gradient).max()#max_mag保存向量中最大的那个数
    for x in range(cell_gradient.shape[0]):
        for y in range(cell_gradient.shape[1]):
            cell_grad = cell_gradient[x][y]
            cell_grad /= max_mag    #猜测是归一化操作,/=运算好像只能用在for循环中
            angle = 0 #初始化angle 变量
            angle_gap = 360/bin_size #其实就是angle_unit
            for magnitude in cell_grad:
                angle_radian = math.radians(angle)#将angle从角度转变为弧度
                #以(x,y)为中点,(x1,y1)和(x2,y2)为端点,angle_radian为角度,math.sqrt(magnitude)为颜色。在一个cell中画bin_size条线
                x1 = int(x * cell_size + magnitude * cell_width * math.cos(angle_radian))
                y1 = int(y * cell_size + magnitude * cell_width * math.sin(angle_radian))
                x2 = int(x * cell_size - magnitude * cell_width * math.cos(angle_radian))
                y2 = int(y * cell_size - magnitude * cell_width * math.sin(angle_radian))
                cv2.line(hog_image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
                angle += angle_gap
                ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200116182404858.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjQ1MTk0,size_16,color_FFFFFF,t_70)
    plt.imshow(hog_image, cmap=plt.cm.gray)
    plt.show()

在这里解释一下下面几行代码的作用:

                x1 = int(x * cell_size + magnitude * cell_width * math.cos(angle_radian))
                y1 = int(y * cell_size + magnitude * cell_width * math.sin(angle_radian))
                x2 = int(x * cell_size - magnitude * cell_width * math.cos(angle_radian))
                y2 = int(y * cell_size - magnitude * cell_width * math.sin(angle_radian))
                cv2.line(hog_image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
                angle += angle_gap

易知x,y为cell在横轴方向和纵轴方向的序号,以cell的左上角作为线段中点,为每个bin画一条线段,线段以(x1,y1)和(x2,y2)为端点,该bin的梯度大小为颜色灰度值。

画出的图像为:
特征图

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值