卷积实现图像边界识别原理

基础知识

首先我们知道图像的矩阵如下图所示:

图像识别边缘遮挡 图像识别边界_图像识别边缘遮挡

其次我们需要了解卷积是怎么样计算的,定义一个卷积核(size较小的矩阵),在图像矩阵上移动,每次移动一个单位,直到把图像矩阵遍历完,如下所示:

图像识别边缘遮挡 图像识别边界_opencv_02

具体是两个矩阵进行点乘,等到的值放进卷积核左上角位置的图像矩阵中,遍历完图像矩阵全部元素,得到一个大小与图像矩阵一样的新矩阵

图像识别边缘遮挡 图像识别边界_opencv_03


图像识别边缘遮挡 图像识别边界_python_04

因此,我们可以用python的numpy库实现线性代数的运算:

np.vdot(a,b)#a与b进行点积运算

a = np.array([[1,2],[3,4]]) 
b = np.array([[11,12],[13,14]]) 
# vdot 将数组展开计算内积
print (np.vdot(a,b))
#结果为130
#1*11 + 2*12 + 3*13 + 4*14 = 130

原理分析

当卷积核为:

[0.25, 0,-0.25],
[0.5, 0 ,-0.5],
[0.25, 0,-0.25]

我们带入图像卷积分析两种情况:

1.到达边界的时候:

图像识别边缘遮挡 图像识别边界_numpy_05

我们可以看见卷积核的右侧在边界上,边界的值不为0,卷积核右侧结果为负而卷积核左侧部分结果为0,因此得到点乘结果小于0。

2.到达图像内部的时候:

我们完全可以由在边界的时候举一反三,当卷积核在内部时,因为卷积核左右互为相反数,则卷积结果为0。

综合:我们可以知道新得到的图像矩阵内部为0,而左边界为负数,右边界为正数,把矩阵转为灰度模式的图像,就可以看见图像的边界了!!!

是不是很神奇,因此不同的卷积核可以得到不同的效果,大家可以自己去思考,例如均匀分布的时候可以让图像变模糊,又称为高斯模糊

所以当卷积核为:

[0.125, 0.125,-0.125],
[0.125,  0  ,-0.125],
[0.125,-0.125,-0.125]

我们观察到元素之和为0,且关于对角线互为相反数;

我们由上面左右互为相反数的卷积核可知,这个是描出上下左右的边界;

代码实验过程

因为对numpy和python都不太熟悉,代码可能不太优雅,多包涵。

只是做实验,我就只定义了9×9的数组矩阵,进行遍历

思路:

1.定义一个beginarr矩阵作为初始图像,定义endarr数组矩阵为结果图像
2.定义卷积核数组矩阵
3.遍历begin数组矩阵,得到卷积核每次移动时下方的矩阵数组,让这个数组与卷积核数组进行点乘操作,并赋值给endarr数组矩阵
4.输出endarr矩阵到img,并显示

代码如下:

import numpy as np
from matplotlib import pyplot as plt
# img1 = np.zeros((9,9), np.uint8)
# arr=np.array(img1[:,::-1])
# print(arr)

#arrbase为卷积核
arrbase=np.array([
    [0.125,0.125,-0.125],
    [0.125,0,-0.125],
    [0.125,-0.125,-0.125]
    ])
#为什么说定义9×9,却是13×13的矩阵,因为遍历的时候,在矩阵边界会出现,元素缺少,不满足3×3的shape,因此我进行了扩展,放在numpy触发广播机制
arrbegin=np.array([
    [1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,10,10,10,10,10,1,1,1],
    [1,1,1,10,10,10,10,10,1,1,1],
    [1,1,1,10,10,10,10,10,1,1,1],
    [1,1,1,10,10,10,10,10,1,1,1],
    [1,1,1,10,10,10,10,10,1,1,1],
    [1,1,1,10,10,10,10,10,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1]
 ])
img1=arrbegin
arrend=np.array([
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0]
])

 #卷积核通过输入的每块区域,得到卷积的输出
for r in range(0, 9):
    for c in range(0, 9):
        a=arrbegin[r:r+3:1,c:c+3:1]
        arrend[r][c]=np.vdot(a,arrbase)
print(arrend)

img2=arrend

plt.subplot(121),plt.imshow(img1,cmap = 'Greys')
plt.title('begin'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img2,cmap = 'Greys')
plt.title('end'), plt.xticks([]), plt.yticks([])
plt.show()

因此我们得到结果:

图像识别边缘遮挡 图像识别边界_opencv_06

由于设置的阶数较小,边界有点粗了,但还是可以看出来确实边界被突出处理了
因此,我们得到了我们之前分析的结果,所以卷积确实可以实现图像边界的处理

代码完善:

'''
@作者:小谢191
出发点:体验卷积描边算法的原理
功能:解决了切片大小不一,可以传入任意大小图片
'''
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
#读取图像
img = cv.imread('qq.jpg',0)
#设置卷积核--可以自己调整元素大小
base=np.array([[0.125, 0.125, -0.125],
       [0.125, 0., -0.125],
       [0.125, -0.125, -0.125]])
#设置填充结果的矩阵
imgend=np.zeros((img.shape[0],img.shape[1]))
#进行卷积运算
for n in range(0,img.shape[0]):
    for m in range(0,img.shape[1]):
        #分块
        t=img[n:n+3,m:m+3]
        #扩展
        t=np.tile(t,(3,3))
        #再分块
        t=t[0:3,0:3]
        #卷积--两矩阵进行点乘
        imgend[n,m]=np.vdot(t,base)
#opencv显示imgend

#plt显示先后对比
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('begin'), plt.xticks([]), plt.yticks([])
#显示用tab10更明显一点,有喜欢的色调可以自己调
plt.subplot(122),plt.imshow(imgend,cmap = 'tab10')
plt.title('end'), plt.xticks([]), plt.yticks([])
plt.show()

opencv的边界处理函数

由于我们自己写的算法速度什么都不如别人,所以真正处理图像还是用别人的比较好

边界处理代码

#opencv的canny函数实现描边
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('bk.jpg',0)#图片换成这个本机图片地址即可bk.jpg
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

结果如下:

图像识别边缘遮挡 图像识别边界_卷积核_07


好吧,确实别人的算法比较好,这就是卷积对边界处理的原理分析,如有不对请指出,多多包涵!!!