概述
图片编辑中常需要用到抠图功能,将图片想要的部分扣出来再合成到别的图中。本片文章主要介绍一种通过手指选取抠图前景进行抠图的方案,主要利用OpenCV 的grabCut分割算法进行抠图。
抠图原理:
1.bitmap转mat。
2.采集mask,用户选择抠图区域时,记录point 加入list中。
3.Imgproc.grabCut 传入mask进行抠图,得到抠出部分并做边缘处理。
4.mat转bitmap,得到最终抠出的bitmap。
流程:
按比例放缩图片--> 采集mask --> grabCut --> 边缘处理 --> 得到抠出的图片grabcutBitmap --> grabcutBitmap转化为纯色colorBitimap -->colorBitimap放缩到原始大小,用于显示最终扣出的图片区域。(原图按比例放缩到目标尺寸,因为opencv 图片越大抠图时间越长,需要适当缩小图片,最终抠图耗时600毫秒左右)
1.选择一张图片
2.选取需要抠出的部分,这里我想抠这只狗子,只需要手指涂抹这只狗子,如图,只需要选中大致区域就可以,需要注意的是,这只狗通体白色,如果只选中白色区域,最后抠出来的图,就会缺少眼睛和鼻子部分,所以需要把眼睛和鼻子的部分也选中。我们需要记录手指滑动经过过位置的point,放入一个list。
3.抠图
3.1 Bitmap 转Mat
Mat imgPR =new Mat();
Utils.bitmapToMat(mScaleBitmap, imgPR);
3.2 收集mask,手指移动是,将point 传给firstMask
Mat firstMask =new Mat();
Size size =new Size(mScaleBitmap.getWidth(), mScaleBitmap.getHeight());
firstMask.create(size, CvType.CV_8UC1);
firstMask.setTo(new Scalar(GC_PR_BGD));//扣前景
for (int j =0; j < points.size(); j++) {
final Point point = points.get(j);
Imgproc.circle(firstMask, mPoint, radiusCircle, new Scalar(GC_FGD)); (mPoint就是触摸的点)}
3.4执行grabCut 方法
grabCut(Mat img, Mat mask, Rect rect, Mat bgdModel, Mat fgdModel, int iterCount, int mode)
img:输入图像,输入图像越小识别越快
mask: 选取的前景mask
rect: 包含前景的矩形
bgModel,fgModel : 前景、背景
iterCount: 迭代次数 次数越多识别越精确,但耗时更长
3.5 最终识别的区域,如下图,最终识别的区域就是这些,还是比较准确的。
3.6 算法原理
在图片中定义(一个或者多个)包含物体的矩形。
矩形外的区域被自动认为是背景。
对于用户定义的矩形区域,可用背景中的数据来区分它里面的前景和背景区域。
用高斯混合模型(GMM)来对背景和前景建模,并将未定义的像素标记为可能的前景或者背景。
图像中的每一个像素都被看做通过虚拟边与周围像素相连接,而每条边都有一个属于前景或者背景的概率,这是基于它与周边像素颜色上的相似性。
每一个像素(即算法中的节点)会与一个前景或背景节点连接。
在节点完成连接后(可能与背景或前景连接),若节点之间的边属于不同终端(即一个节点属于前景,另一个节点属于背景),则会切断他们之间的边,这就能将图像各部分分割出来
ps:当前景与背景像素颜色接近时,识别不精准,选取mask时 尽量包含前景矩形区域内所有颜色。