python200行代碼_如何用200行Python代碼“換臉”

本文將介紹如何編寫一個只有200行的Python腳本,為兩張肖像照上人物的“換臉”。

這個過程可分為四步:

檢測面部標記。

旋轉、縮放和轉換第二張圖像,使之與第一張圖像相適應。

調整第二張圖像的色彩平衡,使之與第一個相匹配。

把第二張圖像的特性混合在第一張圖像中。

ff5c8a01f65e431d64a804c6dfa6afc8.jpgwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

1.使用dlib提取面部標記

該腳本使用dlib的Python綁定來提取面部標記:

63af24468bcfe3414f1ed72d039c3c71.jpgwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

用Dlib實現了論文One Millisecond Face Alignment with an Ensemble of Regression Trees中的算法(http://www.csc.kth.se/~vahidk/papers/KazemiCVPR14.pdf,作者為Vahid Kazemi 和Josephine Sullivan) 。算法本身非常復雜,但dlib接口使用起來非常簡單:

PREDICTOR_PATH = "/home/matt/dlib-18.16/shape_predictor_68_face_landmarks.dat"

detector = dlib.get_frontal_face_detector()

predictor = dlib.shape_predictor(PREDICTOR_PATH)

def get_landmarks(im):

rects = detector(im, 1)

if len(rects) > 1:

raise TooManyFaces

if len(rects) == 0:

raise NoFaces

return numpy.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()])

get_landmarks()函數將一個圖像轉化成numpy數組,并返回一個68 x2元素矩陣,輸入圖像的每個特征點對應每行的一個x,y坐標。

特征提取器(predictor)要一個粗糙的邊界框作為算法輸入,由傳統的能返回一個矩形列表的人臉檢測器(detector)提供,其每個矩形列表在圖像中對應一個臉。

2.用普氏分析(Procrustes analysis)調整臉部

現在我們已經有了兩個標記矩陣,每行有一組坐標對應一個特定的面部特征(如第30行給出的鼻子的坐標)。我們現在要搞清楚如何旋轉、翻譯和規模化第一個向量,使它們盡可能適合第二個向量的點。想法是,可以用相同的變換在第一個圖像上覆蓋第二個圖像。

把它們更數學化,尋找T,s和R,令下面這個表達式的結果最小:

92f14ae99d43ab18ad998723c4878b32.jpgwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

R是個2 x2正交矩陣,s是標量,T是二維向量,pi和qi是上面標記矩陣的行。

本人對于Python學習創建了一個小小的學習圈子,為各位提供了一個平臺,大家一起來討論學習Python。歡迎各位到來Python學習群:923414804一起討論視頻分享學習。Python是未來的發展方向,正在挑戰我們的分析能力及對世界的認知方式,因此,我們與時俱進,迎接變化,并不斷的成長,掌握Python核心技術,才是掌握真正的價值所在。

事實證明,這類問題可以用“常規普氏分析法” (Ordinary Procrustes Analysis) 解決:

def transformation_from_points(points1, points2):

points1 = points1.astype(numpy.float64)

points2 = points2.astype(numpy.float64)

c1 = numpy.mean(points1, axis=0)

c2 = numpy.mean(points2, axis=0)

points1 -= c1

points2 -= c2

s1 = numpy.std(points1)

s2 = numpy.std(points2)

points1 /= s1

points2 /= s2

U, S, Vt = numpy.linalg.svd(points1.T * points2)

R = (U * Vt).T

return numpy.vstack([numpy.hstack(((s2 / s1) * R,

c2.T - (s2 / s1) * R * c1.T)),

numpy.matrix([0., 0., 1.])])

代碼分別實現了下面幾步:

將輸入矩陣轉換為浮點數。這是之后步驟的必要條件。

每一個點集減去它的矩心。一旦為這兩個新的點集找到了一個最佳的縮放和旋轉方法,這兩個矩心c1和c2就可以用來找到完整的解決方案。

同樣,每一個點集除以它的標準偏差。這消除了問題的組件縮放偏差。

使用Singular Value Decomposition計算旋轉部分。可以在維基百科上看到關于解決正交普氏問題的細節(https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem)。

之后,結果可以插入OpenCV的cv2.warpAffine函數,將圖像二映射到圖像一:

def warp_im(im, M, dshape):

