iOS 用opencv实现抠人物头像(最新)

前段时间公司要开发一个自拍换背景的证件照软件,之前从来没有接触过这个方面。于是看了很多相关文章,慢慢的有了思路。开始搞事情。。

2019.1.3 补充了uiimage和cv::mat相互转换,以及放大镜和擦除


简单的介绍一下流程,只需要做以下三步:

第一步:在原图上面画线,得到 mask 图


原图



画线图


mask 图



第二步:调用已经下载好的抠图算法,原图+mask图  融合出

融合图



第三步:用原图+融合图+背景图(以蓝色背景为例)  做融合

结果图

好了 更换背景成功,是不是觉得很神奇。



那下面我们来详细的讲解一下以上三步在iOS中是怎么实现的(大家如果有更好的思路可以提出来互相学习)

第一步:在原图上面画线,得到 mask 图(详解)

1.画线(标记需要处理的区域)

在这里 讲解一下得到mask图原理,以便于理解下面详细的步骤

原理: 一张图片,分为前景和背景,想更换背景,就要把前景和背景分离。已经确认的前景和背景我们不对它进行处理,真正要分离的就是背景和前景的交界处。那么这块要处理的区域我们通过画线来标记,标记为待处理区域。画完线之后,那些没有被画线的点我们如何来标记为前景和背景呢。这里就用到了种子生长算法(不知道的可以去百度一下),首先我们在左上角取一个生长点进行区域生长,生长过的区域我们把它标记为背景,遇到待处理区域,就停止生长。没有生长过 ,也没有标记过的地方把它标记为前景。这样mask图就出来了。说这么多,来一张图吧,如图


mask

前期工作:创建一个全局的可变二维数组和原图矩阵(就原图image转换cv::mat)

思路:创建一个touchview 。根据手势划过的地方,如果设置线宽为20,取到 touchmove 走过的每一个点为中心边长为20的正方形内的点。同时去计算正方形中所有的点距离中心点的距离,把原图矩阵上 距离小于等于10的所有点的rgb值置为你想要设置的颜色,同时在二维数组上面也将这些点置为128。这样两个矩阵中就形成了和贝塞尔曲线 一样的线。现在我们创建一个矩阵大小的cv::mat格式的空白区域,要求8bit,无符号整形, 4通道。然后遍历数组,把矩阵置为跟二维数组一样的值。说到这里你肯定有点懵,来一段代码清醒一下

补充一下画线方法:这里没有采用贝塞尔曲线,而是直接在原图上面修改像素点。将划过的像素点置为255,0,0。 至于线的粗细,可以通过for循环来置。例如线宽10,那么循环就是 x-10--->x+10,  y-10--->y+10

得到的是正方形。

处理一下变成圆形:计算当前点 距离中心点(x,y)这个点的距离,小于半径就可以了。只置半径内的像素点


在画线的事件里面有一点需要非常注意的:

在划的过程中一定要去判断这个点是否被置过,如果置过就不要重复再置了。如果半径为30,每划过一个点都要置3600个点。判断之后只需要置60个点。

2019.1.3 补充:

有很多人问UIimage转cv::mat 和 cv::mat 转UIimage怎么转    贴一下我的转换代码

uiimage转cv::mat


cv::mat转uiimage


现在需要处理的区域是标记了,我们来标记前景和背景

2.种子生长标记前景和背景

选取一个左上角的点对mask矩阵进行种子生长。把生长过的区域置为0,把没有生长过,也没有划过的部分置为255。mask的矩阵就出来了。看代码



(喜欢学习的人可以看一下)错误的思路:创建一个touchview ,创建和图片一样大小的矩阵每个点置为255。根据手势划过的地方用贝塞尔曲线连接,如果设置线宽为20,以 touchmove 走过的每一个点为中心画边长为20的正方形。同时去计算正方形中所有的点距离中心点的距离,把距离小于等于10的所有点置为128。这样矩阵中就形成了和touchview上贝塞尔曲线 一样的线。选取一个左上角的点对矩阵进行种子生长。把生长过的区域置为0,把没有生长过,也没有划过的部分置为255。mask的矩阵就出来了。然后创建一个矩阵大小的cv::mat格式的空白区域,要求8bit,无符号整形, 4通道。这个思路为什么是错的,因为用贝塞尔曲线的思路,如果要实现擦除功能是可以的,但是原图和mask上的点需要一一对应去执行,这点就比较难做到。


第一步走完了,不知道我说明白了没有。第一步能理解,很重要。让我们进入第二步



第二步:调用已经下载好的抠图算法,原图+mask图 =融合图

SharedMatting sm;

sm.loadImage(pathToImage); // load image from pathToImage

sm.loadTrimap(pathToTrimap); // load Trimap from pathToTrimap

sm.solveAlpha(); // do the shared matting algorithm

sm.save(pathToSave); // save result image

以上就是github上面的算法提供的接口,什么意思呢。

传入原图-->传入mask图-->经过吧啦吧啦一系列处理-->得到融合图

传入的是照片本地地址,我看了下它里面还是转成cv::mat格式去执行了,建议修改一下它里面的源码,让这几个接口直接传入cv::mat。这样我们就可以不用保存到本地再传入了。最后一个接口是做本地存储,不想做存储怎么办,在它的代码里面可以新加一个接口。直接把得到的cv::mat返回回来。转换成uiimage就可以展示了。来一张效果图

融合出来的图

这一步需要注意的地方:cv::mat格式的原图和mask图在大小,字节和通道上一定要保持一致,不然报错了找都找不到。



第三步:用原图+融合图+背景图(以蓝色背景为例)  做融合

列一下融合公式(以下都是cv::mat格式的矩阵)

最终的结果图=原图矩阵  x(  融合图矩阵 / 255矩阵) + 背景矩阵 x(255矩阵-融合图矩阵)/255矩阵

简单的解释一下:因为(  融合图矩阵 / 255矩阵)只有0和1.    ➕号之前得到的是前景,➕号之后得到的是背景。 相加就是全景。



该踩的坑都踩过了,应该会简便一些。

因为画线的时候 手指会挡住图片,需要画线的时候放大镜显示,以及画错之后小面积擦除。本人已经做好了,下次找个时间更新文章吧。难以掩盖即将要过元旦节的激动,提前祝大家新年快乐。

2018.12.29    下午 5:27  下班了


2019.1.3  下午3:38 更新

如何实现画线过程中的放大镜(效果图如下)

放大镜演示


第一步:在touchbegan中截屏,在截屏的图上取手势划过的地方(范围自己取)显示在放大镜控件(uiimageview)中

第二步:在touchmove中取手势划过的地方(范围自己取),显示在放大镜控件(uiimageview)中,在事件中不断改变放大镜的位置。这一步关键在于原图已经画线了,放大的部位是从截屏上取的没有画线的,那怎么处理这一步呢。不要重复的去截屏来保持同步显示。在截屏的图中同步画线就行了,这一步很关键

(画线的方法在之前说过了)。

简单的贴下代码


实现擦除功能(这一步比较简单)

实际上就是手势触及到的部位要恢复成原图。

1.做操作之前,保存原图,保存截屏图(用于放大镜的截屏图)

2.手势触及的部位(范围自己取),从原图上取这一块的rgb值,通过for循环来修改已经画线的图对应的位置。

贴一下for循环里面的代码,应该比较好理解

因为代码是属于公司的,不方便透漏,所以就没上传代码。如果我有什么地方说得不明白可以私信我。很乐意一起学习。觉得有用的话,顺手点个赞。谢谢🙏

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容