机器学习与计算机视觉入门项目——视频投篮检测(二)

一、手工特征与CNN特征

在上一次的博客中,介绍了计算机视觉和机器学习的关系、篮球进球检测的基本问题和数据集的制作。这次的我们主要介绍如何从原始图像中提取有用的图像特征,以便应用于之后的分类器。

如下图所示,我们现在要做的是Feature Extraction。在前DeepLearning时代,图像特征都是由人设计的,对于不同的图像如人脸、行人,都有各自所适合的特征,也就是所谓的Hand-crafted Feature。比如常用Haar特征来描述人脸,常用HOG特征来描述行人。

因此,我们可以拿先前中医看病的例子来类比前深度学习时代的工程师是怎样解决计算机视觉问题的。假设有这样一位精通医术的老中医在药店坐堂,前来看病的人病情各不一样。老中医需要根据每位病人的具体情况开出药方,然后抓药,以特定的方式熬制汤药并且服用才能治疗病情。比如,有位病人患了“脸盲症”,老中医开出药方:准备“人脸数据集”一副,取“光照归一化”一两,再称“Haar特征”三钱,送入“Adaboost级联分类器”慢火熬制,连续服用若干代,脸盲症可愈。

从这里我们可以看到,解决CV问题是非常依赖人工经验的,他需要工程师对手头上的“工具”,也就是“特征”和“分类器”的使用条件以及性能都有充分的了解,必要时甚至需要针对这类问题设计新的图像特征。因此拿中医诊病来类比前深度学习时代的CV工程问题一点也不为过。

在卷积神经网络开始大显神威之后,feature extraction这一步基本都交给CNN来做了。利用多个卷积层的级联,依靠数据驱动(data-driven)的学习方式,CNN可以很好地学习到数据集的特定的数据分布,多数情况下,比人类手工设计的特征好使多了。

在篮球进球检测这个简单的任务的开始阶段,我们采用HOG特征足以应对了。

二、HOG特征简介

HOG百度百科上总结了HOG特征的基本思想:

把样本图像分割为若干个像素的单元,把梯度方向平均划分为多个区间,在每个单元里面对所有像素的梯度方向在各个方向区间进行直方图统计,得到一个多维的特征向量,每相邻的单元构成一个区间,把一个区间内的特征向量联起来得到多维的特征向量,用区间对样本图像进行扫描,扫描步长为一个单元。最后将所有块的特征串联起来,就得到了人体的特征。

原版论文在此。我在实习时,王老师要求把文章的每一处地方都推导一遍,然后自己写代码实现HOG算法。因此我主要是参考了上面这篇CVPR2005的最早的文章。另外,知乎上有一篇文章讲的也比较清楚,在这里贴出来图像学习-HOG特征。

网上讲HOG特征的文章不在少数,只要认真看过两三篇,基本可以了解HOG特征是怎么提取的了。所以在这里就不再赘述了。如果希望进一步挖掘HOG特征的潜力,可以看这里。

三、HOG特征的Python实现

学习一个算法最好的办法就是亲自实现一遍。只有亲手经历一遍这样的过程,才能体会到算法实现过程中出现了什么样的问题,才能对算法的性能和算法的擅长领域做到心中有数。下面是我实现的HOG算法的Python代码,当然也参考了前辈的CSDN博文。

