zoukankan      html  css  js  c++  java
  • opencv图像处理之在手机上实现背景虚化

    http://m.blog.csdn.net/blogercn/article/details/75004162

    1.高端数码相机都具有背景虚化功能。背景虚化就是使景深变浅,使焦点聚集在主题上。一般的相机最好的虚拟方法便是用微距拍摄,如果主景与背景相距比较远,由于光学透镜对非焦点处景物的不能清晰成像的特点,可以免强实现类似虚化效果。如下。

    2.相机拍摄背景虚化照片一般需要经过四个步骤:

    (1)使变焦倍率(焦距)尽可能大;

    (2)拍摄物与背景尽可能距离远;

    (3)镜头与拍摄物尽可能距离近;

    (4)光圈在满足拍摄需要的同时尽可能大,即F值小;

    3.对于不支持背景虚化的相机,可以使用软件实现背景虚化,以满足不同客户的需求。

    要使用软件技术精确的背景虚化,需要经过三个步骤:

    一是选定区域,根据远区使用抠图技术实现前背景分离

    二对背景根据需要使用高斯或者平均值等模糊方法处理

    三把处理后的背景和前景合并

    前景背景分离这个里使用OPENCV和grabCut技术实现,项目导入我自己编绎的opencv的AAR包,把OPENCV库放入LIBS文件夹,修改APP的build.gradle,添加LIBS:

    repositories {
        mavenLocal()
        mavenCentral()
        flatDir {
            dirs 'libs'
        }
    }
    
    并在dependencies 节点添加如下声明 
    compile fileTree(dir: 'libs', include: '*.aar')
    
    4.附上代码:
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.content.Intent;
    import android.database.Cursor;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.RectF;
    import android.graphics.drawable.BitmapDrawable;
    import android.net.Uri;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.provider.MediaStore;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.ImageView;
    
    import org.opencv.android.BaseLoaderCallback;
    import org.opencv.android.LoaderCallbackInterface;
    import org.opencv.android.OpenCVLoader;
    import org.opencv.core.Core;
    import org.opencv.core.CvType;
    import org.opencv.core.Mat;
    import org.opencv.core.Point;
    import org.opencv.core.Rect;
    import org.opencv.core.Scalar;
    import org.opencv.core.Size;
    import org.opencv.imgcodecs.Imgcodecs;
    import org.opencv.imgproc.Imgproc;
    
    
    public class MainActivity extends Activity {
    
        static final int REQUEST_OPEN_IMAGE = 1;
    
        String mCurrentPhotoPath;
        Bitmap mBitmap;
        ImageView mImageView;
        int touchCount = 0;
        Point tl;
        Point br;
        boolean targetChose = false;
        ProgressDialog dlg;
        private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
            @Override
            public void onManagerConnected(int status) {
                switch (status) {
                    case LoaderCallbackInterface.SUCCESS:
                    {
                        //Log.i(TAG, "OpenCV loaded successfully");
    
                    } break;
                    default:
                    {
                        super.onManagerConnected(status);
                    } break;
                }
            }
        };
    
        @Override
        public void onResume()
        {
            super.onResume();
            if (!OpenCVLoader.initDebug()) {
                Log.d("OpenCV", "Internal OpenCV library not found. Using OpenCV Manager for initialization");
                OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
            } else {
                Log.d("OpenCV", "OpenCV library found inside package. Using it!");
                mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
            }
        }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mImageView = (ImageView) findViewById(R.id.imgDisplay);
            dlg = new ProgressDialog(this);
            tl = new Point();
            br = new Point();
            // if (!OpenCVLoader.initDebug()) {
            // Handle initialization error
            //}
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_main, menu);
            return true;
        }
        int scaleFactor = 1;
        private void setPic() {
            int targetW = 720;//mImageView.getWidth();
            int targetH = 1128;//mImageView.getHeight();
            Log.i(">>>>>", "targetW="+targetW);
            Log.i(">>>>>", "targetH=" + targetH);
            BitmapFactory.Options bmOptions = new BitmapFactory.Options();
            bmOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
            int photoW = bmOptions.outWidth;
            int photoH = bmOptions.outHeight;
            Log.i(">>>>>", "photoW="+photoW);
            Log.i(">>>>>", "photoH=" + photoH);
    
            scaleFactor = Math.max(photoW / targetW, photoH / targetH)+1;
            Log.i(">>>>>", "photoW / targetW="+(photoW / targetW));
            Log.i(">>>>>", "photoH / targetH="+(photoH / targetH));
    
            bmOptions.inJustDecodeBounds = false;
            bmOptions.inSampleSize = scaleFactor;
            bmOptions.inPurgeable = true;
    
            mBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
            mImageView.setImageBitmap(mBitmap);
            Log.i(">>>>>", "mBitmap.getWidth()="+mBitmap.getWidth());
            Log.i(">>>>>", "mBitmap.getHeight()=" + mBitmap.getHeight());
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            switch (requestCode) {
                case REQUEST_OPEN_IMAGE:
                    if (resultCode == RESULT_OK) {
                        Uri imgUri = data.getData();
                        String[] filePathColumn = { MediaStore.Images.Media.DATA };
    
                        Cursor cursor = getContentResolver().query(imgUri, filePathColumn,
                                null, null, null);
                        cursor.moveToFirst();
    
                        int colIndex = cursor.getColumnIndex(filePathColumn[0]);
                        mCurrentPhotoPath = cursor.getString(colIndex);
                        cursor.close();
                        setPic();
                    }
                    break;
            }
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
    
            switch (id) {
                case R.id.action_open_img:
                    Intent getPictureIntent = new Intent(Intent.ACTION_GET_CONTENT);
                    getPictureIntent.setType("image/*");
                    Intent pickPictureIntent = new Intent(Intent.ACTION_PICK,
                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                    Intent chooserIntent = Intent.createChooser(getPictureIntent, "Select Image");
                    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {
                            pickPictureIntent
                    });
                    startActivityForResult(chooserIntent, REQUEST_OPEN_IMAGE);
                    return true;
                case R.id.action_choose_target:
    
                    if (mCurrentPhotoPath != null)
                        targetChose = false;
                    mImageView.setOnTouchListener(new View.OnTouchListener() {
    
                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
                            int cx = (mImageView.getWidth()-mBitmap.getWidth())/2;
                            int cy = (mImageView.getHeight()-mBitmap.getHeight())/2;
                            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                                if (touchCount == 0) {
                                    tl.x = event.getX();//300;//
                                    tl.y = event.getY();//300;//
                                    touchCount++;
                                }
                                else if (touchCount == 1) {
                                    br.x = event.getX();//1100;//
                                    br.y = event.getY();//1200;//
    
                                    Paint rectPaint = new Paint();
                                    rectPaint.setARGB(255, 255, 0, 0);
                                    rectPaint.setStyle(Paint.Style.STROKE);
                                    rectPaint.setStrokeWidth(3);
                                    Bitmap tmpBm = Bitmap.createBitmap(mBitmap.getWidth(),
                                            mBitmap.getHeight(), Bitmap.Config.RGB_565);
                                    Canvas tmpCanvas = new Canvas(tmpBm);
    
                                    tmpCanvas.drawBitmap(mBitmap, 0, 0, null);
                                    tmpCanvas.drawRect(new RectF((float) tl.x, (float) tl.y, (float) br.x, (float) br.y),
                                            rectPaint);
                                    mImageView.setImageDrawable(new BitmapDrawable(getResources(), tmpBm));
    
                                    targetChose = true;
                                    touchCount = 0;
                                    mImageView.setOnTouchListener(null);
                                }
                            }
    
                            return true;
                        }
                    });
    
                    return true;
                case R.id.action_cut_image:
                    if (mCurrentPhotoPath != null && targetChose) {
                        new ProcessImageTask().execute();
                        targetChose = false;
                    }
                    return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        private class ProcessImageTask extends AsyncTask<Integer, Integer, Integer> {
            Mat img;
            Mat foreground;
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                dlg.setMessage("Processing Image...");
                dlg.setCancelable(false);
                dlg.setIndeterminate(true);
                dlg.show();
            }
    
            @Override
            protected Integer doInBackground(Integer... params) {
                //Mat img = new Mat(mBitmap.getHeight(), mBitmap.getHeight(), CvType.CV_8UC3);
                //Utils.bitmapToMat(mBitmap, img);
                long ll = System.currentTimeMillis();
                Log.i(">>>>>", "start="+ll);
                img = Imgcodecs.imread(mCurrentPhotoPath);
                Imgproc.resize(img, img, new Size(img.cols()/scaleFactor, img.rows()/scaleFactor));
                Log.i(">>>>>", "11111=" + System.currentTimeMillis()+"@@@@@"+(System.currentTimeMillis()-ll));
                Mat background = new Mat(img.size(), CvType.CV_8UC3,
                        new Scalar(255, 255, 255));
    
                Mat firstMask = new Mat();
                Mat bgModel = new Mat();
                Mat fgModel = new Mat();
                Mat mask;
                Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(Imgproc.GC_PR_FGD));
                Mat dst = new Mat();
                Rect rect = new Rect(tl, br);
                Log.i(">>>>>", "22222="+System.currentTimeMillis()+"@@@@@"+(System.currentTimeMillis()-ll));
                Imgproc.grabCut(img, firstMask, rect, bgModel, fgModel,
                        1, Imgproc.GC_INIT_WITH_RECT);
                Log.i(">>>>>", "33333=" + System.currentTimeMillis() + "@@@@@" + (System.currentTimeMillis() - ll));
                Core.compare(firstMask, source, firstMask, Core.CMP_EQ);
                Log.i(">>>>>", "44444=" + System.currentTimeMillis() + "@@@@@" + (System.currentTimeMillis() - ll));
                foreground = new Mat(img.size(), CvType.CV_8UC3,
                        new Scalar(255, 255, 255));
                /////
                img.copyTo(foreground);
                Imgproc.blur(foreground, foreground, new Size(20, 20));
                Log.i(">>>>>", "55555=" + System.currentTimeMillis()+"@@@@@"+(System.currentTimeMillis()-ll));
                /////
                img.copyTo(foreground, firstMask);
                Log.i(">>>>>", "66666=" + System.currentTimeMillis()+"@@@@@"+(System.currentTimeMillis()-ll));
    
                firstMask.release();
                source.release();
                bgModel.release();
                fgModel.release();
    
                Imgcodecs.imwrite(mCurrentPhotoPath + ".png", foreground);
    
                return 0;
            }
    
            @Override
            protected void onPostExecute(Integer result) {
                super.onPostExecute(result);
    
                Bitmap jpg = BitmapFactory
                        .decodeFile(mCurrentPhotoPath + ".png");
    
                mImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
                mImageView.setAdjustViewBounds(true);
                mImageView.setPadding(2, 2, 2, 2);
                mImageView.setImageBitmap(jpg);
                mImageView.invalidate();
    
    
                dlg.dismiss();
            }
        }
    }
    


    5.完整工程可以在GITHUB上看到
    
    https://github.com/blogercn/backgroundblur
    6.用到的关健函数解析:
    a.Imgproc.grabCut,抠动,抠出前景
    b.Imgproc.blur,对背景模糊处理
    c.copyTo.背景背景合并图像

    7.效果如下:

  • 相关阅读:
    内存泄漏 Memory Leaks 内存优化 MD
    Handler Thread 内部类引起内存泄露分析
    为什么不取消注册BroadcastReceiver会导致内存泄漏
    WebChromeClient 简介 API 案例
    WebViewClient 简介 API 案例
    java.net.URI 简介 文档 API
    android.net.Uri 简介 API
    RV 多样式 MultiType 聊天界面 消息类型 MD
    JS函数声明与定义,作用域,函数声明与表达式的区别
    CSS中table tr:nth-child(even)改变tr背景颜色: IE7,8无效
  • 原文地址:https://www.cnblogs.com/jukan/p/7246277.html
Copyright © 2011-2022 走看看