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.效果如下:

opencv图像处理之在手机上实现背景虚化的更多相关文章

  1. opencv在手机上实现照片背景虚化

    概述 使用androidstudio开发,在手机上实现照片背景虚化 详细 代码下载:http://www.demodashi.com/demo/10599.html 一.准备工作 1.需要下载安装an ...

  2. fontforge制作自定义字体及在手机上应用举例——张鑫旭

    一.看似无关紧要的事件背景 之所以花时间折腾fontforge这个软件,去制作什么自定义的字体是有原因滴. 之前提过,最近我抽空将公司的手机软件HTML5网页化.期间碰到这么一个问题,页面低栏上的电话 ...

  3. h5 audio标签在手机上不能自动播放????

    最近在做一个微信端的项目,快到接近尾声的时候,发现还没放入音频,于是乎,放入音频,在电脑端测试一切正常,无阻碍. 后来在手机上测试,发现背景音乐不能播放,于是开始找错,刚开始以为是IIS服务器出错,结 ...

  4. 便捷的方式在手机上查看Unity3D的Console Log(调试信息)

    Logs Viewer 功能描述 Using this tool you can easily check your editor console logs inside the game itsel ...

  5. fastclick与zepto的 tap事件关于在手机上click事件的300ms延迟的区别

    之前翻译过一篇关于fastclick的快速点击文章http://www.cnblogs.com/lilyimage/p/3568773.html,fastclick可以解决在手机上点击事件的300ms ...

  6. UIView点击事件。弹出视图,背景虚化。

    @interface CountryViewController //背景 @property (strong, nonatomic) UIView *BackView; end //设置背景虚化 - ...

  7. 背景虚化 Google Camera App Nokia Refocus HTC One M8 的 Duo景深相机

    背景虚化是单反中一种比较常见的拍照形式,参看 http://www.techbang.com/posts/%2017842 https://refocus.nokia.com/

  8. transform:rotate在手机上显示有锯齿的解决方案

    transform:rotate 属于简单好用的效果,但在手机上显示时,会有比较明显锯齿. 解决方案也很简单, 利用外层容器的overflow:hidden 加上图片margin:-1px 就可以解决 ...

  9. bootstrap导航条在手机上默认展开二级目录,必须用setTimeout才能实现

    bootstrap导航条在手机上默认展开二级目录,必须用setTimeout才能实现 $(document).ready(function() { $('.dropdown').hover(funct ...

随机推荐

  1. Influxdb时序数据库阅读笔记

    时序数据库 2017年2月Facebook开源了beringei时序数据库:到了4月基于PostgreSQL打造的时序数据库TimeScaleDB也开源了,而早在2016年7月,百度云在其天工物联网平 ...

  2. docker使用阿里云镜像仓库

    1:阿里云docker仓库 https://dev.aliyun.com/search.html 2:进去注册帐号后,点击自己的管理中心. 3:在管理中心点击加速器,右边面板会有你的加速地址,右边面板 ...

  3. Android ListView的监听事件

    Android开发时,最常用的控件之一就是ListView了,而使用ListView的同时,必然需要对它设置监听器,常用的监听器有这么几个1.OnItemClickListener 2.OnTouch ...

  4. rabbitmq 用户管理

    rabbitmqctl add_user root cor2016 rabbitmqctl set_user_tags root administrator http://host:15672/#/u ...

  5. spring boot 加载jsp

    1.spring boot启动类继承SpringBootServletInitializer ,并且重写configure方法 package com.springapp.mvc;import jav ...

  6. poj3233—Matrix Power Series

    题目链接:http://poj.org/problem?id=3233 题目意思:给一个矩阵n*n的矩阵A和一个k,求一个式子 S = A + A2 + A3 + … + Ak. 这个需要用到等比数列 ...

  7. python的@classmethod和@staticmethod

    本文是对StackOverflow上的一篇高赞回答的不完全翻译,原文链接:meaning-of-classmethod-and-staticmethod-for-beginner Python面向对象 ...

  8. appium+python自动化测试真机测试时报错“info: [debug] Error: Could not extract PIDs from ps output. PIDS: [], Procs: ["bad pid 'uiautomator'"]”

    刚开始启动服务时,弹出授权提示,以为是手机app权限问题,后来debug后,发现了一个警告日志:UiAutomator did not shut down fast enough, calling i ...

  9. python一两行代码完成的骚操作

    分享一个前几天晚上粉丝问的问题,觉得很实用的一个问题,用python读取Excel并保存字典,如何做? 下面是该同学问题截图和代码 ​ 代码截图是下面这样的 ​ 不知道大家第一眼看了这个代码,什么感受 ...

  10. DevExpress控件学习总结

    1.Navigation & Layout 1.1 Bar Manager 如果想在窗体或用户控件(user control)上添加工具条(bars)或弹出菜单(popup menus),我们 ...