class Hog_descriptor():'''提取灰度图片的HOG特征,画HOG特征图'''def __init__(self, img, cell_size=8, bin_size=9):# 输入的图片数据应是np.array格式的uint8数据,0-255self.img = np.sqrt(img / float(np.max(img)))# 伽马校正self.cell_size = cell_size # 计算梯度直方图的最小单元的尺寸self.bin_size = bin_size # 梯度直方图中的条数self.angle_unit = 360 / self.bin_size # 角度增加的步长height, width = self.img.shape# 将图像尺寸resize成cell_size的整数倍,方便之后运算self.width = int(np.ceil(width/cell_size)*cell_size)self.height = int(np.ceil(height/cell_size)*cell_size)img = cv2.resize(self.img,(width,height),interpolation=cv2.INTER_CUBIC)self.img = imgdef global_gradient(self): # 在原图上求梯度,函数返回与原图尺寸相同# 1,0在x方向求梯度,0,1在y方向求梯度,sobel滤波器尺寸=3gradient_values_x = cv2.Sobel(self.img, cv2.CV_64F, 1, 0, ksize=3)gradient_values_y = cv2.Sobel(self.img, cv2.CV_64F, 0, 1, ksize=3)# 梯度幅值=sqrt(gx^2+gy^2)gradient_magnitude = np.sqrt(np.square(gradient_values_x)+np.square(gradient_values_y))# 梯度相角=arctan(gy/gx)gradient_angle = cv2.phase(gradient_values_x, gradient_values_y, angleInDegrees=True)return gradient_magnitude, gradient_angledef extract(self):# 1.提特征向量 2.画特征图# 对原图每一点求梯度幅值和相角gradient_magnitude, gradient_angle = self.global_gradient()gradient_magnitude = abs(gradient_magnitude)# 记录每一个cell的hog向量(1*bin_size维)的矩阵cell_histogram_vector = np.zeros((int(self.height / self.cell_size), int(self.width / self.cell_size), self.bin_size))# 对每个cell,计算hog向量for i in range(cell_histogram_vector.shape[0]):for j in range(cell_histogram_vector.shape[1]):# 从整张图的梯度信息中抠出该cell的梯度信息cell_magnitude = gradient_magnitude[i * self.cell_size:(i + 1) * self.cell_size,j * self.cell_size:(j + 1) * self.cell_size]cell_angle = gradient_angle[i * self.cell_size:(i + 1) * self.cell_size,j * self.cell_size:(j + 1) * self.cell_size]# 将每一个cell的直方图向量写入总的存储矩阵cell_histogram_vector[i][j] = self.cell_gradient(cell_magnitude, cell_angle)# 可视化所有cell的hog向量hog_image = self.render_gradient(np.zeros([self.height*5, self.width*5]), cell_histogram_vector)# 将block以stride=cell_size为步长滑动,将提取的36维hog向量拼接起来构成描述全图的特征向量hog_vector = []for i in range(cell_histogram_vector.shape[0] - 1):for j in range(cell_histogram_vector.shape[1] - 1):# 在一个block内,拼接4个hog向量,并对其归一化,消除光照不均的影响block_vector = []block_vector.extend(cell_histogram_vector[i][j])block_vector.extend(cell_histogram_vector[i][j + 1])block_vector.extend(cell_histogram_vector[i + 1][j])block_vector.extend(cell_histogram_vector[i + 1][j + 1])mag = lambda vector: math.sqrt(sum(i ** 2 for i in vector))magnitude = mag(block_vector)# lambda定义匿名函数,求L2范,vector是自变量,冒号后是函数体if magnitude != 0:# 用L2范归一化block直方图向量normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector]block_vector = normalize(block_vector, magnitude)hog_vector.extend(block_vector)return hog_vector, hog_imagedef cell_gradient(self, cell_magnitude, cell_angle):# 计算一个cell的直方图向量orientation_centers = [0] * self.bin_size# bin_size维的列表,默认是9维for i in range(cell_magnitude.shape[0]):for j in range(cell_magnitude.shape[1]):gradient_strength = cell_magnitude[i][j]# 遍历cell中的每一个梯度幅值gradient_angle = cell_angle[i][j]# 取出每个幅值对应的角度min_angle, max_angle, mod = self.get_closest_bins(gradient_angle)orientation_centers[min_angle] += (gradient_strength * (1 - (mod / self.angle_unit)))orientation_centers[max_angle] += (gradient_strength * (mod / self.angle_unit))return orientation_centersdef get_closest_bins(self, gradient_angle):'''若有角度330°,bin=9,angle_unit=40°,则330°应落在320°到0°之间,其幅值应按比例加到320和0对应的bin上min_angle = 8,max_angle = (8+1)%9 = 0mod=10°,离320°更近,分配到320对应的bin应占总幅值的3/4离0°远,幅值分配1/4'''idx = int(gradient_angle / self.angle_unit)% self.bin_sizemod = gradient_angle % self.angle_unitreturn idx, (idx + 1) % self.bin_size, moddef render_gradient(self, image, cell_histogram):# 画图# 为了使HOG图像更为清晰,特征图的尺寸扩大为原来的五倍。cell_width = int(self.cell_size) / 2 * 5max_magnitude = np.array(cell_histogram).max()for x_height in range(cell_histogram.shape[0]): # x_height代表img高度,y_width代表img宽度for y_width in range(cell_histogram.shape[1]):cell_hist = cell_histogram[x_height][y_width] # 取一个cell代表的histogramcell_hist /= max_magnitude # 幅值归一化angle = 0# 找cell的中心x_center = 5*(x_height)*self.cell_size+int(self.cell_size*5)/2y_center = 5*(y_width)*self.cell_size+int(self.cell_size*5)/2angle_gap = self.angle_unitfor magnitude in cell_hist:angle_radian = math.radians(angle)x1 = int(x_center + cell_width*5 * math.cos(angle_radian))y1 = int(y_center - cell_width*5 * math.sin(angle_radian))x2 = int(x_center - cell_width*5 * math.cos(angle_radian))y2 = int(y_center + cell_width*5 * math.sin(angle_radian))                    cv2.line(image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))angle += angle_gapreturn imagedef hog_imshow_save(self,hog_img_name = 'hog_img.png'):# 保存特征图vector, image = self.extract()plt.imshow(image, cmap=plt.cm.gray)plt.show()cv2.imwrite(hog_img_name,image)

