Deep Coding


  • 关于

  • 标签

  • 日志

  • 首页

Ubuntu1604装机系列(一):Mac烧系统盘及1080Ti显卡(390版本)驱动安装

发表于 2018-03-27 |

Ubuntu1604系统安装

笔者第一次尝试Mac烧系统盘,没想到比windows简单到爆,过程如下:
 
官网下载Ubuntu iso文件
 
格式化一个U盘
 
接着写入U盘镜像

1
2
3
df -hl       # 记住磁盘名称,Eg:/dev/disk2s
sudo diskutil umount /dev/disk2s # 卸载该磁盘
sudo dd bs=1m if=/Users/<user name>/Desktop/ubuntu-16.04.4-desktop-amd64.iso of=/dev/disk2s

静静等待10分钟,U盘即可写入完毕。接着就是设置电脑U盘启动,开始安装Ubuntu系统了

ubuntu 换源

清华大学开源软件镜像站寻找对应系统的源

1
2
3
4
5
6
7
8
9
10
11
cd /etc/apt
sudo cp sources.list sources.list.old #先将之前的源做个备份

vi sources.list # 拷贝清华的镜像到该list

deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security main restricted universe multiver

sudo apt-get update # 更新源完成,发现网速飞快了

apt-get相关常用指令
1
2
3
4
sudo apt-get update  更新源
sudo apt-get install package 安装包
sudo apt-cache search package 搜索软件包
sudo apt-get remove package --purge 删除包,包括配置文件等

显卡驱动安装

Nvidia显卡驱动踩了很多坑,到网上百度了N多教程,大致尝试的如下:

  • 显卡线插集显上,装好驱动后把线再插回N卡
  • BIOS设置禁用Secure boot,cpu做显卡
  • 采用PPA源,尝试各种375、381、390的安装
  • 打开ubuntu的软件更新,更新显卡驱动

经过上述步骤,会提示显卡驱动已经安装成功,但nvidia-smi依然无效…该最简单但最高效的选手登场了

编译 NVIDIA 驱动程序

Nvidia官网下载最新的显卡驱动程序,ubuntu的一般就几十兆左右。
 
将nouveau driver加入黑名单

1
2
3
vi /etc/modprobe.d/blacklist.conf
blacklist nouveau
options modset=0

输入如下命令使驱动禁用生效

1
sudo update-initramfs -u

终端输入lspci | grep nouveau,如果没有显示证明该驱动被kill

1
reboot # 一定要重启

杀掉LightDM或GDM登录显示器,进入纯文本tty1模式

1
2
sudo service lightdm stop
sudo dpkg-reconfigure gdm # 有的人可能安装的不是LightDM界面,可先通过该命令切到LightDM,或者直接stop GDM等界面(第二种笔者未尝试)

编译安装NVIDIA驱动,此过程大约耗时3~5分钟

1
2
chmod +x NVIDIA-xxx.run
sudo ./NVIDIA-xxx.run

需要注意的:
1、此处认真看提示,并不是一路选择”yes”。
2、假如上一步在安装时,Warning了gcc版本不对,则退出驱动安装,先安装对应版本的gcc之后再继续。笔者安装的驱动程序提示需要gcc-5.4.0,又是一番折腾,还好在冯博的指导下,找到了精干快速的方法。不介意的话可以移步笔者之前的文章Ubuntu下gcc/g++多版本切换
 
安装成功后,重新开启桌面测试。

1
2
sudo service lightdm start
nvidia-smi

成功后的截图
图片名称

Ref:

📎:Ubuntu下轻松切换GDM, LightDM , KDM
📎:Ubuntu 16.04安装安装iBus中文输入法

MacOS安装OpenCV

发表于 2018-03-22 |

brew安装

首先更换Mac brew 安装源,下载软件分分钟

1
2
cd "$(brew --repo)" && git remote set-url origin https://git.coding.net/homebrew/homebrew.git
cd $home && brew update

开始安装
1
sudo brew install opencv
配置环境变量:安装好的opencv位于/usr/local/Cellar/下
1
2
3
vi .bash_profile
export DYLD_LIBRARY_PATH=/usr/local/Cellar/opencv/3.4.1_2/lib:$DYLD_LIBRARY_PATH
source .bash_profile # 使路径生效(下同)
配置python环境变量
1
2
3
4
5
6
cd /Library/Python/2.7/site-packages/

sudo ln -s /usr/local/Cellar/opencv/3.4.1_2/lib/python2.7/site-packages/cv.py cv.py
sudo ln -s /usr/local/Cellar/opencv/3.4.1_2/lib/python2.7/site-packages/cv2.so cv2.so

export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python2.7/site-package # 将python路径写入系统目录
生成SDK还需要一个pkg-config,用来find package位置
1
2
3
4
wget http://pkgconfig.freedesktop.org/releases/pkg-config-0.29.tar.gz .
env LDFLAGS="-framework CoreFoundation -framework Carbon" ./configure --with-internal-glib
make
sudo make install
在系统路径中把cv路径写入到pkg-config
1
2
3
vi .bash_profile
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/Cellar/opencv/3.4.1_2/lib/pkgconfig
export PKG_CONFIG_PATH
show me a picture!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
Mat img = imread("test.jpg", CV_LOAD_IMAGE_UNCHANGED);