output_im = numpy.zeros(dshape, dtype=im.dtype)

cv2.warpAffine(im,

M[:2],

(dshape[1], dshape[0]),

dst=output_im,

borderMode=cv2.BORDER_TRANSPARENT,

flags=cv2.WARP_INVERSE_MAP)

return output_im

圖像對齊結果如下:

c46de7522f64da675f9b336599a8bf6b.gifwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

3.校正第二張圖像的顏色

如果我們試圖直接覆蓋面部特征,很快就會看到一個問題:

3e9098aac838ed0e5fada1ab8bbe7836.jpgwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

兩幅圖像之間不同的膚色和光線造成了覆蓋區域的邊緣不連續。我們試著修正:

COLOUR_CORRECT_BLUR_FRAC = 0.6

LEFT_EYE_POINTS = list(range(42, 48))

RIGHT_EYE_POINTS = list(range(36, 42))

def correct_colours(im1, im2, landmarks1):

blur_amount = COLOUR_CORRECT_BLUR_FRAC * numpy.linalg.norm(

numpy.mean(landmarks1[LEFT_EYE_POINTS], axis=0) -

numpy.mean(landmarks1[RIGHT_EYE_POINTS], axis=0))

blur_amount = int(blur_amount)

if blur_amount % 2 == 0:

blur_amount += 1

im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)

im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)

# Avoid divide-by-zero errors.

im2_blur += 128 * (im2_blur <= 1.0)

return (im2.astype(numpy.float64) * im1_blur.astype(numpy.float64) /

im2_blur.astype(numpy.float64))

結果是這樣:

c9b256907176ba2b4f3cc1143d6ebdf3.jpgwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

此函數試圖改變圖像2的顏色來匹配圖像1。它通過用im2除以im2的高斯模糊,然后乘以im1的高斯模糊。這里的想法是用RGB縮放校色,但是不是用所有圖像的整體常數比例因子,每個像素都有自己的局部比例因子。

用這種方法兩圖像之間光線的差異只能在某種程度上被修正。例如,如果圖像1是從一邊照亮,但圖像2是均勻照明的,色彩校正后圖像2也會出現未照亮邊暗一些的現象。

也就是說,這是一個相當粗糙的辦法,而且解決問題的關鍵是一個適當的高斯內核大小。如果太小,第一個圖像的面部特征將顯示在第二個圖像中。過大,內核之外區域像素被覆蓋,并發生變色。這里的內核用了一個0.6 *的瞳孔距離。

4.把第二張圖像的特性混合在第一張圖像中

用一個遮罩來選擇圖像2和圖像1的哪些部分應該是最終顯示的圖像:

bc496341dda005c2342992136ae5ef5c.jpgwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

值為1(白色)的地方為圖像2應該顯示出的區域,值為0(黑色)的地方為圖像1應該顯示出的區域。值在0和1之間為圖像1和圖像2的混合區域。

這是生成上面那張圖的代碼:

LEFT_EYE_POINTS = list(range(42, 48))

RIGHT_EYE_POINTS = list(range(36, 42))

LEFT_BROW_POINTS = list(range(22, 27))

RIGHT_BROW_POINTS = list(range(17, 22))

NOSE_POINTS = list(range(27, 35))

MOUTH_POINTS = list(range(48, 61))

OVERLAY_POINTS = [

LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS,

NOSE_POINTS + MOUTH_POINTS,

]

FEATHER_AMOUNT = 11

def draw_convex_hull(im, points, color):

points = cv2.convexHull(points)

cv2.fillConvexPoly(im, points, color=color)

def get_face_mask(im, landmarks):

im = numpy.zeros(im.shape[:2], dtype=numpy.float64)

for group in OVERLAY_POINTS:

draw_convex_hull(im,

landmarks[group],

color=1)

im = numpy.array([im, im, im]).transpose((1, 2, 0))

im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0

im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)

return im

mask = get_face_mask(im2, landmarks2)

warped_mask = warp_im(mask, M, im1.shape)

combined_mask = numpy.max([get_face_mask(im1, landmarks1), warped_mask],

axis=0)

我們把上述代碼分解:

