计算机视觉(Computer Vision)
计算机视觉是一门研究如何使机器“看”的科学,更进一步的说,就是是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理,使电脑处理成为更适合人眼观察或传送给仪器检测的图像。
定义
计算机视觉是让计算机去认知世界,实现人工智能的一门高深学问,其核心内容即为三个方面的综合:图像处理+机器学习+三维理论。
作用
- 图像识别
- 无人汽车
- AR,VR
- 工程测绘
- 图像分割
与机器视觉(Machine Vision)的区别
在这里,想阐述一下计算机视觉和机器视觉是因为,17年暑假留校期间在机器人实验班做了一个关于机器视觉进行图像采集,颜色追踪的项目,所以拿来区别一下。
计算机视觉是采用图像处理、模式识别、人工智能技术相结合的手段,着重于一幅或多幅图像的计算机分析。
图像可以由多个或者多个传感器获取,也可以是单个传感器在不同时刻获取的图像序列。分析师对目标物体的识别,确定目标物体的位置和姿态,对三维景物进行符号描述和解释。在计算机视觉研究中, 经常使用几何模型、复杂的知识表达,采用基于模型的匹配和搜索技术,搜索的策略常使用自底向上、自顶向下、分层和启发式控制策略。
机器视觉则偏重于计算机视觉技术工程化,能够自动获取和分析特定的图像,以控制相应的行为。
具体的说,计算机视觉为机器视觉提供图像和景物分析的理论及算法基础,机器视觉为计算机视觉的实现提供传感器模型、系统构造和实现手段。因此可以认为,一个机器视觉系统就是一个能自动获取一幅或多幅目标物体图像,对所获取图像的各种特征量进行处理、分析和测量,并对测量就诶过做出定性分析和定量解释,从而得到有关目标物体的某种认识并作出相应决策的系统。功能包括:物体定位、特征检测、缺陷判断、目标识别、计数和运动跟踪。
- OpenMV是国外的一种基于micropython驱动的开源机器视觉模块,是针对MCU,也就是单片机级别的视频库,对标 ARM Cortex M系列内核架构。
- OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上,是针对各种操作系统,硬件针对ARM Cortex A系列的处理器级别以上和x86处理器等。
如果使用单片机开发,需要使用OpenMV,如果使用单板机(SBC)或者PC机类开发,使用OpenCV。
openMV可以视为是运行在单片机上面的openCV
与图像处理的关系
图像处理侧重在“处理”图像:如增强,还原,去噪,分割,等等;而计算机视觉在于使用计算机(也许是可移动式的)来模拟人的视觉,因此模拟才是计算机视觉领域的最终目标。要实现这个目标,至少有两件事要做,第一是图像处理,第二是图像理解。比如一个机器人眼睛读入的数据可能是模糊的,可能是有噪声的,那么首先要进行去噪和还原。之后机器人要能理解这个图像意味着什么,比如特定的军事目标,那么它可能要进行分割,然后用统计学的方式进行模式识别。显然识别这个部分就属于图像理解,而非单纯的图像处理了。
计算机视觉组成之一–图像处理
- 图像的运算
1.图像的代数运算
2.图像的几何运算(插值、运算、旋转) - 图像的变换
1.傅里叶变换
2.离散余弦变换(去噪、滤波(高斯模糊))
3.Radon变换 - 图像的增强
1.灰度增强(直方图均衡化)
2.线性滤波器 - 图像的形态学分析
1.腐蚀
2.膨胀
3.开运算
4.闭运算 - 图像的分析
1.边缘检测
2.hough直线检测 - 图像的恢复
傅里叶变换
傅里叶变换是将时域信号分解为不同频率的正弦信号或余弦函数叠加之和。连续情况下要求原始信号在一个周期内满足绝对可积条件。离散情况下,傅里叶变换一定存在。一个恰当的比喻是将傅里叶变换比作一个玻璃棱镜。棱镜是可以将光分解为不同颜色的物理仪器,每个成分的颜色由波长(或频率)决定。傅里叶变换可以看做是数学上的棱镜,将函数基于频率分解为不同的成分。当我们考虑光时,讨论它的光谱或频率谱。同样,傅里叶变换使我们能通过频率成分来分析一个函数。其性质主要有:
- 线性,对称性(可以用在计算信号的傅里叶变换里面);
- 时移性:函数在时域中的时移,对应于其在频率域中附加产生的相移,而幅度频谱则保持不变;
- 频移性;函数在时域中乘以e^jwt,可以使整个频谱搬移w。这个也叫调制定理,通讯里面信号的频分复用需要用到这个特性(将不同的信号调制到不同的频段上同时传输);
- 卷积定理:时域卷积等于频域乘积;时域乘积等于频域卷积(附加一个系数)。在图像处理中是重点
高斯模糊
要学习高斯模糊我们首先要知道一些基本概念:
线性滤波与卷积的基本概念
线性滤波可以说是图像处理最基本的方法,它可以允许我们对图像进行处理,产生很多不同的效果。做法很简单。首先,我们有一个二维的滤波器矩阵(有个高大上的名字叫卷积核)和一个要处理的二维图像。然后,对于图像的每一个像素点,计算它的邻域像素和滤波器矩阵的对应元素的乘积,然后加起来,作为该像素位置的值。这样就完成了滤波过程。
对图像和滤波矩阵进行逐个元素相乘再求和的操作就相当于将一个二维的函数移动到另一个二维函数的所有位置,这个操作就叫卷积或者协相关。卷积和协相关的差别是,卷积需要先对滤波矩阵进行180的翻转,但如果矩阵是对称的,那么两者就没有什么差别了。
对于滤波器的规则要求
1)滤波器的大小应该是奇数,这样它才有一个中心,例如3x3,5x5或者7x7。有中心了,也有了半径的称呼,例如5x5大小的核的半径就是2。
2)滤波器矩阵所有的元素之和应该要等于1,这是为了保证滤波前后图像的亮度保持不变。当然了,这不是硬性要求了。
3)如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗。
4)对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截断到0和255之间即可。对于负数,也可以取绝对值。
卷积核
无操作
这个滤波器啥也没有做,得到的图像和原图是一样的。因为只有中心点的值是1。邻域点的权值都是0,对滤波后的取值没有任何影响。
图像锐化滤波器Sharpness Filter
图像的锐化和边缘检测很像,首先找到边缘,然后把边缘加到原来的图像上面,这样就强化了图像的边缘,使图像看起来更加锐利了。这两者操作统一起来就是锐化滤波器了,也就是在边缘检测滤波器的基础上,再在中心的位置加1,这样滤波后的图像就会和原始的图像具有同样的亮度了,但是会更加锐利。
我们把核加大,就可以得到更加精细的锐化效果
另外,下面的滤波器会更强调边缘:
主要是强调图像的细节。最简单的3x3的锐化滤波器如下:
锐化滤波器实际上就是计算当前点和周围点的差别,然后将这个差别加到原来的位置上。
边缘检测Edge Detection
我们要找水平的边缘:需要注意的是,这里矩阵的元素和是0,所以滤波后的图像会很暗,只有边缘的地方是有亮度的。
为什么这个滤波器可以寻找到水平边缘呢?因为用这个滤波器卷积相当于求导的离散版本:你将当前的像素值减去前一个像素值,这样你就可以得到这个函数在这两个位置的差别或者斜率。下面的滤波器可以找到垂直方向的边缘,这里像素上和下的像素值都使用:
再下面这个滤波器可以找到45度的边缘:取-2不为了什么,只是为了让矩阵的元素和为0而已。
那下面这个滤波器就可以检测所有方向的边缘:
为了检测边缘,我们需要在图像对应的方向计算梯度。用下面的卷积核来卷积图像,就可以了。但在实际中,这种简单的方法会把噪声也放大了。另外,需要注意的是,矩阵所有的值加起来要是0.
浮雕Embossing Filter
浮雕滤波器可以给图像一种3D阴影的效果。只要将中心一边的像素减去另一边的像素就可以了。这时候,像素值有可能是负数,我们将负数当成阴影,将正数当成光,然后我们对结果图像加上128的偏移。这时候,图像大部分就变成灰色了。
下面是45度的浮雕滤波器
我们只要加大滤波器,就可以得到更加夸张的效果了
这种效果非常的漂亮,就像是将一副图像雕刻在一块石头上面一样,然后从一个方向照亮它。它和前面的滤波器不同,它是非对称的。另外,它会产生负数值,所以我们需要将结果偏移,以得到图像灰度的范围。
A:原图像。B:锐化。C:边缘检测。D:浮雕
运动模糊Motion Blur
运动模糊可以通过只在一个方向模糊达到,例如下面9x9的运动模糊滤波器。注意,求和结果要除以9。
这个效果就好像,摄像机是从左上角移动的右下角。
切入主题–模糊
均值模糊Box Filter (Averaging)
我们可以将当前像素和它的四邻域的像素一起取平均,然后再除以5,或者直接在滤波器的5个地方取0.2的值即可,如下图:
可以看到,这个模糊还是比较温柔的,我们可以把滤波器变大,这样就会变得粗暴了:注意要将和再除以13.
所以,如果你想要更模糊的效果,加大滤波器的大小即可。或者对图像应用多次模糊也可以。
高斯模糊
其实模糊滤波器就是对周围像素进行加权平均处理,均值模糊很简单,周围像素的权值都相同,所以不是很平滑。高斯模糊就有这个优点,所以被广泛用在图像降噪上。特别是在边缘检测之前,都会用来移除细节。那么下面我们就看看高斯模糊的权值是如何分配的。
正态分布的权重
正态分布显然是一种可取的权重分配模式。在图形上,正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。计算平均值的时候,我们只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。
对应图形为:
更远的点以此类推。下面就是5*5的高斯滤波器和平滑效果:
计算机视觉组成之二–图像理解
根据具体实际情况进行适当操作,拿车牌识别系统来举例说明。在这一部分,需要进行车牌定位,核心技术为图像分割,利用二值化图像设置恰当阈值进行切割,主要方法有:
- 区域生长法(如下图)
- 极限集分割
- 统计学算法(fast Rcnn,yolo)
培训代码及分析
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char ** argv)
{
Mat src = imread("D:/Camera Roll/I Love/abc.jpg", 1);
//将真彩色RGB图像转换成灰度图像。(RGB并不发生变化)
cvtColor(src, src, COLOR_BGR2GRAY);
//创建出窗口,显示的规定窗口名称
namedWindow("src", CV_WINDOW_AUTOSIZE);
imshow("src", src);//显示图像
//将已有数组赋给Mat矩阵
Mat dImg = Mat(src.rows, src.cols - 2, CV_8UC1);
//差分,按行列遍历,相当于三个框移动,(i, j + 1)减去(i, j - 1)的值赋给Mat矩阵的(i, j - 1)
for (int i = 0;i < src.rows;i++) {
for (int j = 1;j < src.cols - 1;j++) {
dImg.at<uchar>(i, j - 1) = src.at<uchar>(i, j + 1) - src.at<uchar>(i, j - 1);
}
}
namedWindow("dst", CV_WINDOW_AUTOSIZE);
imshow("dst", dImg);
waitKey(0);
}
运行效果
(?)灰度图像 (?)滤波图像
#include<opencv2/opencv.hpp>
#define PI 3.1415926
using namespace std;
using namespace cv;
int main(int argc, char ** argv)
{
//读入图像
Mat src = imread("D:/Camera Roll/I Love/abc.jpg", 0);
//创建窗口
namedWindow("src", WINDOW_AUTOSIZE);
imshow("src", src);
//定义5x5卷积模板
Mat model = Mat(5, 5, CV_64FC1);
double sigma = 80;
//利用二维形式的高斯函数按正态分布求系数
for (int i = -2; i <= 2; i++)
{
for (int j = -2; j <= 2; j++)
{
model.at<double>(i + 2, j + 2) =
exp(-(i * i + j * j) / (2 * sigma * sigma)) /
(2 * PI * sigma * sigma);
}
}
double gaussSum = 0;
gaussSum = sum(model).val[0];
//得到最终的model.at<double>(i, j)
for (int i = 0; i < model.rows; i++)
{
for (int j = 0; j < 5; j++)
{
model.at<double>(i, j) = model.at<double>(i, j)
/gaussSum;
}
}
Mat dst = Mat(src.rows - 4, src.cols - 4, CV_8UC1);
//利用二维形式的高斯函数进行卷积操作
for (int i = 2; i < src.rows - 2; i++)
{
for (int j = 2; j < src.cols - 2; j++)
{
double sum = 0;
for (int m = 0; m < model.rows; m++)
{
for (int n = 0; n < model.cols; n++)
{
sum += (double)src.at<uchar>(i + m - 2, j + n - 2) *
model.at<double>(m, n);
}
}
//卷积操作后赋给dst矩阵
dst.at<uchar>(i - 2, j - 2) = (uchar)sum;
}
}
namedWindow("gaussBlur", WINDOW_AUTOSIZE);
//输出高斯模糊后的图像
imshow("gaussBlur", dst);
waitKey(0);
}
运行效果
(?)原图像(?高斯模糊)
关于一些函数的说明
- waitKey()函数详解
waitKey()–这个函数是在一个给定的时间内(单位ms)等待用户按键触发;如果用户没有按下键,则接续等待(循环)
while(1){ if(waitKey(100)==27)break; } 在这个程序中,我们告诉OpenCV等待用户触发事件,等待时间为100ms,如果在这个时间段内, 用户按下ESC(ASCII码为27),则跳出循环,否则,等待
如果设置waitKey(0),则表示程序会无限制的等待用户的按键事件
- 读写图像
?imwrite(pathm, img);
?imread(path, flag); // 0 灰度图 1 原图 - 显示图像
? namedWindow(“name”,1); //1 大小和图像相同,窗口不可拉伸 0 窗口可拉伸
?imshow(“name”, img); //可以直接用imshow
?waitKey(0); //图像上点击Enter退出程序 - 矩阵元素访问
?img.at<类型>(i,j) //类型一定和img相同
?CV_8UC1 unsigned char
?CV_32FC1 float
关于高斯模糊的理解
- 5×5的矩阵下利用二维形式的高斯函数按正态分布求取系数
- 除以gaussSum得到最终的model.at(i, j)
- 利用二维形式的高斯函数进行卷积操作(此时数组行列各少两组)
- 卷积操作后赋给dst矩阵,即得到高斯模糊图像对应的矩阵
培训?感受
在没有加Android实验室之前,从来没有想过自己会做这么多事情,比如自己会练习写第一篇博客…虽然只是短短的一下午的培训,但回家查阅计算机视觉这部分的确开阔了视野。未来的学习还有很长的一段路要走,这篇博客可能有些地方不太完善或者存在错误,还请指正!