前段時間公司要開發(fā)一個自拍換背景的證件照軟件,之前從來沒有接觸過這個方面醇滥。于是看了很多相關(guān)文章黎比,慢慢的有了思路。開始搞事情鸳玩。阅虫。
2019.1.3 補充了uiimage和cv::mat相互轉(zhuǎn)換,以及放大鏡和擦除
簡單的介紹一下流程不跟,只需要做以下三步:
第一步:在原圖上面畫線颓帝,得到 mask 圖
第二步:調(diào)用已經(jīng)下載好的摳圖算法,原圖+mask圖 ?融合出
第三步:用原圖+融合圖+背景圖(以藍色背景為例) ?做融合
好了 更換背景成功窝革,是不是覺得很神奇购城。
那下面我們來詳細的講解一下以上三步在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圖就出來了鼻忠。說這么多涵但,來一張圖吧,如圖
前期工作:創(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)換代碼
現(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í)。覺得有用的話摩骨,順手點個贊通贞。謝謝??