iOS 用opencv實現(xiàn)摳人物頭像(最新)

前段時間公司要開發(fā)一個自拍換背景的證件照軟件,之前從來沒有接觸過這個方面醇滥。于是看了很多相關(guān)文章黎比,慢慢的有了思路。開始搞事情鸳玩。阅虫。

2019.1.3 補充了uiimage和cv::mat相互轉(zhuǎn)換,以及放大鏡和擦除


簡單的介紹一下流程不跟,只需要做以下三步:

第一步:在原圖上面畫線颓帝,得到 mask 圖


原圖



畫線圖


mask 圖



第二步:調(diào)用已經(jīng)下載好的摳圖算法,原圖+mask圖 ?融合出

融合圖



第三步:用原圖+融合圖+背景圖(以藍色背景為例) ?做融合

結(jié)果圖

好了 更換背景成功窝革,是不是覺得很神奇购城。



那下面我們來詳細的講解一下以上三步在iOS中是怎么實現(xiàn)的(大家如果有更好的思路可以提出來互相學(xué)習(xí))

第一步:在原圖上面畫線,得到 mask 圖(詳解)

1.畫線(標(biāo)記需要處理的區(qū)域)

在這里 講解一下得到mask圖原理虐译,以便于理解下面詳細的步驟

原理: 一張圖片瘪板,分為前景和背景,想更換背景漆诽,就要把前景和背景分離侮攀。已經(jīng)確認的前景和背景我們不對它進行處理,真正要分離的就是背景和前景的交界處厢拭。那么這塊要處理的區(qū)域我們通過畫線來標(biāo)記兰英,標(biāo)記為待處理區(qū)域。畫完線之后供鸠,那些沒有被畫線的點我們?nèi)绾蝸順?biāo)記為前景和背景呢畦贸。這里就用到了種子生長算法(不知道的可以去百度一下),首先我們在左上角取一個生長點進行區(qū)域生長楞捂,生長過的區(qū)域我們把它標(biāo)記為背景薄坏,遇到待處理區(qū)域正林,就停止生長。沒有生長過 颤殴,也沒有標(biāo)記過的地方把它標(biāo)記為前景。這樣mask圖就出來了鼻忠。說這么多涵但,來一張圖吧,如圖


mask

前期工作:創(chuàng)建一個全局的可變二維數(shù)組和原圖矩陣(就原圖image轉(zhuǎn)換cv::mat)

思路:創(chuàng)建一個touchview 帖蔓。根據(jù)手勢劃過的地方矮瘟,如果設(shè)置線寬為20收苏,取到 touchmove 走過的每一個點為中心邊長為20的正方形內(nèi)的點吓著。同時去計算正方形中所有的點距離中心點的距離褐着,把原圖矩陣上 距離小于等于10的所有點的rgb值置為你想要設(shè)置的顏色煞聪,同時在二維數(shù)組上面也將這些點置為128园细。這樣兩個矩陣中就形成了和貝塞爾曲線 一樣的線∩〔澹現(xiàn)在我們創(chuàng)建一個矩陣大小的cv::mat格式的空白區(qū)域号阿,要求8bit斟珊,無符號整形写妥, 4通道拳球。然后遍歷數(shù)組,把矩陣置為跟二維數(shù)組一樣的值珍特。說到這里你肯定有點懵祝峻,來一段代碼清醒一下

補充一下畫線方法:這里沒有采用貝塞爾曲線,而是直接在原圖上面修改像素點扎筒。將劃過的像素點置為255莱找,0,0嗜桌。 至于線的粗細奥溺,可以通過for循環(huán)來置。例如線寬10症脂,那么循環(huán)就是 x-10--->x+10, ?y-10--->y+10

得到的是正方形谚赎。

處理一下變成圓形:計算當(dāng)前點 距離中心點(x,y)這個點的距離,小于半徑就可以了诱篷。只置半徑內(nèi)的像素點


在畫線的事件里面有一點需要非常注意的:

在劃的過程中一定要去判斷這個點是否被置過壶唤,如果置過就不要重復(fù)再置了。如果半徑為30棕所,每劃過一個點都要置3600個點闸盔。判斷之后只需要置60個點。

2019.1.3 補充:

有很多人問UIimage轉(zhuǎn)cv::mat 和 cv::mat 轉(zhuǎn)UIimage怎么轉(zhuǎn) ? ?貼一下我的轉(zhuǎn)換代碼

uiimage轉(zhuǎn)cv::mat


cv::mat轉(zhuǎn)uiimage


現(xiàn)在需要處理的區(qū)域是標(biāo)記了琳省,我們來標(biāo)記前景和背景

2.種子生長標(biāo)記前景和背景

選取一個左上角的點對mask矩陣進行種子生長迎吵。把生長過的區(qū)域置為0躲撰,把沒有生長過,也沒有劃過的部分置為255击费。mask的矩陣就出來了拢蛋。看代碼



(喜歡學(xué)習(xí)的人可以看一下)錯誤的思路:創(chuàng)建一個touchview 蔫巩,創(chuàng)建和圖片一樣大小的矩陣每個點置為255谆棱。根據(jù)手勢劃過的地方用貝塞爾曲線連接,如果設(shè)置線寬為20圆仔,以 touchmove 走過的每一個點為中心畫邊長為20的正方形垃瞧。同時去計算正方形中所有的點距離中心點的距離,把距離小于等于10的所有點置為128坪郭。這樣矩陣中就形成了和touchview上貝塞爾曲線 一樣的線个从。選取一個左上角的點對矩陣進行種子生長。把生長過的區(qū)域置為0歪沃,把沒有生長過嗦锐,也沒有劃過的部分置為255。mask的矩陣就出來了绸罗。然后創(chuàng)建一個矩陣大小的cv::mat格式的空白區(qū)域意推,要求8bit,無符號整形珊蟀, 4通道菊值。這個思路為什么是錯的,因為用貝塞爾曲線的思路育灸,如果要實現(xiàn)擦除功能是可以的腻窒,但是原圖和mask上的點需要一一對應(yīng)去執(zhí)行,這點就比較難做到磅崭。


