效果如下
原图
背景图片
处理效果图片
1、简介
1-1、使用 Opencv 实现单一背景的自动抠图功能,用 Opencv 去画出对应的要扣出来的图像的 mask 区域,
1-2、把这块黑白的 mask 进行模糊处理和腐蚀,目的就是为了平滑边缘,和后面进行原图元素点和背景元素点进行元素融合,达到比较好点的抠图效果(就是尽量减少噪点)
1-3、然后从原图(要进行抠图的图像)上抠出来对应的 mask 这块的原图图像和边缘元素点和背景图元素点进行元素点融合处理。
2、准备工具
2-1、Opencv 环境
3、代码区
3-1、核心代码区域
@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);
Log.d(TAG, "testCutImage| bitmap : " + bitmap);
Mat img = new Mat();
Utils.bitmapToMat(bitmap, img);
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));
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.GaussianBlur(mask, blurMask, new Size(3, 3), 0, 0);
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 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.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 {
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);
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() {
@Override
public void run() {
FileUtils.delAllFile(FileConts.PATH_CUT_IMAGE);
FileUtils.exitOrCreatePath(FileConts.PATH_CUT_IMAGE);
}
});
}
@Override
public 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);
}
@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);
Log.d(TAG, "testCutImage| bitmap : " + bitmap);
Mat img = new Mat();
Utils.bitmapToMat(bitmap, img);
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));
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.GaussianBlur(mask, blurMask, new Size(3, 3), 0, 0);
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 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.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 {
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);
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;
}
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());
}
@Override
public void onCameraViewStarted(int width, int height) {
}
@Override
public void onCameraViewStopped() {
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
return inputFrame.rgba();
}
}