这是调用Hog_descriptor类提取图像特征并展示特征图的example。

# example of hog class
img = cv2.imread('test.png', cv2.IMREAD_GRAYSCALE) # 读入灰度图
hog = Hog_descriptor(img, cell_size=8, bin_size=9)
hog.hog_imshow_save('D:/hog_img.png')

效果如下。


第二张图是第一张图的尺寸放大五倍后的效果,可以看到,条状亮线是每一个cell特征可视化的结果,很好地反映了原图的边缘信息。

四、一个小问题

HOG特征描述的是图像的边缘信息,因为边缘处梯度的幅值才比较大。那么针对我们的篮球进球检测问题来说,如果由于摄像头的视线原因,篮球恰好出现在篮网前面,就像下图这样:

它和正常进球的样本区别就在于几根比较稀疏的篮网线在篮球的前面或者后面,此时,我们的HOG特征还能准确地描述两者在纹理、边缘、轮廓上的不同吗?考虑到一张图片的尺寸大约在60*50,是比较小的,没有办法把篮网的轮廓精细地描绘出来。下面的hog特征图就是上面这个迷惑性很强的负样本的可视化图。

我认为在HOG特征这个层次上,实际上是无法分清这两者的区别的,所幸的是,实时的进球检测效果看起来还不错,至少模型没有对这样的假阳性样本出现误判。在随后的使用的MLP和CNN做检测时,对这类样本的鲁棒性要更好一些。

五、制作整个数据集的HOG特征集

在第一部分我们提取了视频中的篮筐位置并保存为小图片。为了之后存取的方便,将其储存成pkl文件形式。

img_dir = 'D:/dataset/'
filelist = os.listdir(img_dir)
# 先读一张图片,获得尺寸信息
img = cv2.imread(img_dir + filelist[0])
height,width = np.shape(img)
# 开辟空矩阵
img_to_save = np.zeros((np.shape(filelist)[0],height,width),np.uint8)
for i in range(0,len(filelist)):tmp = cv2.imread(img_dir+filelist[i])img_to_save[i] = tmp
# 存成pkl文件
img_output = open('img.pkl','wb')
pickle.dump(img_to_save,img_output)
img_output.close()

现在对img.pkl的每一张图片都提取hog特征

# 读数据集
img_input = open('img.pkl','rb')
img_matrix = pickle.load(img_input)
img_input.close()hog_vector = []
for i in range(0,np.shape(img_matrix)[0]):hog = Hog_descriptor(img_matrix[i], cell_size=8, bin_size=9)temp,_ = hog.extract()hog_vector.append(temp)if i % 500 == 0:print(i)
hog_output = open('hog.pkl','wb')
pickle.dump(hog_vector,hog_output)
hog_output.close()

