opencv抠图很简单,只需要调用grabCut函数就可以实现,不过在android端对图片尺寸有要求,太大了会卡死
补充说明一下这种抠图方法,这是一种在一个矩形范围内自动扣的方法,不是交互式抠图,交互式抠图也是用grabCut函数,不过还要进行其他操作
原图
抠图
图片缩小后看起来还好
蒙版
贴代码
Mat img = new Mat();
//缩小图片尺寸
Bitmap bm = Bitmap.createScaledBitmap(bitmap,bitmap.getWidth()/10,bitmap.getHeight()/10,true);
//bitmap->mat
Utils.bitmapToMat(bm, img);
//转成CV_8UC3格式
Imgproc.cvtColor(img, img, Imgproc.COLOR_RGBA2RGB);
//设置抠图范围的左上角和右下角
Point tl=new Point(64, 10);
Point br=new Point(230, 300);
Rect rect = new Rect(tl, br);
//生成遮板
Mat firstMask = new Mat();
Mat bgModel = new Mat();
Mat fgModel = new Mat();
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(Imgproc.GC_PR_FGD));
Imgproc.grabCut(img, firstMask, rect, bgModel, fgModel,5, Imgproc.GC_INIT_WITH_RECT);
Core.compare(firstMask, source, firstMask, Core.CMP_EQ);
//抠图
Mat foreground = new Mat(img.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
img.copyTo(foreground, firstMask);
//mat->bitmap
Bitmap b = Bitmap.createBitmap(bitmap.getWidth()/10,bitmap.getHeight()/10, Bitmap.Config.ARGB_8888);
Utils.matToBitmap(foreground,b);
imageView.setImageBitmap(b);
这是java层实现的代码,jni实现也差不多
java代码
Mat img = new Mat();
Bitmap bm = Bitmap.createScaledBitmap(bitmap,bitmap.getWidth()/10,bitmap.getHeight()/10,true);
Utils.bitmapToMat(bm, img);
Imgproc.cvtColor(img, img, Imgproc.COLOR_RGBA2RGB);
Mat dst = new Mat(grabCutFromJNI(img.nativeObj,64,10,230,300));
Bitmap b = Bitmap.createBitmap(bitmap.getWidth()/10,bitmap.getHeight()/10, Bitmap.Config.ARGB_8888);
Utils.matToBitmap(dst,b);
imageView.setImageBitmap(b);
java调用c++函数
public native long grabCutFromJNI(long m,int l,int t,int r,int b);
c++代码
extern "C"
JNIEXPORT Mat* JNICALL
Java_com_example_hyq_opencv_MainActivity_grabCutFromJNI(JNIEnv *env, jobject instance, jlong m,
jint l, jint t, jint r, jint b) {
// TODO
Mat *img = (Mat *) m ;
Rect rect(l,t,r - l,b - t);
Mat firstMask;
Mat bgModel;
Mat fgModel;
grabCut(*img,
firstMask,
rect,
bgModel,fgModel,
5,
GC_INIT_WITH_RECT);
compare(firstMask,GC_PR_FGD,firstMask,CMP_EQ);
Mat *foreground = new Mat(img->size(),CV_8UC3,Scalar(255,255,255));
img->copyTo(*foreground,firstMask);
return foreground;
}
如果想实现原图抠图,可以先缩小生成遮板,再放大蒙版进行抠图,当然还要进行优化,不然很难看
也可以试试线程抠图,可能是因为主线程阻塞导致闪退
2017/12/03更新
发现这篇博文看的人比较多,最近又写了交互式抠图所以就把那篇博文的链接加上来
2020/4/16更新
想把demo上传到github,可是发现原来的demo不停报错,也不知道为啥,以前测试都没问题啊,就又改改,问题根源在java和c++的内存管理,他们是相互独立的,比如一个Mat同时存在java和c++的时候,当java把资源释放了,而c++不知道java把那个Mat释放了,c++调用的时候就崩溃了,当然反过来的情况也是一样的,所以就把资源统一由java或c++管理,最好不要混合使用
修改上传到GitHub了,RectActivity是简单的抠图,GCActivity是交互抠图,MainActivity和GrabCut这两个文件是我测试用的请无视