一:简介
OpenCV(Open Source Computer Vision Library:http://OpenCV.org)是一个开源的基于BSD许可的库,它包括数百种计算机视觉算法。
OpenCV具有模块化结构,这就意味着开发包里面包含多个共享库或者静态库。下面是可使用的模块:
- 核心功能(Core functionality) - 一个紧凑的模块,定义了基本的数据结构,包括密集的多维Mat数组和被其他模块使用的基本功能。
- 图像处理(Image processing) - 一个图像处理模块,它包括线性和非线性图像滤波,几何图形转化(重置大小,放射和透视变形,通用基本表格重置映射),色彩空间转换,直方图等。
- 影像分析(video) - 一个影像分析模块,它包括动作判断,背景弱化和目标跟踪算法。
- 3D校准(calib3d) - 基于多视图的几何算法,平面和立体摄像机校准,对象姿势判断,立体匹配算法,和3D元素的重建。
- 平面特征(features2d) - 突出的特征判断,特征描述和对特征描述的对比。
- 对象侦查(objdetect) - 目标和预定义类别实例化的侦查(例如:脸、眼睛、杯子、人、汽车等等)。
- highgui - 一个容易使用的用户功能界面。
- 视频输入输出(videoio) - 一个容易使用的视频采集和视频解码器。
- GPU - 来自不同OpenCV模块的GPU加速算法。
- 一些其他的辅助模块,比如FLANN和谷歌的测试封装,Python,android等绑定和其他。
二:Android OpenCV简介
为Android开发OpenCV程序, 有两种方式, 第一种用java形式的OpenCV库, 第二种用C++形式的OpenCV库, 这两种库都在OpenCV官方提供的SDK中。 我们可以从官网下载 http://OpenCV.org/releases.html。
第一种形式, 需要在开发环境中导入 OpenCV 的 jre包, 还需要安装 Android OpenCV Manager, 有的书上例子也是用这种方式写的。 Android OpenCV Manager是用来管理OpenCV 库的, 减少APP内存占用, 支持特定硬件优化, 定期更新库等, 具体看它的介绍(http://docs.OpenCV.org/2.4/platforms/android/service/doc/index.html)。
第二种形式, 需要使用C++的OpenCV库, 以jni的形式来调用。底层NDK的方式开发可以使用这种。
在本文中采取第一种方式进行。
三:Android OpenCV 开发的准备
1.先准备Android studio开发环境
2.到官网(https://OpenCV.org/releases.html)下载sdk,然后import library到工程里面,添加到项目进行使用
四:OpenCV在Android上的简单使用
1.在OpenCV中存储图像
OpenCV中使用名为Mat的自定义对象存储图像,该对象保存了行数,列数,数据等能唯一标识该图像的信息,并能在需要的时候重新创建图像。不同的图像包含的数据信息也不同,
例如色彩不同的图片色彩多的比灰度多的图片包含信息多,因为彩色图片是通过RGB三通道的,而灰度图片是单通道的。
2.OpenCV中的线性滤波器
越清晰的图片,图片的信息越丰富,进行图片好事操作的时候计算时间比包含信息少的图片要更长,为了解决这个问题,需要对图片进行模糊处理。
很多线性滤波器都采用了核(kernel)的数字向量。核可以看作沿着像素滑动的窗口,并把计算结果输出给该像素。大概如下图
3.几种常见的模糊方法,均值模糊方法,高斯模糊方法,中值模糊方法
均值模糊方法:均值滤波是最简单的滤波,对核进行均值计算,每一个邻域像素都有相同的权值。
在OpenCV for Android中的方法如下
// Bitmap转为Mat
Mat src = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4);
Utils.bitmapToMat(bitmap, src);
// 均值模糊方法 Imgproc.blur(src, src, new Size(3,3));
// Mat转Bitmap
Bitmap processedImage = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(src, processedImage);
高斯模糊方法:高斯核是通过高斯函数所获得的,大概是临近像素对特定像素结果的影响比那些较远像素的影响要高。
Imgproc.GaussianBlur(src, src, new Size(3, 3), 0);(Mat核bitmap转换不写了)
函数声明为: void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT ) ;
功能:对输入的图像src进行高斯滤波后用dst输出。
参数:src和dst当然分别是输入图像和输出图像。Ksize为高斯滤波器模板大小,sigmaX和sigmaY分别为高斯滤波在横线和竖向的滤波系数。
borderType为边缘点插值类型。
理解:数字图像的滤波可以简单的这么理解,就是对原图像的每一个像素滤波,那么对应这个像素滤波后的值是根据其相邻像素(包括自己那个点)与一个滤波模板进行相乘即可。
所以具体到高斯滤波,我们只要知道这个高斯滤波的模板即可。
中值模糊方法:椒盐噪声是一种图片中常见的噪点,噪点随机发呢不在图片中,中值模糊就是为了除去这些噪点,将核中的像素按照升序或者降序排列,
取中值作为输出结果。
Imgproc.medianBlur(src,src,3);(Mat核bitmap转换不写了)
锐化:锐化也可以看作是一种线性滤波操作,并且锚点像素有较高的权重,而周围的像素权重较低。
因此,我们可以自定义下面一些这样的核。
步骤:先自定义一个核,然后呢进行锐化处理
Mat kenrl = new Mat(3, 3, CvType.CV_16SC1);
kenrl.put(0, 0, 0, -1, 0, -1, 5, -1, 0, -1, 0);
Imgproc.filter2D(src, src, src.depth(), kenrl);
五.检测图片的基本特征
1.边缘检测
边缘检测的一般步骤:
- 滤波——消除噪声
- 增强——使边界轮廓更加明显
- 检测——选出边缘点
高斯差分法检测边缘
算法原理:先将图片灰度化,然后进行两个半径的高斯模糊,然后降高斯模糊后的两个图相减,再反转二值阈值化,再讲mat转成图片
代码过程:
第一步 将图像转为灰度图像 Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步 用两个不同的模糊半径对灰度图像执行高斯模糊(取得两幅高斯模糊图像)
Imgproc.GaussianBlur(grayMat, blur1, new Size(15, 15), 5);
Imgproc.GaussianBlur(grayMat, blur2, new Size(21, 21), 5);
第三步 将两幅高斯模糊图像做减法,得到一幅包含边缘点的结果图像
Mat diff = new Mat();
Core.absdiff(blur1, blur2, diff);
Canny边缘检测器
是一种被广泛使用的算法,并被认为是边缘检测最优的算法,该方法使用了比高斯差分算法更复杂的技巧,如多向灰度梯度和滞后阈值化。
平滑图像:通过使用合适的模糊半径执行高斯模糊来减少图像内的噪声。
计算图像的梯度:这里计算图像的梯度,并将梯度分类为垂直、水平和斜对角。这一步的输出用于在下一步中计算真正的边缘。
非最大值抑制:利用上一步计算出来的梯度方向,检测某一像素在梯度的正方向和负方向上是否是局部最大值,如果是,则抑制该像素(像素不属于边缘)。
这是一种边缘细化技术,用最急剧的变换选出边缘点。
用滞后阈值化选择边缘:最后一步,检查某一条边缘是否明显到足以作为最终输出,最后去除所有不明显的边缘。
第一步:使用很简单,首先将图像灰度化 Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步:然后调用Img
proc.Canny()方法即可
Imgproc.Canny(grayMat, cannyEdges,
10
,
100
);
Sobel滤波器
Sobel滤波器也叫Sobel算子,与Canny边缘检测一样,需要计算像素的灰度梯度,只不过是换用另一种方式。
大致步骤:1.将图像进行灰度化,2.计算水平方向灰度梯度的绝对值,3.计算数值方向灰度梯度的绝对值,4计算最终的梯度
具体计算如下:
Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)
+(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)
+(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)
= [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)]
Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)
+0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)
+(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)
= [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)]
其中f(a,b), 表示图像(a,b)点的灰度值;
使用Sobel算计计算边缘的步骤:
第一步:将图像转为灰度图像 Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步:计算水平方向灰度梯度的绝对值
Imgproc.Sobel(grayMat, grad_x, CvType.CV_16S, 1, 0, 3, 1, 0);
Core.convertScaleAbs(grad_x, abs_grad_x);
第三步:计算垂直方法灰度梯度的绝对值
Imgproc.Sobel(grayMat, grad_y, CvType.CV_16S, 0, 1, 3, 1, 0);
Core.convertScaleAbs(grad_y, abs_grad_y);
第四步:计算最终梯度
Core.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 1, sobel);
Harris角点检测
角点是两条边缘的交点或者在局部邻域中有多个显著边缘方向的点。Harris角点检测是一种在角点检测中最常见的技术。
Harris角点检测器在图像上使用滑动窗口计算亮度的变化。
第一步:将图像转为灰度图像 Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步:找出harri角点 Imgproc.cornerHarris(grayMat,tempDst,2,3,0.04);
第三步:在新的图片上绘制角点
Random r = new Random();
for(int i = 0; i < tempDstNorm.cols(); i++){
for (int j = 0;j <tempDstNorm.rows(); j++){
double[] value = tempDstNorm.get(j,i);
if(value[0] > 250){
//决定了画出哪些角点,值越大选择画出的点就越少。如果程序跑的比较慢,就是由于值选取的太小,导致画的点过多
Imgproc.circle(corners, new Point(i,j),5,new Scalar(r.nextInt(255)),2);
}
}
}
测彩直方图:先调用Imgproc.calcHist(matList.subList(0, 1), channels, new Mat(), hist_b, histSize, ranges, false);
然后再将得到的直方图会知道mat上然后转换成bitmap展示出来
对比两张图片的相似度
/**
* 比较来个矩阵的相似度
*
* @param srcMat
* @param desMat
*/
public void comPareHist(Mat srcMat, Mat desMat) {
srcMat.convertTo(srcMat, CvType.CV_32F);
desMat.convertTo(desMat, CvType.CV_32F);
double target = Imgproc.compareHist(srcMat, desMat, Imgproc.CV_COMP_CORREL);
Log.e(TAG, "相似度 : ==" + target);
Toast.makeText(this, "相似度 : ==" + target, Toast.LENGTH_LONG).show();
}
找出两张图片中的不同
Core.subtract(mat1, mat2, mat11);
Core.subtract(mat2, mat1, mat22);
Core.add(mat11, mat22, result);
// 二值化处理
Bitmap bmp = Bitmap.createBitmap(result.cols(), result.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(result,bmp);
六:检测目标
1.尺度不变特征变换 SIFT(scale Invariant Feature Transform)
SIFT算法的实现过程大致如下:对源图像进行高斯模糊处理,根据源图尺寸和相关设定参数生成图像的高斯金字塔和高斯差分金字塔(DOG尺度空间),
在DOG尺度空间中搜索特征点,计算尺度,构建特征描述子。本文以SIFT的参数为主线来尝试说明SIFT算法的原理。
性质:对目标的旋转核缩放具有不变性
对三维视角和光照变化具有部分不变性
很大一部分的关键点(特征)可以从单幅图像中获取
SIFT遵循匹配文件局部特征的策略:尺度空间极值检测,关键点定位,方向分配,关键点描述子
详细原理 https://blog.csdn.net/maweifei/article/details/59501184
https://blog.csdn.net/kksc1099054857/article/details/77894532
用法:
DescriptorMatcher descriptormatcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
descriptormatcher.match(test, train, matches);
人脸识别
openCV自带的提供人脸识别等功能,提供人脸眼睛笑脸等识别功能
Rect[] obj = face.detectObject(mRgba, matOfRect);
for (Rect rect : obj) {
Imgproc.rectangle(mRgba, rect.tl(), rect.br(), face.getRectColor(), 3);
}
其中face等是通过相应的配置文件(xml文件)生成,直接将检测结果显示在预览图上