[深度学习] 增加样本——弹性变换算法实现
来源:互联网 发布:财务预算软件 编辑:程序博客网 时间:2024/06/06 13:49
我们都知道,深度学习的成功的原因主要有两点:
(1)当前计算机的计算能力有很大提升;
(2)随着大数据时代的到来,当前的训练样本数目有很大的提升。
然而深度学习的一大问题是,有的问题并没有大量的训练数据,而由于深度神经网络具有非常强的学习能力,如果没有大量的训练数据,会造成过拟合,训练出的模型难以应用。
因此对于一些没有足够样本数量的问题,可以通过已有的样本,对其进行变化,人工增加训练样本。
对于图像而言,常用的增加训练样本的方法主要有对图像进行旋转、移位等仿射变换,也可以使用镜像变换等等,这里介绍一种常用于字符样本上的变换方法,弹性变换算法(Elastic Distortion)。该算法最先是由Patrice等人在2003年的ICDAR上发表的《Best Practices for Convolutional Neural Networks Applied to Visual Document Analysis》提出的,最开始应用在mnist手写体数字识别数据集中,发现对原图像进行弹性变换的操作扩充样本以后,对于手写体数字的识别效果有明显的提升。当前也有很多人把该方法应用到手写体汉字的识别问题中,是一种很普遍的扩充字符样本图像的方式。
下面来详细介绍一下算法流程:
(1)首先需要对图像中的每个像素点(x,y)产生两个-1~1之间的随机数,Δx(x,y)和Δy(x,y),分别表示该像素点的x方向和y方向的移动距离;
(2)生成一个以0为均值,以σ为标准差的高斯核k_nn,并用前面的随机数与之做卷积,并将结果作用于原图像。
作者在这里提出σ的大小与弹性变换的处理结果息息相关,如果σ过小,则生成的结果类似与对图像每个像素进行随机移动,而如果σ过大,则生成的结果与原图基本类似。
但是作者在文中并没有解释高斯核k_nn中n的大小对图像处理结果的影响,下面我的操作结果以n和σ为变量,进行了实验,我的原图是573*573大小,产生的卷积结果实验结果Δx(x,y)和Δy(x,y)各自扩大了100倍(因为Δx(x,y)和Δy(x,y)是-1~1的,如果不扩大的话基本看不出变化),实验结果如下图所示:
(1)n = 5,σ=4,8,16时:
(2)n = 21,σ= 4,8,16时:
(3)n = 105,σ = 4,8,16时:
可以看出来,只有在n足够大(与要处理的图像相比),且σ大小合适时才能够到合适的扭曲图像,如图所示,这里n = 105且σ=8时比较合适。
现在的论文中,只是介绍了算法流程,但具体的实现方法并没有介绍,我在实现的过程中一直很不清楚把Δx(x,y)和Δy(x,y)和卷积核做卷积具体是怎样做,而且也不太清楚卷积核的大小是多少,最终自己摸索这得到结果,应该是像我代码的方法。我的代码如下,对于高斯卷积和图像的读取使用的Opencv的函数,最终也可以自己在实现的函数中添加变量控制n和σ,我这里图方便,懒得加了。
Mat elasticDistort(Mat& img){ //转成灰度图像 cvtColor(img,img,CV_RGB2GRAY); //分别存放原图各个像素点对应的Δx(x,y)和Δy(x,y) Mat Xmv(img.rows,img.cols,CV_32FC1); Mat Ymv(img.rows,img.cols,CV_32FC1); //初始化Xmv srand(time(NULL)); for(int i = 0; i<img.rows; i++) { float* p = Xmv.ptr<float>(i); for(int j = 0; j<img.cols; j++) { p[j] = (rand() % 3 - 1) * 100;//注意,Xmv和Ymv两个矩阵是产生随机数的基础上进行高斯模糊得到的,这里是产生随机数。2017-04-26 } } //初始化Ymv srand(time(NULL) + 100); for(int i = 0; i<img.rows; i++) { float* p = Ymv.ptr<float>(i); for(int j = 0; j<img.cols; j++) { p[j] = (rand() % 3 - 1) * 100;//注意,Xmv和Ymv两个矩阵是产生随机数的基础上进行高斯模糊得到的,这里是产生随机数。2017-04-26 } } //使用Opencv函数,对Xmv和Ymv分别做卷积,这里的105和4分别为n和σ GaussianBlur(Xmv,Xmv,Size(105,105),4,4); GaussianBlur(Ymv,Ymv,Size(105,105),4,4); //处理结果图像 Mat result(img.rows,img.cols,img.type()); result.setTo(255); //把Xmv和Ymv作用到原图像中,得到结果图像 for(int i = 0; i<img.rows; i++) { for(int j = 0; j<img.cols; j++) { int newX = i + cvRound(Xmv.at<float>(i,j)); int newY = j + cvRound(Ymv.at<float>(i,j)); newX = newX < 0 ? 0 : newX; newY = newY < 0 ? 0 : newY; newX = newX >= img.rows ? img.rows - 1 : newX; newY = newY >= img.cols ? img.cols - 1 : newY; result.at<uchar>(newX,newY) = img.at<uchar>(i,j); } } //先对图像做腐蚀操作,再做膨胀操作,可以去掉字符中的白色纹理 //Mat element = getStructuringElement(MORPH_RECT, Size(2, 2)); //erode(result,result,element); //dilate(result,result, element); return result;}
——————————————————————————————————分割线——————————————————————————————————
更新:添加了一个average的过程,这样可以起到一定的平滑作用使得生成的图像更自然
//对图像进行弹性变换,扩充训练数据量,这种方法相比对图像进行仿射变换效果要好Mat elasticDistort(Mat& img,int gaussianWindowSize,double gaussianKernelSize){ cvtColor(img,img,CV_RGB2GRAY); Mat Xmv(img.rows,img.cols,CV_32FC1); Mat Ymv(img.rows,img.cols,CV_32FC1); //srand(time(NULL)); for(int i = 0; i<img.rows; i++) { float* p = Xmv.ptr<float>(i); for(int j = 0; j<img.cols; j++) { p[j] = (rand() % 3 - 1) * 10;//注意,Xmv和Ymv两个矩阵是产生随机数的基础上进行高斯模糊得到的,这里是产生随机数。2017-04-26 } } //srand(time(NULL) + 100); srand(clock()); for(int i = 0; i<img.rows; i++) { float* p = Ymv.ptr<float>(i); for(int j = 0; j<img.cols; j++) { p[j] = (rand() % 3 - 1) * 10;//注意,Xmv和Ymv两个矩阵是产生随机数的基础上进行高斯模糊得到的,这里是产生随机数。2017-04-26 } } GaussianBlur(Xmv,Xmv,Size(gaussianWindowSize,gaussianWindowSize),gaussianKernelSize,gaussianKernelSize); GaussianBlur(Ymv,Ymv,Size(gaussianWindowSize,gaussianWindowSize),gaussianKernelSize,gaussianKernelSize); Mat result(img.rows,img.cols,img.type()); result.setTo(255); for(int i = 0; i<img.rows; i++) { for(int j = 0; j<img.cols; j++)//这里进行了更改! { int newX_low = i + cvFloor(Xmv.at<float>(i,j)); int newX_high = i + cvCeil(Xmv.at<float>(i,j)); int newY_low = j + cvFloor(Ymv.at<float>(i,j)); int newY_high = j + cvCeil(Ymv.at<float>(i,j)); newX_low = newX_low < 0 ? 0 : newX_low; newX_high = newX_high < 0 ? 0 : newX_high; newY_low = newY_low < 0 ? 0 : newY_low; newY_high = newY_high < 0 ? 0 : newY_high; newX_low = newX_low >= img.rows ? img.rows - 1 : newX_low; newX_high = newX_high >= img.rows ? img.rows - 1 : newX_high; newY_low = newY_low >= img.cols ? img.cols - 1 : newY_low; newY_high = newY_high >= img.cols ? img.cols - 1 : newY_high; int sum = img.at<uchar>(newX_low,newY_low) + img.at<uchar>(newX_low,newY_high)+ img.at<uchar>(newX_high,newY_low) + img.at<uchar>(newX_high,newY_high); double avg = sum * 1.0 * 0.25; if(avg < 110) { avg = 0; } else if(avg > 145) avg = 255; result.at<uchar>(i,j) = avg; //result.at<uchar>(newX,newY) = img.at<uchar>(i,j); } } //cout<<result<<endl; return result;}
————————————————————2017年4月26日更新——————————————————————
Mat Xmv(img.rows,img.cols,CV_32FC1); Mat Ymv(img.rows,img.cols,CV_32FC1); //srand(time(NULL)); for(int i = 0; i<img.rows; i++) { float* p = Xmv.ptr<float>(i); for(int j = 0; j<img.cols; j++) { p[j] = (rand() % 3 - 1) * 10;//注意,Xmv和Ymv两个矩阵是产生随机数的基础上进行高斯模糊得到的,这里是产生随机数。2017-04-26 } } //srand(time(NULL) + 100); srand(clock()); for(int i = 0; i<img.rows; i++) { float* p = Ymv.ptr<float>(i); for(int j = 0; j<img.cols; j++) { p[j] = (rand() % 3 - 1) * 10;//注意,Xmv和Ymv两个矩阵是产生随机数的基础上进行高斯模糊得到的,这里是产生随机数。2017-04-26 } } GaussianBlur(Xmv,Xmv,Size(gaussianWindowSize,gaussianWindowSize),gaussianKernelSize,gaussianKernelSize); GaussianBlur(Ymv,Ymv,Size(gaussianWindowSize,gaussianWindowSize),gaussianKernelSize,gaussianKernelSize);
上面的代码是Xmv和Ymv矩阵的生成代码,从代码中Xmv和Ymv的生成方法实际上就是先产生两个随机矩阵,然后在该随机矩阵上进行高斯模糊,上面两个for循环做的就是生成随机矩阵,如果要弹性变形的图像比较大,可以考虑在较大的范围内生成随机数,如p[j] = (rand() % 5 - 2) * 10,即生成-20到20之间的随机数,这样会更适合较大的图像的随机弹性形变样本生成。
- [深度学习] 增加样本——弹性变换算法实现
- 深度学习样本采集
- 深度学习算法实现
- 深度学习 -- 数据样本方面
- 弹性域学习——说明性弹性域
- 弹性域学习——键弹性域
- 弹性域学习——键弹性域
- OpenCV学习——物体跟踪的粒子滤波算法实现之粒子集合变换
- 深度学习之对抗样本问题
- 深度神经网络中的对抗样本与学习
- 深度学习样本生成data augmentation
- 使用机器学习算法对流量分类的尝试——基于样本分类
- 使用机器学习算法对流量分类的尝试——基于样本分类
- 机器学习深度学习基础笔记(4)——Backpropagation算法实现
- 深度学习算法原理——神经网络
- 常用算法——深度学习
- 深度学习笔记——算法总结
- 二维哈尔小波变换算法——MATLAB、C++实现
- 切比雪夫不等式的证明
- 抽象类和接口
- 添加本地jar包到maven仓库
- POJ 1125 Stockbroker Grapevine
- SMP,NUMA,MPP
- [深度学习] 增加样本——弹性变换算法实现
- linux驱动开发中的互斥机制与irq下半部处理机制
- 143. Reorder List**
- ASP.NET Aries 入门开发教程3:开发一个列表页面及操控查询区_0
- Java程序:汉诺塔
- 笔记:对话框太大,超出屏幕范围,导致找不到“确定”按钮,如何解决?
- NoSql介绍与分布式Mongo
- 最小二乘网格优化
- 通过 Grub 来引导启动 UBUNTU