Pythonで画像中の二人の顔を入れ替える

では、過去記事二つのプログラムを使い、スマホアプリ「Snow」 みたく 同一写真上の二人の顔を入れ替えるプログラムを作ってみます。
プログラム書いてる人の技術力の問題で、めっちゃ雑ですけれども。

必要なモジュール

github.com
新しいめの PythonOpenGLライブラリ。 今回はこちらを使ってみます

使用する過去記事

facelandmark.py

boxheadroom.hatenablog.com

facepoly.py

boxheadroom.hatenablog.com

プログラム

fakeswap.py

from datetime import datetime
import numpy as np
import cv2
import moderngl
import facepoly
import facelandmark
import sys

def one2(x,y):
return 2*x-1,2*y-1
def mkbg(ctx):
vx=np.array([-1,1,1,-1],dtype=np.float)
vy=np.array([-1,-1,1,1],dtype=np.float)
tx=np.array([0,1,1,0],dtype=np.float)
ty=np.array([0,0,1,1],dtype=np.float)
index=np.array([0,1,2,0,2,3],dtype=np.int32)
bg=GLObj(ctx)
bg.setvert(vx,vy)
bg.setindex(index)
bg.tx=tx
bg.ty=ty
return bg
class GLRenderer(object):
def __init__(self):
#オフスクリーンレンダリング
self.ctx=moderngl.create_standalone_context()
def setframe(self,w,h):
self.W=w
self.H=h
self.fbo = self.ctx.simple_framebuffer((self.W,self.H))
self.fbo.use()
def mktex(self,im):
im2=cv2.cvtColor(im,cv2.COLOR_BGR2RGB)
h,w,c=im2.shape
tex = self.ctx.texture( (w,h) , 3, im2.tobytes())
return tex
def render(self,objlist):
self.ctx.clear(1.0, 1.0, 1.0)
#self.ctx.enable(moderngl.DEPTH_TEST)
self.fbo.clear(0.0, 0.0, 0.0, 1.0)
for obj in objlist:
obj.tex0.use(0)
obj.prog['Color0'].value = 0
obj.vao.render(moderngl.TRIANGLES)
im=np.fromstring(self.fbo.read(),dtype=np.uint8)
im=im.reshape(self.H,self.W,3)
cv2.cvtColor(im,cv2.COLOR_BGR2RGB,im)
#cv2.flip(im,0,im)
return im
class GLObj(object):
def __init__(self,ctx):
self.ctx=ctx
self.prog = self.ctx.program(
vertex_shader='''
#version 330
in vec2 in_vert;
in vec2 in_text;
out vec2 v_text;
void main() {
gl_Position = vec4(in_vert,0, 1.0);
v_text = in_text;
}
''',
fragment_shader='''
#version 330
uniform sampler2D Color0;
in vec2 v_text;
out vec4 f_color;
void main() {
vec3 color1 = texture(Color0, v_text).rgb;
f_color = vec4(color1, 1.0);
}
''',
)

def updatebuf(self):
vertices = np.dstack([self.vx, self.vy, self.tx,self.ty])
index =self.index
self.vbo = self.ctx.buffer(vertices.astype('f4').tobytes())
self.ibo = self.ctx.buffer(index.astype('i4').tobytes())
self.vao = self.ctx.simple_vertex_array(self.prog, self.vbo, 'in_vert', 'in_text',
index_buffer=self.ibo)

def setindex(self,index):
self.index=index

def setvert(self,vx,vy):
self.vx=vx
self.vy=vy

def settex(self,tex,tx,ty):
self.tex0=tex
self.tx=tx
self.ty=ty


def mkface(ctx):
fc=GLObj(ctx)
fcindex = facepoly.triangles
fc.setindex(fcindex)
return fc

if __name__=="__main__":
fn=sys.argv[1]
renderer=GLRenderer()
ctx=renderer.ctx
#
bg=mkbg(ctx)
fc0=mkface(ctx)
fc1=mkface(ctx)
im=cv2.imread(fn)
#顔検出
faces=facelandmark.landmark(im,nfaces=2)
tx0,ty0=faces[0]
tx1,ty1=faces[1]
tex=renderer.mktex(im)
#入れ替え
vx0,vy0=one2(tx1,ty1)
vx1,vy1=one2(tx0,ty0)
#フレームバッファ設定
h,w,c=im.shape
renderer.setframe(w,h)
#
fc0.settex(tex,tx0,ty0)
fc0.setvert(vx0,vy0)

fc1.settex(tex,tx1,ty1)
fc1.setvert(vx1,vy1)
#bg
bg.settex(tex,bg.tx, bg.ty)

objlist= [bg,fc0,fc1]
for obj in objlist:
obj.updatebuf()
im2=renderer.render(objlist)
cv2.imshow("faceswap",im2)
k=cv2.waitKey(1)
today=datetime.today()
fnout=today.strftime("%Y%m%d_%H%M%S.jpg")
cv2.imwrite(fnout,im2)
input("pause")


元画像
f:id:boxheadroom:20181021165613j:plain
加工後
f:id:boxheadroom:20181021165715j:plain
めっちゃ雑!