机器视觉常用算法原理及opencv实现源码
视觉/图像重磅干货,第一时间送达
来源:机器视觉课堂
1机器视觉的成像
机器视觉应用到工业自动化检测领域,可以代替人工从而提高工作效率并降低成本。目前,基于视觉的工业检测主要用在定性分析和定量检测中,但在大部分实际应用过程中,最终落脚仍为定量检测,定性分析任务只作为定量检测任务的前提。因此,需要关注图像的生成过程。图像的生成包括两个关键问题,一是物理世界中点与图像中点的对应,二是图像中点的亮度。
1.1相机模型
相机成像的小孔模型:
在世界坐标系中的点和相机坐标系中的点的对应关系可以用数学公式描述为:
1.2图像亮度
图像亮度与物体的辐照强度和传感器的光谱灵敏的决定,辐照强度指照射到像平面的“辐射能”在单位面积上的功率,而辐照强度又由场景辐射强度(光照强度)决定,它们成正比例关系,比例系数有成像系统的参数决定。
要使光线能到达像平面,镜头的光圈必须有一定的直径,因此前述小孔模型只是一个理想模型。当相机光圈直径不为0时,物体中一个点将在像平面上成一个圆斑,导致图像不清晰,因此需要在成像系统中加入透镜来对光线进行汇聚。
1.3 透镜
一个理想的透镜具有如下两个特征:1:它的投影方式和小孔模型相同;2,:将一定数量的光线汇聚到一起。完美的透镜系统中,射向透镜中心的光线不发生偏转,射向透镜边缘的光线将会偏转并和射向透镜中心的光线汇聚。但这一理想透镜只会对特定距离的物体才会准确聚焦,物体前后移动生成的光斑小于传感器分辨率的范围被称为成像域深度,也称景深。透镜直径越大,成像域深度就越小,因此在调节视觉系统时越有可能造成的聚焦误差。
任何简单透镜都会产生缺陷和像差,但根据理想简单透镜的成像原理和光路的可逆性,将一块透镜放到另一块透镜的焦距附近,可以提高成像质量,因此将多个透镜沿光轴仔细排列成为组合透镜。
不管是组合透镜还是简单透镜,其投影方式不可能和理想小孔模型一样,对所有光线的准确聚焦也是无法实现的,总会产生像差。但是对于一个设计精良的透镜系统,这些缺陷会尽可能的做到最小。
2.灰度图像基本操作
任何颜色都有红、绿、蓝三原色组成,在opencv中使用cvtColor函数使用COLOR_RGB2GRAY参数把彩色图转为灰度图。灰度是指黑白图像中的颜色深度,每个像素一般使用一个字节存储,范围为0-255,共256个灰度等级。白色为255,黑色为0。
灰度图的预处理包含逐像素处理和卷积处理等。逐像素处理针对每一个像素独立处理,处理前后同样位置的像素具有相同的映射关系,亮度变化,直方图均衡化属于逐像素处理操作。卷积处理后图中一个像素值与原图中多个像素值相关,滤波,特征点提取等都属于卷积操作。
2.1逐像素处理操作
对每个像素点独立进行函数运算,返回的仍然是图像,通常用于把机器视觉关注的特征凸显出来。
2.1.1图像二值化
图像二值化就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。
图像二值化公式:
图像的二值化需要确定阈值,阈值可以人为根据先验指定,也可以根据图像全局特征进行计算。
图像自动二值化阈值计算算法有大津法(OTSU)和三角形法(TRIANGLE)。
三角法使用直方图数据,基于纯几何方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值。当最大波峰在暗的一侧时,可通过对直方图进行翻转进行处理。
OpenCV提供了图像二值化的函数cvThreshold和inRange,cvThreshold函数可以手动指定阈值,也可以指定参数THRESH_OTSU使用OTSU算法,指定参数THRESH_TRIANGLE使用三角法。inRange可以指定阈值分割的上下界,并可同时对多个通道操作。
如下图是电子jj镜头获取到的一幅图像,任务是要识别视野中的红色气球中心。
首先将彩色图转换为灰度图,发现无法使用二值化方法直接分割。
因此使用彩色图像单通道数据进行分割,经过测试,单使用任一通道都无法有效分割,因此将彩色图像转到LAB颜色空间,对三个通道分别二值化,然后求交得到,中心的十字可以使用膨胀和腐蚀形态学操作消除。
算法主要代码如下:
Mat ThresholdLAB(Mat src, Vec3d low, Vec3d high)
{
Mat bgr, lab, dst;
src.convertTo(bgr, CV_32FC3, 1.0 / 255, 0);
cvtColor(bgr, lab, CV_BGR2Lab);
Mat mask;
inRange(lab, Scalar(low[0], low[1], low[2]), Scalar(high[0], high[1], high[2]), mask);
Mat gray_all;
GaussianBlur(mask, gray_all, Size(3, 3), 0);
threshold(gray_all, gray_all, 100, 255, THRESH_BINARY);
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
//去除瞄准十字
erode(gray_all, gray_all, element);
dilate(gray_all, gray_all, element);
return gray_all;
}
2.1.2亮度变换
亮度变换有两个功能,一是提高对比度,二是消除或者减轻因环境光照强度、物体表面反射、相机增益等引起的亮度变化。为了补偿这些因素,对图像进行转换,以使所得像素值的均值为0,方差为1。
在实际的图像处理中,如果认为图像的绝对亮度不包含视觉任务需求的关键信息,则可以使用这种方式对图像进行处理。
原始图片是两张亮度不同的人脸图片,它们的像素均值和标准差都不同,可以进过亮度变化,使第二张图的像素亮度和方差变为与第一张图相等。处理结果如下:
使用公式如下:
均值和标准差可以使用OPENCV中的函数meanStdDev实现。
算法主要代码:
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
int tmp = img.at<uchar>(i, j);
double yy = (tmp - mean2) / stddev2*stddev1 + mean1;
yy = yy > 255 ? 255 : yy < 0 ? 0 : yy;
img.at<uchar>(i, j) = yy;
}
}
2.1.3直方图均衡化
图像的直方图可以反映图像的一些全局特征,比如偏暗,偏亮,亮度集中等。
直方图均衡化处理的“中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改变成“均匀”分布直方图分布。直方图均衡化的映射函数为:
Opencv中equalizeHist函数可以完成上述操作,输入的图像必须是8位的单通道图像。
2.2卷积操作
数字图像处理中的预处理步骤很多属于卷积操作,各种滤波,特征提取等都是利用了卷积操作完成。进行卷积操作时,定义一个卷积核,使用这个卷积核和图像进行卷积,对于图像上的一个点,让卷积核的原点和该点重合,然后卷积核上的点和图像上对应的点相乘,然后各点的积相加,就得到了该点的卷积值。对图像上的每个点都进行相同处理。
2.2.1基于卷积运算的图像滤波
图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
Opencv中提供的滤波器有方框滤波boxFilter,高斯滤波GaussianBlur,中值滤波medianBlur以及双边滤波bilateralFilter,前两者为线性滤波,后两者为非线性滤波。双边滤波严格上不属于卷积操作,因为卷积核在图像上滑动时会根据特征调整卷积核(权重)。
方框滤波器的卷积核为
中值滤波器的卷积核为
高斯滤波的卷积核通过二维高斯分布进行计算
高斯滤波算子通常也作为其它图像处理算子的一部分,比如LoG(Laplace of Gaussian)算子和DoG(Difference of Gaussian)算子。
双边滤波的基本思路是同时考虑将要被滤波的像素点的空域信息和值域信息。首先,对于图像滤波来说,通常认为图像在空间中变化缓慢,因此相邻的像素点会更相近。但是这个假设在图像的边缘处变得不成立。如果在边缘处也用这种思路来进行滤波的话,边缘会模糊掉。因此考虑再利用像素点的值的大小进行补充,因为边缘两侧的点的像素值差别很大,因此会使得其加权的时候权重具有很大的差别,从而使得只考虑自己所属的一边的邻域。可以理解成先根据像素值对要用来进行滤波的邻域做一个分割或分类,再给该点所属的类别相对较高的权重,然后进行邻域加权求和,得到最终结果。
2.2.2基于卷积运算的特征提取
特征提取是图象处理中的一个初级运算,它检查每个像素来确定该像素是否代表一个特征,因此一般作为更大的算法的一部分。图像中的特征指边,角,交叉,区域等。有些特征可以使用卷积的方式提取。
Robert 算子:Robert算子是一种种利用局部差分算子寻找边缘的算子,对具有陡峭的低噪声的图像效过较好。Robert算子的卷积核为:
Sobel算子:Sobel算子主要用作边缘检测,它属于一阶离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量。Sobel算子的卷积核有两个,对应两个方向。
Opencv中Sobel的使用:
Mat task4 = imread("pic/task4-2.jpg");
Mat gray,sobel1,sobel2,sobel;
cvtColor(task4, gray, COLOR_RGB2GRAY);
Sobel(gray, sobel1, CV_8U, 1, 0, 3);
Sobel(gray, sobel2, CV_8U, 0, 1, 3);
addWeighted(sobel1, 0.5, sobel2, 0.5, 0, sobel);
imshow("task4", task4);
imshow("sobel", sobel);
waitKey();
Soble算子处理效果:
Laplacian算子:Laplacian算子属于二阶微分算子,由于拉普拉斯算子是一种微分算子,因此强调的是图像中灰度的突变,并不强调灰度缓慢变化的区域。这将产生把浅灰色边线和突变点叠加到暗色背景中的图像。将原图像和拉普拉斯图像叠加在一起,可以复原背景特性并保持拉普拉斯锐化处理的效果。Laplacian算子是一种特别容易受到噪声干扰的边缘发现算子,所以经常对要处理的图像首先进行一个高斯模糊,然后再进行拉普拉斯算子的边缘提取。先进行高斯滤波,再使用拉普拉斯算子的过程统称为LoG(Laplace of Gaussian function)算子。
Laplacian算子的卷积核有两种,分为4邻域和8邻域:
Opencv中Laplacian的使用:
Mat task5 = imread("pic/task4-3.jpg");
cvtColor(task5, task5, COLOR_BGR2GRAY);
Mat gray_all, laplacian;
GaussianBlur(task5, gray_all, Size(3, 3), 0);
Laplacian(task5, laplacian, CV_8U, 3);
imshow("task5", task5);
imshow("laplacian", laplacian);
waitKey();
Laplacian算子处理效果:
Canny 边缘检测算法:Canny边缘检测算法使用了上述介绍的部分算子,比如高斯滤波算子,Sobel算子,其关注具体的边缘检测应用。该算法由Canny在1986年提出,虽然年代久远,但它是边缘检测的一种标准算法,在研究中有广泛使用。Canny边缘检测算子的目标是找到一个最优的边缘检测算法,即算法要尽可能的检测边缘,检测到的边缘要和实际边缘尽量接近,对图像的中边缘只标识一次。Canny算法检测边缘点的具体步骤为:灰度化,高斯滤波,用一阶偏导有限差分算子计算梯度幅值和方向,对梯度幅值进行非极大值抑制,用双阈值算法检测和连接边缘,边界跟踪。在opencv中,差分算子使用sobel算子,可以手动指定尺寸,双阈值由手动指定,边界跟踪使用滞后滤波。
3.灰度图像特征提取
除了上面提到的使用逐像素操作和卷积操作提取图像边缘特征的操作外,数字图像处理中有更加高级的特征提取方法。
3.1边界特征法
边界特征法该方法通过对边界特征的描述来获取图像的形状参数,其中Hough变换检测平行直线方法和边界方向直方图方法是经典方法。
Hough 变换是利用图像全局特性而将边缘像素连接起来组成区域封闭边界的一种方法,其基本思想是点—线的对偶性;
边界方向直方图法首先微分图像求得图像边缘,然后,做出关于边缘大小和方向的直方图,通常的方法是构造图像灰度梯度方向矩阵。
Hough直线检测代码:
Mat srcImage = imread("pic/task9-2.jpg"),gray,show;
show = srcImage.clone();
cvtColor(srcImage, gray, COLOR_RGB2GRAY);
Canny(gray, gray, 100, 200, 3, false);
vector<Vec2f> lines;
HoughLines(gray, lines, 1.0, CV_PI / 60, 100, 0, 0);
RNG rng(time(0));for (size_t i = 0; i < lines.size(); i++)
{
int rand1 = rng.uniform(0, 150);
int rand2 = rng.uniform(0, 150);
int rand3 = rng.uniform(0, 150);
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = arho, y0 = brho;
pt1.x = cvRound(x0 + 2000 (-b)); //把浮点数转化成整数
pt1.y = cvRound(y0 + 2000 (a));
pt2.x = cvRound(x0 - 2000 (-b));
pt2.y = cvRound(y0 - 2000 (a));
line(show, pt1, pt2, Scalar(rand1, rand2, rand3), 4, LINE_AA);
}
imshow("output", show);
waitKey();
Hough直线检测实验效果:
3.2几何参数法
形状的表达和匹配采用更为简单的区域特征描述方法,例如采用形状定量测度比如面积、周长,一阶矩,二阶矩,不变矩等形状参数。形状最小包围矩形的面积,周长,长边方向等,对于椭圆等形状,可以利用圆度、偏心率、主轴方向等几何参数,进行基于形状特征的图像检索。形状参数的提取,必须以图像处理及图像分割为前提,几何参数的准确性必然受到图像分割效果的影响,对分割效果很差的图像,几何参数甚至无法提取。
3.2.1轮廓面积和周长
在Opencv中使用contourArea计算轮廓的面积,单位是像素个数;使用arcLength来计算轮廓和曲线的长度。
轮廓的面积和周长可以用来筛选目标,轮廓面积和周长的比例和目标的细长程度有关,也可作为筛选条件。
3.2.2图像的矩
矩函数在模式识别、目标分类、目标识别与方位估计、图像的编码与重构等都有广泛应用。从一幅图像计算出来的矩,不仅可以描述图像形状的全局特征,而且可以提供大量关于该图像不同的几何特征信息,如大小,位置、方向和形状等。图像矩这种描述能力广泛应用于各种图像处理、计算机视觉和机器人技术领域的目标识别与方位估计中。
一阶矩与形状有关系,可以用来计算重心,二阶矩表示目标相对原点扩展程度,不具有选择不变性,三阶矩不单独使用,和二阶矩组合可以得到Hu不变矩,具有旋转,平移,缩放不变性,用来做匹配。Opencv中函数moments用来计算矩,使用Humoments计算Hu不变矩。Opencv模板匹配函数matchShape使用Hu不变矩进行模板匹配并给出匹配参数。
其中前6个具有平移,缩放,旋转和翻转不变性,第7个矩表示图像或特征的翻转。
Hu对于图像中的大物体和纹理特征简单的图识别效果较好,可以用来对车牌字符进行识别等应用。下图是对字母K进行缩放和旋转处理后计算出的Hu矩,7个值距离很近。
opencv代码:
string path = "pic/task6-" + to_string(i+4) + ".png";
Mat task6 = imread(path, IMREAD_GRAYSCALE);
threshold(task6, task6,100,255, THRESH_BINARY);
Moments mu = moments(task6, true);
double humoments[7];
HuMoments(mu, humoments);
for (int j = 0; j < 7; j++)
{
humoments[j] = -1 copysign(1.0, humoments[j]) log10(abs(humoments[j]));
cout << "Hu[" << j << "]:" << humoments[j] << endl;
}
cout << endl;
OpenCV中除了对整幅图像计算Hu矩外,还可以对轮廓计算Hu矩,因此可以对轮廓进行旋转平移缩放不变的轮廓匹配,OpenCV中实现函数是matchShape。其衡量两个轮廓匹配好坏的指标函数可以通过参数指定,共有三种,分别是:
3.2.3轮廓包围形状
OpenCV提供三种常见轮廓包围形状,分别是包围矩形,最小包围矩形和最下包围圆。函数分别是BoundingRect,MinAreaRect和MinEnclosingCircle。
使用轮廓包围形状的特征可提取目标位姿信息,尺寸信息,缺陷信息等。
3.2.4圆度,主轴方向,偏心率
对于圆形目标,在经过相机后畸变近似为椭圆。在识别到椭圆目标后,得到椭圆的长轴方向,偏心率等,除了可以完成目标的定位,还可以根据偏心参数还原圆形目标。
下图所示为工业摄影测量中使用的Schneider编码靶标,靶标的定位使用中心的圆,靶标的信息由外环的颜色顺序进行编码。
Schneider编码靶标识别算法流程图
算法效果:
3.3 特征点及特征向量法
3.3.1 Harris角点检测
Harris角点检测的基本思想:算法基本思想是使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么可以认为该窗口中存在角点。
Harris角点检测可以分为5个步骤:
Harris角点检测算子对图像的亮度和对比度变化不敏感,该算子具有旋转不变性,不具有缩放不变性。
OpenCV计算Harris角点检测代码:
Mat show = imread("pic/task9-1.jpg");
Mat srcImage, dst, norm_dst;
cvtColor(show, srcImage, COLOR_RGB2GRAY);
cornerHarris(srcImage, dst, 2, 3, 0.04);
normalize(dst, norm_dst, 0, 255, NORM_MINMAX, CV_8UC1, Mat()); //CV_32FC1
for (int i = 0; i < srcImage.rows; i++)
for (int j = 0; j < srcImage.cols; j++)
if (norm_dst.at<uchar>(i, j) > 128)
circle(show, Point(j, i), 1, Scalar(0, 255, 0), 2);
imshow("show", show);
waitKey();
加粗样式
3.3.2 SIFT 检测
SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。
SIFT算法分解为如下四步:
尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。
关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。
关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。
SIFT算法在OpenCV中的contrib的SIFT_Impl中实现。
3.3.3 SURF 特征检测
SURF是speeded up roubst feature的简称,在不同尺度空间定位关键点,并给出每个点的特征向量。SURF特征检测的步骤:
1.尺度空间的极值检测:搜索所有尺度空间上的图像,通过Hessian来识别潜在的对尺度和选择不变的兴趣点。
2.特征点过滤并进行精确定位。
3.特征方向赋值:统计特征点圆形邻域内的Harr小波特征。即在60度扇形内,每次将60度扇形区域旋转0.2弧度进行统计,将值最大的那个扇形的方向作为该特征点的主方向。
4.特征点描述:沿着特征点主方向周围的邻域内,取4×4×4个矩形小区域,统计每个小区域的Haar特征,然后每个区域得到一个4维的特征向量。一个特征点共有64维的特征向量作为SURF特征的描述子。
SURF算法OpenCV中的contrib的SURF_Impl中实现。
3.3.4 FAST角点检测
Fast算法的思想是:角点所在像素和周围领域像素差别较大,因此,检测一个像素邻域有多少与处在不同区域,即像素值相差较大的像素个数。相差较大的像素个数越多,该点越有可能是角点。
FAST算法步骤:
在OpenCV中,FAST算法在feature2D模块的FastFeatureDetector中实现。
opencv实现代码:
Mat srcImage = imread("pic/task9-1.jpg", IMREAD_GRAYSCALE);
vector<KeyPoint>detectKeyPoint;
Mat keyPointImage;
Ptr<FastFeatureDetector> fast = FastFeatureDetector::create();
fast->detect(srcImage, detectKeyPoint);
drawKeypoints(srcImage, detectKeyPoint, keyPointImage, Scalar(0, 0, 255));
imshow("src image", srcImage);
imshow("keyPoint image", keyPointImage);
imwrite("pic/pic/task9-1.jpg", keyPointImage);
waitKey();
3.4.5 总结
角点检测可以提取图像的低级特征,可以用来做特征的定位,比如相机参数标定算法中的棋盘格角点定位,也可作为后续高级特征生成的输入。带有特征向量的特征点用于对图像拼接,三维重构等应用中。
下图是在实验室环境下获取到的传送带运动时的连续两帧图像,通过对角点的检测,实现前后两张图像的拼接。
获取到传送带运动时的视频,实时运行以上算法,将面阵相机获取到的视频拼接为一幅长图。
—版权声明—
仅用于学术分享,版权属于原作者。