opencv在手机上实现照片背景虚化
概述
使用androidstudio开发,在手机上实现照片背景虚化
详细
一、准备工作
1、需要下载安装androidstudio和相关JDK,这两者下载安装和普通软件一样,
JDK:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
androidstuido:
http://www.android-studio.org/
2、本例子在安卓手机上开发图像美化效果,用以在手机相机上实现类似高羰数码相机的背景虚化特效
3.软件下载安装配置完成后,下载本实例解压,打开androidstudio,选择FILE-》OPEN菜单,在弹出的对话中选择实例解压的位置,打开就行了
二、程序实现
1、主程序包含一个源码主文件MainActivity一个布局文件layout及相当菜单资源
2、实现思路怎样
要使用软件技术精确的背景虚化,需要经过三个步骤:
一是选定区域,根据远区使用抠图技术实现前背景分离
二对背景根据需要使用高斯或者平均值等模糊方法处理
三把处理后的背景和前景合并
3、具体设计到哪些代码
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(); } } }
4、配置文件说明
三、运行效果
运行,右键项目:Run as -》Android Application1、运行时的截图
四、其他补充
1、Imgproc.grab分割前景效果比较好,但速度比较慢,如果是确认是固定的比如人脸,水果,植物,可以使用其他OPENCV提代的其他分割方法以加快速度
2.有关模糊,可以根据需要选择合适的模糊算法,比如高斯,比如运动模糊等