1. 程式人生 > 實用技巧 >畢業設計-戶外場景行人檢測

畢業設計-戶外場景行人檢測

時光飛逝,大學時光如白馬過隙,加上今年的疫情影響。。。畢業的一剎那真的感慨萬千,話不多說,開始記錄一下我的畢業設計。

我的畢業設計是由老師選題,語言自由選擇,我在網上搜索許久,發現行人檢測多以Python+深度學習完成,本著推陳出新的精神我選擇了Java+OpenCv(其實是pyhon不太熟悉哈哈)。當然,Java+OpenCv不知侷限於行人檢測,合適的資料集+核函式能應用於大多數的物 體檢測,像車牌檢測、超市商品檢測諸如此類。

如果說採用Java+OpenCv的設計,第一步首先是OpenCv包的配置,這個很簡單,各個網站(CSDN、部落格園。。)都有詳細說明,按照步驟即可,我配置的是OpenCv4..2.0,配置完後可以用下面的小demo測試一下

package com.opencv;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.features2d.FastFeatureDetector; import org.opencv.features2d.Feature2D; import org.opencv.highgui.HighGui; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import org.opencv.objdetect.HOGDescriptor; public class OpenCvMain { //靜態程式碼塊載入動態連結庫 static
{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } public static void main(String[] args) { /* * IMREAD_UNCHANGED = -1 :不進行轉化,比如儲存為了16位的圖片,讀取出來仍然為16位。 * IMREAD_GRAYSCALE = 0 :進行轉化為灰度圖,比如儲存為了16位的圖片,讀取出來為8位,型別為CV_8UC1。 * IMREAD_COLOR = 1 :進行轉化為三通道影象。 * IMREAD_ANYDEPTH = 2 :如果影象深度為16位則讀出為16位,32位則讀出為32位,其餘的轉化為8位。 * IMREAD_ANYCOLOR = 4 :影象以任何可能的顏色格式讀取 * IMREAD_LOAD_GDAL = 8 :使用GDAL驅動讀取檔案,GDAL(Geospatial Data Abstraction * Library)是一個在X/MIT許可協議下的開源柵格空間資料轉換庫。它利用抽象資料模型來表達所支援的各種檔案格式。 * 它還有一系列命令列工具來進行資料轉換和處理。 */ Mat src = Imgcodecs.imread("D:\\123.jpg");//待匹配圖片,這裡寫你自己測試的圖片地址 HighGui.imshow("原圖", src); HighGui.waitKey(); Mat gary=new Mat(); //圖片轉灰 https://blog.csdn.net/ren365880/article/details/103869207 Imgproc.cvtColor(src, gary, Imgproc.COLOR_BGR2GRAY); /* * 使用預設引數建立HOG檢測器。 * 預設值(Size(64,128),Size(16,16),Size(8,8),Size(8,8),9) */ HOGDescriptor hog=new HOGDescriptor(); /* * 設定線性SVM分類器的係數。 線性SVM分類器的 * @param svmdetector係數。 * HOGDescriptor.getDefaultPeopleDetector()返回經過訓練可進行人員檢測的分類器的係數(對於64x128視窗)。 */ hog.setSVMDetector(HOGDescriptor.getDefaultPeopleDetector()); MatOfRect rect=new MatOfRect(); /* * 檢測輸入影象中不同大小的物件。 檢測到的物件將作為矩形列表返回。 * @param img型別CV_8U或CV_8UC3的矩陣,其中包含檢測到物件的影象。 * @param foundLocations矩形的向量,其中每個矩形都包含檢測到的物件。 * @param foundWeights向量,它將包含每個檢測到的物件的置信度值。 * @param hitThreshold要素與SVM分類平面之間距離的閾值,通常為0,應在檢測器係數中指定 * (作為最後一個自由係數),但是如果省略自由係數(允許),則可以指定 在這裡手動操作。 * @param winStride視窗跨度。 它必須是跨步的倍數。 * @param padding填充 */ hog.detectMultiScale(gary, rect, new MatOfDouble(),0,new Size(8,8),new Size(0,0)); Rect[] rects = rect.toArray(); for (int i = 0; i < rects.length; i++) { /* * 繪製一個簡單的,粗的或實心的直角矩形。 函式cv :: rectangle繪製一個矩形輪廓或一個填充的矩形,其兩個相對角為pt1和pt2。 * @param img圖片。 * @param pt1矩形的頂點。 * @param pt2與pt1相反的矩形的頂點。 * @param color矩形的顏色或亮度(灰度影象)。 * @param thickness組成矩形的線的粗細。 負值(如#FILLED)表示該函式必須繪製一個填充的矩形。 * @param lineType線的型別。 請參閱https://blog.csdn.net/ren365880/article/details/103952856 */ Imgproc.rectangle(src, new Point(rects[i].x,rects[i].y), new Point(rects[i].x+rects[i].width,rects[i].y+rects[i].height), new Scalar(0,0,255), 2, Imgproc.LINE_AA); } HighGui.imshow("行人檢測", src); HighGui.waitKey(); } }
View Code

  效果圖大致這樣

第二步就是要訓練模型,這一步沒啥好說的,相信大家也看到有類似的專案說要什麼伽馬歸一化、灰度化、計算HOG特徵梯度啊等等,看上去很頭大其實在配置OpenCv後只是一句方法的事,所以大家不要害怕,都是紙老虎而已。在這一步既然是要訓練模型,必然要進行資料集樣本的匯入,方法很多,可以採用給圖片加分類標籤匯入亦或是全部匯入再加標籤,我採用的是第二種,大家不要侷限住,就是將資料集樣本放進Mat矩陣而已,for迴圈足以;接下來我說一下所謂的灰度化等各個步驟:

1、灰度化

 Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);//灰度化
View Code

src是儲存資料樣本的mat矩陣,也就是第一步儲存資料集樣本的矩陣。

2、歸一化

  Imgproc.resize(src, src, new Size(64,128));

src同上,如果感覺麻煩可以新建一個mat,將src儲存到新mat,new Size()方法,請注意這裡,務必與HOG描述子成一定比例,否則會報錯

3、計算HOG梯度

 HOGDescriptor hog = new HOGDescriptor(new Size(64 ,128), new Size(16, 16), new Size(8, 8), new Size(8, 8), 9);//除錯

記住這裡的提前的比例一定要與歸一化的成比例,慎記。

   hog.compute(src,descriptors);

OpenCv都包含各種方法,計算是一句話的事。

第三步就是要開始訓練模型,這裡記住核函式的選取和調參很重要,還有迭代次數及終止條件,

svm.setType(SVM.C_SVC);//SVM的型別,預設是:SVM.C_SVC向量迴歸機
            svm.setKernel(SVM.RBF);//使用預先定義的核心初始化
            svm.setGamma(0.5);//核函式的引數
            svm.setCoef0(1.0);//核函式有關引數
            svm.setC(0.01);//SVM優化問題的引數C
            svm.setP(0.1);//SVM優化問題的引數p  EPS_SVR設定
            svm.setNu(0.5);//SVM優化問題引數
            svm.setTermCriteria(new TermCriteria(TermCriteria.EPS, nImgNum, 1e-5));
View Code

這裡的核引數沒有註釋掉不代表需要全部都要用!!!我只用了Kernel和Gamma、setP,這裡的選取和調參是老師幫助的,時間久遠加上當時我也不太懂,所以我也不是很明白,這裡的話建議問問有經驗的人的意見,合適的核引數很重要。對於svm.setTermCriteria()方法,這個是訓練次數終止條件設定,

該類變數需要3個引數,一個是型別,第二個引數為迭代的最大次數,最後一個是特定的閾值
//setTermCriteria是用來設定演算法的終止條件, SVM訓練的過程就是一個通過 迭代 方式解決約束條件下的二次優化問題,這裡我們指定一個最大迭代次數和容許誤差,以允許演算法在適當的條件下停止計算
.MAX_ITER迭代到最大迭代次數終止.EPS 迭代到閾值終止 .MAX_ITER+ TermCriteria.EPS 上述兩者都作為迭代終止條件

可以看一下。然後將模型儲存為.xml檔案。

第四步就是檢測階段,這裡就比較簡單了,一個檢測器加一個滑動視窗方法即可。

public Mat myDetector() {
        // TODO 自動生成的方法存根
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        //SVM svm = SVM.load("E:\\BiShe\\svm_java\\SVM_HOG_2400PosINRIA.xml");
        SVM svm = SVM.load("E:\\BiShe\\svm_java\\SVM_HOG_2400PosINRIA_12000Neg.xml");
        Mat src = svm.getSupportVectors();
        int numofsv = src.rows();//支援向量個數
        //System.out.println("支援向量個數:"+numofsv);
        int svdim = svm.getVarCount();//特徵向量維數,即HOG描述子的維數
        //System.out.println("特徵向量維數:"+svdim);
        //初始化alphamat和svindex
        Mat alphaMat = Mat.zeros(1, numofsv, CvType.CV_32FC1);
        Mat supportVectorMat = Mat.zeros(numofsv, svdim, CvType.CV_32FC1);//建立----32位無符號的單通道---灰度圖片
        Mat resultMat = Mat.zeros(1, svdim, CvType.CV_32FC1);
        Mat svidx = Mat.zeros(1, numofsv, CvType.CV_32FC1);
        //獲得模型中的rho
        double rho = svm.getDecisionFunction(0, alphaMat, svidx);
       // System.out.println("rho:"+rho);
        //convertTo()函式負責轉換資料型別不同的Mat,即可以將類似float型的Mat轉換到imwrite()函式能夠接受的型別。
        alphaMat.convertTo(alphaMat, CvType.CV_32FC1);
       // System.out.println(alphaMat.rows()+","+alphaMat.cols());
        //將支援向量和alpha複製到對應Mat中
        supportVectorMat = src;
        Core.gemm(alphaMat, supportVectorMat, -1, new Mat(), 0, resultMat);//矩陣點乘?
      //定義一個大一維的向量,便於後面新增rho
        Mat myDetector = new Mat(1, svdim+1, CvType.CV_32FC1);
        for(int j=0;j<svdim;j++)
         {
           double[] value2 = resultMat.get(0, j);
           myDetector.put(0, j, value2[0]);
           
         }
        myDetector.put(0, svdim, rho);
        System.out.println("rho:"+myDetector.get(0, svdim)[0]);
        return myDetector;
        //開始檢測
  
    }
     /**
         * 判斷兩個矩形的重疊面積
         * @param a
         * @param b
         * @return
         */
     public  int getOverLappingArea(Rect a,Rect b){
         int overLappingArea = 0; 
         int startX = Math.min(a.x,b.x); 
         int endX = Math.max(a.x + a.width, b.x + b.width); 
         int overLappingWidth = a.width + b.width - (endX - startX); 
         int startY = Math.min(a.y, b.y);  
         int endY = Math.max(a.y + a.height, b.y + b.height);  
         int overLappingHeight = a.height + b.height - (endY - startY);  
         if(overLappingWidth <= 0 || overLappingHeight <= 0)  
         {  
             overLappingArea = 0;  
         }else  {  
             overLappingArea = overLappingWidth * overLappingHeight;  
         }  
         return overLappingArea;  
         
     }
}
View Code

這裡網上就有現成的,抄就完事了,也不難,自己寫也沒問題。

然後在通過呼叫這兩個方法完成檢測

         System.loadLibrary(Core.NATIVE_LIBRARY_NAME);     System.load("E:\\BiShe\\opencv\\build\\java\\x64\\opencv_java420.dll");
            System.out.println("類庫載入成功");
            MyDetector MD =  new MyDetector();
            Mat myDetector = new MyDetector().myDetector();
            File dir1 = new File("E:\\BiShe\\NITCA_train\\part_test");//測試圖片
            File[] files1 = dir1.listFiles();
            int nImgNum = files1.length;
            float sum ;
             HOGDescriptor hog = new HOGDescriptor(new Size(64,128), new Size(16, 16), new Size(8, 8), new Size(8, 8), 9);
          //     hog.setSVMDetector(HOGDescriptor.getDefaultPeopleDetector());
                hog.setSVMDetector(myDetector);
          //檢測視窗(64,128),塊尺寸(16,16),塊步長(8,8),cell尺寸(8,8),直方圖bin個數9
            Mat dataMat = null, resMat = null;
            System.out.println("行人檢測"); dataMat=Imgcodecs.imread("E:\\BiShe\\INRIADATA\\original_images\\train\\pos\\crop001578.png");
            Mat dst = new Mat();
            MatOfDouble mod = new MatOfDouble();        
            hog.detectMultiScale(dataMat, mor, mod, 0, new Size(8, 8), new Size(32, 32), 1.05, 2); // 呼叫方法進行檢測  
            //System.out.println("檢測完畢!畫出矩形...");
                  if(mor.toArray().length > 0){ //判斷是否檢測到目標物件,如果有就畫矩形,沒有就執行下一步 
            //找出所有沒有巢狀的矩形框r,並放入found_filtered中,如果有巢狀的話,則取外面最大的那個矩形框放入found_filtered中 
                     Rect[] found = mor.toArray();
                     List<Rect> found_filtered = new ArrayList<Rect>();
                     //先判斷是否有巢狀
                     for(int m=0;m<found.length;m++)
                         {
                         Rect r = found[m];
                         int area = r.width*r.height;
                         //System.out.println(r.x+","+r.y+";"+r.width+","+r.height);
                         int n=0;
                         for(;n<found.length;n++)
                             {
                               if(n!=m && MD.getOverLappingArea(r, found[n])==area)//且found[n]在r內
                               break;
                             }
                          if(n==found.length)
                          {
                              found_filtered.add(r);
                          }
                         }
                     for(int j=0;j<found_filtered.size();j++){
                         Rect r = found_filtered.get(j);
                        Imgproc.rectangle(dataMat, new Point(r.x, r.y), new Point(r.x + r.width, r.y + r.height),new Scalar(0, 0, 255), 2);               
                     }
                     System.out.println("矩形繪製完畢!正在輸出...");
                     HighGui.imshow("行人檢測", dataMat);
                    HighGui.waitKey();

            }else{
                System.out.println("未檢測到目標!繪製矩形失敗!輸出原檔案!");
            }
    }
View Code

好了,大體就是這些了,圖片需要一張張檢測,可以用svm.predict()來大概估計下精確率

測試精度

訓練精度

完結撒花,大學生涯已經過去,希望工作以後還能繼續更新,生命不息,學習不止!