- 抗扭曲函数deskew
- 利用opencv中svm算法学习图片和识别图片
- 抽取特征向量函数hot分析
车牌识别中涉及字符的识别,识别方法可以用opencv自带的机器学习算法svm(支持向量机)来实现,参见https://docs.opencv.org/3.1.0/dd/d3b/tutorial_py_svm_opencv.html
训练数据来自网上,都是20*20像素。数字和大写字母图片保存在train\chars2目录下,一万三千多张图片。
抗扭曲函数deskew
训练数据中有些图像是扭曲的,需要做抗扭曲处理,也就是把歪了的图片摆正
以下函数是在网上搜来的
# 使用方向梯度直方图Histogram of Oriented Gradients (HOG)作为特征向量
def deskew(img): #对一个图像进行抗扭斜(deskew)处理,把歪了的图片摆正
m = cv2.moments(img) # 计算图像中的中心矩(最高到三阶)
if abs(m['mu02']) < 1e-2:
return img.copy()
skew = m['mu11']/m['mu02']
M = np.float32([[1, skew, -0.5*SZ*skew], [0, 1, 0]])
# 图像的平移,参数:输入图像、变换矩阵、变换后的大小
img = cv2.warpAffine(img, M, (SZ, SZ), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)
return img
测试一下
原图
抗扭曲处理后
cv2.imwrite('0-deskew.jpg', deskew(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)))
图像摆正了。
利用opencv中svm算法学习图片和识别图片
下面就该开始学习图像中的字符识别了。
关于svm可以参见
https://zhuanlan.zhihu.com/p/32188239
https://zhuanlan.zhihu.com/p/32305140
https://zhuanlan.zhihu.com/p/32319251
https://zhuanlan.zhihu.com/p/32372743
https://zhuanlan.zhihu.com/p/32390371
其实svm的核函数,训练,预测都可以自己写代码来实现,不过这里都是用opencv自带的方法。
具体实现如下,说明在代码注释里基本都有。
主要函数包含:
-
train_svm,训练识别图片中字符。
- 初始化SVM实例,设置属性。这是opencv自带的
- 依次读取train\chars2目录中训练用图片,转化为灰度图,保存到训练数据集合。
- 保存分类标签到对应的标签集合。每个字符图片保存在以该字符名称对应的目录中,所以把目录名称对应的ASCII码作为训练数据的分类。
- 调用deskew函数,对训练图片(灰度图)进行抗扭曲处理,摆正
- 调用hot函数,获得每张图片的特征向量(64个)
- 调用SVM实例的train方法,送入训练数据集合和标签集合进行机器学习。
- 学习结果(支持向量机)保存在svm.dat文件中,用于识别图片(车牌)时候使用。识别是利用svm的predict方法,也是opencv中自带的。
-
hot,用于从图片中抽取特征向量(64个)。
- 计算图像 X 方向和 Y 方向的 Sobel 导数
- 计算得到每个像素的梯度角度angle和梯度大小magnitude
- 把这个梯度的角度转换成 0至16 之间的整数
- 将图像分为4个小的方块,对每一个小方块计算它们梯度角度的直方图histogram(16个 bin),使用梯度的大小做权重。每一个小方块都会得到一个含有16个值的向量,4 个小方块的4个向量就组成了这个图像的特征向量(包含64个值)。
关于直方图,可以参见https://blog.csdn.net/on2way/article/details/46881599。
- 直方图就是对图像中的像素点的值进行统计,得到一个统一的整体的灰度概念。
- 一般情况下直方图都是灰度图像,直方图x轴是灰度值(一般0~255),y轴就是图像中每一个灰度级对应的像素点的个数。
- 直方图的好处就在于可以清晰了解图像的整体灰度分布,这对于后面依据直方图处理图像来说至关重要。
- 直方图计算实现可以使用np.histogram和np.bincount()函数。bincount计算的速度更快。
至于为什么要利用梯度直方图来获取图像特征值,可以参见https://www.leiphone.com/news/201708/ZKsGd2JRKr766wEd.html
SZ = 20 #训练图片长宽
class StatModel(object):
def load(self, fn):
self.model = self.model.load(fn)
def save(self, fn):
self.model.save(fn)
#利用OpenCV中的SVM进行机器学习
class SVM(StatModel):
def __init__(self, C = 1, gamma = 0.5):
self.model = cv2.ml.SVM_create() #创建SVM model
#属性设置
self.model.setGamma(gamma)
self.model.setC(C)
self.model.setKernel(cv2.ml.SVM_RBF) #径向基核函数((Radial Basis Function),比较好的选择,gamma>0;
self.model.setType(cv2.ml.SVM_C_SVC)
#训练svm
def train(self, samples, responses): #SVM的训练函数
self.model.train(samples, cv2.ml.ROW_SAMPLE, responses)
#字符识别
def predict(self, samples):
r = self.model.predict(samples)
return r[1].ravel()
#来自opencv的sample,用于svm训练
#获得数据的特征向量
def hog(digits):
samples = []
'''
step1.先计算图像 X 方向和 Y 方向的 Sobel 导数。
step2.然后计算得到每个像素的梯度角度angle和梯度大小magnitude。
step3.把这个梯度的角度转换成 0至16 之间的整数。
step4.将图像分为 4 个小的方块,对每一个小方块计算它们梯度角度的直方图(16 个 bin),使用梯度的大小做权重。
这样每一个小方块都会得到一个含有 16 个值的向量。
4 个小方块的 4 个向量就组成了这个图像的特征向量(包含 64 个值)。
这就是我们要训练数据的特征向量。
'''
for img in digits:
#plt.subplot(221)
#plt.imshow(img,'gray')
# step1.计算图像的 X 方向和 Y 方向的 Sobel 导数
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
mag, ang = cv2.cartToPolar(gx, gy) # step2.笛卡尔坐标(直角/斜角坐标)转换为极坐标, → magnitude, angle
bin_n = 16
bin = np.int32(bin_n*ang/(2*np.pi)) #step3. quantizing binvalues in (0...16)。2π就是360度。
#step4. Divide to 4 sub-squares
bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:]
mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:]
#zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
#a = [1,2,3];b = [4,5,6];zipped = zip(a,b) 结果[(1, 4), (2, 5), (3, 6)]
hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
hist = np.hstack(hists) # hist is a 64 bit vector
#plt.subplot(223)
#plt.plot(hist)
# transform to Hellinger kernel
eps = 1e-7
hist /= hist.sum() + eps
hist = np.sqrt(hist)
hist /= norm(hist) + eps
#plt.subplot(224)
#plt.plot(hist)
#plt.show()
samples.append(hist)
return np.float32(samples)
def train_svm():
#识别英文字母和数字
model = SVM(C=1, gamma=0.5)
if os.path.exists("svm.dat"):
model.load("svm.dat") #如果存在,不再训练,直接导入训练好的结果
else:
chars_train = []
chars_label = []
start_time = datetime.datetime.now()
for root, dirs, files in os.walk("train\\chars2"):
"""
root:所指的是当前正在遍历的这个文件夹的本身的地址
dirs:是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录)
files:同样是 list , 内容是该文件夹中所有的文件(不包括子目录)
train\chars2目录下保存有数字和大写字母图片,用于训练
"""
#os.path.basename(),返回path最后的文件名
#例:root=train\chars2\7,那么os.path.basename(root)=7
if len(os.path.basename(root)) > 1:#目录是单个字母或者数字
continue
root_int = ord(os.path.basename(root)) #转化为ASCII字符
for filename in files:
filepath = os.path.join(root,filename)
digit_img = cv2.imread(filepath)
#https://www.aiuai.cn/aifarm365.html
#把图片转化为灰度图
#print 'filename: '+filename
digit_img = cv2.cvtColor(digit_img, cv2.COLOR_BGR2GRAY)
#print digit_img.shape #打印测试一下,可以看到是单通道的灰度图。(20L, 20L)
#采用PIL库可视化显示一下灰度图
#img_pil = Image.fromarray(digit_img); #Image.fromarray实现array到image的转换
#img_pil.show()
#print '***************************************\n'
#print digit_img #打印一下转化的灰度图矩阵
chars_train.append(digit_img) #训练样本集合
#chars_label.append(1)
chars_label.append(root_int) #训练样本标签,这里用字符的ASCII表示
end_time = datetime.datetime.now()
print '----------'
print start_time,end_time,(end_time - start_time).seconds #116秒
#map() 会根据提供的函数对指定序列做映射。
#第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
#把灰度图的训练样本集合中每个元素逐一送入deskew函数进行抗扭斜处理--也就是把图片摆正
chars_train = list(map(deskew, chars_train))
chars_train = hog(chars_train)#获得特征向量
#print '---chars_label---'
#print chars_label
#chars_train = chars_train.reshape(-1, 20, 20).astype(np.float32)
chars_label = np.array(chars_label)
print(chars_train.shape) #(13161L, 64L),13161个图片文件-训练数据,每个数据有64个特征值
print chars_train[1,:] #打印第一行,看一下数据
'''
[0.24786814 0.1749242 0.08778299 0.00753186 0.01336179 0.00632773
0.00311627 0.19113976 0.17932086 0.10021677 0.16336742 0.00659011
0.04025392 0.00959834 0.13348056 0.14601281 0.22560228 0.11686099
0.0091089 0.04411138 0.11550057 0.10916684 0.07274474 0.25117615
0.15250775 0.00763328 0. 0. 0.03396014 0.04497053
0.13273223 0.2175272 0.16252412 0. 0. 0.
0. 0.07764773 0.18924566 0.23582923 0.19936399 0.00881415
0. 0.03948144 0.10984423 0.08513948 0.11275985 0.22381228
0.25372562 0.07138055 0.11284041 0.11512272 0.01229106 0.
0.00824488 0.17906618 0.31182316 0.04935603 0.03621576 0.
0. 0. 0. 0.16095926]
'''
print (chars_label.shape) #一维矩阵,长度13161L。每个训练数据的标签(分类)
model.train(chars_train, chars_label) #SVM训练,opencv自带
if not os.path.exists("svm.dat"):
model.save("svm.dat")
#测试一下训练结果
img = cv2.imread('test-0.jpg')
img = cv2.resize(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), (SZ, SZ), interpolation=cv2.INTER_AREA)
#print img
resp = model.predict(hog([img]))
charactor = chr(resp[0])
print '--result---'+charactor #--result---0
代码中使用的测试图片如下
结果正确。
抽取特征向量函数hot分析
尝试把测试数据test-0.jpg在hot中中间变量逐一打印出来,可以理解转化和抽取特征变量过程
1.把梯度的角度转化为0至16之间的整数
bin = np.int32(bin_n*ang/(2 *np.pi))
print bin,打印结果,20x20。因为图片是20x20像素的。
[[ 0 0 8 8 0 0 0 0 8 8 0 0 8 8 8 8 8 0 0 0]
[ 0 0 6 6 1 0 0 0 9 11 12 12 14 6 6 6 8 0 0 0]
[ 0 15 12 8 14 0 0 0 9 10 12 13 14 7 6 7 7 0 0 0]
[ 4 0 12 11 12 0 0 8 9 9 12 15 15 9 7 7 9 0 0 0]
[12 8 1 3 5 15 15 8 8 9 9 15 0 0 8 8 9 0 0 0]
[12 9 0 0 4 0 15 8 7 7 1 0 0 15 8 8 6 0 0 0]
[ 0 1 1 11 14 0 0 7 8 8 14 15 15 15 8 8 8 0 0 0]
[12 14 9 7 14 15 0 7 8 8 0 15 15 0 7 8 9 0 0 0]
[ 0 14 12 14 4 0 0 7 7 7 12 15 0 15 8 7 6 0 0 0]
[ 0 0 0 0 0 0 0 8 7 7 4 0 15 15 7 7 8 0 0 0]
[ 0 0 0 0 0 0 0 8 8 8 6 15 15 0 7 8 8 0 0 0]
[ 0 0 0 0 0 0 0 7 7 8 10 0 0 0 8 8 6 0 0 0]
[ 0 0 0 0 0 0 0 7 8 8 4 0 15 15 7 8 8 0 0 0]
[ 0 0 0 0 0 0 0 8 8 8 0 0 15 15 7 7 9 0 0 0]
[ 0 0 0 0 0 0 0 7 7 8 0 0 0 1 8 8 0 0 0 0]
[ 4 3 4 1 0 15 15 7 7 6 0 0 0 6 8 8 0 0 0 0]
[ 4 1 4 8 0 15 15 7 6 6 2 1 0 9 9 8 6 0 0 0]
[ 4 2 7 4 2 15 15 15 6 5 2 2 1 9 9 9 8 0 0 0]
[12 15 8 0 14 15 14 14 5 4 3 1 9 8 8 6 8 0 0 0]
[ 0 0 8 0 0 0 0 0 0 8 0 0 8 8 8 8 8 0 0 0]]
2.将图像分为 4 个小的方块
bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:],经过转化的角度
说明:获得矩阵中的子矩阵,[行开始:行结束,列开始:列结束]
print bin_cells,打印结果
(array([[ 0, 0, 8, 8, 0, 0, 0, 0, 8, 8],
[ 0, 0, 6, 6, 1, 0, 0, 0, 9, 11],
[ 0, 15, 12, 8, 14, 0, 0, 0, 9, 10],
[ 4, 0, 12, 11, 12, 0, 0, 8, 9, 9],
[12, 8, 1, 3, 5, 15, 15, 8, 8, 9],
[12, 9, 0, 0, 4, 0, 15, 8, 7, 7],
[ 0, 1, 1, 11, 14, 0, 0, 7, 8, 8],
[12, 14, 9, 7, 14, 15, 0, 7, 8, 8],
[ 0, 14, 12, 14, 4, 0, 0, 7, 7, 7],
[ 0, 0, 0, 0, 0, 0, 0, 8, 7, 7]]),
array([[ 0, 0, 0, 0, 0, 0, 0, 8, 8, 8],
[ 0, 0, 0, 0, 0, 0, 0, 7, 7, 8],
[ 0, 0, 0, 0, 0, 0, 0, 7, 8, 8],
[ 0, 0, 0, 0, 0, 0, 0, 8, 8, 8],
[ 0, 0, 0, 0, 0, 0, 0, 7, 7, 8],
[ 4, 3, 4, 1, 0, 15, 15, 7, 7, 6],
[ 4, 1, 4, 8, 0, 15, 15, 7, 6, 6],
[ 4, 2, 7, 4, 2, 15, 15, 15, 6, 5],
[12, 15, 8, 0, 14, 15, 14, 14, 5, 4],
[ 0, 0, 8, 0, 0, 0, 0, 0, 0, 8]]),
array([[ 0, 0, 8, 8, 8, 8, 8, 0, 0, 0],
[12, 12, 14, 6, 6, 6, 8, 0, 0, 0],
[12, 13, 14, 7, 6, 7, 7, 0, 0, 0],
[12, 15, 15, 9, 7, 7, 9, 0, 0, 0],
[ 9, 15, 0, 0, 8, 8, 9, 0, 0, 0],
[ 1, 0, 0, 15, 8, 8, 6, 0, 0, 0],
[14, 15, 15, 15, 8, 8, 8, 0, 0, 0],
[ 0, 15, 15, 0, 7, 8, 9, 0, 0, 0],
[12, 15, 0, 15, 8, 7, 6, 0, 0, 0],
[ 4, 0, 15, 15, 7, 7, 8, 0, 0, 0]]),
array([[ 6, 15, 15, 0, 7, 8, 8, 0, 0, 0],
[10, 0, 0, 0, 8, 8, 6, 0, 0, 0],
[ 4, 0, 15, 15, 7, 8, 8, 0, 0, 0],
[ 0, 0, 15, 15, 7, 7, 9, 0, 0, 0],
[ 0, 0, 0, 1, 8, 8, 0, 0, 0, 0],
[ 0, 0, 0, 6, 8, 8, 0, 0, 0, 0],
[ 2, 1, 0, 9, 9, 8, 6, 0, 0, 0],
[ 2, 2, 1, 9, 9, 9, 8, 0, 0, 0],
[ 3, 1, 9, 8, 8, 6, 8, 0, 0, 0],
[ 0, 0, 8, 8, 8, 8, 8, 0, 0, 0]]))
mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:],梯度大小。
print mag_cells,打印结果
(array([[ 0. , 6. , 2. , 2. ,
2. , 274. , 1016. , 736. ,
16. , 10. ],
[ 0. , 7.071068 , 2.828427 , 2.828427 ,
4.2426405, 433.24356 , 1052.821 , 618.5208 ,
344.07266 , 742.5416 ],
[ 0. , 6.3245554, 2. , 2. ,
2.828427 , 674.87036 , 1042.5986 , 240.07498 ,
865.9157 , 1036.2866 ],
[ 4. , 0. , 5.0990195, 5.0990195,
4. , 909.1754 , 1038.5615 , 371.6154 ,
1108.2562 , 611.3673 ],
[ 2. , 3.1622777, 2.828427 , 5.0990195,
4.472136 , 1017.00055 , 1011.00446 , 846.3569 ,
1042.3099 , 226.45972 ],
[ 2. , 1.4142135, 4. , 2. ,
3.1622777, 1012.0079 , 1008.002 , 962.00214 ,
1013.0044 , 44.04543 ],
[ 0. , 2.828427 , 1.4142135, 5.0990195,
7.071068 , 1013.0005 , 1004.008 , 961.00055 ,
1012.00195 , 47.09565 ],
[ 2. , 4.472136 , 1.4142135, 3.1622777,
4.472136 , 1017.0123 , 1012.0079 , 969.02527 ,
1016.002 , 43.011627 ],
[ 0. , 2.828427 , 4. , 1.4142135,
2. , 1017.0005 , 1020. , 971.00464 ,
1019.02405 , 43.104523 ],
[ 0. , 0. , 0. , 4. ,
0. , 1016. , 1020. , 968. ,
1015.00446 , 50.159744 ]], dtype=float32), array([[ 0. , 0. , 0. , 4. ,
0. , 1016. , 1020. , 972.00824 ,
1011.0243 , 48.04165 ],
[ 0. , 0. , 0. , 4. ,
0. , 1016. , 1020. , 974.0021 ,
1013.0005 , 45.099888 ],
[ 0. , 0. , 0. , 4. ,
0. , 1016. , 1020. , 971.0005 ,
1018.00006 , 44. ],
[ 0. , 0. , 0. , 4. ,
0. , 1016. , 1020. , 970. ,
1020. , 43.011627 ],
[ 0. , 0. , 0. , 4. ,
0. , 1016. , 1020. , 968.0021 ,
1020.0079 , 49.0102 ],
[ 2. , 3.1622777, 4. , 2.828427 ,
0. , 1014.00195 , 1020.0079 , 838.7729 ,
1055.6439 , 233.65787 ],
[ 2. , 2.828427 , 2. , 2. ,
2. , 905.8267 , 1041.2684 , 360.51352 ,
1108.1967 , 608.69037 ],
[ 4. , 4.472136 , 3.1622777, 2. ,
7.2111025, 676.5042 , 1038.2524 , 230.21729 ,
850.73267 , 1004.6502 ],
[ 2. , 3.1622777, 6. , 4. ,
2.828427 , 432.66617 , 977.7372 , 710.6757 ,
161.22655 , 678.56323 ],
[ 0. , 4. , 6. , 4. ,
0. , 268. , 766. , 744. ,
240. , 4. ]], dtype=float32), array([[ 14.000001 , 16. , 222. , 1018.00006 ,
790. , 2. , 4. , 0. ,
0. , 0. ],
[ 880.786 , 642.162 , 101.82338 , 937.183 ,
1002.7203 , 231.93533 , 4. , 0. ,
0. , 0. ],
[ 992.5684 , 1045.5859 , 724.50946 , 434.01382 ,
1099.298 , 588.6561 , 5.0990195, 0. ,
0. , 0. ],
[ 147.38385 , 833.09186 , 955.1178 , 11.401754 ,
1014.6576 , 819.5267 , 4.472136 , 0. ,
0. , 0. ],
[ 1.4142135, 761.0007 , 1012.00195 , 106.16968 ,
1016. , 871.0052 , 1.4142135, 0. ,
0. , 0. ],
[ 5.8309517, 758.0238 , 1013.0005 , 102.176315 ,
1017.01227 , 868. , 1.4142135, 0. ,
0. , 0. ],
[ 8.944272 , 759.006 , 1008.002 , 100.02 ,
1014.00195 , 865.00055 , 2. , 0. ,
0. , 0. ],
[ 5.0990195, 758.01056 , 1013.0005 , 101.12369 ,
1017.0044 , 865.00055 , 1.4142135, 0. ,
0. , 0. ],
[ 3.1622777, 759.0007 , 1016. , 99.12618 ,
1016.00793 , 864.0023 , 2.828427 , 0. ,
0. , 0. ],
[ 2. , 762.0026 , 1011.0005 , 99.00505 ,
1012.00195 , 863.00055 , 4. , 0. ,
0. , 0. ]], dtype=float32), array([[ 1.4142135, 758.01056 , 1010.0179 , 106. ,
1015.00055 , 864.0023 , 3.1622777, 0. ,
0. , 0. ],
[ 5.8309517, 760. , 1016.0492 , 99.04544 ,
1016.0177 , 860. , 4.472136 , 0. ,
0. , 0. ],
[ 6. , 760.0105 , 1018.00793 , 93.00538 ,
1015.00446 , 858. , 5.0990195, 0. ,
0. , 0. ],
[ 7.071068 , 751.0007 , 1009.04016 , 109.11462 ,
1014.03156 , 867.0052 , 2.828427 , 0. ,
0. , 0. ],
[ 18.110771 , 814.2789 , 1002.20764 , 83.4386 ,
1012.00195 , 873.00055 , 0. , 0. ,
0. , 0. ],
[ 34.928497 , 939.2444 , 991.3728 , 94.75231 ,
1016.00195 , 869.0052 , 0. , 0. ,
0. , 0. ],
[ 475.1105 , 1099.9972 , 779.4318 , 413.03754 ,
1105.0901 , 683.6987 , 2.828427 , 0. ,
0. , 0. ],
[1108.4584 , 978.57446 , 193.74725 , 910.949 ,
1057.463 , 306.88434 , 4. , 0. ,
0. , 0. ],
[ 653.65436 , 268.71545 , 469.57428 , 1097.6603 ,
628.05414 , 1.4142135, 4. , 0. ,
0. , 0. ],
[ 12. , 0. , 626. , 1014. ,
388. , 2. , 4. , 0. ,
0. , 0. ]], dtype=float32))
3.每一个小方块计算它们梯度角度的直方图(16 个 bin),使用梯度的大小做权重。
For each sub-square, calculate the histogram of direction (16 bins) weighted with their magnitude.
每个小方块都会得到一个包含16个值的向量,四个这样的矢量(四个小方块)一起给出了包含64个值的特征向量
hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
先打印一下for b, m in zip(bin_cells, mag_cells)中的b和m
其实就是把4个小方块的梯度角度和梯度大小一一对应起来
[[ 0 0 8 8 0 0 0 0 8 8]
[ 0 0 6 6 1 0 0 0 9 11]
[ 0 15 12 8 14 0 0 0 9 10]
[ 4 0 12 11 12 0 0 8 9 9]
[12 8 1 3 5 15 15 8 8 9]
[12 9 0 0 4 0 15 8 7 7]
[ 0 1 1 11 14 0 0 7 8 8]
[12 14 9 7 14 15 0 7 8 8]
[ 0 14 12 14 4 0 0 7 7 7]
[ 0 0 0 0 0 0 0 8 7 7]]
[[ 0. 6. 2. 2. 2.
274. 1016. 736. 16. 10. ]
[ 0. 7.071068 2.828427 2.828427 4.2426405
433.24356 1052.821 618.5208 344.07266 742.5416 ]
[ 0. 6.3245554 2. 2. 2.828427
674.87036 1042.5986 240.07498 865.9157 1036.2866 ]
[ 4. 0. 5.0990195 5.0990195 4.
909.1754 1038.5615 371.6154 1108.2562 611.3673 ]
[ 2. 3.1622777 2.828427 5.0990195 4.472136
1017.00055 1011.00446 846.3569 1042.3099 226.45972 ]
[ 2. 1.4142135 4. 2. 3.1622777
1012.0079 1008.002 962.00214 1013.0044 44.04543 ]
[ 0. 2.828427 1.4142135 5.0990195 7.071068
1013.0005 1004.008 961.00055 1012.00195 47.09565 ]
[ 2. 4.472136 1.4142135 3.1622777 4.472136
1017.0123 1012.0079 969.02527 1016.002 43.011627 ]
[ 0. 2.828427 4. 1.4142135 2.
1017.0005 1020. 971.00464 1019.02405 43.104523 ]
[ 0. 0. 0. 4. 0.
1016. 1020. 968. 1015.00446 50.159744 ]]
[[ 0 0 0 0 0 0 0 8 8 8]
[ 0 0 0 0 0 0 0 7 7 8]
[ 0 0 0 0 0 0 0 7 8 8]
[ 0 0 0 0 0 0 0 8 8 8]
[ 0 0 0 0 0 0 0 7 7 8]
[ 4 3 4 1 0 15 15 7 7 6]
[ 4 1 4 8 0 15 15 7 6 6]
[ 4 2 7 4 2 15 15 15 6 5]
[12 15 8 0 14 15 14 14 5 4]
[ 0 0 8 0 0 0 0 0 0 8]]
[[ 0. 0. 0. 4. 0.
1016. 1020. 972.00824 1011.0243 48.04165 ]
[ 0. 0. 0. 4. 0.
1016. 1020. 974.0021 1013.0005 45.099888 ]
[ 0. 0. 0. 4. 0.
1016. 1020. 971.0005 1018.00006 44. ]
[ 0. 0. 0. 4. 0.
1016. 1020. 970. 1020. 43.011627 ]
[ 0. 0. 0. 4. 0.
1016. 1020. 968.0021 1020.0079 49.0102 ]
[ 2. 3.1622777 4. 2.828427 0.
1014.00195 1020.0079 838.7729 1055.6439 233.65787 ]
[ 2. 2.828427 2. 2. 2.
905.8267 1041.2684 360.51352 1108.1967 608.69037 ]
[ 4. 4.472136 3.1622777 2. 7.2111025
676.5042 1038.2524 230.21729 850.73267 1004.6502 ]
[ 2. 3.1622777 6. 4. 2.828427
432.66617 977.7372 710.6757 161.22655 678.56323 ]
[ 0. 4. 6. 4. 0.
268. 766. 744. 240. 4. ]]
[[ 0 0 8 8 8 8 8 0 0 0]
[12 12 14 6 6 6 8 0 0 0]
[12 13 14 7 6 7 7 0 0 0]
[12 15 15 9 7 7 9 0 0 0]
[ 9 15 0 0 8 8 9 0 0 0]
[ 1 0 0 15 8 8 6 0 0 0]
[14 15 15 15 8 8 8 0 0 0]
[ 0 15 15 0 7 8 9 0 0 0]
[12 15 0 15 8 7 6 0 0 0]
[ 4 0 15 15 7 7 8 0 0 0]]
[[ 14.000001 16. 222. 1018.00006 790.
2. 4. 0. 0. 0. ]
[ 880.786 642.162 101.82338 937.183 1002.7203
231.93533 4. 0. 0. 0. ]
[ 992.5684 1045.5859 724.50946 434.01382 1099.298
588.6561 5.0990195 0. 0. 0. ]
[ 147.38385 833.09186 955.1178 11.401754 1014.6576
819.5267 4.472136 0. 0. 0. ]
[ 1.4142135 761.0007 1012.00195 106.16968 1016.
871.0052 1.4142135 0. 0. 0. ]
[ 5.8309517 758.0238 1013.0005 102.176315 1017.01227
868. 1.4142135 0. 0. 0. ]
[ 8.944272 759.006 1008.002 100.02 1014.00195
865.00055 2. 0. 0. 0. ]
[ 5.0990195 758.01056 1013.0005 101.12369 1017.0044
865.00055 1.4142135 0. 0. 0. ]
[ 3.1622777 759.0007 1016. 99.12618 1016.00793
864.0023 2.828427 0. 0. 0. ]
[ 2. 762.0026 1011.0005 99.00505 1012.00195
863.00055 4. 0. 0. 0. ]]
[[ 6 15 15 0 7 8 8 0 0 0]
[10 0 0 0 8 8 6 0 0 0]
[ 4 0 15 15 7 8 8 0 0 0]
[ 0 0 15 15 7 7 9 0 0 0]
[ 0 0 0 1 8 8 0 0 0 0]
[ 0 0 0 6 8 8 0 0 0 0]
[ 2 1 0 9 9 8 6 0 0 0]
[ 2 2 1 9 9 9 8 0 0 0]
[ 3 1 9 8 8 6 8 0 0 0]
[ 0 0 8 8 8 8 8 0 0 0]]
[[ 1.4142135 758.01056 1010.0179 106. 1015.00055
864.0023 3.1622777 0. 0. 0. ]
[ 5.8309517 760. 1016.0492 99.04544 1016.0177
860. 4.472136 0. 0. 0. ]
[ 6. 760.0105 1018.00793 93.00538 1015.00446
858. 5.0990195 0. 0. 0. ]
[ 7.071068 751.0007 1009.04016 109.11462 1014.03156
867.0052 2.828427 0. 0. 0. ]
[ 18.110771 814.2789 1002.20764 83.4386 1012.00195
873.00055 0. 0. 0. 0. ]
[ 34.928497 939.2444 991.3728 94.75231 1016.00195
869.0052 0. 0. 0. 0. ]
[ 475.1105 1099.9972 779.4318 413.03754 1105.0901
683.6987 2.828427 0. 0. 0. ]
[1108.4584 978.57446 193.74725 910.949 1057.463
306.88434 4. 0. 0. 0. ]
[ 653.65436 268.71545 469.57428 1097.6603 628.05414
1.4142135 4. 0. 0. 0. ]
[ 12. 0. 626. 1014. 388.
2. 4. 0. 0. 0. ]]
再打印一下b.ravel(),可以知道ravel作用就是把压扁了,降为1维
[ 0 0 8 8 0 0 0 0 8 8 0 0 6 6 1 0 0 0 9 11 0 15 12 8
14 0 0 0 9 10 4 0 12 11 12 0 0 8 9 9 12 8 1 3 5 15 15 8
8 9 12 9 0 0 4 0 15 8 7 7 0 1 1 11 14 0 0 7 8 8 12 14
9 7 14 15 0 7 8 8 0 14 12 14 4 0 0 7 7 7 0 0 0 0 0 0
0 8 7 7]
[ 0 0 0 0 0 0 0 8 8 8 0 0 0 0 0 0 0 7 7 8 0 0 0 0
0 0 0 7 8 8 0 0 0 0 0 0 0 8 8 8 0 0 0 0 0 0 0 7
7 8 4 3 4 1 0 15 15 7 7 6 4 1 4 8 0 15 15 7 6 6 4 2
7 4 2 15 15 15 6 5 12 15 8 0 14 15 14 14 5 4 0 0 8 0 0 0
0 0 0 8]
[ 0 0 8 8 8 8 8 0 0 0 12 12 14 6 6 6 8 0 0 0 12 13 14 7
6 7 7 0 0 0 12 15 15 9 7 7 9 0 0 0 9 15 0 0 8 8 9 0
0 0 1 0 0 15 8 8 6 0 0 0 14 15 15 15 8 8 8 0 0 0 0 15
15 0 7 8 9 0 0 0 12 15 0 15 8 7 6 0 0 0 4 0 15 15 7 7
8 0 0 0]
[ 6 15 15 0 7 8 8 0 0 0 10 0 0 0 8 8 6 0 0 0 4 0 15 15
7 8 8 0 0 0 0 0 15 15 7 7 9 0 0 0 0 0 0 1 8 8 0 0
0 0 0 0 0 6 8 8 0 0 0 0 2 1 0 9 9 8 6 0 0 0 2 2
1 9 9 9 8 0 0 0 3 1 9 8 8 6 8 0 0 0 0 0 8 8 8 8
8 0 0 0]
然后print hists,这里用到了bitcount函数,它有3个参数,
- b.ravel()是梯度角度的1维数组。bitcount会统计数组b.ravel()中每个数(从0至15)出现的次数,然后放入结果数组对应的位置。例如,数n在b.ravel()出现了m次,最终输出Out[n]=m。
- m.ravel()是梯度的大小,这个是bitcount函数的权重。矩阵大小必须与第一个参数一致。它的作用是,假设数n在b.ravel()中出现在b[2],b[5],b[7]中,那么就要去找m.ravel()中对应的m[2],m[5],m[7]的值,最终输出出变为Out[n]=m[2]+m[5]+m[7]。例如:在第四个方块中,数字4在b中出现了1次,位置是21。于是去m中的位置21找到对应的值为6,最终bitcount输出结果,位置4的值就是6。下面打印的结果可以证实: 6.00000000e+00。
- bin_n,输出的数组最小包含16个数
详细说明可以参见https://www.cnblogs.com/eilearn/p/9015375.html
[array([1.61749622e+04, 1.13137082e+01, 0.00000000e+00, 5.09901953e+00,
9.16227770e+00, 4.47213602e+00, 5.65685415e+00, 6.08853527e+03,
6.34355785e+03, 3.15890005e+03, 1.03628662e+03, 7.52739665e+02,
2.10990195e+01, 0.00000000e+00, 2.30864080e+01, 4.05934390e+03]), array([1.22320000e+04, 5.65685415e+00, 1.16832385e+01, 3.16227770e+00,
6.94563232e+02, 1.16587675e+03, 2.80127756e+03, 7.20410542e+03,
5.23819590e+03, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
2.00000000e+00, 0.00000000e+00, 1.69124133e+03, 6.36190736e+03]), array([4.80342119e+03, 5.83095169e+00, 0.00000000e+00, 0.00000000e+00,
2.00000000e+00, 0.00000000e+00, 3.27537928e+03, 6.61796251e+03,
9.57802844e+03, 2.01165301e+01, 0.00000000e+00, 0.00000000e+00,
2.66606255e+03, 1.04558594e+03, 8.35277112e+02, 8.25755807e+03]), array([8.09075155e+03, 1.64589851e+03, 2.56214334e+03, 6.53654358e+02,
6.00000000e+00, 0.00000000e+00, 1.04881302e+02, 3.91104169e+03,
1.18277043e+04, 4.26582660e+03, 5.83095169e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 3.99719636e+03])]
hist = np.hstack(hists) # hist is a 64 bit vector
print hist
[1.61749622e+04 1.13137082e+01 0.00000000e+00 5.09901953e+00
9.16227770e+00 4.47213602e+00 5.65685415e+00 6.08853527e+03
6.34355785e+03 3.15890005e+03 1.03628662e+03 7.52739665e+02
2.10990195e+01 0.00000000e+00 2.30864080e+01 4.05934390e+03
1.22320000e+04 5.65685415e+00 1.16832385e+01 3.16227770e+00
6.94563232e+02 1.16587675e+03 2.80127756e+03 7.20410542e+03
5.23819590e+03 0.00000000e+00 0.00000000e+00 0.00000000e+00
2.00000000e+00 0.00000000e+00 1.69124133e+03 6.36190736e+03
4.80342119e+03 5.83095169e+00 0.00000000e+00 0.00000000e+00
2.00000000e+00 0.00000000e+00 3.27537928e+03 6.61796251e+03
9.57802844e+03 2.01165301e+01 0.00000000e+00 0.00000000e+00
2.66606255e+03 1.04558594e+03 8.35277112e+02 8.25755807e+03
8.09075155e+03 1.64589851e+03 2.56214334e+03 6.53654358e+02
6.00000000e+00 0.00000000e+00 1.04881302e+02 3.91104169e+03
1.18277043e+04 4.26582660e+03 5.83095169e+00 0.00000000e+00
0.00000000e+00 0.00000000e+00 0.00000000e+00 3.99719636e+03]
hstack函数,实现从水平方向合并hists。
4.transform to Hellinger kernel。
关于这个,我猜测是要进行特征数据的归一化。
因为处理后的数据明显收敛,可以看后面打印的图,y轴取值范围变小。
不过网上常用归一化方法没查到Hellinger转化。
eps = 1e-7
hist /= hist.sum() + eps
hist = np.sqrt(hist)
hist /= norm(hist) + eps
打印一下hist
[0.32916595 0.00870554 0. 0.00584436 0.00783421 0.00547332
0.00615575 0.20195271 0.20613879 0.14546591 0.08331692 0.07100937
0.01188843 0. 0.01243573 0.1649002 0.2862477 0.00615575
0.00884657 0.00460249 0.06821018 0.088373 0.13698447 0.21967635
0.18732 0. 0. 0. 0.00366023 0.
0.10643784 0.20643672 0.17937776 0.00624976 0. 0.
0.00366023 0. 0.14812354 0.21055009 0.25329775 0.01160833
0. 0. 0.13363753 0.08368992 0.0748012 0.23519013
0.23280253 0.10500133 0.13100715 0.06617095 0.0063397 0.
0.02650588 0.16185998 0.28147738 0.1690421 0.00624976 0.
0. 0. 0. 0.16363305]
5.打印图像
把输入的灰度图,直方图计算结果,和Hellinger kernel的转化结果一起打印一下。
然后再把x方向的导数 ,y方向的导数,以及梯度的大小(magnitude)以图像方式打印一下
plt.subplot(221)
plt.imshow(img,'gray')
plt.subplot(223)
plt.imshow(gx,'gray')
plt.subplot(224)
plt.imshow(gy,'gray')
plt.subplot(222)
plt.imshow(mag,'gray')
plt.show()
右上方梯度的大小(magnitude)反应的是图像有剧烈变化的地方
左下方x方向导数凸显了图像垂直方向的线条
右下方x方向导数凸显了图像水平方向的线条
通过4个图对比可以直观的了解变化过程。
图像的梯度去掉了很多不必要的信息(比如不变的背景色),加重了轮廓。