最近在研究图像出来这一块,网上查的比较多,试过基础的opencv 的抠图,效果不是很理想
opencv抠图参考
https://blog.csdn.net/u010302327/article/details/78898781
https://blog.csdn.net/hardWork_yulu/article/details/78757665
试了很多,最终找到一个还不错哦的,基本能完成自己想要的效果,那就是这个开源的modnet算法,基于PaddleSeg的MODNet算法实现人像抠图,但是一些复杂的图片还是不行,也花了很多时间,所以来分析一哈。
分享案例可以选择相册或者直接拍照体验哈
先来看看效果图:(可以替换成自己想要的背景图片)
功能:
- 在人像抠图Demo中,默认会载入一张人像图像,并会在图像下方给出CPU的预测结果和预测时延;
- 在人像抠图Demo中,你还可以通过右上角的"打开本地相册"和"打开摄像头拍照"按钮分别从相册或相机中加载测试图像然后进行预测推理;
关键代码:
public Bitmap draw(Bitmap inputImage, Tensor outputTensor,Bitmap bg){
float[] output = outputTensor.getFloatData();
long outputShape[] = outputTensor.shape();
int outputSize = 1;
for (long s : outputShape) {
outputSize *= s;
}
List<Float> arralist = new LinkedList<>();
for (int i=0; i<outputSize;i++){
arralist.add((float)output[i]);
}
Bitmap mALPHA_IMAGE = floatArrayToBitmap(arralist,(int)outputShape[3],(int)outputShape[2]);
//调整尺寸
Bitmap alpha = Bitmap.createScaledBitmap(mALPHA_IMAGE,inputImage.getWidth(),inputImage.getHeight(),true);
Bitmap bgImg = Bitmap.createScaledBitmap(bg,inputImage.getWidth(),inputImage.getHeight(),true);
//重新合成图像
Bitmap result = synthetizeBitmap(inputImage,bgImg, alpha);
return result;
}
//将float数组转成bitmap格式的图片
private Bitmap floatArrayToBitmap(List<Float> floatArray,int width,int height){
byte alpha = (byte) 255 ;
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) ;
ByteBuffer byteBuffer = ByteBuffer.allocate(width*height*4*3) ;
float Maximum = Collections.max(floatArray);
float minmum = Collections.min(floatArray);
float delta = Maximum - minmum + 0.00000000001f ;
int i = 0 ;
for (float value : floatArray){
byte temValue = (byte) ((((value-minmum)/delta)*255));
byteBuffer.put(4*i, temValue) ;
byteBuffer.put(4*i+1, temValue) ;
byteBuffer.put(4*i+2, temValue) ;
byteBuffer.put(4*i+3, alpha) ;
i++;
}
bmp.copyPixelsFromBuffer(byteBuffer) ;
return bmp ;
}
//将原图与背景按照推理得到的alpha图进行合成
private Bitmap synthetizeBitmap(Bitmap front,Bitmap background, Bitmap alpha){
int width = front.getWidth();
int height = front.getHeight();
Bitmap result=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
int[] frontPixels = new int[width * height];
int[] backgroundPixels = new int[width * height];
int[] alphaPixels = new int[width * height];
front.getPixels(frontPixels,0,width,0,0,width,height);
background.getPixels(backgroundPixels,0,width,0,0,width,height);
alpha.getPixels(alphaPixels,0,width,0,0,width,height);
float frontA = 0,frontR = 0,frontG = 0,frontB = 0;
float backgroundR = 0,backgroundG = 0,backgroundB = 0;
float alphaR = 0,alphaG = 0,alphaB = 0;
int index=0;
//逐个像素赋值(这种写法比较耗时,后续可以优化)
for (int row=0; row < height; row++){
for (int col=0; col < width; col++){
index = width*row +col;
//取出前景图像像素值
frontA=(frontPixels[index]>>24)&0xff;
frontR=(frontPixels[index]>>16)&0xff;
frontG=(frontPixels[index]>>8)&0xff;
frontB=frontPixels[index]&0xff;
//取出alpha像素值
alphaR=(alphaPixels[index]>>16)&0xff;
alphaG=(alphaPixels[index]>>8)&0xff;
alphaB=alphaPixels[index]&0xff;
//取出背景图像像素值
backgroundR=(backgroundPixels[index]>>16)&0xff;
backgroundG=(backgroundPixels[index]>>8)&0xff;
backgroundB=backgroundPixels[index]&0xff;
//重新合成 ImgOut = F * alpha/255 + BG * ( 1 - alpha/255 )
frontR= frontR*alphaR/255 + backgroundR*(1-alphaR/255);
frontG=frontG*alphaG/255 + backgroundG*(1-alphaG/255);
frontB=frontB*alphaB/255 + backgroundB*(1-alphaB/255);
frontPixels[index]=(int)frontA<<24|((int)frontR<<16)|((int)frontG<<8)|(int)frontB;
}
}
result.setPixels(frontPixels,0,width,0,0,width,height);;
return result;
}
PC端版本体验:(点击地址,上传 图片体验)
https://sight-x.cn/portrait_matting/
如何使用,参考官网文档,下载sdk配置即可
参考地址:
https://www.paddlepaddle.org.cn/
https://github.com/PaddlePaddle/PaddleSeg
https://github.com/ZHKKKe/MODNet
本人练习案例代码地址: