顔の特徴操作・顔の合成(Glow)
人工知能で「顔の特徴」(属性)の操作、「二人の顔を合成」します。
※属性は人物の若さ(年齢)、性別、髪の色、ヒゲなど40種類を変更できます。
次は2枚の人物の顔を合成しています。※0.00と1.00が元の画像です。

元の画像が±0.00です。値が高いと効果が強くなり、低くなると逆効果になります。
1. 髭の剃り残しがある(5_o_Clock_Shadow)

2. 三日月眉(Arched_Eyebrows)

3. 魅力的(Attractive)

4. 目の下のたるみ(Bags_Under_Eyes)

5. スキンヘッド(Bald)

6. 前髪(Bangs)

7. 大きな唇(Big_Lips)

8. 大きな鼻(Big_Nose)

9. 黒髪(Black_Hair)

10. 金髪(Blond_Hair)

11. ぼやけた(Blurry)

12. 茶髪(Brown_Hair)

13. ゲジゲジ眉毛(Bushy_Eyebrows)

14. ぽっちゃり(Chubby)

15. 2重あご(Double_Chin)

16. メガネ(Eyeglasses)

17. やぎひげ(Goatee)

18. 白髪(Gray_Hair)

19. 厚化粧(Heavy_Makeup)

20. 高い頬骨(High_Cheekbones)

21. 男(Male) ※性別

22. 口が少し開いている(Mouth_Slightly_Open)

23. 口ひげ(Mustache)

24. 細目(Narrow_Eyes)

25. ひげが無い(No_Beard)

26. うりざね顔(Oval_Face)

27. 顔色が悪い(Pale_Skin)

28. とがった鼻(Pointy_Nose)

29. 額がはげ上がった(Receding_Hairline)

30. 紅顔(Rosy_Cheeks)

31. 頬ひげ(Sideburns)

32. 笑顔(Smiling)

33. ストレート(髪型)(Straight_Hair)

34. カール(髪型)(Wavy_Hair)

35. イアリング(Wearing_Earrings)

36. 帽子(Wearing_Hat)

37. 口紅(Wearing_Lipstick)

38. ネックレス(Wearing_Necklace)

39. ネクタイ(Wearing_Necktie)

40. 若さ(Young)

