Update
代码已经上传到github上了,可以点这里
Cutting
一直说这要整理一下Computer Vision课程的大作业,拖了好久。这两天忙着写一个订单处理的第三方库,陷入了僵局,所以换个口味,把大作业整理一下。
Requirement
Water depth measurement.
实现目标:通过使用计算机视觉及图像处理技术,通过正确检测插入水体的标尺和水体水平面的刻度值来确定水位高度。图像数据见附件。
可允许用户输入标尺最上端的高度值、照相机镜头距离标尺最上端的和水平面形成的夹角、刻度尺正面和照相机之间夹角值,以及标尺每个刻度的高度值。
评分标准:
- 能否解决存在的多种问题,其中包括:
a. 标尺刻度靠近水面的部分可能由于长期浸泡在污水中出现污渍而无法识别。
b. 水面可能出现的雾气,造成识别困难。
c. 标尺可能有一定的弧度,造成精确度量存在问题。 - 计算效率:使用任意目前流行的Intel i3处理器及更快的处理器,每个4096*4096像素分辨率以内的图像测量时间不超过20秒(包含图像读取及数据值输出)。
3.系统完整性。
使用语言:Visual C++(可使用OpenCV)
部分附件
样例图
Train of Thought
整个过程大致分为四个阶段:图像预处理、识别、过滤、数据处理
1. 预处理
首先会进行一个直方图均衡化的操作。再由于输入的图像差别较大,有如上图这种十分清晰的,也存在模糊到人工识别也比较吃力的。所以显然不同的清晰度应该有不同的处理方式。这里简单的将清晰度分为清晰
、模糊
两类。
对于清晰
的图片,进行适度腐蚀膨胀操作,以进一步提高图片中标尺的对比度。
//腐蚀、膨胀int erosion_size = 3;Mat element = getStructuringElement( MORPH_RECT,Size( 2*erosion_size + 1, 2*erosion_size+1 ),Point( erosion_size, erosion_size ) );/// 腐蚀操作erode( origin, origin, element );dilate(origin, origin, element);
对于模糊
的图片数据,先进行滤波,再提高对比度
//创建并初始化滤波模板cv::Mat kernel(3,3,CV_32F,cv::Scalar(0));kernel.at<float>(1,1) = 5.0;kernel.at<float>(0,1) = -1.0;kernel.at<float>(1,0) = -1.0;kernel.at<float>(1,2) = -1.0;kernel.at<float>(2,1) = -1.0;cv::filter2D(origin,origin,origin.depth(),kernel);int alpha = 1.5;int beta = 50;for( int y = 0; y < origin.rows; y++ ){for( int x = 0; x < origin.cols; x++ ){for( int c = 0; c < 3; c++ ){origin.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha*( origin.at<Vec3b>(y,x)[c] ) + beta );}}}
对比效果:
腐蚀膨胀
2. 识别
考查了Haar Like
、SIFT
、LBP
、HOG
等算法。Haar Like
多用于人脸识别,LBP
多用于基于纹理特征的监测,所以不是很适合。SIFT
做了简单测试,识别效果如下:
SIFT识别效果
不是很理想,所以最后选定了HOG
算法。
- 训练素材准备
由于检测的目标多位于水边,环境多为山、天空、水、泥土等,所以额外加入了这些素材作为负样本。由于负样本创建有一定规格要求,所以使用Python脚本,批量裁剪,最后共得到负样本42360个。 - 训练识别过程
这一过程网上有不少代码可供参考,主要是对于参数等选择比较重要。这里我们定义部分参数如下:
//识别与检测的参数
#define WIN_SIZE Size(64,64)
#define BLOCK_SIZE Size(8,8)
#define BLOCK_STRIDE Size(4,4)
#define CELL_SIZE Size(4,4)
#define BIN 12
2. 过滤
对于识别结果需要进行过滤。这里我们定义了一个Mask
算法,用以合并多个过滤算法多结果。
对于每一种过滤算法,都会有各自的保留区域,将它们叠加,通过某个大小的矩形扫描,如果该矩形区域内,每一层的保留区域面积占比大于一个可调参数Threshold
,则认为该矩形区域应该保留。
示意图
这里我们使用了两种过滤算法:
-
ColorFilter
基于颜色特征的过滤。由于标尺上的颜色固定,故可以丢弃与之无关的颜色区域颜色过滤
-
Canny边缘检测过滤
边缘检测过滤
最后运用上面提到的多层Mask合并算法,得到最终的过滤保留区域。Mask叠加
可以看到将过滤结果应用于识别结果时,大量的误识别被过滤掉了。原图非常大,绿色的框框即为识别结果。
-
单峰过滤
由于一张图中仅有一个标尺,所以通过前面的过滤后,我们认为,矩形在图片上的分布应该如图所示。将除最高峰以外的矩形丢弃。 -
Rectangle修正
标尺中的"E"标示实际分布是均匀的,所以即使无法完全识别,也可以通过算法进行一个修正,自动补全出未被识别的"E"
效果如下:修正效果
4. 数据处理
最后的处理是针对识别结果进行纯数值分析的优化。我们认为识别系统稳定后,会存在一定的固有误差,可以通过线性拟合的方式进行一个修正。将识别数据与真实数据进行拟合(这一点老师不是很赞成,认为没有必要)。
数据拟合