一:簡介
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文件)生成,直接將檢測結果顯示在預覽圖上