このAIで使用している「CelebA」(データセット)に40の属性(Attribute)がある為、顔の編集は40種類です。
このAI(人工知能)の結果は調査及び研究・教育目的で公開しています。
本来は改良した「TensorFlow Lite」を用いて学習済みモデルで推論する予定でしたが、重すぎるので結果のみを公開しています。
使用しているオープンソースはOpenAIの「Glow」です。学習済みモデルなどのローディングに2分ぐらいで一時的にメモリが約16GBが必要です。ロード後の顔の特徴変更、顔の合成は約500msで処理はおわる、その後は7GBぐらいのメモリを常時必要とする。このメモリの使用量でVPSでは厳しい。GCPなどでも良いが、かなりの高額になるので本稼働するには自宅サーバーじゃないと経済的にきついと思います。
これが軽量ならば顔編集アプリを簡単に作れます。メイン処理はサーバーで、クライアントはブラウザまたはスマートフォン。
環境が複雑なのとハイメモリなので実際に試す場合は最初はColab Proをお勧めします。
これらの顔の操作は組み合わせ可能です。例えば「若さ」(年齢)と「黒髪」だと2つの推論を繰り返して実行すればOKです。
次に備忘録としてGlowの使い方を書いておきますね。
Glowの使い方
最初にColab Proの使い方。次にGlowの入手です。
!git clone https://github.com/openai/glow.git
必要なPythonパッケージは requirements.txt にあります。これはあとからエラーがでてからインストールしてください。TensorFlowは1系ですのでご注意を!
学習済みモデルを動作させる場合はdemoです。ここに英文で説明があるように最初にシェルスクリプトを実行します。カレントディレクトリを移動してから実行してください。
!sh script.sh
学習済みモデルは最適化された「graph_optimized.pb」がデフォルトですが、これだと動作しない場合があるので「graph_unoptimized.pb」をダウンロードします。※このコマンドはscript.shに記載されています。
!curl https://openaipublic.azureedge.net/glow-demo/large3/graph_unoptimized.pb > graph_unoptimized.pb
次にglow/demo/model.pyの25行目のoptimized = Trueをoptimized = Falseに変更します。これで準備完了です。
次のコマンドを実行するとtest/smile.pngが作成されます。
!python model.py
以下はWindowsのJupyter Notebookで実行しています。
モデルの読み込み(glow/demo/model.py)
import model
glow/demo/model.pyの基本的なメソッドはencode()、decode()、manipulate()、mix()、random()の5つです。
次は顔の特徴操作の40パータンの画像を作る方法です。 ※PNGファイルはdemo/web/mediaにある
import numpy as np from PIL import Image,ImageDraw,ImageFont import matplotlib.pyplot as plt # 単体のコード #image = Image.open(r'C:\Users\xxx\Desktop\rashida.png') #input_data = np.reshape(image, [1, 256, 256, 3]) #eps = model.encode(input_data) #dec, _ = model.manipulate(eps, 9, 0.66) #img = Image.fromarray(dec[0]) #plt.imshow(img) eps = [] image =[] image.append(Image.open(r'C:\Users\xxx\Desktop\leo.png')) input_data = np.reshape(image[0], [1, 256, 256, 3]) eps.append(model.encode(input_data)) image.append(Image.open(r'C:\Users\xxx\Desktop\geoff.png')) input_data = np.reshape(image[1], [1, 256, 256, 3]) eps.append(model.encode(input_data)) image.append(Image.open(r'C:\Users\xxx\Desktop\rashida.png')) input_data = np.reshape(image[2], [1, 256, 256, 3]) eps.append(model.encode(input_data)) for i in range(40): output = Image.new("RGB",[1280,798],(255,255,255)) draw = ImageDraw.Draw(output) font = ImageFont.truetype("C:/Windows/Fonts/msmincho.ttc", 24) draw.text((95,0), "-0.99", fill="#000",font=font) draw.text((95 + (256*1),0), "-0.66", fill="#000",font=font) draw.text((90 + (256*2),0), "±0.00", fill="#000",font=font) draw.text((95 + (256*3),0), "+0.66", fill="#000",font=font) draw.text((95 + (256*4),0), "+0.99", fill="#000",font=font) for key, val in enumerate(eps): # -0.99 dec, _ = model.manipulate(val, i, -0.99) result = Image.fromarray(dec[0]) output.paste(result,(0, key * 256 + 30)) # -0.66 dec, _ = model.manipulate(val, i, -0.66) result = Image.fromarray(dec[0]) output.paste(result,(256, key * 256+30)) # 元画像 output.paste(image[key],(512, key * 256+30)) # 0.66 dec, _ = model.manipulate(val, i, 0.66) result = Image.fromarray(dec[0]) output.paste(result,(768, key * 256+30)) # 0.99 dec, _ = model.manipulate(val, i, 0.99) result = Image.fromarray(dec[0]) output.paste(result,(1024, key * 256+30)) output.save('C:/Users/xxx/Desktop/bbb/' + str(i+1) + '.jpg')
顔の合成のコード
import numpy as np from PIL import Image import matplotlib.pyplot as plt image = Image.open(r'C:\Users\xxx\Desktop\beyonce.png') input_data1 = np.reshape(image, [1, 256, 256, 3]) image = Image.open(r'C:\Users\xxx\Desktop\leo.png') input_data2 = np.reshape(image, [1, 256, 256, 3]) eps1 = model.encode(input_data1) eps2 = model.encode(input_data2) output = Image.new("RGB",[1280,286],(255,255,255)) draw = ImageDraw.Draw(output) font = ImageFont.truetype("C:/Windows/Fonts/msmincho.ttc", 24) draw.text((95,0), "0.00", fill="#000",font=font) draw.text((95 + (256*1),0), "0.25", fill="#000",font=font) draw.text((90 + (256*2),0), "0.50", fill="#000",font=font) draw.text((95 + (256*3),0), "0.75", fill="#000",font=font) draw.text((95 + (256*4),0), "1.00", fill="#000",font=font) dec, _ = model.mix(eps1, eps2, 0) result = Image.fromarray(dec[0]) output.paste(result,(0, 30)) dec, _ = model.mix(eps1, eps2, 0.25) result = Image.fromarray(dec[0]) output.paste(result,(256, 30)) dec, _ = model.mix(eps1, eps2, 0.5) result = Image.fromarray(dec[0]) output.paste(result,(512, 30)) dec, _ = model.mix(eps1, eps2, 0.75) result = Image.fromarray(dec[0]) output.paste(result,(768, 30)) dec, _ = model.mix(eps1, eps2, 1) result = Image.fromarray(dec[0]) output.paste(result,(1024, 30)) output.save('C:/Users/xxx/Desktop/bbb/mix.jpg') # 単体のコード #dec, _ = model.mix(eps1, eps2, 0.5) #img = Image.fromarray(dec[0]) #plt.imshow(img)
顔をランダムで生成 ※精度はよくない
import numpy as np from PIL import Image import matplotlib.pyplot as plt _img, _z = model.random(1) plt.imshow(_img[0]) img = Image.fromarray(_img[0]) img.save(r'C:\Users\xxx\Desktop\xxx.jpg')
以上となります。コードはリファクタリングしていません。