get_face_mask()的定義是為一張圖像和一個標記矩陣生成一個遮罩,它畫出了兩個白色的凸多邊形:一個是眼睛周圍的區域,一個是鼻子和嘴部周圍的區域。之后它由11個像素向遮罩的邊緣外部羽化擴展,可以幫助隱藏任何不連續的區域。

這樣一個遮罩同時為這兩個圖像生成,使用與步驟2中相同的轉換,可以使圖像2的遮罩轉化為圖像1的坐標空間。

之后,通過一個element-wise最大值,這兩個遮罩結合成一個。結合這兩個遮罩是為了確保圖像1被掩蓋,而顯現出圖像2的特性。

最后,應用遮罩,給出最終的圖像:

output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask

080667e1cac946b3f01bffee3c6d4eaf.jpgwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/271289.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/271289.shtml
英文地址,請注明出處:http://en.pswp.cn/news/271289.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

git的smart Checkout跟force checkout的區別

1:在切換分支的時候,常常會遇到下圖的問題 是因為我在test分支上修改了代碼&#xff0c;但是沒有commit&#xff0c;切換到其他分支上就彈出了這個窗口 我們需要怎么處理呢 2:可以看到彈框底部有Force Checkout Dont checkout Smart Checkout,表示什么意思呢 Smart …

python畫圖中grid等于true_Python中的matplotlib畫圖總結

# -*- coding: utf-8 -*-import matplotlib.pyplot as pltfrom numpy.random import randnimport numpy as npfrom io import StringIOimport pandas as pd#Create figurefig plt.figure()#創建子圖subplot: 表示2X2&#xff0c;即4個子圖中的第1個圖&#xff0c;編號為1#參數…

數據挖掘相關知識介紹

1、數據挖掘定義把數據庫中大量數據背后隱藏的重要信息抽取出來&#xff0c;然后為公司創造很多潛在的利潤&#xff0c;針對這種海量數據庫中挖掘數據信息的技術稱為數據挖掘&#xff08;DM&#xff09;。2、數據挖掘的分類按照數據庫種類&#xff1a;關系型數據庫的數據挖掘、…

Yii2 behavior運用

1 class ReturnDataTypeBehaviors extends Behavior2 {3 4 public $type json;5 public $pcOrMobile pc; // or mobile6 7 //控制器執行之后事件8 public function events()9 { 10 return [Controller::EVENT_BEFORE_ACTION > beforeType]; …

c語言數字靈活多變的訪問形式_學習C語言你必須知道的事兒!

是新朋友嗎&#xff1f;記得先點藍字關注我哦&#xff5e;今日課程菜單Java全棧開發 | Web前端H5大數據開發 | 大數據分析人工智能Python | 人工智能物聯網有聽過這樣一段話&#xff1a;在編程界&#xff0c;C語言就是道家的“三”&#xff0c;A生B&#xff0c;B生C&#xff0c…

IDEA通過git怎么回滾到某個提交節點或某個版本

1:先右鍵點擊項目&#xff0c;選擇git,接著Show History 2:這里會顯示有歷史提交的版本記錄,假設我要回滾到箭頭處到提交&#xff0c;操作如下 3:右鍵點擊&#xff0c;點擊Copy Revision Number 在編輯器里粘貼&#xff0c;可以看到如下 4:右擊選擇項目&#xff0c;選擇git -&…

linux與mysql_Linux與MySQL

mysql -h localhost -u 用戶名 -p 密碼 //連接數據庫use desk_show; //使用數據庫show tables; //顯示數據表describe desk6_0; …

關系數據庫基礎知識介紹

1、關系的相關名詞介紹屬性&#xff08;Attribute&#xff09;:描述事物的若干特征稱為屬性。比如學號、姓名、職位、年齡等。域&#xff08;Domain&#xff09;&#xff1a;針對屬性的取值范圍集合。比如性別取值為男、女、學號的長度為8位等。一般在關系數據模型中&#xff0…

android中xmlns:tools屬性詳解

第一部分 安卓開發中&#xff0c;在寫布局代碼的時候&#xff0c;ide可以看到布局的預覽效果。 但是有些效果則必須在運行之后才能看見&#xff0c;比如這種情況&#xff1a;TextView在xml中沒有設置任何字符&#xff0c;而是在activity中設置了text。因此為了在ide中預覽效果&…

mysql sleep 5908_mysql連接卡死,很多線程sleep狀態,導致CPU中mysqld占用率極高(問題原因還待考證)...

mysql> show processlist;—–————-——————–| Id | User | Host | db | Command | Time| State | Info—–————-——————–|207|root |192.168.0.2:51621 |mytest | Sleep | 5 | | NULL|208|root |192.168.0.2:51622 |mytest | Sleep | 5 | | NULL|220|ro…

python excel庫 linux_用python寫一個簡單的excel表格獲取當時的linux系統信息

最近在學習excel表格的制作&#xff0c;順便結合之前學習的內容&#xff0c;利用python的兩個模板&#xff0c;分別是獲取系統信息的psutil&#xff0c;和生成excel表格的xlsxwriter。利用這兩個模板將生成一個簡單的excel表格&#xff0c;獲取當時的linux系統信息&#xff0c;…

mac下安裝brew下載非常慢解決方法

一鍵解決&#xff1a;自動腳本(全部國內地址)&#xff08;在Mac os終端中復制粘貼回車下面這句話) /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"輸入y 在終端環境下&#xff0c;brew --version 查看brew的版本&#xf…

關系數據庫模式和完整性約束相關概念

一、關系數據庫模式介紹關系的描述形式&#xff1a;R(U,D.dom,F)&#xff0c;簡化形式&#xff1a;R(U)、R(A1,A2,A3...An)R:表示關系名&#xff1b;U組成該關系的屬性名集合&#xff1b;D是屬性的域&#xff1b;dom是屬性向域的映像集合&#xff1b;F為屬性間數據的依賴關系集…

分布式鎖的實現方式

在進行大型網站技術架構設計以及業務實現的過程中&#xff0c;多少都會遇到需要使用分布式鎖的情況。那么問題也就接踵而至。分布式鎖zk和memcached以及redis三者都能實現&#xff0c;同樣是分布式鎖&#xff0c;三者的區別何在&#xff1f;各自適用什么場景&#xff1f; 一、Z…

關系數據庫基礎:關系代數運算知識筆記

1、關系代數運算符集合運算符&#xff1a;并(U)、差(-)、交(∩)、笛卡爾積()專門的關系運算符&#xff1a;選擇(?)、投影(π)、連接&#xff08;∞&#xff09;、除()算術比較符&#xff1a;大于&#xff08;&#xff1e;&#xff09;、大于等于&#xff08;≥&#xff09;、小…

POJ 1308 Is It A Tree? (并查集)

Is It A Tree?題目鏈接&#xff1a; http://acm.hust.edu.cn/vjudge/contest/123393#problem/M Description A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between node…

Mysql分頁加pagebean_Spring+MyBatis+SpringMvc+Mysql+Druid+PageHelper分頁實現

我是阿福&#xff0c;公眾號「阿福聊編程」作者&#xff0c;一個在后端技術路上摸盤滾打的程序員&#xff0c;在進階的路上&#xff0c;共勉&#xff01;文章已收錄在 JavaSharing 中&#xff0c;包含Java技術文章&#xff0c;面試指南&#xff0c;資源分享。思路分析MyBatis的…

python csv使用_python CSV模塊的使用

簡介 CSV&#xff08;comma separated values&#xff09;&#xff0c;逗號分隔值&#xff08;字符分割值&#xff0c;字符可以不是逗號&#xff09;&#xff0c;常用的文本格式&#xff0c;用以存儲表格數據&#xff0c;包括數字或者字符。kaggle就是csv格式&#xff0c;pytho…

JDK 與 JRE區別

JDK 與 JRE JDK 與 JRE 是我們經常遇到的概念&#xff0c;但許多學習了幾年的開發都搞不懂他們之間的區別。簡單地說 JRE&#xff08;Java Runtime Environment&#xff09;僅包含運行 Java 程序的必需組件&#xff0c;包括 Java 虛擬機以及 Java 核心類庫等。而 JDK&#xff…

數據庫技術基礎:查詢優化相關知識筆記

1、查詢優化的基本概念1.1 查詢處理查詢處理是指從數據庫中提取數據的一系列活動。主要包括:將高級數據庫查詢語句翻譯成文件系統這一物理 層次的表達式&#xff0c;為優化查詢進行各種轉換以及查詢的實際執行。1.2 查詢處理的代價查詢處理的代價通常由磁盤的訪問&#xff0c;因…