if(img.empty())
{
fprintf(stderr, "failed to load input image\n");
return -1;
}

imshow("Display Image", img);
waitKey(0);

// no need to release anything with c++ !
return 0;
}

上述文件命名为test.cpp,用下面的指令编译成一个超简单的Demo

1
g++ -o test test.cpp `pkg-config --libs opencv` `pkg-config --cflags opencv`

./test运行代码

多个cpp文件工程的编译

假如test文件下存在imageRank.h、imageRank.cpp、main.cpp文件.h文件不用管,在编译.cpp时会自动调用。

1
2
3
4
5
g++ -c imageRank.cpp -o imageRank.o -I ./   # 先编译函数cpp文件
g++ main.cpp imageRank.o -o main `pkg-config --libs opencv` `pkg-config --cflags opencv` # 直接生成可执行文件
ar -r libimageRank.a imageRank.o # 或者编译出静态库文件给第三方
g++ -o main -L. -ldetection main.cpp `pkg-config --libs opencv` `pkg-config --cflags opencv`
生成可执行文件

源码编译安装

源码编译安装跟Ubuntu opencv的安装非常类似,注意修改ccmake中各个项目路径即可,楼主在编译安装时仅下面一点需要注意:
安装过程中,提示缺少boostdesc_bgm.i库,下载链接: https://pan.baidu.com/s/1XbCdDoAvwvF_fiWvSYJTxQ 密码: 7hpm
解压后复制到contrib/modules/xfeatures2d/src目录即可。

Ref:
📎:Mac OS X 安装并测试 OpenCV
📎:mac os 安装 pkg-config

论文笔记:《Faster R-CNN》

发表于 2018-03-08 |

Faster R-CNN

Faster R-CNN检测速度更快(5fps/s),是Fast R-CNN的10倍,R-CNN的250倍,检测精度则稍好于Fast版本。主要贡献就是采用卷积网络提取Region proposal,取代了传统的Selective search算法。

RPN网络概述

Faster R-CNN提出了Region Proposal Network(RPN)思想,它与物体特征提取网络共享较低层卷积信息。具体方法:以feature map上每个特征点为中心,选取其周围$[n,n]$的窗口区域,进行滑动。再根据缩放关系换算到源图,假如某个窗口与ground truth计算的IOU值大于某个阈值,则认为该窗口内存在物体。
 
为了增加多尺度检测能力,每个特征点换算到原图后,会将原图的窗口进行调大或调小操作。论文中作者一共设置了9种(窗口面积为 {128, 256, 512} x 长宽比为 {1:1, 1:2, 2:1})。原图中的窗口为了和feature map窗口进行区分,称为anchor。直观来看,如下图所示:
图片名称

RPN训练阶段
  • 对于[60, 40]大小的一张feature map,那么原图中可能的anchor数目约为$60\ast 40\ast 9 \approx 20000$个
  • 忽略超出边界的anchors剩下6000个
  • 通过anchor与ground truth计算的IOU值,可以进行每个anchor为前景$or$背景的自动标注,即:输入RPN网络的训练样本为box坐标 + box内是否有物体。
  • 同样采用多任务训练,完成前、背景分类及bounding box的粗回归。
RPN测试阶段
  • RPN网络对每幅图的6000个region均映射为一个概率值score和对应的四个坐标值。
  • 然后利用该score对6000个regions进行NMS非极大抑制,筛选到约为2000个anchor。
  • 接着再筛选出Top-N(论文中N=300)个区域用于Fast RCNN detect网络的训练(作者在论文中对2000、300个region proposal进行对比实验,效果差别不大,也进一步说明了RPN提取region的有效性、针对性)
RPN网络结构

作者在论文中给出的一张图有些误导,查阅前辈们写的博客时候才理解RPN网络具体形式。重新绘制网络结构如下:

图片名称

论文中提到的256-d,不是整个feature map生成一个256维的向量,feature map上的每一个“点”都对应一个256维向量。同时该“点”对应了9个anchors,18个cls输出,36个reg输出。
 
小结:RPN的基本思想就是通过对feature map进行窗口滑动,再在该窗口上累加一个小网络,最终训练得到某些可能为前景的regions,以及该regions对应的bouning box。该网络专门判断该区域是否存在物体,而并不检测是哪种特定的物体。

RPN + Fast RCNN

有了RPN后,怎样跟Fast RCNN连接起来呢?作者采用了叫做4-Step Alternating Training的方式:
step 1 :采用预训练的VGG网络,前13层用作特征提取层,后面的网络采用RPN架构,用均值为0的高斯分布随机初始化网络参数
step 2 :同样采用预训练的VGG前13层网络,用step 1中 RPN得到的region proposal,进行Fast RCNN网络的训练。这两步还未实现共享卷积层
step 3 :采用step 2中Fast RCNN的卷积层参数初始化RPN网络,并固定共享层网络参数,仅仅fine tune RPN网络层参数,得到新的region proposal
step 4 :继续固定共享层网络参数,仅仅fine tune Fast RCNN,得到最终的模型。

