diff --git a/caption_naturalizer.py b/caption_naturalizer.py index f13d6ee..8df9c62 100644 --- a/caption_naturalizer.py +++ b/caption_naturalizer.py @@ -356,6 +356,18 @@ def possessive_pronoun(subject: str) -> str: return "Her" if subject == "woman" else "His" +def _couple_clothing_sentence(clothing: str) -> str: + clothing = _clean_text(clothing) + lower = clothing.lower() + partner_text = re.sub(r"\bPartner ([AB]) wears\b", r"Partner \1 wearing", clothing) + partner_text = re.sub(r"\bPartner ([AB]) has\b", r"Partner \1 with", partner_text) + if lower.startswith("partner a "): + return f"The outfits show {partner_text}" + if lower.startswith(("two ", "paired ", "coordinated ")): + return f"The outfits are {partner_text}" + return f"They wear {clothing}" + + def _couple_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str] | None: subject = _clean_text(row.get("subject_phrase") or row.get("primary_subject")) primary = _clean_text(row.get("primary_subject")) @@ -381,7 +393,7 @@ def _couple_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) - if body: parts.append(f"Their body types are {body}") if clothing: - parts.append(f"They wear {clothing}") + parts.append(_couple_clothing_sentence(clothing)) if pose: parts.append(f"The pose is {pose}") if scene: @@ -396,7 +408,7 @@ def _couple_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) - def _configured_cast_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str] | None: - if _clean_text(row.get("subject_type")) != "configured_cast" and not _clean_text(row.get("women_count")): + if _clean_text(row.get("subject_type")) != "configured_cast": if "hardcore sexual poses" not in _clean_text(row.get("main_category")).lower(): return None diff --git a/categories/default_categories.json b/categories/default_categories.json index 48e464f..6ab6770 100644 --- a/categories/default_categories.json +++ b/categories/default_categories.json @@ -826,6 +826,809 @@ ] } ] + }, + { + "name": "Men casual clothes", + "slug": "men_casual_clothes", + "weight": 1.0, + "subject_type": "man", + "item_label": "Clothing", + "style": "tasteful adult menswear fashion-editorial coloured-pencil comic illustration with casual everyday styling", + "positive_suffix": "Use crisp clean comic linework, structured fabric texture, detailed hatching, natural light, and tactile textured paper.", + "expression_pools": ["men_casual_expressions"], + "composition_pools": ["men_casual_compositions"], + "subcategories": [ + { + "name": "Street casual", + "slug": "street_casual", + "weight": 1.0, + "inherit_expressions": false, + "inherit_compositions": false, + "scene_pools": ["casual_men_scenes", "casual_urban_scenes"], + "expression_pools": ["men_casual_expressions"], + "composition_pools": ["men_casual_compositions"], + "items": ["men street casual outfit generator"], + "item_templates": [ + "{outerwear} over {top}, with {bottom}, {footwear}, and {accessory}", + "{top} with {bottom}, {outerwear}, {bag}, and {texture_detail}", + "{set_piece} styled with {footwear}, {accessory}, and {color_mood}", + "{outerwear} half-open over {top}, with {bottom}, {footwear}, and {detail}", + "{top} tucked into {bottom}, paired with {bag}, {jewelry}, and {texture_detail}", + "{set_piece} with {outerwear}, {footwear}, and {detail}", + "{outerwear} layered over {top}, with {bottom}, {bag}, and {color_mood}", + "{top} under {outerwear}, with {bottom}, {accessory}, and {footwear}" + ], + "item_axes": { + "accessory": [ + "a ribbed beanie", + "thin sunglasses", + "a baseball cap", + "wire-frame glasses", + "a canvas belt", + "a low-profile headphones detail", + "a silver watch", + "a folded scarf" + ], + "bag": [ + "a canvas tote", + "a nylon sling bag", + "a compact backpack", + "a messenger bag", + "a small crossbody pouch", + "a leather satchel", + "a gym duffel", + "a simple tote bag" + ], + "bottom": [ + "relaxed cargo pants", + "straight-leg jeans", + "washed black denim", + "wide-leg work pants", + "pleated utility trousers", + "soft joggers", + "cropped chinos", + "loose fatigue pants" + ], + "color_mood": [ + "olive, black, and cream tones", + "washed denim and white", + "charcoal with muted blue accents", + "warm brown and graphite", + "navy and grey layers", + "black and stone contrast", + "forest green and off-white", + "muted city neutrals" + ], + "detail": [ + "rolled cuffs", + "clean oversized proportions", + "visible layered hems", + "a relaxed shoulder line", + "a sharp cropped jacket shape", + "loose off-duty styling", + "subtle workwear seams", + "a neat high-waist line" + ], + "footwear": [ + "clean white sneakers", + "retro running shoes", + "canvas high-tops", + "leather sneakers", + "black work boots", + "chunky trainers", + "suede desert boots", + "minimal low-top sneakers" + ], + "jewelry": [ + "a simple watch", + "a narrow chain necklace", + "a signet ring", + "a thin bracelet", + "a small pendant", + "minimal silver rings", + "a leather watch strap", + "a low-key chain" + ], + "outerwear": [ + "oversized hoodie", + "boxy denim jacket", + "open flannel shirt", + "cropped bomber jacket", + "canvas chore jacket", + "lightweight varsity jacket", + "short work jacket", + "relaxed zip hoodie" + ], + "set_piece": [ + "hoodie and cargo-pant outfit", + "denim jacket and straight-jean outfit", + "workwear jacket and fatigue-pant outfit", + "open flannel and chino outfit", + "monochrome hoodie-and-jogger set", + "varsity jacket and denim outfit", + "relaxed tee and overshirt outfit", + "canvas jacket and work-pant outfit" + ], + "texture_detail": [ + "washed cotton texture", + "structured denim seams", + "soft fleece texture", + "matte canvas fabric", + "smooth leather accents", + "creased everyday fabric", + "brushed flannel texture", + "ribbed knit trim" + ], + "top": [ + "plain white tee", + "soft black tee", + "ribbed tank under a shirt", + "graphic tee", + "long-sleeve thermal shirt", + "boxy striped tee", + "simple henley", + "lightweight sweatshirt" + ] + }, + "poses": [ + "standing with hands in jacket pockets", + "leaning against a wall with one foot raised", + "walking forward with a relaxed street-style stride", + "adjusting sunglasses while facing the camera", + "sitting on a low bench with elbows on knees", + "turning slightly to show the jacket shape", + "holding a tote at one side", + "standing in a mirror outfit-check pose" + ] + }, + { + "name": "Smart menswear", + "slug": "smart_menswear", + "weight": 1.0, + "inherit_expressions": false, + "inherit_compositions": false, + "scene_pools": ["casual_men_scenes", "casual_smart_scenes"], + "expression_pools": ["men_casual_expressions"], + "composition_pools": ["men_casual_compositions"], + "items": ["men smart casual outfit generator"], + "item_templates": [ + "{jacket} over {top}, with {bottom}, {footwear}, and {accessory}", + "{set_piece} styled with {footwear}, {bag}, and {color_mood}", + "{top} tucked into {bottom}, paired with {jacket}, {jewelry}, and {detail}", + "{jacket} with {bottom}, {top}, {bag}, and {texture_detail}", + "{set_piece} with {accessory}, {jewelry}, and {detail}", + "{top} under {layer}, with {bottom}, {footwear}, and {color_mood}", + "{jacket} worn open over {top}, with {bottom}, {footwear}, and {texture_detail}", + "{set_piece} paired with {layer}, {bag}, and {accessory}" + ], + "item_axes": { + "accessory": [ + "a slim leather belt", + "wire-frame glasses", + "a folded pocket square", + "a clean watch", + "thin sunglasses", + "a merino scarf", + "a neat phone in hand", + "a low-key lapel pin" + ], + "bag": [ + "a leather laptop bag", + "a structured tote", + "a compact briefcase", + "a slim messenger bag", + "a suede satchel", + "a black crossbody bag", + "a polished weekender bag", + "a minimal portfolio case" + ], + "bottom": [ + "tailored wool trousers", + "cropped pleated trousers", + "dark straight-leg jeans", + "slim chinos", + "wide-leg dress trousers", + "pressed cotton trousers", + "charcoal ankle trousers", + "relaxed suit trousers" + ], + "color_mood": [ + "navy, ivory, and charcoal", + "camel and black contrast", + "deep green and warm grey", + "soft brown and cream", + "graphite with pale blue accents", + "black, white, and silver", + "wine and dark denim tones", + "stone, olive, and navy" + ], + "detail": [ + "rolled blazer sleeves", + "a clean tucked waist", + "softly structured tailoring", + "a relaxed collar line", + "a polished off-duty fit", + "subtle layered hems", + "neat ankle-length trousers", + "a sharp shoulder silhouette" + ], + "footwear": [ + "leather loafers", + "suede loafers", + "minimal white sneakers", + "Chelsea boots", + "polished derbies", + "sleek leather sneakers", + "black ankle boots", + "soft driving shoes" + ], + "jacket": [ + "unstructured blazer", + "soft wool overshirt", + "short trench jacket", + "boxy chore blazer", + "clean leather jacket", + "linen blazer", + "tailored cardigan jacket", + "minimal bomber jacket" + ], + "jewelry": [ + "a simple watch", + "a narrow ring", + "a thin chain necklace", + "a leather bracelet", + "a small tie bar", + "minimal silver cuff", + "a signet ring", + "a refined watch strap" + ], + "layer": [ + "light trench coat", + "fine-gauge cardigan", + "short wool coat", + "open tailored vest", + "relaxed blazer", + "clean overshirt", + "soft knit jacket", + "minimal mac coat" + ], + "set_piece": [ + "soft blazer and trouser set", + "knit polo and pleated-trouser outfit", + "shirt and dark denim outfit", + "tailored vest and trouser outfit", + "minimal workday separates", + "cardigan and chino outfit", + "overshirt and wool-trouser outfit", + "linen blazer and straight-jean outfit" + ], + "texture_detail": [ + "smooth wool texture", + "crisp cotton folds", + "matte twill fabric", + "fine ribbed knit", + "soft suede accents", + "polished leather details", + "light linen slubs", + "subtle woven texture" + ], + "top": [ + "buttoned Oxford shirt", + "fine knit polo", + "plain white tee", + "ribbed mock-neck top", + "soft merino sweater", + "open-collar shirt", + "minimal black tee", + "lightweight turtleneck" + ] + }, + "poses": [ + "adjusting a jacket cuff", + "standing with one hand holding a laptop bag", + "walking through a lobby with relaxed posture", + "leaning beside a cafe counter", + "sitting with ankles crossed and shoulders relaxed", + "checking a watch while facing the camera", + "turning slightly to show the tailoring", + "standing near a mirror with one hand in a pocket" + ] + }, + { + "name": "Men athleisure", + "slug": "men_athleisure", + "weight": 1.0, + "inherit_expressions": false, + "inherit_compositions": false, + "scene_pools": ["casual_athleisure_scenes", "casual_men_scenes"], + "expression_pools": ["men_casual_expressions", "athleisure_focused_expressions"], + "composition_pools": ["men_casual_compositions", "athleisure_compositions"], + "items": ["men athleisure outfit generator"], + "item_templates": [ + "{top} with {bottom}, {footwear}, {bag}, and {texture_detail}", + "{set_piece} with {layer}, {accessory}, and {color_mood}", + "{layer} over {top}, with {bottom}, {footwear}, and {detail}", + "{top} tucked into {bottom}, paired with {prop}, {accessory}, and {texture_detail}", + "{set_piece} styled with {footwear}, {bag}, and {detail}", + "{top} with {bottom}, {layer}, {accessory}, and {color_mood}", + "{layer} tied around the waist over {top}, with {bottom}, {footwear}, and {prop}", + "{set_piece} with {texture_detail}, {bag}, and {accessory}" + ], + "item_axes": { + "accessory": [ + "a baseball cap", + "sport sunglasses", + "wireless earbuds", + "a fitness watch", + "a sweatband at the wrist", + "a towel over one shoulder", + "a zippered phone armband", + "a lightweight beanie" + ], + "bag": [ + "a compact gym bag", + "a nylon sling bag", + "a drawstring sport bag", + "a small backpack", + "a rolled yoga mat strap", + "a minimal duffel", + "a crossbody phone pouch", + "a canvas tote" + ], + "bottom": [ + "tapered joggers", + "training shorts", + "wide-leg track pants", + "running tights under shorts", + "cropped sweatpants", + "technical cargo pants", + "straight track pants", + "soft knit training pants" + ], + "color_mood": [ + "black and white athletic contrast", + "charcoal with neon yellow accents", + "navy and silver tones", + "sage green and graphite", + "warm grey and cream", + "deep blue and black", + "forest green and white", + "muted red and charcoal" + ], + "detail": [ + "clean athletic seam lines", + "relaxed post-workout styling", + "sleek compression layers", + "cropped sporty proportions", + "breathable layered fit", + "a sharp tapered leg", + "loose warm-up styling", + "body-skimming stretch fit" + ], + "footwear": [ + "running shoes", + "clean white trainers", + "chunky gym sneakers", + "minimal walking shoes", + "retro trainers", + "trail sneakers", + "sleek black trainers", + "soft slip-on sneakers" + ], + "layer": [ + "cropped zip hoodie", + "light windbreaker", + "oversized warm-up jacket", + "mesh training jacket", + "soft track jacket", + "sleeveless puffer vest", + "relaxed crewneck sweatshirt", + "technical overshirt" + ], + "prop": [ + "a stainless water bottle", + "a towel tucked under one arm", + "a phone in hand", + "a folded hoodie nearby", + "a pair of headphones", + "a locker key band", + "a smoothie cup", + "a rolled mat nearby" + ], + "set_piece": [ + "track jacket and jogger set", + "training tee and short outfit", + "hoodie and technical-pant outfit", + "running top and track-pant set", + "monochrome gym outfit", + "windbreaker and training-short outfit", + "crewneck and jogger set", + "mesh jacket and tapered-pant outfit" + ], + "texture_detail": [ + "matte performance knit", + "smooth stretch fabric", + "mesh panel details", + "light nylon sheen", + "soft fleece trim", + "breathable jersey fabric", + "subtle compression seams", + "ribbed athletic texture" + ], + "top": [ + "fitted sport tee", + "sleeveless training top", + "longline workout tee", + "ribbed athletic tank", + "light running shirt", + "cropped crewneck sweatshirt", + "mesh-layered sport top", + "zip-neck training top" + ] + }, + "poses": [ + "standing with a water bottle in one hand", + "stretching one arm across the chest", + "leaning against a locker with relaxed confidence", + "walking away from a gym mirror while looking back", + "sitting on a bench with elbows on knees", + "adjusting earbuds before a run", + "standing in a mirror outfit-check pose", + "tying a hoodie around the waist" + ] + } + ] + }, + { + "name": "Couple casual clothes", + "slug": "couple_casual_clothes", + "weight": 1.0, + "subject_type": "couple", + "item_label": "Clothing", + "style": "tasteful adult couple fashion-editorial coloured-pencil comic illustration with coordinated casual styling", + "positive_suffix": "Use crisp clean comic linework, readable full outfits, detailed hatching, warm natural light, and tactile textured paper.", + "expression_pools": ["couple_casual_expressions"], + "composition_pools": ["couple_casual_compositions"], + "subcategories": [ + { + "name": "Coordinated casual", + "slug": "coordinated_casual", + "weight": 1.0, + "inherit_expressions": false, + "inherit_compositions": false, + "scene_pools": ["casual_couple_scenes"], + "expression_pools": ["couple_casual_expressions"], + "composition_pools": ["couple_casual_compositions"], + "items": ["coordinated couple casual outfit generator"], + "item_templates": [ + "Partner A wears {a_top} with {a_bottom} and {a_layer}; Partner B wears {b_top} with {b_bottom} and {b_layer}; both share {shared_detail}", + "Partner A in {a_set_piece}; Partner B in {b_set_piece}; coordinated with {shared_color} and {accessory}", + "Partner A wears {a_layer} over {a_top} with {a_bottom}; Partner B wears {b_layer} over {b_top} with {b_bottom}; styled with {footwear_mood}", + "Partner A has {a_top}, {a_bottom}, and {a_bag}; Partner B has {b_top}, {b_bottom}, and {b_bag}; linked by {shared_detail}", + "two coordinated casual outfits: Partner A in {a_set_piece}, Partner B in {b_set_piece}, with {shared_color}, {accessory}, and {footwear_mood}", + "Partner A wears {a_top} tucked into {a_bottom}; Partner B wears {b_top} with {b_bottom}; both layered with {shared_outerwear}", + "Partner A styled in {a_layer}, {a_top}, and {a_bottom}; Partner B styled in {b_layer}, {b_top}, and {b_bottom}; matching through {shared_detail}", + "paired weekend outfits with Partner A in {a_set_piece}, Partner B in {b_set_piece}, plus {shared_outerwear} and {accessory}" + ], + "item_axes": { + "a_bag": [ + "a canvas tote", + "a compact crossbody bag", + "a small backpack", + "a phone pouch", + "a structured tote", + "a nylon sling bag", + "a leather satchel", + "a woven tote" + ], + "a_bottom": [ + "straight-leg jeans", + "wide-leg trousers", + "relaxed cargo pants", + "cropped chinos", + "relaxed linen trousers", + "soft joggers", + "tailored ankle trousers", + "light denim shorts" + ], + "a_layer": [ + "cropped denim jacket", + "soft cardigan", + "oversized hoodie", + "linen overshirt", + "short trench jacket", + "boxy bomber jacket", + "open flannel shirt", + "light knit cardigan" + ], + "a_set_piece": [ + "a denim jacket and straight-jean outfit", + "a cardigan and wide-trouser outfit", + "a hoodie and jogger set", + "a linen shirt and relaxed-trouser outfit", + "a cropped jacket and straight-trouser outfit", + "a smart-casual blazer and jean outfit", + "a summer shirt-and-short outfit", + "a soft knit co-ord" + ], + "a_top": [ + "a fitted white tee", + "a ribbed tank", + "a soft buttoned shirt", + "a cropped sweatshirt", + "a fine knit top", + "a loose cotton shirt", + "a graphic tee", + "a clean sleeveless top" + ], + "accessory": [ + "thin sunglasses", + "matching watches", + "minimal metal details", + "a shared scarf color", + "clean phone-in-hand styling", + "coordinated bags", + "small watch and ring details", + "minimal caps" + ], + "b_bag": [ + "a messenger bag", + "a compact backpack", + "a leather laptop bag", + "a nylon sling bag", + "a canvas tote", + "a simple duffel", + "a small crossbody bag", + "a minimal satchel" + ], + "b_bottom": [ + "dark straight-leg jeans", + "pleated trousers", + "relaxed chinos", + "wide-leg work pants", + "tapered joggers", + "washed black denim", + "tailored wool trousers", + "soft track pants" + ], + "b_layer": [ + "boxy denim jacket", + "unstructured blazer", + "open flannel shirt", + "canvas chore jacket", + "light windbreaker", + "short wool coat", + "relaxed zip hoodie", + "minimal bomber jacket" + ], + "b_set_piece": [ + "a chore jacket and work-pant outfit", + "a blazer and dark denim outfit", + "a flannel and chino outfit", + "a hoodie and cargo-pant outfit", + "a track jacket and jogger outfit", + "a knit polo and pleated-trouser outfit", + "a varsity jacket and jean outfit", + "an overshirt and straight-trouser outfit" + ], + "b_top": [ + "a plain white tee", + "a fine knit polo", + "an open-collar shirt", + "a graphic tee", + "a ribbed mock-neck top", + "a lightweight sweatshirt", + "a soft black tee", + "a buttoned Oxford shirt" + ], + "footwear_mood": [ + "clean sneakers on both partners", + "loafers and minimal sneakers", + "boots and platform sneakers", + "white trainers with relaxed socks", + "polished loafers and leather sneakers", + "matching low-top sneakers", + "ankle boots and suede loafers", + "sporty trainers with casual socks" + ], + "shared_color": [ + "navy and cream coordination", + "olive and charcoal tones", + "soft denim blues", + "black and warm white contrast", + "sage and graphite accents", + "camel and soft grey layers", + "muted red and deep navy", + "fresh white and silver details" + ], + "shared_detail": [ + "matching denim texture", + "coordinated neutral layers", + "similar relaxed silhouettes", + "shared warm color accents", + "matching bag proportions", + "parallel clean-waist styling", + "balanced oversized layers", + "clean everyday fabric texture" + ], + "shared_outerwear": [ + "coordinated denim jackets", + "soft trench-inspired layers", + "matching cardigan textures", + "parallel bomber jackets", + "clean overshirt layers", + "lightweight city jackets", + "similar hooded layers", + "polished blazer-like jackets" + ] + }, + "poses": [ + "standing side by side with relaxed shoulders", + "walking together while both outfits stay fully visible", + "one partner seated while the other stands nearby", + "leaning together near a cafe window", + "posing in a shared mirror outfit-check", + "standing back to back with slight head turns", + "one partner adjusting the other's collar", + "sitting close together with hands visible and no body overlap" + ] + }, + { + "name": "Smart couple", + "slug": "smart_couple", + "weight": 1.0, + "inherit_expressions": false, + "inherit_compositions": false, + "scene_pools": ["casual_couple_scenes", "casual_smart_scenes"], + "expression_pools": ["couple_casual_expressions"], + "composition_pools": ["couple_casual_compositions"], + "items": ["smart couple outfit generator"], + "item_templates": [ + "Partner A wears {a_smart_piece}; Partner B wears {b_smart_piece}; both coordinated through {shared_color} and {shared_texture}", + "Partner A in {a_jacket} over {a_top} with {a_bottom}; Partner B in {b_jacket} over {b_top} with {b_bottom}; finished with {footwear_mood}", + "two smart-casual looks: Partner A has {a_smart_piece}, Partner B has {b_smart_piece}, with {accessory} and {shared_detail}", + "Partner A wears {a_top}, {a_bottom}, and {a_jacket}; Partner B wears {b_top}, {b_bottom}, and {b_jacket}; linked by {shared_texture}", + "coordinated polished outfits with Partner A in {a_smart_piece}, Partner B in {b_smart_piece}, plus {shared_color}", + "Partner A styled with {a_jacket}, {a_top}, and {a_bottom}; Partner B styled with {b_jacket}, {b_top}, and {b_bottom}; both using {accessory}", + "Partner A wears {a_smart_piece} with {a_bag}; Partner B wears {b_smart_piece} with {b_bag}; tied together by {shared_detail}", + "paired city-date outfits: Partner A in {a_smart_piece}, Partner B in {b_smart_piece}, with {footwear_mood}, {shared_color}, and {shared_texture}" + ], + "item_axes": { + "a_bag": [ + "a structured tote", + "a compact shoulder bag", + "a slim laptop bag", + "a slim portfolio pouch", + "a polished commuter bag", + "a clean crossbody bag" + ], + "a_bottom": [ + "tailored ankle trousers", + "pressed cotton trousers", + "wide-leg pleated trousers", + "dark straight-leg jeans", + "straight tailored trousers", + "cropped cigarette trousers" + ], + "a_jacket": [ + "soft oversized blazer", + "short textured jacket", + "short trench jacket", + "clean leather jacket", + "tailored sleeveless vest", + "open cardigan blazer" + ], + "a_smart_piece": [ + "a soft blazer and wide-trouser outfit", + "a ribbed knit top and pleated-trouser outfit", + "a buttoned shirt and tailored-trouser outfit", + "a cardigan and pleated-trouser outfit", + "a sleeveless vest and trouser outfit", + "a clean knit top and wool-trouser outfit" + ], + "a_top": [ + "a silk-blend crew-neck top", + "a soft buttoned shirt", + "a ribbed mock-neck top", + "a buttoned cotton shirt", + "a fitted knit tee", + "a draped satin shirt" + ], + "accessory": [ + "subtle metal details", + "matching watches", + "thin sunglasses", + "coordinated leather bags", + "minimal silver details", + "a shared scarf accent", + "clean phone-camera styling", + "polished belt details" + ], + "b_bag": [ + "a leather laptop bag", + "a slim messenger bag", + "a compact briefcase", + "a structured tote", + "a polished weekender bag", + "a minimal portfolio case" + ], + "b_bottom": [ + "tailored wool trousers", + "dark straight-leg jeans", + "cropped pleated trousers", + "pressed cotton trousers", + "relaxed suit trousers", + "slim chinos" + ], + "b_jacket": [ + "unstructured blazer", + "linen blazer", + "short trench jacket", + "tailored cardigan jacket", + "clean leather jacket", + "minimal bomber jacket" + ], + "b_smart_piece": [ + "an unstructured blazer and trouser outfit", + "a knit polo and pleated-trouser outfit", + "a shirt and dark denim outfit", + "a tailored vest and trouser outfit", + "a cardigan and chino outfit", + "a linen blazer and straight-jean outfit" + ], + "b_top": [ + "a buttoned Oxford shirt", + "a fine knit polo", + "a plain white tee", + "a ribbed mock-neck top", + "an open-collar shirt", + "a soft merino sweater" + ], + "footwear_mood": [ + "loafers and polished ankle boots", + "minimal sneakers and sleek loafers", + "Chelsea boots and ankle boots", + "polished derbies and clean ankle boots", + "white sneakers on both partners", + "suede loafers and polished sneakers" + ], + "shared_color": [ + "navy, ivory, and charcoal coordination", + "camel and black contrast", + "deep green and warm grey", + "soft brown and cream", + "graphite with pale blue accents", + "black, white, and silver" + ], + "shared_detail": [ + "matching tailored proportions", + "balanced jacket lengths", + "coordinated polished bags", + "similar clean collar lines", + "shared muted accent color", + "parallel smart-casual layering" + ], + "shared_texture": [ + "smooth wool texture", + "crisp cotton folds", + "fine ribbed knit", + "matte crepe fabric", + "polished leather accents", + "light linen slubs" + ] + }, + "poses": [ + "standing together in a polished lobby outfit-check", + "walking side by side with relaxed formal posture", + "one partner holding a bag while the other adjusts a sleeve", + "sitting together at a cafe table with both outfits visible", + "posing near a gallery wall with coordinated spacing", + "standing in a mirror frame with both shoes visible", + "turning slightly toward each other while facing the camera", + "standing close with clean posture and no body overlap" + ] + } + ] } ] } diff --git a/categories/expression_composition_pools.json b/categories/expression_composition_pools.json index 72bbf04..77537f9 100644 --- a/categories/expression_composition_pools.json +++ b/categories/expression_composition_pools.json @@ -75,6 +75,30 @@ "casual earbuds-in expression", "bright outdoor training look" ], + "men_casual_expressions": [ + "calm direct menswear stare", + "relaxed closed-mouth smile", + "focused outfit-check expression", + "quiet confident side glance", + "composed street-photo look", + "soft serious gaze", + "casual half-smile", + "thoughtful coffee-shop expression", + "model-off-duty menswear stare", + "easy weekend expression" + ], + "couple_casual_expressions": [ + "one partner smiling while the other keeps a calm direct gaze", + "shared relaxed eye contact", + "quiet coordinated outfit-check expressions", + "soft candid smiles between partners", + "one partner looking at the camera while the other glances aside", + "calm weekend-couple expressions", + "subtle matching confidence", + "warm but restrained shared smiles", + "composed two-person fashion stare", + "relaxed city-date expressions" + ], "softcore_creator_expressions": [ "direct creator-shot eye contact", "heavy-lidded bedroom gaze", @@ -349,6 +373,30 @@ "bright studio mirror composition", "post-workout lifestyle frame with prop visible" ], + "men_casual_compositions": [ + "full-body menswear outfit-check frame", + "three-quarter menswear portrait with jacket and shoes readable", + "mirror crop with full outfit and room context", + "walking street-style frame with clean posture", + "seated cafe portrait with shoes visible", + "waist-up crop focused on collar, watch, and layering", + "side-profile menswear silhouette", + "low-angle phone shot emphasizing trousers and footwear", + "clean catalog-style menswear composition", + "travel outfit frame with bag and full silhouette visible" + ], + "couple_casual_compositions": [ + "full-body two-person outfit-check frame", + "paired mirror view with both outfits visible", + "walking side-by-side lifestyle composition", + "seated cafe couple frame with hands and shoes visible", + "three-quarter two-person fashion crop", + "over-the-shoulder couple street-photo composition", + "balanced frame with both faces and full silhouettes readable", + "soft side-profile two-person silhouette", + "travel-couple frame with bags and footwear visible", + "coordinated outfit portrait with relaxed negative space" + ], "softcore_creator_compositions": [ "handheld selfie crop from face to hips", "mirror selfie with phone visible and body framed clearly", diff --git a/categories/location_pools.json b/categories/location_pools.json index e0845b6..a6ce246 100644 --- a/categories/location_pools.json +++ b/categories/location_pools.json @@ -51,6 +51,26 @@ {"slug": "track_side_morning", "prompt": "outdoor track edge with morning sun, lane markings, and open activewear framing"}, {"slug": "smoothie_bar_postworkout", "prompt": "smoothie bar counter near a gym entrance with bright tiles and casual post-workout light"} ], + "casual_men_scenes": [ + {"slug": "menswear_store_mirror", "prompt": "menswear store mirror with folded shirts, warm shelves, and a clean outfit-check angle"}, + {"slug": "barber_shop_window", "prompt": "barber shop window corner with leather chair, street reflections, and warm afternoon light"}, + {"slug": "record_store_menswear", "prompt": "record store aisle with poster walls, vinyl bins, and relaxed menswear styling"}, + {"slug": "workshop_loft_casual", "prompt": "creative workshop loft with wood table, metal shelving, and soft industrial daylight"}, + {"slug": "coffee_bar_menswear", "prompt": "minimal coffee bar with stone counter, stools, and a composed casual portrait angle"}, + {"slug": "train_station_outfit", "prompt": "clean train station concourse with glass roof, tiled floor, and travel outfit framing"}, + {"slug": "rooftop_menswear_evening", "prompt": "city rooftop at early evening with concrete walls, skyline, and quiet menswear mood"}, + {"slug": "hotel_lobby_menswear", "prompt": "modern hotel lobby with lounge chairs, polished floor, and relaxed daywear lighting"} + ], + "casual_couple_scenes": [ + {"slug": "couple_cafe_window", "prompt": "quiet cafe window table with two chairs, street reflections, and casual couple framing"}, + {"slug": "couple_gallery_walk", "prompt": "small gallery corridor with framed art, warm lights, and space for two full outfits"}, + {"slug": "couple_weekend_market", "prompt": "weekend market walkway with canvas awnings, produce stands, and candid two-person spacing"}, + {"slug": "couple_hotel_lobby", "prompt": "bright hotel lobby lounge with paired seating, marble floor, and polished travel styling"}, + {"slug": "couple_rooftop_terrace", "prompt": "rooftop terrace with plants, city skyline, and relaxed two-person outfit-check framing"}, + {"slug": "couple_bookstore_corner", "prompt": "bookstore corner with display tables, warm lamps, and a quiet shared-photo setup"}, + {"slug": "couple_train_platform", "prompt": "clean train platform with glass panels, luggage nearby, and casual travel-couple framing"}, + {"slug": "couple_apartment_mirror", "prompt": "apartment entry mirror with coats, bags, and enough room for two coordinated outfits"} + ], "softcore_creator_scenes": [ {"slug": "creator_bedroom_ring_light", "prompt": "private creator bedroom with a ring light, phone tripod, rumpled bedding, and warm lamps"}, {"slug": "onlyfans_mirror_bedroom", "prompt": "bedroom mirror selfie setup with a visible phone, messy sheets, and soft amber light"}, diff --git a/krea_formatter.py b/krea_formatter.py index 75d1229..11483c6 100644 --- a/krea_formatter.py +++ b/krea_formatter.py @@ -52,6 +52,14 @@ def _paragraph(parts: list[str]) -> str: return " ".join(part for part in (_sentence(part) for part in parts) if part) +def _with_indefinite_article(text: str) -> str: + text = _clean(text) + if not text or text.lower().startswith(("a ", "an ")): + return text + article = "an" if text[:1].lower() in "aeiou" else "a" + return f"{article} {text}" + + def _maybe_json(text: str) -> dict[str, Any] | None: text = _clean(text) if not text.startswith("{"): @@ -212,6 +220,18 @@ def _style_phrase(row: dict[str, Any], style_mode: str) -> str: return style or suffix +def _couple_clothing_phrase(item: str) -> str: + item = _clean(item) + lower = item.lower() + partner_text = re.sub(r"\bPartner ([AB]) wears\b", r"Partner \1 wearing", item) + partner_text = re.sub(r"\bPartner ([AB]) has\b", r"Partner \1 with", partner_text) + if lower.startswith("partner a "): + return f"The outfits show {partner_text}" + if lower.startswith(("two ", "paired ", "coordinated ")): + return f"The outfits are {partner_text}" + return f"The couple wears {item}" + + def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) -> tuple[str, str]: subject_type = _clean(row.get("subject_type")) primary = _clean(row.get("primary_subject")) @@ -245,7 +265,7 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) subject = _age_subject(row, "adult woman") appearance = _appearance_phrase(row) parts = [ - f"A {subject}" if not subject.lower().startswith(("a ", "an ")) else subject, + _with_indefinite_article(subject), f"with {appearance}" if appearance else "", f"wearing {item}" if item else "", f"{pose}" if pose else "", @@ -257,6 +277,26 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) ] return _paragraph([", ".join(part for part in parts[:6] if part), *parts[6:]]), "metadata(single)" + if subject_type == "couple" or primary in ("two women", "two men", "a woman and a man"): + subject = _clean(row.get("subject_phrase") or primary or "adult couple") + if subject == "woman and man": + subject = "a woman and a man" + ages = _row_value(row, "age", ("Ages",)) or _clean(row.get("age_band")) + body = _row_value(row, "body", ("Body types",)) or _clean(row.get("body_type")) + parts = [ + f"An adult couple: {subject}, all visibly adult", + f"Age detail: {ages}" if ages else "", + f"Body types: {body}" if body else "", + _couple_clothing_phrase(item) if item else "", + f"The pose is {pose}" if pose else "", + f"The setting is {scene}" if scene else "", + f"Facial expressions are {expression}" if expression else "", + f"The image is framed as {composition}" if composition else "", + camera, + style if detail_level != "concise" else "", + ] + return _paragraph(parts), "metadata(couple)" + subject = _age_subject(row, primary or "adult scene") parts = [ f"{subject}", diff --git a/prompt_builder.py b/prompt_builder.py index 7670a51..63ec850 100644 --- a/prompt_builder.py +++ b/prompt_builder.py @@ -1270,6 +1270,28 @@ def _configured_cast_context(women_count: int, men_count: int) -> dict[str, str] } +def _couple_type_from_counts( + rng: random.Random, + women_count: int, + men_count: int, +) -> tuple[str, str, str, int, int]: + women_count = max(0, int(women_count)) + men_count = max(0, int(men_count)) + if women_count >= 2 and men_count == 0: + return "two women", "two women", "close affectionate couple pose", 2, 0 + if men_count >= 2 and women_count == 0: + return "two men", "two men", "relaxed romantic couple pose", 0, 2 + if women_count >= 1 and men_count >= 1: + return "woman and man", "a woman and a man", "playful date-night pose", 1, 1 + + primary_subject, subject_phrase, pose = g.choose(rng, g.COUPLE_TYPES) + if primary_subject == "two women": + return primary_subject, subject_phrase, pose, 2, 0 + if primary_subject == "two men": + return primary_subject, subject_phrase, pose, 0, 2 + return primary_subject, subject_phrase, pose, 1, 1 + + def _lettered(prefix: str, count: int) -> list[str]: letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" return [f"{prefix.capitalize()} {letters[index]}" for index in range(max(0, count))] @@ -1438,7 +1460,11 @@ def _subject_context( return _configured_cast_context(women_count, men_count) if subject_type == "couple": - primary_subject, subject_phrase, pose = g.choose(rng, g.COUPLE_TYPES) + primary_subject, subject_phrase, pose, effective_women_count, effective_men_count = _couple_type_from_counts( + rng, + women_count, + men_count, + ) return { "subject_type": "couple", "subject": primary_subject, @@ -1450,6 +1476,9 @@ def _subject_context( "eyes": "", "body_phrase": "", "fallback_pose": pose, + "women_count": str(effective_women_count), + "men_count": str(effective_men_count), + "person_count": "2", } if subject_type == "group":