File size: 9,353 Bytes
a70eb3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env python3
"""Generate a diverse character dataset using Flux 1 + PuLID for LoRA training."""

import json
import urllib.request
import time
import os

COMFYUI_URL = "http://127.0.0.1:80"
DATASET_DIR = "/home/azureuser/ai-toolkit/character_dataset"
TRIGGER_WORD = "ohwx"

# Diverse prompts covering different angles, scenes, lighting, outfits
PROMPTS = [
    # Close-ups / headshots
    (f"close up portrait photo of {TRIGGER_WORD} woman, natural lighting, soft smile, looking at camera, shallow depth of field", "closeup_front"),
    (f"close up portrait of {TRIGGER_WORD} woman, side profile view, golden hour lighting, outdoors", "closeup_profile"),
    (f"close up portrait of {TRIGGER_WORD} woman, three quarter view, studio lighting, neutral expression", "closeup_34"),
    (f"headshot of {TRIGGER_WORD} woman, looking slightly up, soft natural light, gentle smile", "closeup_up"),
    (f"close up of {TRIGGER_WORD} woman, looking down at something in her hands, natural indoor lighting", "closeup_down"),

    # Half body shots
    (f"half body photo of {TRIGGER_WORD} woman in a white blouse, sitting at a cafe table, warm ambient light, candid", "half_cafe"),
    (f"half body photo of {TRIGGER_WORD} woman wearing a leather jacket, urban street background, overcast day", "half_street"),
    (f"half body photo of {TRIGGER_WORD} woman in athletic wear, gym setting, bright lighting", "half_gym"),
    (f"half body photo of {TRIGGER_WORD} woman in a sundress, garden background, dappled sunlight", "half_garden"),
    (f"half body photo of {TRIGGER_WORD} woman in business attire, modern office, professional lighting", "half_office"),

    # Full body shots
    (f"full body photo of {TRIGGER_WORD} woman walking on the beach, sunset, casual summer outfit, warm tones", "full_beach"),
    (f"full body photo of {TRIGGER_WORD} woman standing in a city street, winter coat, evening city lights", "full_city"),
    (f"full body photo of {TRIGGER_WORD} woman hiking on a mountain trail, athletic outfit, golden hour", "full_hike"),
    (f"full body photo of {TRIGGER_WORD} woman leaning against a wall, casual jeans and t-shirt, natural daylight", "full_casual"),

    # Different lighting conditions
    (f"portrait of {TRIGGER_WORD} woman, dramatic side lighting, dark moody background, artistic photo", "light_dramatic"),
    (f"portrait of {TRIGGER_WORD} woman, bright overcast natural light, white background, clean look", "light_bright"),
    (f"portrait of {TRIGGER_WORD} woman, warm golden hour backlight, hair glowing, outdoor", "light_golden"),
    (f"portrait of {TRIGGER_WORD} woman, soft window light, sitting on a couch, cozy indoor setting", "light_window"),

    # Different expressions
    (f"photo of {TRIGGER_WORD} woman laughing genuinely, candid moment, natural setting", "expr_laugh"),
    (f"photo of {TRIGGER_WORD} woman with a serious contemplative expression, looking into distance", "expr_serious"),
]

# Corresponding captions (without trigger word for variety, will be added by trainer)
CAPTIONS = {
    "closeup_front": f"close up portrait photo of {TRIGGER_WORD} woman, natural lighting, soft smile, looking at camera",
    "closeup_profile": f"close up portrait of {TRIGGER_WORD} woman, side profile view, golden hour lighting, outdoors",
    "closeup_34": f"close up portrait of {TRIGGER_WORD} woman, three quarter view, studio lighting, neutral expression",
    "closeup_up": f"headshot of {TRIGGER_WORD} woman, looking slightly up, soft natural light, gentle smile",
    "closeup_down": f"close up of {TRIGGER_WORD} woman, looking down, natural indoor lighting",
    "half_cafe": f"half body photo of {TRIGGER_WORD} woman in a white blouse, sitting at a cafe, warm ambient light",
    "half_street": f"half body photo of {TRIGGER_WORD} woman wearing a leather jacket, urban street, overcast day",
    "half_gym": f"half body photo of {TRIGGER_WORD} woman in athletic wear, gym setting, bright lighting",
    "half_garden": f"half body photo of {TRIGGER_WORD} woman in a sundress, garden, dappled sunlight",
    "half_office": f"half body photo of {TRIGGER_WORD} woman in business attire, modern office",
    "full_beach": f"full body photo of {TRIGGER_WORD} woman walking on the beach, sunset, casual summer outfit",
    "full_city": f"full body photo of {TRIGGER_WORD} woman standing in city street, winter coat, evening lights",
    "full_hike": f"full body photo of {TRIGGER_WORD} woman hiking on mountain trail, athletic outfit, golden hour",
    "full_casual": f"full body photo of {TRIGGER_WORD} woman leaning against wall, jeans and t-shirt, daylight",
    "light_dramatic": f"portrait of {TRIGGER_WORD} woman, dramatic side lighting, dark moody background",
    "light_bright": f"portrait of {TRIGGER_WORD} woman, bright overcast light, white background, clean",
    "light_golden": f"portrait of {TRIGGER_WORD} woman, warm golden hour backlight, hair glowing, outdoor",
    "light_window": f"portrait of {TRIGGER_WORD} woman, soft window light, sitting on couch, cozy indoor",
    "expr_laugh": f"photo of {TRIGGER_WORD} woman laughing genuinely, candid moment, natural setting",
    "expr_serious": f"photo of {TRIGGER_WORD} woman with serious contemplative expression, looking into distance",
}