第一步走完了儿子,不知道我說明白了沒有。第一步能理解砸喻,很重要柔逼。讓我們進入第二步



第二步:調(diào)用已經(jīng)下載好的摳圖算法,原圖+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圖-->經(jīng)過吧啦吧啦一系列處理-->得到融合圖

傳入的是照片本地地址,我看了下它里面還是轉(zhuǎn)成cv::mat格式去執(zhí)行了癣漆,建議修改一下它里面的源碼维咸,讓這幾個接口直接傳入cv::mat。這樣我們就可以不用保存到本地再傳入了。最后一個接口是做本地存儲癌蓖,不想做存儲怎么辦瞬哼,在它的代碼里面可以新加一個接口。直接把得到的cv::mat返回回來租副。轉(zhuǎn)換成uiimage就可以展示了坐慰。來一張效果圖

融合出來的圖

這一步需要注意的地方:cv::mat格式的原圖和mask圖在大小,字節(jié)和通道上一定要保持一致用僧,不然報錯了找都找不到讨越。



第三步:用原圖+融合圖+背景圖(以藍色背景為例) ?做融合

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

最終的結(jié)果圖=原圖矩陣 ?x( ?融合圖矩陣 / 255矩陣) + 背景矩陣 x(255矩陣-融合圖矩陣)/255矩陣

簡單的解釋一下:因為( ?融合圖矩陣 / 255矩陣)只有0和1. ? ??號之前得到的是前景,?號之后得到的是背景永毅。 相加就是全景。



該踩的坑都踩過了人弓,應(yīng)該會簡便一些沼死。

因為畫線的時候 手指會擋住圖片,需要畫線的時候放大鏡顯示崔赌,以及畫錯之后小面積擦除意蛀。本人已經(jīng)做好了,下次找個時間更新文章吧健芭。難以掩蓋即將要過元旦節(jié)的激動县钥,提前祝大家新年快樂。

2018.12.29 ? ?下午 5:27 ?下班了


2019.1.3 ?下午3:38 更新

如何實現(xiàn)畫線過程中的放大鏡(效果圖如下)

放大鏡演示


第一步:在touchbegan中截屏慈迈,在截屏的圖上取手勢劃過的地方(范圍自己热糁)顯示在放大鏡控件(uiimageview)中

第二步:在touchmove中取手勢劃過的地方(范圍自己取)痒留,顯示在放大鏡控件(uiimageview)中谴麦,在事件中不斷改變放大鏡的位置。這一步關(guān)鍵在于原圖已經(jīng)畫線了伸头,放大的部位是從截屏上取的沒有畫線的匾效,那怎么處理這一步呢。不要重復(fù)的去截屏來保持同步顯示恤磷。在截屏的圖中同步畫線就行了面哼,這一步很關(guān)鍵

(畫線的方法在之前說過了)。

簡單的貼下代碼


實現(xiàn)擦除功能(這一步比較簡單)

實際上就是手勢觸及到的部位要恢復(fù)成原圖扫步。

1.做操作之前魔策,保存原圖,保存截屏圖(用于放大鏡的截屏圖)

2.手勢觸及的部位(范圍自己刃科蕖)代乃,從原圖上取這一塊的rgb值,通過for循環(huán)來修改已經(jīng)畫線的圖對應(yīng)的位置。

貼一下for循環(huán)里面的代碼搁吓,應(yīng)該比較好理解

因為代碼是屬于公司的原茅,不方便透漏,所以就沒上傳代碼堕仔。如果我有什么地方說得不明白可以私信我擂橘。很樂意一起學(xué)習(xí)。覺得有用的話摩骨,順手點個贊通贞。謝謝??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恼五,隨后出現(xiàn)的幾起案子昌罩,更是在濱河造成了極大的恐慌,老刑警劉巖灾馒,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茎用,死亡現(xiàn)場離奇詭異,居然都是意外死亡睬罗,警方通過查閱死者的電腦和手機轨功,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來容达,“玉大人古涧,你說我怎么就攤上這事』ㄑ危” “怎么了羡滑?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長算芯。 經(jīng)常有香客問我啄栓,道長,這世上最難降的妖魔是什么也祠? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任昙楚,我火速辦了婚禮,結(jié)果婚禮上诈嘿,老公的妹妹穿的比我還像新娘堪旧。我一直安慰自己,他們只是感情好奖亚,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布淳梦。 她就那樣靜靜地躺著,像睡著了一般昔字。 火紅的嫁衣襯著肌膚如雪爆袍。 梳的紋絲不亂的頭發(fā)上首繁,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音陨囊,去河邊找鬼弦疮。 笑死,一個胖子當(dāng)著我的面吹牛蜘醋,可吹牛的內(nèi)容都是我干的胁塞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼压语,長吁一口氣:“原來是場噩夢啊……” “哼啸罢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胎食,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤扰才,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后厕怜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體训桶,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年酣倾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤专。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡躁锡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出置侍,到底是詐尸還是另有隱情映之,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布蜡坊,位于F島的核電站杠输,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏秕衙。R本人自食惡果不足惜蠢甲,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望据忘。 院中可真熱鬧鹦牛,春花似錦、人聲如沸勇吊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汉规。三九已至礼殊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晶伦。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工碟狞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坝辫。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓篷就,卻偏偏與公主長得像,于是被迫代替她去往敵國和親近忙。 傳聞我的和親對象是個殘疾皇子竭业,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容