IP-Adapter-FaceID-PlusV2 で同一人物の写真が何枚も作れるのでLoRA学習用データをつくってみました。

はじめに

「IP-Adapter-FaceID-PlusV2」を使うと1枚の顔写真からその人物の写真を何枚も作成することが可能になります。
touch-sp.hatenablog.com
現時点での弱点と思われるのは使える画像が一枚ということです。そのためか顔の再現性は完璧ではありません。

しかし、作成する画像の一貫性は高いです。

実例を見た方がわかりやすです

元画像


作成画像




作成された4枚の画像に写る人物は同じ人に見えます。ただし、元画像の人と同一人物かと言えば疑問が残ります。

目的

今回は「IP-Adapter-FaceID-PlusV2」で同一人物の写真を大量に作成し、それをLoRA学習に使ってみようとする試みです。

同一人物の写真を作成したいなら「IP-Adapter-FaceID-PlusV2」を使えば十分です。

今回の目的は一人の人物の写真が複数枚用意できた時に、LoRAでどこまでその人物を再現できるかを試すことです。

「IP-Adapter-FaceID-PlusV2」は学習用データを作るだけに使いました。

最近Pivotal Tuningというのを組み合わせたLoRAが発表されました。それを使ってみようと思います。
huggingface.co

Pythonスクリプト

以下のスクリプトで120枚の学習用データを作成しました。

import os
import cv2
from insightface.app import FaceAnalysis
from insightface.utils import face_align
import torch
from diffusers import StableDiffusionXLPipeline, DPMSolverMultistepScheduler
import itertools
import argparse

from ip_adapter.ip_adapter_faceid import IPAdapterFaceIDPlusXL

parser = argparse.ArgumentParser()
parser.add_argument(
    '--repeat',
    type=int,
    default=5,
    help="nuber of repeat",
)
args = parser.parse_args()

repeat = args.repeat
image_size = 640  # image_size%112 or image_size%128 must be 0

app = FaceAnalysis(name="buffalo_l", providers=['CUDAExecutionProvider'])
app.prepare(ctx_id=0, det_size=(640, 640))

image = cv2.imread("face.png")
faces = app.get(image)

faceid_embeds = torch.from_numpy(faces[0].normed_embedding).unsqueeze(0)
face_image = face_align.norm_crop(image, landmark=faces[0].kps, image_size=image_size)

base_model_path = "model/fudukiMix_v20"
image_encoder_path = "CLIP-ViT-H-14-laion2B-s32B-b79K"
ip_ckpt = "IP-Adapter-FaceID/ip-adapter-faceid-plusv2_sdxl.bin"
device = "cuda"

noise_scheduler = DPMSolverMultistepScheduler(
    num_train_timesteps=1000,
    beta_start=0.00085,
    beta_end=0.012,
    beta_schedule="scaled_linear",
    steps_offset=1,
    algorithm_type="sde-dpmsolver++",
    use_karras_sigmas=True,
)
pipe = StableDiffusionXLPipeline.from_pretrained(
    base_model_path,
    scheduler=noise_scheduler,
    torch_dtype=torch.float16,
    variant="fp16"
)

ip_model = IPAdapterFaceIDPlusXL(pipe, image_encoder_path, ip_ckpt, device)

negative_prompt = "hand, hands, finger, cleavage, illustration, 3d, 2d, painting, cartoons, sketch, watercolor, monotone, kimono, crossed eyes, strabismus"

model_name = os.path.basename(base_model_path)
save_folder = f"face_dataset_for_train{model_name}"
os.makedirs(save_folder, exist_ok=True)

shot_list = ["face shot", "close-up shot"]
angle_list = ["straight-on", "from side"]
lighting_list = ["natural lighting", "cinematic lighting", "studio lighting"]

for (shot, angle, lighting) in itertools.product(shot_list, angle_list, lighting_list):
    for i in range(repeat):
        prompt = f"japanese woman, wavy medium hair, {shot}, {angle}, {lighting}, best quality"
        images = ip_model.generate(
            prompt=prompt,
            negative_prompt=negative_prompt,
            face_image=face_image,
            faceid_embeds=faceid_embeds,
            shortcut=True,
            s_scale=1.0,
            num_samples=1,
            width=1024,
            height=1024,
            num_inference_steps=40,
            guidance_scale=7.5
        )
        save_fname = ''.join(f"{model_name}_{shot}_{angle}_{lighting}_{i}.png".split())
        images[0].save(os.path.join(save_folder, save_fname))

学習

つづきはこちらに書きました。
touch-sp.hatenablog.com




このエントリーをはてなブックマークに追加