def queue_prompt(prompt_text, filename, seed):
    workflow = {
        "1": {"class_type": "UNETLoader", "inputs": {"unet_name": "flux1-dev.safetensors", "weight_dtype": "default"}},
        "2": {"class_type": "DualCLIPLoader", "inputs": {"clip_name1": "t5xxl_fp16.safetensors", "clip_name2": "clip_l.safetensors", "type": "flux"}},
        "3": {"class_type": "VAELoader", "inputs": {"vae_name": "ae.safetensors"}},
        "4": {"class_type": "PulidFluxModelLoader", "inputs": {"pulid_file": "pulid_flux_v0.9.1.safetensors"}},
        "5": {"class_type": "PulidFluxInsightFaceLoader", "inputs": {"provider": "CUDA"}},
        "6": {"class_type": "PulidFluxEvaClipLoader", "inputs": {}},
        "7": {"class_type": "LoadImage", "inputs": {"image": "reference_face.png"}},
        "8": {"class_type": "ApplyPulidFlux", "inputs": {
            "model": ["1", 0], "pulid_flux": ["4", 0], "eva_clip": ["6", 0],
            "face_analysis": ["5", 0], "image": ["7", 0],
            "weight": 0.85, "start_at": 0.0, "end_at": 1.0
        }},
        "9": {"class_type": "CLIPTextEncodeFlux", "inputs": {
            "clip": ["2", 0],
            "clip_l": prompt_text[:77],
            "t5xxl": prompt_text,
            "guidance": 3.5
        }},
        "10": {"class_type": "EmptySD3LatentImage", "inputs": {"width": 1024, "height": 1024, "batch_size": 1}},
        "11": {"class_type": "KSampler", "inputs": {
            "model": ["8", 0], "positive": ["9", 0], "negative": ["9", 0],
            "latent_image": ["10", 0], "seed": seed,
            "control_after_generate": "fixed", "steps": 20, "cfg": 1.0,
            "sampler_name": "euler", "scheduler": "simple", "denoise": 1.0
        }},
        "12": {"class_type": "VAEDecode", "inputs": {"samples": ["11", 0], "vae": ["3", 0]}},
        "13": {"class_type": "SaveImage", "inputs": {"images": ["12", 0], "filename_prefix": f"dataset_{filename}"}}
    }

    data = json.dumps({"prompt": workflow}).encode()
    req = urllib.request.Request(f'{COMFYUI_URL}/prompt', data=data, headers={'Content-Type': 'application/json'})
    resp = urllib.request.urlopen(req)
    return json.loads(resp.read())['prompt_id']


def wait_for_completion(prompt_id, timeout=600):
    start = time.time()
    while time.time() - start < timeout:
        req = urllib.request.Request(f'{COMFYUI_URL}/history/{prompt_id}')
        resp = urllib.request.urlopen(req)
        history = json.loads(resp.read())
        if prompt_id in history:
            h = history[prompt_id]
            status = h.get('status', {}).get('status_str', '')
            if status == 'success':
                for nid, out in h['outputs'].items():
                    if 'images' in out:
                        return out['images'][0]['filename']
            elif status == 'error':
                msgs = h.get('status', {}).get('messages', [])
                for m in msgs:
                    if m[0] == 'execution_error':
                        print(f"  ERROR: {m[1].get('exception_message', 'unknown')[:200]}")
                return None
        time.sleep(2)
    return None


def main():
    print(f"Generating {len(PROMPTS)} character images for LoRA training...\n")

    for i, (prompt, name) in enumerate(PROMPTS):
        seed = 10000 + i * 1337
        print(f"[{i+1}/{len(PROMPTS)}] {name} (seed={seed})")
        prompt_id = queue_prompt(prompt, name, seed)
        filename = wait_for_completion(prompt_id)

        if filename:
            # Copy to dataset folder
            src = f"/home/azureuser/ComfyUI/output/{filename}"
            dst = os.path.join(DATASET_DIR, f"{name}.png")
            os.system(f"cp '{src}' '{dst}'")

            # Write caption
            caption = CAPTIONS[name]
            with open(os.path.join(DATASET_DIR, f"{name}.txt"), 'w') as f:
                f.write(caption)

            print(f"  -> saved {name}.png + caption")
        else:
            print(f"  -> FAILED")

    print(f"\nDone! Dataset at: {DATASET_DIR}")
    print(f"Files: {len(os.listdir(DATASET_DIR))}")


if __name__ == "__main__":
    main()