1. 程式人生 > >圖片處理——基於openCV實現美顏相機

圖片處理——基於openCV實現美顏相機

今天是2017年最後一晚,希望大家元旦前夕玩得開心,準備迎接2018全新的一年,活出程式設計師的態度。

        最近發現有些女孩在朋友圈發的自拍照膚白貌美,甚至頭上魔幻般地長出貓耳朵、貓鼻子、貓鬍鬚,各種調皮搞怪。這一切歸功於程式設計師們不懈努力,推動科技發展,最終科技提高生活品質。美顏相機、美妝相機、秒拍、天天P圖們讓自拍更加精彩,帶有濾鏡、美顏、寵萌等各種效果。講真的,一開始我比較好奇的是寵萌效果,認真分析後總結出實現過程經過三個步驟:人臉檢測——>計算放置位置——>繪製寵萌圖示。按照國際慣例,先看下圖片效果:


其實,最關鍵是第一步:人臉檢測。這裡採用openCV開源庫實現(如果不瞭解openCV這個計算機視覺開源庫的,可以去官網學習:

https://opencv.org/),先把openCVLibrary整合到專案裡,使用訓練好的haarcascade模型來初始化Detector,在攝像頭每幀預覽資料回撥時,對圖片區域搜尋式掃描進行人臉檢測。需要注意的是,Android使用的是Bitmap,而openCV使用的是Mat,兩者需要進行轉換。關鍵程式碼如下:

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();
        // 檢測人臉
        Rect[] object = mFaceDetector.detectObject(mGray, mObject);
        if(object != null && object.length > 0){
            //檢測到人臉矩形
            Rect rect = object[0];
            //矩形標識
//            Imgproc.rectangle(mRgba, rect.tl(), rect.br(), mFaceDetector.getRectColor(), 3);
            if(beauty != null){
                //新增寵萌妝飾
                addBeauty((int)rect.tl().y, (int)(rect.tl().x+rect.br().x-beauty.cols())/2);
            }
        }
        //拍照一幀資料回撥
        if(onPhotoTakenListener != null){
            onPhotoTakenListener.onPhotoTaken(mRgba);
        }
        return mRgba;
    }

        在檢測到人臉後,得到人臉在圖片的矩形位置,然後計算出寵萌特效放置的位置,y軸座標為矩形的top,x軸座標為(矩形left+矩形right-beauty寬度)/2。接著繪製圖標:

    /**
     * 新增寵萌效果
     * @param offsetX x座標偏移量
     * @param offsetY y座標偏移量
     */
    private void addBeauty(int offsetX, int offsetY){
        offsetX -= 200;//高度校正
        if(offsetX < 0){
            offsetX = 0;
        }
        for (int x=0; x<beauty.rows(); x++){
            for (int y=0; y<beauty.cols(); y++){
                double[] array = beauty.get(x, y);
                if(array[0] != 0) {//過濾全黑畫素
                    mRgba.put(x+offsetX, y+offsetY, array);
                }
            }
        }
    }
    /**
     * 獲取寵萌妝飾list集合
     */
    private void getBeauty(){
        Drawable drawable1 = getResources().getDrawable(R.drawable.cat, null);
        Bitmap bitmap1 = ((BitmapDrawable) drawable1).getBitmap();
        bitmap1 = Bitmap.createScaledBitmap(bitmap1, 320, 320, true);
        Mat beauty1 = new Mat();
        Utils.bitmapToMat(bitmap1, beauty1);
        beautyList.add(beauty1);
        Drawable drawable2 = getResources().getDrawable(R.drawable.rabbit, null);
        Bitmap bitmap2 = ((BitmapDrawable) drawable2).getBitmap();
        bitmap2 = Bitmap.createScaledBitmap(bitmap2, 320, 320, true);
        Mat beauty2 = new Mat();
        Utils.bitmapToMat(bitmap2, beauty2);
        beautyList.add(beauty2);
    }
        在拍照時,回撥資料格式是Mat,需要先轉成Bitmap,然後儲存圖片:
    /**
     * 儲存圖片
     * @param frameData 幀資料
     */
    private void savePicture(Mat frameData){
        Bitmap bitmap = Bitmap.createBitmap(frameData.width(), frameData.height(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(frameData, bitmap);
        String fileName = PATH + File.separator + dataFormat.format(new Date(System.currentTimeMillis())) + ".jpg";
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(fileName);
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
        如果覺得靜態圖片的寵萌效果不夠酷,那麼來看下拍照預覽的動態效果(GIF圖處理比較模糊,大家勿噴):

        接下來我會繼續研究美顏、濾鏡、摳圖。歡迎熱愛圖片處理與多媒體開發的同行朋友相互交流,互相學習。