机器学习与计算机视觉入门项目——视频投篮检测(二)相关推荐

  1. 机器学习与计算机视觉入门项目——视频投篮检测(一)

    机器学习与计算机视觉入门项目--视频投篮检测(一) 随着机器学习.深度学习技术的迅猛发展,计算机视觉无疑是近年来发展最快的AI领域之一.正因如此,互联网上有关计算机视觉和机器学习.深度学习的社区.博文 ...

  2. 机器学习与计算机视觉入门项目——视频投篮检测(三)

    机器学习与计算机视觉入门项目--视频投篮检测(三) 分类器的设计和选择 几种损失函数 Logistic回归 基本原理 损失函数 Logistic回归的求解--梯度下降法 损失函数的梯度计算 在第二部分 ...

  3. 【收藏】23个机器学习最佳入门项目(附源代码)

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 整理:深度学习技术前沿 [导读]本文为你介绍23种机器学习项目创意,以获取有关该增长技 ...

  4. 计算机视觉(视频追踪检测分类、监控追踪)常用测试数据集

    计算机视觉(视频追踪检测分类.监控追踪)常用测试数据集 (1).WallFlower dataset [链接]: 用于评价背景建模算法的好坏, Ground-truth foreground prov ...

  5. 23个机器学习最佳入门项目!(附数据+源代码)

    本文介绍23种机器学习项目创意,以获取有关该增长技术的真实经验. 大家都知道,教科书上所学与实际操作还是有出入的,那关于机器学习有什么好的项目可以实操吗? 这次给大家分享一个涵盖面向初学者,中级专家和 ...

  6. 23 个机器学习最佳入门项目(附数据+源代码)

    本文介绍23种机器学习项目创意,以获取有关该增长技术的真实经验. 大家都知道,教科书上所学与实际操作还是有出入的,那关于机器学习有什么好的项目可以实操吗? 这次给大家分享一个涵盖面向初学者,中级专家和 ...

  7. windows下nodejs express安装及入门网站,视频资料,开源项目介绍

    windows下nodejs express安装及入门网站,视频资料,开源项目介绍,pm2,supervisor,npm,Pomelo,Grunt安装使用注意事项等总结 第一步:下载安装文件 下载地址 ...

  8. 【使用OpenCV进行视频人脸检测】------机器学习(附完整代码和数据集)

    上篇文章介绍了OpenCV进行照片人脸检测,今天学习的是OpenCV进行视频人脸检测,所有的参考博文.文献.视频.代码都会在文末附上链接或文件压缩包. 本文的目录如下: 内容目录 一.Opencv 进 ...

  9. 机器学习,计算机视觉相关资料

    Deep Learning(深度学习) ufldl的2个教程(这个没得说,入门绝对的好教程,Ng的,逻辑清晰有练习):一 ufldl的2个教程(这个没得说,入门绝对的好教程,Ng的,逻辑清晰有练习): ...

最新文章

  1. 关于hp惠普笔记本电脑清洗(真的要水洗哟)
  2. 初探性能优化--2个月到4小时的性能提升!
  3. form 多个submit php,一个复杂的PHP表单处理方案?
  4. php mysql log文件,mysql log文件【读书笔记1】_MySQL
  5. 【NLP】完全解析!Bert Transformer 阅读理解源码详解
  6. 服务上的图片直接在浏览器上可以打开,但是在img上报404错误
  7. 国际站html代码,国际站必须看得懂的HTML代码
  8. c#+handle.exe实现升级程序在运行时自动解除文件被占用的问题
  9. Java-Arrays类
  10. 自定义 HBase-MapReduce1
  11. 【网络】SSH本地/远程/动态端口转发
  12. mybatis 添加语句返回对象_Mybatis底层源码分析
  13. matlab自带python_在matlab中直接在python中使用sklearn
  14. Java实现一个字符串的反转
  15. SpringMVC学习记录二——非注解和注解的处理器映射器和适配器
  16. AdminLTE-2.4.10源码包
  17. LabView 2018破解版下载
  18. Caused by: java.lang.ClassNotFoundException: org.jaxen.JaxenException
  19. 冒泡排序算法-java实现
  20. PGSQL创建管理员账号

热门文章

  1. STM32学习笔记—PWM输出
  2. matlab数据采集工具箱,matlab数据采集工具箱
  3. 读《乔布斯传记》有感4-----回到苹果
  4. 交流充电桩电路图_交流充电桩原理
  5. 基于jsp+mysql+Spring+SpringMVC+mybatis的ssm理论课_考勤,作业,签到管理系统
  6. 运气指数测试软件,测一测最近的运势如何,有什么测试运势的软件
  7. oppo android10的字体修改器,超好看的字体:OPPOSANS,替换系统原字体,完爆微软雅黑,提升体验....
  8. 中国RPA 未来趋势
  9. 电子名片-vcard(一)
  10. .NET Framework 4.6.2发布