Fast RCNN 与 Faster RCNN结果对比

图片名称

Faster RCNN网络结构(来源于网络)

图片名称

Ref:

📎: 区域推荐网络RPN
📎:faster-rcnn 之 RPN网络的结构解析

论文笔记:《Fast R-CNN》

发表于 2018-03-07 |

SPP Net

R-CNN的进阶版Fast R-CNN可以说是在RCNN的基础上采纳了SPP Net方法,使得性能进一步提高。所以有必要先了解下何凯明提出的SPP Net。SPP:Spatial Pyramid Pooling(空间金字塔池化)。他有两个显著的特点:
 
创新点1:实现CNN的多尺度输入,忽略图片尺寸的影响
为了实现全联接层可以正确匹配pooling层后的维度,R-CNN中笔者先对Region proposal Crop到某一固定维度,但这样会造成一定的图像失真。有没有不Crop图像从而实现维度匹配的方法呢?一是调整最后的pooling层维度,二是调节全联接层的结构,使它接受任意维度特征。后一种方法是全卷积网络(FCN),本文是采用的是前一种,即SPP:空间金字塔pooling。
 
SPP的具体实现方式:假设与全联接层匹配的最后一个pooling层,输出维度应当是$[2\ast 2]$。而此时ROI区域的大小为$[5\ast 7]$(Region proposal在该论文中又称为ROI区域)。那么此时pooling核的size应当为$[\frac{5}{2} \ast \frac{7}{2}]$,才能使该ROI区域pooling后,得到匹配的size。但核的size不能为小数啊,所以采用理论size做间隔,最后向下取整得到每个pooling核的size。
图片名称
如上图所示:H方向以2.5为间隔$[0, 2.5, 5]$,W方向以3.5取间隔$[0, 3.5, 7]$。最后每个pooling核起始点坐标为H:$[2, 5]$,W:$[3, 7]$。更广泛地,我们用$[h, w]$代表ROI区域的高和宽,$[H, W]$代表希望pooling后得到的高和宽。那么每个网格动态调整后的大小为$[\frac{h}{H} \ast \frac{w}{W}]$。
ps:假如ROI区域$[2,2]$小于pooling后目标维度$[3,3]$怎么办?一样的处理方式,每个pooling核起始点坐标为H:$[1,1,2]$,W:$[[1,1,2]$,类似于图像的最近邻法上采样(其实这样取整的方式引入了很多误差,后面的Mask RCNN中将ROI pooling层修改为ROI Align)
小结:SPP说到底就是计算一个比例关系,以动态调整pooling核的大小,实在是非常简单

创新点2:只对原图提取一次卷积特征
RCNN中,一张图片Region proposal的数量为2K个,其中存在大量的重复区域。而用CNN提取这些Region,显然是非常复杂的一件事儿。SPP Net中的做法是对整张图像用CNN仅提取一次特征,feature map一般计算到最后一个pooling层。接着再根据相应的缩放比例,对源图的Regions进行一一映射,找到其在feature map的位置即可。该算法看似简单,但对于regions的特征提取,速度提升约146倍。

Fast R-CNN

Fast R-CNN是在SPP Net基础上做的进一步改进,作者把SVM分类器换成Softmax,和CNN整合成一个网络,同时把bounding box regression也整合进网络中,三层联动调节参数,从而带来了效果的进一步提升。Fast RCNN架构如图所示:
图片名称
该网络是典型的多任务学习,低层的卷积层参数共享(采用VGG16提取特征),高层分别训练分类及回归网络。多任务学习一般用在task具有一定相似度的情境下。网络总的损失函数是两部分乘系数相加$L = L_{cls} + \lambda \times L_{loc}$,对于$L_{cls} $采用softmax-loss损失函数,对于Bounding box regression采用特殊的一种损失函数,如下:
$$L_{loc}(t^{u}, v) = \sum_{i = {x,y,w,h}}smooth_{L1}(t_{i}^{u}-v_{i})$$
$$if \left | x \right |<1 \qquad smooth_{L1}(x) =0.5x^{2} \qquad || \qquad Otherwise \qquad smooth_{L1}(x) = \left | x \right | -1 $$
这部分没有深究,其中$t_{i}, v_{i}$分别代表Region proposal和ground truth的坐标。Fast R-CNN的联合损失函数可以直接进行反向传播,从而达到端到端训练的目的。

Ref:

📎:Fast RCNN
📎:论文笔记:Fast(er) RCNN
📎:一箭N雕:多任务深度学习实战
📎:RCNN,Fast RCNN,Faster RCNN 总结

论文笔记:《R-CNN》

发表于 2018-03-05 |

背景

本篇文章要介绍的,是神经网络应用于物体检测领域的开山鼻祖R-CNN:Regions with CNN features。检测整体框架如下图所示,相信在读过Selective Search的论文后,对上图的架构并不陌生(不介意的可移步笔者对Selective Search的解读)
图片名称
R-CNN物体检测的步骤如下:

  • 输入源图,通过Selective Search算法,提取2k个左右的待选区域Regions
  • 对每个Region进行Crop/Scale,缩放到同样的尺寸(为了与CNN网络最后的全联接层对齐)
  • 之后对每个Region利用Pre-train的AlexNet,抽取特征(替代DPM、HOG等传统算子)(pool5 feature)[维度:4096]
  • 将这些特征丢入SVM分类器(二分类)进行训练,得到的就是一个region-bbox以及对应的类别。SVM权值矩阵[4096, N],N:类别数
  • 采用NMS非极大抑制算法,删除大量SS算法产生的重复区域
  • 最后利用pool5 feature,训练一个线性回归模型,提高bounding box精度

创新点

从上述流程看,R-CNN与Selective Search模型差别不大,主要就是在特征提取部分,采用了2012年ILSVRC:ImageNet Large Scale Visual Recognition Chal- lenge竞赛冠军网络AlexNet,其结构如图所示:
图片名称

创新点1:采用CNN提取图像特征

由于Alexnet参数众多,但PASCAL VOC任务又不像分类任务,标注难度大,数据量较少,直接训练容易造成过拟合。所以作者是对预训练的AlexNet进行fine-tuning。
 
具体做法是将FC-7层的1000维向量更改为21维(包括20个类及背景),并随机初始化这一全连接层的参数。训练样本的正样本来源是PASCAL VOC中人工标注的bounding box区域及类别;正样本是与bounding box计算的IoU值>=50%的Regions(这些候选Regions通过SS算法得到)。对图像进行一定程度的Crop,同时设定好batchsize、lr、SGD等超参数,进行迭代训练。需要注意这里的神经网络参数是单独训练得到的
 
提取上图中Pool5层的特征,作为类别的特征向量,存到硬盘中,之后再训练一个SVM二分类器,如图所示(图片来源冠军的试炼)。对于20类,即训练20个SVM二分类器。但与CNN不同的,作者通过实验,重新设置了IoU的阈值为0.3,也就是将IoU>=30%的Regions都当作正样本。
图片名称

创新点2:SVM分类后采用线性回归模型精绘bounding box

训练阶段

由于SS算法对某个类的bounding box框的不够完美,所以训练一个回归模型,作者论文中采用Bounding-box regression提升了3.5个map。选取的Regions与人工标注的bounding box IoU应当>=0.6,并且仅选取IoU最大的那个作为候选Region。(此处IoU设置比较严格,作者发现如果假设函数与bounding box差距太大话,是学不到东西的)
 
总体目标就是对Region进行两次平移$\Delta x, \Delta y$,再进行两次缩放$S_{x}, S_{y}$。简单说就是输入pool5层的特征向量,经过4次运算后,得到更加精确的bounding box区域。论文公式如下,就是比较简单的线性回归,损失函数采用梯度下降进行优化。
图片名称
图片名称
其中$d_{x}(P)$、$d_{y}(P)$为平移算术因子,$d_{w}(P)$、$d_{h}(P)$为尺度缩放算术因子。以其中的$d_{x}(P)$来说:它的实际值就是$w\phi$,为权重w乘以特征向量的形式。而其理论值是$t_{\ast }^{i}$,可以通过对(1)、(2)、(3)、(4)变换得到。最终梯度下降的优化目标便是通过最小化均方误差函数来求解该w矩阵。

测试阶段

训练阶段,我们可以采用IoU最大的Region进行训练,那test阶段呢?同一物体便可能包含大量重复的矩形框,这里作者采用的是非极大值抑制(non-maximum suppression)方法对这些区域进行筛选。NMS基于一个窗口区域被认为是最有可能表示某一个物体的时候,那么和这个窗口区域交叉面积大的proposal就可以认为不是需要的窗口区域。
 
具体思路为:从所有矩形框(Regions)中(不分类别)选取SVM分类器得分最高的Region,将与该Region IoU面积超过阈值的都删除。再从剩余的矩形框中选取SVM得分最高的Region,重复上述步骤,直到没有矩形框可选为止。

CNN特征提取相关:

CNN为什么选取pool5层的特征?

真实答案是如果不对模型进行fine-tuning,那么采用pool5、fc6、fc7特征训练的SVM分类器,性能是相似的;如果对模型用VOC数据fine-tuning,利用这些特征训练的分类器性能都会提升,而fc6、fc7会进一步得到更大提升,如下所示:

为什么不直接采用CNN端到端训练,而要接一个SVM?

确实也是可以的,但由于VOC数据集较小,如果直接端到端训练,CNN十分容易过拟合。而SVM则适合小样本训练,所以最后采用SVM进行分类。并且SVM虽然适合小样本,但作者经过调参,还是设置IoU>0.3即为正样本,比pretrain Alex Net时(IoU>0.5)的标准宽松,正样例会多一些

PS:CNN的尺度不变性:并不是一定不变,而是大概率不变。比如对于两幅size不同,内容相同的图像。假设某个卷积核提取的是图像边缘信息,那么对其Feature map进行Maxpooling操作后,都会把梯度最大值提取出来,调整Pooling的size或stride,很大概率会得到相同的map图。

RCNN存在四个明显问题:

1、多个候选区域对应的图像需要预先提取,占用较大的磁盘空间;
2、针对传统CNN需要固定尺寸的输入图像,crop/warp(归一化)产生物体截断或拉伸,会导致输入CNN的信息丢失;
3、每一个ProposalRegion都需要进入CNN网络计算,上千个Region存在大量的范围重叠,重复的特征提取带来巨大的计算浪费。
4、CNN、SVM、回归模型需要按顺序,进行单独训练,不能端到端训练。

Ref:

📎:Rbg大神个人主页,包含论文、slides、code等
📎:基于深度学习的目标检测技术演进:R-CNN、Fast R-CNN、Faster R-CNN
📎:目标检测-RCNN系列
📎:论文笔记:Rich feature hierarchies for accurate object detection and semantic segmentation
📎:RCNN学习笔记

论文笔记:《Selective Search for Object Recognition》

发表于 2018-03-03 |

背景

即将学习基于R-CNN的图像目标检测(Object Recognition),不得不提的一篇论文是《Selective Search for Object Recognition》,虽然仅用了传统的机器学习算法Selective Search + DPM/Hog特征 + SVM分类器,但其结构简单,可解释性强,对后面的Deep Learning物体检测模型,都有很大的借鉴意义。
 
本篇论文的创新点在于选择性搜索算法(Selective Search)。不同于传统的蛮力搜索(Exhaustive Search):需要不断改变窗口大小,遍历整张图像。Selective Search可以选择性地找出物体可能的存在区域,节省了大量不必要的特征提取开销。
 
结论先行:如图红色的区域便是Selective Search算法提取的可能存在物体的窗口:
图片名称

Selective Search算法描述

文章一开始便用这4幅图,说明了同一物体组成成分多样性的问题。图1显示了区分桌子和它上面的餐具需要利用一定的层次关系;图2的猫需要通过颜色来区分;图3的变色龙颜色不行,需要通过纹理特征(texture)来区分;图4中的车和车轮看似粘连在一起,但我们可以通过颜色、纹理两种特征加以区分。
图片名称小结:这样的特性说明了两个问题,物体识别时候,需要充分考虑图像物体的多样性(diversity),单一特征可能分得乱七八糟;另外,图像中物体布局有一定的层次(hierarchical)关系,在算法中的体现就是从小区域开始,一步步分层而上进行合并。

Selective Search算法详解

Selective Search算法对传统的exhaustive search从以下两方面进行了改进:

  • 采用多种先验知识对各个区域进行判别,避免一些无用的搜索,提高速度和精度
  • Selective Search只负责快速生成可能存在物体的窗口,并不做具体的特征检测

执行流程图

图片名称重点翻译:

  • input:彩色图像
  • output:物体可能的位置(很多个矩形坐标)
  • 首先借助这篇论文的方法,对图像实现一个粗略的分割:R = r1, r2…rn。调用方式比较简单,不再赘述,但要注意源图需是.ppm格式
  • 初始化一个空集S
  • 计算所有相邻区域之间的相似度,并放入S中(实质存放的是一个区域对,及其相似度信息)
  • 找出S中相似度最高的两个区域进行合并,Eg:r1, r2,进行合并为r_t1。删除S中所有与r1, r2相邻的元素,并重新计算r_t1与周围相邻区域的相似度,放入集合S中,并将r_t1放入另一个集合R中。重复这个步骤,直到S为空。此时所有小区域被合并成为原图的大小,但每次的合并信息,都被存放在了集合R中,最后的Combining Locations便是对R中的区域进行标号排序。
  • 从R中找出所有被标号的区域,计算其bounding box,便得到output。

PS:以上第三步的图像分割方法中考虑场景以及光照条件等影响,采用了RGB、灰度、HSV等8种颜色空间进行分割计算

相似度计算方式

相似度计算直接影响合并区域顺序,对最终的检测结果,有至关重要的影响。此处同样综合了多种维度的相似度信息,分别是颜色(color)相似度、纹理(texture)相似度、大小(size)相似度和吻合(fit)相似度,最终将4种相似度计算结果,用如下公式合并:$${s(r_{i}, r_{j})= a_{1}s_{colour}(r_{i}, r_{j}) + a_{2}s_{texture}(r_{i}, r_{j})
+a_{3}s_{size}(r_{i}, r_{j}) +a_{4}s_{fill}(r_{i}, r_{j})}$$

这里仅就图像的颜色空间相似度进行介绍

颜色是区分不同物体的一个重要因素,文中将每个Region的颜色信息统计成一个75维的直方图,即每一个颜色通道为25bins的直方图(255/25 == 9,25bins的意思就是每隔9个数值统计像素数量),之后用L1范数进行归一化(除以各维度绝对值之和)。相似度计算方式如下:$$s_{colour}(r_{i}, r_{j}) = \sum_{k=1}^{n}min(c\tfrac{k}{i},c\tfrac{j}{k})$$其实质就是计算两个直方图的交集,而新的区域特征向量采用如下与区域面积加权的方式实现。$$C_{t} = \frac{size(r_{i})\ast C_{i}+size(r_{j})\ast C_{j}}{size(r_{i})+size(r_{j})}$$而新区域面积就是两个源区域面积的简单相加$size(r_{i})+size(r_{j})$

Combining Locations

完成上述步骤,提取出了图像中大量的bounding box区域,而实际存在物体的区域可能只有其中的几个,这时候便需要对备选的bounding box赋予一定的优先级,从而进一步缩小后文特征提取的范围,文中称这一步处理为Combining Locations。
 
具体方法为:因为Selective Search是一个逐步合并的层级结构,所以最大的一张图标为’1’,合并成’1’的子图标为’2’、’3’,以此类推。但这样的话会造成越大的bounding box排序越靠前。所以作者又加入了一定的shuffle机制,即在每个序号前,乘上一个随机数$RAND\in (0,1)$。通过新计算出的数值,按从小到大排序,得出Region最终的排序结果。一般candidates数量为2K个

Object Recognition目标识别

接下来采用HOG/DPM等算子分别提取正负样本特征,加入SVM分类器进行训练,整个架构如图所示:
图片名称其中正样本为人工标注的bounding box,负样本为与正样本重叠程度20%~50%的Regions。特别地,删去负样本中,彼此重叠程度>70%的Regions。重复迭代过程中再加入hard negative example进行训练。
PS:重叠程度即IoU(intersection-over-union,是一个定位精度的评价标准)参数,计算图示如下所示:
图片名称

参考

📎:《Selective Search for Object Recognition》
📎:Selective Search算法的python实现
📎:Graph Based Image Segmentation
📎:http://blog.csdn.net/niaolianjiulin/article/details/52950797#t2
📎:http://jermmy.xyz/2017/05/04/2017-5-4-paper-notes-selective-search/

信号相关知识

发表于 2018-03-03 |

傅立叶级数

周期为T的周期信号,若满足“狄里赫利”条件,则可以将其展开为有限或无限个离散的正余弦函数累加的形式。它的物理意义是很明显的,这就是把一个比较复杂的周期运动看成许多不同运动的叠加,如下:
$$f(t) = A_{0}+\sum_{n=1}^{\infty }A_{n}sin(n\omega t + \varphi )$$
而对于任何一个非周期信号,同样可以用傅立叶展开的形式,只不过它的频域曲线是连续的(后文有涉及),在计算上也从累加符号变成了积分符号。两者的关系如下:
图片名称
那傅立叶变换的用处是什么?给你一个正弦曲线,让你从中拿走某个频率的分量(即滤波),时域图中基本无法看懂,频域中只要拿走几条竖线就好了。而在反向传播算法中,FFT可以把微分、积分,在频域中变为加减乘除的简单运算。

时域&频域

时域反应的是现实世界中我们最能感知的信号,横轴为时间,纵轴为振幅;而频域反应的是频率与振幅的关系。将一个表示波的函数从时域转化为频域的关系,便称为傅立叶变换。两个例子如下所示:
图片名称

PS:玄学傅立叶:

我们看似不规律的事情反而是规律的正弦波在时域上的投影,而正弦波又是一个旋转的圆在直线上的投影。
我们眼中的世界就像皮影戏的大幕布,幕布的后面有无数的齿轮,大齿轮带动小齿轮,小齿轮再带动更小的。在最外面的小齿轮上有一个小人——那就是我们自己。我们只看到这个小人毫无规律的在幕布前表演,却无法预测他下一步会去哪。而幕布后面的齿轮却永远一直那样不停的旋转,永不停歇。

如何用一幅图总结频域、时域、相位谱的关系(引自知乎)
图片名称

采样

以音频文件采样为例:同图像bmp文件一样,pcm文件保存的是未压缩的音频信息。
16bits 编码是指,每次采样的音频信息用2个字节保存。可以对比下bmp文件用分别用2个字节保存RGB颜色的信息。
16000采样率 是指 1秒钟采样 16000次。当前声卡常用的采样频率一般为44.1KHz,即一秒采样44100次,人耳最敏感的听力范围20Hz到8000Hz。显然采样频率越高声音的还原就越真实越自然。
即:1秒的16000采样率音频文件大小是 $2\times16000 = 32000$字节 ,约为32K
采样定理:衡量的是采样频率与信号频率之间的关系。采样定理规定,采样频率必须大于等于信号频率的两倍,才能够将原始信号从采样定理中完全恢复出来,否则会造成频率曲线混叠

图像大小变换

图像像素数量 = 图像分辨率相乘,例如:图像分辨率为$1280\times720$,那么这张图片像素有100万。在同一台设备上,图片分辨率越高,这张图片1:1放大时,图片面积越大;图片分辨率越低,这张图片1:1缩放时,图片面积越小。
所以,将$1280 \times720 $的图像缩小到$720 \times 540$时,便需要采用下采样(如果采用简单的丢弃或者最近邻插值,将会导致图像较大程度地失真,推荐参考双线性插值算法),这样再在同一台设备上,处理后的图片size就比较小。python代码如下:

1
2
3
4
5
6
7
8
9
from PIL import Image
import numpy
filename = "/Users/wxj/Desktop/WechatIMG2.jpeg"
img = Image.open(filename)
image = np.array(img) # PIL 图像转 numpy
imgSize = img.size #图片的长和宽
print (imgSize) # 1920*1080
out = img.resize((1080,640),Image.ANTIALIAS) #降低分辨率
out.save("1.jpg")

图像尺寸怎么计算呢?

pic_size = 分辨率$\times$3字节(rgb)图像,但这样发现跟电脑中显示的大小不同,因为图像文件(.jpg、png)等,都是有损压缩,一般会压缩几十到几百倍不等这样子。

图像金字塔

图像金字塔算法是对图像的下采样、上采样:

  • 其中下采样为采用高斯内核卷积;之后去除偶数行列
  • 上采样为将图像在每个方向扩大为原来的两倍,新增的行和列以0填充;再使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值
    而resize函数改变大小与图像金字塔的不同,采用的是线性插值补全丢失的像素,但实际用下来差别不大。

 
插个话:图像金字塔在目标检测中的应用
滑窗的尺寸一般是固定的,而多尺度通过图像金字塔实现。这样相比不断改变滑窗大小,效率更高。至于缩小多少倍,源图缩到滑动窗口大小即可以,这样代表整张图片是某个类。

假如人脸size[20,20],滑动窗口(box)Size[40,40]那么可能检不出这张人脸(人脸占据比例太小),而随着图像金字塔变换人脸size会越来越小,那人脸占据整个box的比例也会更低,导致无法检出。
换句话说:MinFaceSize(40),只能检测最小约为[40,40]的人脸。

算法时间复杂度相关

1、归并排序,分而治之,O(NlogN)
图片名称

2、二叉搜索树,左子树<根节点<右子树,O(logN)

3、B+树
查找一个特定值这个树挺好用,但是当你需要查找两个值之间的多个元素时,就会有大麻烦了。你的成本将是 O(N)。例如:查找B、C之间的值,需要中序遍历后(DBEAFC)再进行筛选。
这就需要用到B+tree:搜索复杂度还是在 O(log(N))(只多了一层);另外,最底层的节点是跟后续节点相连接的
图片名称

4、哈希表
哈希表这种数据结构可以用关键字来快速找到一个元素。为了构建一个哈希表,你需要定义下面两个参数:

  • 关键字的哈希函数。关键字计算出来的哈希值给出了元素的位置(叫做哈希桶)(哈希函数设计极大影响算法复杂度)
  • 关键字比较函数。一旦你找到正确的哈希桶,你必须用比较函数在桶内找到你要的元素。

Ref:

傅里叶分析之掐死教程(完整版)更新于2014.06.06

目标检测任务中的Bounding box绘制

发表于 2018-03-02 |

背景

计算机视觉目标检测任务中,输出的result是一系列图像及对应的txt文档,文档里面记录了检测到类别的bounding box信息:其中每行是一个类别,第1列代表哪一类,第2列是该类别置信度,后面4列是该bounding box的左上、右下的坐标信息(以图的左上方为坐标0点),如下所示:

7 0.9482899 157.50272 192.24951 216.85303 248.50638
12 0.7890044 247.94312 211.89938 304.7307 280.8666
18 0.5444266 101.857285 215.64401 181.53456 261.96036

解决方案

为了直观显示每张图像包含了哪些类别,我们用python对bounding box进行了重绘,并将图像按类别放置于对应的文件夹中,文件夹名称为1~21,以字典形式对应于21个类别,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# -*- coding:utf-8 -*-
#!/usr/bin/env Python
import os
import shutil
from PIL import Image, ImageDraw

#==========================建立索引文件夹==========================

Path1_A = "/home/tucoder/Desktop/demo1_result/S1/" # 图像按类型分配到各个文件夹
Path2_A = "/home/tucoder/Desktop/demo1_result/S2/" # 存储类型文本

dict1 = {'1':'飞机','2':'自行车','3':'鸟','4':'船','5':'瓶子','6':'公共汽车','7':'车','8':'猫','9':'椅子','10':'牛','11':'餐桌','12':'狗', \
'13':'马','14':'摩托车','15':'人','16':'植物','17':'羊','18':'沙发','19':'火车','20':'电视'} # 21类

os.mkdir(Path1_A)

os.mkdir(Path2_A)

for i in range(1, 21):
os.mkdir(Path1_A+str(i))

def draw(Path1, x1, y1, x2, y2, path2):
'''
绘制矩形框函数
原pic位置path1
处理后的pic被存储到path2中
'''
im = Image.open(Path1)
draw = ImageDraw.Draw(im)
line = 5
x, y = x1, y1
x_1, y_1 = x2, y2

for i in range(1, line + 1):
draw.rectangle((x + (line - i), y + (line - i), x_1 + i, y_1 + i), outline='red')
im.save(path2)

#==========================main==========================
if __name__ == "__main__":
result = []
alllist=os.listdir("/home/tucoder/Desktop/demo1_result")
for i in alllist:
if "." in i:
aa, bb=i.split(".")
if bb == "txt":
result.append(aa+"."+bb)
A = set()
for filename in result:
set1 = set()
string = '' # 每幅图片包含其class属性
str1 = filename[:filename.rindex(".")]
with open(filename, 'r') as f:
for line in f.readlines():
list1 = line.split()
if float(list1[1]) >= 0.80: # 类别置信度>=0.8,判定其为真
A.add(list1[0])
Path_1 = str1+".jpg"
Path_2 = Path1_A+'/'+list1[0]+"/"+str1+".jpg"
if list1[0] not in set1:
set1.add(list1[0])
draw(Path_1, float(list1[2]), float(list1[3]), float(list1[4]), float(list1[5]), Path_2)
string = string +list1[0]+"-"
else:
draw(Path_2, float(list1[2]), float(list1[3]), float(list1[4]), float(list1[5]), Path_2)
set1.clear()
List = []
for str_i in list(A):
List.append(dict1[str_i])
print(List)
with open(Path2_A+'text', 'w') as f:
f.write(str(List))

最终效果如图:

图片名称

调用百度SDK进行长语音识别

发表于 2018-02-28 |

背景

项目中需要用到语音识别技术,之前百度的语音识别SDK只支持时长小于1分钟的语音,所以对于十几分钟的电影,笔者需要自己先进行断句,然后一句句送入百度SDK,这样既麻烦,而且句与句之间的连贯性不够好,经常会造成一些啼笑皆非的识别。

长语音识别

2018年1月,百度开放了长语音识别SDK,可以进行自动断句,目测是根据短时能量法(即停顿时间)完成的,对识别后的语句还会自动修改,应该引入了语言模型进行修正。笔者今天上午又进行了一番尝试,至于识别效果嘛,是比以前好一些,但依然不敢恭维,以下:
首先采用ffmpeg模块,将视频转化为wav音频文件;接着将文件转码为采样率16K或8K的PCM无损格式

1
2
ffmpeg -i $audiopath -f wav -vn ${audiobasename}".wav" # 提取视频中的音频文件
ffmpeg -y -i $audiobasename".wav" -acodec pcm_s16le -f s16le -ac 1 -ar 16000 16k-0.pcm # wav音频文件转PCM

接着去百度Ai开放平台,下载对应平台的SDK。解压后直接cd到sample/asr目录下, 运行build.sh(实际执行的是/src/main.cpp),便可以识别样例中的一个PCM文件。
 
若要识别你自己的音频文件,需要首先接入百度AI服务,由于Access Token的有效期为30天,我们把鉴权请求机制写入build.sh中,这样每次调用SDK都会首先先执行鉴权。

1
2
3
4
#!/bin/bash
curl -i -k 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxxx&client_secret=xxxx'
# 'xxxx'的部分填入你在百度自己申请的id和key
make && echo && echo "build success, wait 3s to run" && sleep 3 && ./main

之后修改main.cpp中的asr_set_config_params、asr_set_start_params两个参数,具体如下:

1
2
3
const std::string app_id = "xxxxx"; 填入你申请的ID
const std::string chunk_key = "xxxxxxx";
const std::string secret_key = "xxxxxxxx";

1
2
3
// 设置启动参数
void asr_set_start_params(bds::BDSSDKMessage& start_params) {
const std::string app = "xxxxx"; # 填入你申请的任务名称

将之前转好的音频文件,命名为16k-0.pcm,放置于/asr/pcm路径下,重新执行build.sh,便可以在Terminal生成识别后的音频文件!
PS: 建议的环境是GCC、G++ 4.8.5,笔者在GCC 5.4.0也进行了测试,无法正确运行。

Textrank

Textrank是传统抽取文章摘要的方式,算法思想借鉴了网页排序的pagerank技术,这里我们采用Github中已开源的TextRank4ZH模块,安装及调用文档都比较详细,此处不再赘述。

Ref:

https://www.baidu.com/s?ie=UTF-8&wd=textrank4zh
http://ai.baidu.com/tech/speech/lsr

Conv/Pooling的方向传播

发表于 2018-02-27 |
12345
祥吉

祥吉

CV/NLP,Studying,Coding

49 日志
10 标签
E-Mail 知乎 微博
© 2018 祥吉
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.4
博客全站共37.2k字
本站访客数: