1. 程式人生 > >基於Mask rcnn的行人檢測與安卓客戶端的移動監控系統

基於Mask rcnn的行人檢測與安卓客戶端的移動監控系統

一. Mask rcnn簡述

Mask rcnn是何凱明基於以往的faster rcnn架構提出的新的卷積網路,一舉完成了object instance segmentation. 該方法在有效地目標的同時完成了高質量的語義分割。 文章的主要思路就是把原有的Faster-RCNN進行擴充套件,新增一個分支使用現有的檢測對目標進行並行預測。同時,這個網路結構比較容易實現和訓練,速度5fps也算比較快點,可以很方便的應用到其他的領域,像目標檢測,分割,和人物關鍵點檢測等。並且比著現有的演算法效果都要好,在後面的實驗結果部分有展示出來。

場景理解

Mask R-CNN: overview

從上面可以知道,mask rcnn主要的貢獻在於如下:

1. 強化的基礎網路

通過 ResNeXt-101+FPN 用作特徵提取網路,達到 state-of-the-art 的效果。作者替換了在faster rcnn中使用的vgg網路,轉而使用特徵表達能力更強的殘差網路,另外為了挖掘多尺度資訊,作者還使用了FPN網路。

2. ROIAlign解決Misalignment 的問題

說到這裡,自然要與roi pooling對比。

我們先看看roi pooling的原理,這裡我們可以看下面的動圖,一目瞭然。

roi_pooling_animation.gif

對於roi pooling,經歷了兩個量化的過程:

第一個:從roi proposal到feature map的對映過程。方法是[x/16],這裡x是原始roi的座標值,而方框代表四捨五入。

第二個:從feature map劃分成7*7的bin,每個bin使用max pooling。

如上,roi對映到feature map後,不再進行四捨五入。然後將候選區域分割成k x k個單元, 在每個單元中計算固定四個座標位置,用雙線性內插的方法計算出這四個位置的值,然後進行最大池化操作。

這裡對上述步驟的第三點作一些說明:這個固定位置是指在每一個矩形單元(bin)中按照比例確定的相對位置。比如,如果取樣點數是1,那麼就是這個單元的中心點。如果取樣點數是4,那麼就是把這個單元平均分割成四個小方塊以後它們分別的中心點。顯然這些取樣點的座標通常是浮點數,所以需要使用插值的方法得到它的畫素值。在相關實驗中,作者發現將取樣點設為4會獲得最佳效能,甚至直接設為1在效能上也相差無幾。事實上,ROI Align 在遍歷取樣點的數量上沒有ROIPooling那麼多,但卻可以獲得更好的效能,這主要歸功於解決了misalignment的問題。值得一提的是,在做實驗的時候發現,ROI Align在VOC2007資料集上的提升效果並不如在COCO上明顯。經過分析為造成這種區別的原因是COCO上小目標的數量更多,而小目標misalignment問題的影響更為明顯(比如,同樣是0.5個畫素點的偏差,對於較大的目標而言顯得微不足道,但是對於小目標,誤差的影響就要高很多)

二. 實驗結果

三. 將圖片進行推流,並在安卓端顯示

from flask import Flask, render_template, Response
from camera import VideoCamera

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

def gen(camera):
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')

@app.route('/video_feed')
def video_feed():
    return Response(gen(VideoCamera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

其實完成第一步就幾乎完成了很大部分的工作了,用手機瀏覽器輸入ip地址就可以檢視。但是編寫手機客戶端有利於攝像頭的管

我在這裡主要說說圖片獲取之後的處理和顯示的方法。

http獲取圖片資料流


  private void doGet() {
        //get http img
        String url = "http://192.168.0.105:8080/?action=snapshot";
//        Log.d(TAG, url);
        myHttpThreadGet = new HttpThreadGet(url, HttpThreadGet.GETIMG, handler);
        myHttpThreadGet.start();
}

訊息傳遞獲取,這裡只用了獲取圖片

/************************** msg接收 *************************/
 
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case HttpThreadPost.POST:
                        result = (String) msg.obj;
                        break;
                    case HttpThreadGet.GET:
                        result = (String) msg.obj;
                        break;
                    case HttpThreadGet.GETIMG:
                        buffer = (byte[])msg.obj;
                        try {
                            image = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, BitmapFactoryinfo);
                            faceDetect(image);
                        } catch (Exception e) {
                            Log.e(TAG, e.toString());
                        }
                        doGet();    //再請求
                        break;
                    default:
                        break;
                }
            }
        };

由於圖片是JPEG格式的資料流,需要通過BitmapFactory重新編碼,如果要實現人臉識別FaceDetector的話需要將圖片格式化Bitmap.Config.RGB_565。

/************************** face detect *************************/
 
    private void faceDetect(Bitmap fBitmap) {
//        myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.baby, BitmapFactoryOptionsbfo);
        int imageWidth = fBitmap.getWidth();
        int imageHeight = fBitmap.getHeight();
        myFace = new FaceDetector.Face[numberOfFace];       //分配人臉陣列空間
        myFaceDetect = new FaceDetector(imageWidth, imageHeight, numberOfFace);
        numberOfFaceDetected = myFaceDetect.findFaces(fBitmap, myFace);    //FaceDetector 構造例項並解析人臉
        countertext.setText("numberOfFaceDetected is " + numberOfFaceDetected);
        Log.i(TAG,"numberOfFaceDetected is " + numberOfFaceDetected);
        Bitmap bitmapTemp = Bitmap.createBitmap(fBitmap.getWidth(), fBitmap.getHeight(), Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmapTemp);
        canvas.drawColor(Color.TRANSPARENT);
        Paint myPaint = new Paint();
        myPaint.setColor(Color.GREEN);
        myPaint.setStyle(Paint.Style.STROKE);
        myPaint.setStrokeWidth(3);          //設定點陣圖上paint操作的引數
        canvas.drawBitmap(fBitmap, 0, 0, myPaint);
 
        for(int i=0; i < numberOfFaceDetected; i++){
            Face face = myFace[i];
            PointF myMidPoint = new PointF();
            face.getMidPoint(myMidPoint);
            myEyesDistance = face.eyesDistance();   //得到人臉中心點和眼間距離引數,並對每個人臉進行畫框
            canvas.drawRect(            //矩形框的位置引數
                    (int)(myMidPoint.x - myEyesDistance),
                    (int)(myMidPoint.y - myEyesDistance),
                    (int)(myMidPoint.x + myEyesDistance),
                    (int)(myMidPoint.y + myEyesDistance),
                    myPaint);
        }
        imageview.setImageBitmap(bitmapTemp);
}