支持技术分享,转载或复制,请指出文章来源 此博客作者为Jack__0023

效果如下

原图

背景图片

处理效果图片

1、简介

1-1、使用 Opencv 实现单一背景的自动抠图功能,用 Opencv 去画出对应的要扣出来的图像的 mask 区域,
1-2、把这块黑白的 mask 进行模糊处理和腐蚀,目的就是为了平滑边缘,和后面进行原图元素点和背景元素点进行元素融合,达到比较好点的抠图效果(就是尽量减少噪点)
1-3、然后从原图(要进行抠图的图像)上抠出来对应的 mask 这块的原图图像和边缘元素点和背景图元素点进行元素点融合处理。

2、准备工具

2-1、Opencv 环境
如果没有安装的过的请参考我这篇博客,里面有介绍 Opencv 人实现人脸识别检测

3、代码区

3-1、核心代码区域
/*** @Description 进行图片抠图* @author 姚旭民* @date 2019/12/30 15:07*/@SuppressLint("ResourceType")public void testCutImage(Bitmap bitmap) {try {//这个是我写的测试用的背景图,你可以后面修改成参数传递进来Resources r = getApplicationContext().getResources();InputStream is = r.openRawResource(R.drawable.test4);BitmapDrawable bmpDraw = new BitmapDrawable(is);Bitmap backgroundBitmap = bmpDraw.getBitmap();backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, bitmap.getWidth(), bitmap.getHeight(), true);Log.d(TAG, "testCutImage| 开始变换 bitmap : " + bitmap);/*FileInputStream fis = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/CutOutPeople/test2.jpg");Bitmap bitmap = BitmapFactory.decodeStream(fis);*/Log.d(TAG, "testCutImage| bitmap : " + bitmap);Mat img = new Mat();//缩小图片尺寸
//            Bitmap bm = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), true);//bitmap->matUtils.bitmapToMat(bitmap, img);//转成CV_8UC3格式Imgproc.cvtColor(img, img, Imgproc.COLOR_RGBA2RGB);//这个是抠图范围左上角和右下角,如果有问题修改这里Rect rect = new Rect(10, 10, bitmap.getWidth() - 50, bitmap.getHeight() - 10);//生成遮板Mat mask = new Mat();Mat bgModel = new Mat();Mat fgModel = new Mat(0, 0, CvType.CV_8U, new Scalar(155, 15, 10));Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(Imgproc.GC_PR_FGD));
//            firstMask.create(img.size(), CvType.CV_8UC1, new Scalar(0));
//            bgModel.create(img.size(),CvType.CV_8UC1);
//            fgModel.create(img.size(),CvType.CV_8UC1);Imgproc.grabCut(img, mask, rect, bgModel, fgModel, 5, Imgproc.GC_INIT_WITH_RECT);Core.compare(mask, source, mask, Core.CMP_EQ);//            边缘模糊处理Mat blurMask = new Mat();Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1));//膨胀Imgproc.dilate(mask, mask, element, new Point(-1, -1), 1);//腐蚀Mat kernelErode = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1));Imgproc.erode(mask, mask, kernelErode);
//            双边模糊
//            Imgproc.bilateralFilter(mask, blurMask, 3, 200, 200);//高斯模糊Imgproc.GaussianBlur(mask, blurMask, new Size(3, 3), 0, 0);//保存模糊后的mask图像Bitmap b = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(blurMask, b);//图片保存saveBitmap(b, FileConts.PATH_CUT_IMAGE + "mask" + System.currentTimeMillis() + ".png");//抠图
//            Mat foreground = new Mat(img.size(), CvType.CV_8U, new Scalar(196, 15, 24, 0));Mat destMat = new Mat(img.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));Mat backgroundMat = new Mat();Utils.bitmapToMat(backgroundBitmap, backgroundMat);Imgproc.cvtColor(backgroundMat, backgroundMat, Imgproc.COLOR_RGBA2RGB);Bitmap c = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(destMat, c);//将img图像的mask选中的图像扣出来放在foreground中img.copyTo(destMat, blurMask);//进行像素替换double[] m, backgroundColor, imgColor;int blackSum = 0;int whiteSum = 0;int otherSum = 0;//用来计算权重double w;double imgColor1, imgColor2, imgColor3;double backgroundColor1, backgroundColor2, backgroundColor3;double[] mixColor = new double[3];int length1 = 0;int length2 = 0;int length3 = 0;int imgLength1 = 0;int imgLength2 = 0;int imgLength3 = 0;int backgroundLength1 = 0;int backgroundLength2 = 0;int backgroundLength3 = 0;//这里是比较重要,进行图像的替换和原图边缘和背景图的元素融合处理for (int i = 0; i < img.rows(); i++) {for (int j = 0; j < img.cols(); j++) {//当前元素数组m = blurMask.get(i, j);if (m[0] == 255) {//抠图部分destMat.put(i, j, img.get(i, j));whiteSum++;} else if (m[0] == 0) {//背景黑色部分用别的图片代替destMat.put(i, j, backgroundMat.get(i, j));blackSum++;} else {//mask 被模糊处理之后的人像边缘区域,要进行元素融合处理,让抠图效果更好一点otherSum++;//计算权重w = m[0] / 255.0;//获取前景元素imgColor = img.get(i, j);//获取背景元素backgroundColor = backgroundMat.get(i, j);//前景元素imgColor1 = imgColor[0];imgColor2 = imgColor[1];imgColor3 = imgColor[2];//背景元素backgroundColor1 = backgroundColor[0];backgroundColor2 = backgroundColor[1];backgroundColor3 = backgroundColor[2];//元素点混合mixColor[0] = imgColor1 * w + backgroundColor1 * (1.0 - w);mixColor[1] = imgColor2 * w + backgroundColor2 * (1.0 - w);mixColor[2] = imgColor3 * w + backgroundColor3 * (1.0 - w);//进行存放destMat.put(i, j, mixColor);}}}Log.d(TAG, "替换完毕,展示各个累加值,blackSum : " + blackSum + ",whiteSum : " + whiteSum + ",otherSum : " + otherSum);Log.d(TAG, "替换完毕,展示各个累加值,length1 : " + length1 + ",length2 : " + length2 + ",length3 : " + length3);Log.d(TAG, "替换完毕,展示各个累加值,backgroundLength1 : " + backgroundLength1 + ",backgroundLength2 : " + backgroundLength2 + ",backgroundLength3 : " + backgroundLength3);Log.d(TAG, "替换完毕,展示各个累加值,imgLength1 : " + imgLength1 + ",imgLength2 : " + imgLength2 + ",imgLength3 : " + imgLength3);//将处理的foreground图像保存Utils.matToBitmap(destMat, b);mImage.setImageBitmap(b);Mat img3 = new Mat();img.copyTo(img3);img3.setTo(new Scalar(0), blurMask);Bitmap img3Three = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(img3, img3Three);//图像保存saveBitmap(b, FileConts.PATH_CUT_IMAGE + System.currentTimeMillis() + ".png");saveBitmap(c, FileConts.PATH_CUT_IMAGE + "forMat" + System.currentTimeMillis() + ".png");saveBitmap(backgroundBitmap, FileConts.PATH_CUT_IMAGE + "background" + System.currentTimeMillis() + ".png");saveBitmap(img3Three, FileConts.PATH_CUT_IMAGE + "imgThree" + System.currentTimeMillis() + ".png");} catch (Exception e) {Log.e(TAG, "异常信息为 : " + e.toString());e.printStackTrace();}}
3-1、完整 Activity 类(里面有我自己的一些工具类,不过是一些小功能,自己实现就好)

package com.yxm.cutoutpeople.activity;import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.Gravity;
import android.widget.ImageView;
import android.widget.Toast;import com.yxm.cutoutpeople.R;
import com.yxm.cutoutpeople.common.conts.FileConts;
import com.yxm.cutoutpeople.common.thread.MgThread;
import com.yxm.cutoutpeople.common.utils.FileUtils;import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
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.imgproc.Imgproc;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;public class TestActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2 {private static final String TAG = "yaoxuminTest";private Handler mHandler;ImageView mImage;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mImage = findViewById(R.id.testImg);mHandler = new Handler();//清理上次的保存结果MgThread.exceute(new Runnable() {@Overridepublic void run() {FileUtils.delAllFile(FileConts.PATH_CUT_IMAGE);FileUtils.exitOrCreatePath(FileConts.PATH_CUT_IMAGE);}});}@Overridepublic void onResume() {super.onResume();if (!OpenCVLoader.initDebug()) {Log.e(TAG, "OpenCV init error");}Resources r = getApplicationContext().getResources();@SuppressLint("ResourceType") InputStream is = r.openRawResource(R.drawable.test2);BitmapDrawable bmpDraw = new BitmapDrawable(is);Bitmap bitmap = bmpDraw.getBitmap();testCutImage(bitmap);}/*** @Description 进行图片抠图* @author 姚旭民* @date 2019/12/30 15:07*/@SuppressLint("ResourceType")public void testCutImage(Bitmap bitmap) {try {//这个是我写的测试用的背景图,你可以后面修改成参数传递进来Resources r = getApplicationContext().getResources();InputStream is = r.openRawResource(R.drawable.test4);BitmapDrawable bmpDraw = new BitmapDrawable(is);Bitmap backgroundBitmap = bmpDraw.getBitmap();backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, bitmap.getWidth(), bitmap.getHeight(), true);Log.d(TAG, "testCutImage| 开始变换 bitmap : " + bitmap);/*FileInputStream fis = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/CutOutPeople/test2.jpg");Bitmap bitmap = BitmapFactory.decodeStream(fis);*/Log.d(TAG, "testCutImage| bitmap : " + bitmap);Mat img = new Mat();//缩小图片尺寸
//            Bitmap bm = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), true);//bitmap->matUtils.bitmapToMat(bitmap, img);//转成CV_8UC3格式Imgproc.cvtColor(img, img, Imgproc.COLOR_RGBA2RGB);//这个是抠图范围左上角和右下角,如果有问题修改这里Rect rect = new Rect(10, 10, bitmap.getWidth() - 50, bitmap.getHeight() - 10);//生成遮板Mat mask = new Mat();Mat bgModel = new Mat();Mat fgModel = new Mat(0, 0, CvType.CV_8U, new Scalar(155, 15, 10));Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(Imgproc.GC_PR_FGD));
//            firstMask.create(img.size(), CvType.CV_8UC1, new Scalar(0));
//            bgModel.create(img.size(),CvType.CV_8UC1);
//            fgModel.create(img.size(),CvType.CV_8UC1);Imgproc.grabCut(img, mask, rect, bgModel, fgModel, 5, Imgproc.GC_INIT_WITH_RECT);Core.compare(mask, source, mask, Core.CMP_EQ);//            边缘模糊处理Mat blurMask = new Mat();Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1));//膨胀Imgproc.dilate(mask, mask, element, new Point(-1, -1), 1);//腐蚀Mat kernelErode = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1));Imgproc.erode(mask, mask, kernelErode);
//            双边模糊
//            Imgproc.bilateralFilter(mask, blurMask, 3, 200, 200);//高斯模糊Imgproc.GaussianBlur(mask, blurMask, new Size(3, 3), 0, 0);//保存模糊后的mask图像Bitmap b = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(blurMask, b);//图片保存saveBitmap(b, FileConts.PATH_CUT_IMAGE + "mask" + System.currentTimeMillis() + ".png");//抠图
//            Mat foreground = new Mat(img.size(), CvType.CV_8U, new Scalar(196, 15, 24, 0));Mat destMat = new Mat(img.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));Mat backgroundMat = new Mat();Utils.bitmapToMat(backgroundBitmap, backgroundMat);Imgproc.cvtColor(backgroundMat, backgroundMat, Imgproc.COLOR_RGBA2RGB);Bitmap c = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(destMat, c);//将img图像的mask选中的图像扣出来放在foreground中img.copyTo(destMat, blurMask);//进行像素替换double[] m, backgroundColor, imgColor;int blackSum = 0;int whiteSum = 0;int otherSum = 0;//用来计算权重double w;double imgColor1, imgColor2, imgColor3;double backgroundColor1, backgroundColor2, backgroundColor3;double[] mixColor = new double[3];int length1 = 0;int length2 = 0;int length3 = 0;int imgLength1 = 0;int imgLength2 = 0;int imgLength3 = 0;int backgroundLength1 = 0;int backgroundLength2 = 0;int backgroundLength3 = 0;//这里是比较重要,进行图像的替换和原图边缘和背景图的元素融合处理for (int i = 0; i < img.rows(); i++) {for (int j = 0; j < img.cols(); j++) {//当前元素数组m = blurMask.get(i, j);if (m[0] == 255) {//抠图部分destMat.put(i, j, img.get(i, j));whiteSum++;} else if (m[0] == 0) {//背景黑色部分用别的图片代替destMat.put(i, j, backgroundMat.get(i, j));blackSum++;} else {//mask 被模糊处理之后的人像边缘区域,要进行元素融合处理,让抠图效果更好一点otherSum++;//计算权重w = m[0] / 255.0;//获取前景元素imgColor = img.get(i, j);//获取背景元素backgroundColor = backgroundMat.get(i, j);//前景元素imgColor1 = imgColor[0];imgColor2 = imgColor[1];imgColor3 = imgColor[2];//背景元素backgroundColor1 = backgroundColor[0];backgroundColor2 = backgroundColor[1];backgroundColor3 = backgroundColor[2];//元素点混合mixColor[0] = imgColor1 * w + backgroundColor1 * (1.0 - w);mixColor[1] = imgColor2 * w + backgroundColor2 * (1.0 - w);mixColor[2] = imgColor3 * w + backgroundColor3 * (1.0 - w);//进行存放destMat.put(i, j, mixColor);}}}Log.d(TAG, "替换完毕,展示各个累加值,blackSum : " + blackSum + ",whiteSum : " + whiteSum + ",otherSum : " + otherSum);Log.d(TAG, "替换完毕,展示各个累加值,length1 : " + length1 + ",length2 : " + length2 + ",length3 : " + length3);Log.d(TAG, "替换完毕,展示各个累加值,backgroundLength1 : " + backgroundLength1 + ",backgroundLength2 : " + backgroundLength2 + ",backgroundLength3 : " + backgroundLength3);Log.d(TAG, "替换完毕,展示各个累加值,imgLength1 : " + imgLength1 + ",imgLength2 : " + imgLength2 + ",imgLength3 : " + imgLength3);//将处理的foreground图像保存Utils.matToBitmap(destMat, b);mImage.setImageBitmap(b);Mat img3 = new Mat();img.copyTo(img3);img3.setTo(new Scalar(0), blurMask);Bitmap img3Three = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(img3, img3Three);//图像保存saveBitmap(b, FileConts.PATH_CUT_IMAGE + System.currentTimeMillis() + ".png");saveBitmap(c, FileConts.PATH_CUT_IMAGE + "forMat" + System.currentTimeMillis() + ".png");saveBitmap(backgroundBitmap, FileConts.PATH_CUT_IMAGE + "background" + System.currentTimeMillis() + ".png");saveBitmap(img3Three, FileConts.PATH_CUT_IMAGE + "imgThree" + System.currentTimeMillis() + ".png");} catch (Exception e) {Log.e(TAG, "异常信息为 : " + e.toString());e.printStackTrace();}}public static void saveBitmap(Bitmap bitmap, String path) {String savePath;File filePic;if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {savePath = path;} else {Log.d(TAG, "saveBitmap failure : sdcard not mounted");return;}
//        Log.d(TAG, "saveBitmap savePath : " + savePath);try {filePic = new File(savePath);if (!filePic.exists()) {filePic.getParentFile().mkdirs();filePic.createNewFile();}FileOutputStream fos = new FileOutputStream(filePic);bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);fos.flush();fos.close();} catch (IOException e) {Log.d(TAG, "saveBitmap: " + e.getMessage());return;}Log.d(TAG, "saveBitmap success: " + filePic.getAbsolutePath());}@Overridepublic void onCameraViewStarted(int width, int height) {}@Overridepublic void onCameraViewStopped() {}/*** @Description 在这里实现人脸检测和性别年龄识别* @author 姚旭民* @date 2019/7/24 12:16*/@Overridepublic Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {return inputFrame.rgba();}
}

Android 使用opencv实现单一背景抠图并且替换背景相关推荐

  1. 怎样用电脑抠图换背景?抠图怎么把背景变透明?

    大家在平时的生活工作中经常遇到抠图(https://www.yasuotu.com/koutu)换背景的情况,虽然PS专业但绝大部分人并不能熟练使用PS,而且PS抠图比较耗时.有没有一款简单易用又无需 ...

  2. (六)图像背景移除/去背景/换背景/抠图/抠像代码示例:AI实时抠图、AI实时抠像、PaddlePaddle模型、虚拟现实视频会议、沉浸式会议场景、人像去背景、视频背景消除、摄像头背景移除

    (六)图像背景移除/去背景/换背景/抠图/抠像代码示例:AI实时抠图.AI实时抠像.PaddlePaddle模型.虚拟现实视频会议.沉浸式会议场景.人像去背景.视频背景消除.摄像头背景移除 本文与前几 ...

  3. python复杂背景抠图_Opencv实现抠图背景图替换功能

    本文实例为大家分享了opencv实现抠图替换背景图的具体代码,供大家参考,具体内容如下 下面简单图片演示一下: 提取mask: ===> 替换背景:  + = python的opencv代码如下 ...

  4. 【Android 性能优化】布局渲染优化 ( 过渡绘制 | 背景设置产生的过度绘制 | Android 系统的渲染优化 | 自定义布局渲染优化 )

    文章目录 一. 背景设置产生的过度绘制 二. Android 系统的渲染优化 1. 透明组件数据传递 2. GPU 存储机制 3. Android 7.0 之后的优化机制 三. 自定义布局渲染优化 一 ...

  5. android主题编辑器,使用 Theme Editor 设计应用主题背景

    警告:从版本 3.3 开始,Theme Editor 不再包含在 Android Studio 中. Android Studio 包含一个名为 Theme Editor 的视觉辅助工具,该工具可以帮 ...

  6. android 自定义圆形pop,Android布局自定义Shap圆形ImageView可以单独设置背景与图片...

    一.图片预览: 一.实现功能: 需求要实现布局中为圆形图片,图片背景与图标分开且合并到一个ImageView. 二.具体实现: XML中布局中定义ImageView,关健设置两个参数 Android: ...

  7. 怎样抠图怎么把背景换成白色?几个步骤教你轻松掌握

    有时候我们一些照片背景色比较杂乱需要想要换成纯色,或者一些证件照需要更换白色底该怎么操作呢?相信一些制作简历,或者从事电商图片制作的小伙伴可能都遇到过这种问题,但是很多在线处理的小网页也不敢上传图片怕 ...

  8. 基于OpenCV-python3实现抠图替换背景图

    简述 在上一篇博客进行了证件照更换背景颜色,纯蓝色,红色,白色之间的替换,有人私信我,可以不可以把背景换成其他图片,而不是单纯的颜色填充.这在photoshop里面就是选中一个图层然后复制到另外一张图 ...

  9. 如何抠图人像换背景?教你一个在线操作的方法

    如何抠图人像换背景?抠图是图像处理中常见的一种技术操作,是将一张图片的一部分截取出来作为单独的图层与另外的背景信息进行设计合成.一张没有背景的图片我们可以通过反复用到各种不同场景中,现在电脑上有很多抠 ...

最新文章

  1. 一.Python 基础
  2. WebView 实现JS效果和a标签的点击事件
  3. -%3e运算符在c语言中的作用,C语言逻辑运算符知识整理
  4. 登录注册的基本加密方法(可逆)
  5. Remoting and MSMQ 结合做的一个DEMO
  6. 康纳的表情包(思维)
  7. 关于select的描述计算机,计算机二级考试MySQL数据库每日一练 12月21日
  8. python 路径的操作
  9. CentOS下的sudo相关配置的总结归纳
  10. adb刷入第三方recovery_全网热门机型TWRP_Recovery最全面整理合集覆盖安卓全机型
  11. SecureCRT 64位/32位 8.3.3 中文破解版(附上1.解决SecureCRT乱码问题2.解决Hostname lookup failed: host not found问题)
  12. 按头安利!好看又实用的电机 SolidWorks模型素材看这里
  13. php后台权限授权管理系统的思路
  14. 2020-09-03解决pip install安装非常慢[Errno 101] 网络不可达问题
  15. matlab icol,人脸识别-2dpca之Matlab程序
  16. 语法基础——C语法基础
  17. MySQL学习笔记:过滤数据+数据过滤
  18. C/C++ 换行符、回车符与退格符
  19. 深度学习论文: BAM: Bottleneck Attention Module及其PyTorch实现
  20. 分享一键群发各大博客社区平台的工具

热门文章

  1. qt QCustomPlot学习
  2. 画论48 高濂《燕闲清赏笺·论画》
  3. 013❤pycharm安装教程以及使用设置
  4. 为什么红黑树查询快_红黑树为什么比二叉查找树更高效
  5. Unidirectional TSP—dp
  6. HNU程序设计训练 斯诺克台球(屑题)
  7. 关于osi与tcp/ip模型和网络模型的概述
  8. java/php/net/python二手商品交易平台的设计与实现设计
  9. 『DNS隧道工具』— iodine
  10. R语言使用epiDisplay包的followup.plot函数可视化多个ID(病例)监测指标的纵向随访图、使用line.col参数自定义曲线的颜色(色彩)