Add expression enable controls
This commit is contained in:
+292
-15
@@ -890,6 +890,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"balanced": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.5,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -900,6 +901,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"casual_clean": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.35,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -910,6 +912,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"evocative_softcore": {
|
||||
"clothing": "minimal",
|
||||
"poses": "evocative",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.65,
|
||||
"backside_bias": 0.2,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -920,6 +923,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"hardcore_intense": {
|
||||
"clothing": "minimal",
|
||||
"poses": "evocative",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.9,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -930,6 +934,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"krea2_friendly": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.55,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -940,6 +945,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"flux_original": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.5,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -1039,6 +1045,7 @@ def build_generation_profile_json(
|
||||
minimal_clothing_ratio: float = -1.0,
|
||||
standard_pose_ratio: float = -1.0,
|
||||
trigger_policy: str = "profile_default",
|
||||
expression_enabled: bool = True,
|
||||
) -> str:
|
||||
profile = profile if profile in GENERATION_PROFILE_PRESETS else "balanced"
|
||||
config = dict(GENERATION_PROFILE_PRESETS[profile])
|
||||
@@ -1046,6 +1053,7 @@ def build_generation_profile_json(
|
||||
config["clothing"] = clothing_override
|
||||
if poses_override in ("standard", "evocative"):
|
||||
config["poses"] = poses_override
|
||||
config["expression_enabled"] = not _is_false(expression_enabled)
|
||||
if float(expression_intensity) >= 0:
|
||||
config["expression_intensity"] = _clamped_float(expression_intensity, config["expression_intensity"])
|
||||
if float(backside_bias) >= 0:
|
||||
@@ -1079,6 +1087,7 @@ def _parse_generation_profile(profile_config: str | dict[str, Any] | None) -> di
|
||||
parsed.update(raw)
|
||||
parsed["clothing"] = parsed["clothing"] if parsed.get("clothing") in ("full", "minimal") else "full"
|
||||
parsed["poses"] = parsed["poses"] if parsed.get("poses") in ("standard", "evocative") else "standard"
|
||||
parsed["expression_enabled"] = not _is_false(parsed.get("expression_enabled", True))
|
||||
parsed["expression_intensity"] = _clamped_float(parsed.get("expression_intensity"), 0.5)
|
||||
parsed["backside_bias"] = _clamped_float(parsed.get("backside_bias"), 0.0)
|
||||
parsed["minimal_clothing_ratio"] = _clamped_float(parsed.get("minimal_clothing_ratio"), -1.0, -1.0, 1.0)
|
||||
@@ -1325,6 +1334,54 @@ def _format(template: str, context: dict[str, Any]) -> str:
|
||||
return template.format_map(safe_context)
|
||||
|
||||
|
||||
def _clean_prompt_punctuation(text: str) -> str:
|
||||
text = re.sub(r"\s+", " ", str(text or "")).strip()
|
||||
text = re.sub(r"\s+([,.;:])", r"\1", text)
|
||||
text = re.sub(r"(?:,\s*){2,}", ", ", text)
|
||||
text = re.sub(r"\.\s*\.", ".", text)
|
||||
text = re.sub(r":\s*\.", ".", text)
|
||||
return text.strip()
|
||||
|
||||
|
||||
def _strip_expression_text(text: str, expression: Any = "") -> str:
|
||||
text = str(text or "")
|
||||
if not text:
|
||||
return ""
|
||||
text = re.sub(r"\s*Facial expressions?:\s*[^.]*\.\s*", " ", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r",\s*one with [^,]+ and the other with [^,]+(?=,)", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r",\s*a lively mix of expressions from [^,]+(?=,)", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\s+with\s+(?:an?|the)\s+[^,]*expression(?=,)", "", text, flags=re.IGNORECASE)
|
||||
expression_text = str(expression or "").strip()
|
||||
if expression_text:
|
||||
for part in [piece.strip() for piece in expression_text.split(";") if piece.strip()]:
|
||||
escaped = re.escape(part)
|
||||
text = re.sub(rf",\s*{escaped}(?=,)", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(rf"\s+with\s+(?:an?|the)?\s*{escaped}", "", text, flags=re.IGNORECASE)
|
||||
return _clean_prompt_punctuation(text)
|
||||
|
||||
|
||||
def _disable_row_expression(row: dict[str, Any], source: str = "disabled") -> dict[str, Any]:
|
||||
previous_expression = row.get("expression", "")
|
||||
row["prompt"] = _strip_expression_text(row.get("prompt", ""), previous_expression)
|
||||
row["caption"] = _strip_expression_text(row.get("caption", ""), previous_expression)
|
||||
row["expression"] = ""
|
||||
row["shared_expression"] = ""
|
||||
row["character_expressions"] = []
|
||||
row["character_expression_text"] = ""
|
||||
row["expression_enabled"] = False
|
||||
row["expression_disabled"] = True
|
||||
row["expression_intensity"] = None
|
||||
row["expression_intensity_source"] = source
|
||||
return row
|
||||
|
||||
|
||||
def _labeled_expression_sentence(label: str, expression: Any) -> str:
|
||||
expression = str(expression or "").strip()
|
||||
if not expression:
|
||||
return ""
|
||||
return f"{label}: {expression}. "
|
||||
|
||||
|
||||
def _prepend_trigger(prompt: str, trigger: str, enabled: bool) -> str:
|
||||
trigger = trigger.strip()
|
||||
if not enabled or not trigger:
|
||||
@@ -1801,6 +1858,111 @@ def _normalize_descriptor_detail(value: Any) -> str:
|
||||
return text if text in CHARACTER_DESCRIPTOR_DETAIL_CHOICES else "auto"
|
||||
|
||||
|
||||
def _normalize_slot_expression_intensity(value: Any) -> float:
|
||||
try:
|
||||
intensity = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return -1.0
|
||||
if intensity < 0:
|
||||
return -1.0
|
||||
return _clamped_float(intensity, 0.5)
|
||||
|
||||
|
||||
def _slot_expression_enabled(slot: dict[str, Any] | None) -> bool:
|
||||
if not slot:
|
||||
return True
|
||||
return not _is_false(slot.get("expression_enabled", True))
|
||||
|
||||
|
||||
def _slot_expression_intensity(slot: dict[str, Any] | None) -> float | None:
|
||||
if not slot or not _slot_expression_enabled(slot):
|
||||
return None
|
||||
intensity = _normalize_slot_expression_intensity(slot.get("expression_intensity"))
|
||||
return intensity if intensity >= 0 else None
|
||||
|
||||
|
||||
def _mean(values: list[float]) -> float:
|
||||
return sum(values) / len(values)
|
||||
|
||||
|
||||
def _cast_expression_intensity_override(
|
||||
fallback: float,
|
||||
label_map: dict[str, dict[str, Any]],
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
) -> tuple[float | None, str]:
|
||||
groups: list[tuple[str, list[str]]] = [
|
||||
("women", [f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))]),
|
||||
("men", [f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))]),
|
||||
]
|
||||
all_values: list[float] = []
|
||||
matching_slots: list[dict[str, Any]] = []
|
||||
for group_name, labels in groups:
|
||||
values: list[float] = []
|
||||
value_labels: list[str] = []
|
||||
for label in labels:
|
||||
slot = label_map.get(label)
|
||||
if slot:
|
||||
matching_slots.append(slot)
|
||||
value = _slot_expression_intensity(slot)
|
||||
if value is not None:
|
||||
values.append(value)
|
||||
value_labels.append(label)
|
||||
all_values.append(value)
|
||||
if values:
|
||||
if len(values) == 1:
|
||||
return values[0], f"character_slot:{value_labels[0]}"
|
||||
return _mean(values), f"character_slots:{group_name}"
|
||||
if all_values:
|
||||
return _mean(all_values), "character_slots:cast"
|
||||
if matching_slots and all(not _slot_expression_enabled(slot) for slot in matching_slots):
|
||||
return None, "character_slots:disabled"
|
||||
return fallback, "input"
|
||||
|
||||
|
||||
def _character_expression_entries(
|
||||
rng: random.Random,
|
||||
expression_pool: list[Any],
|
||||
fallback_intensity: float,
|
||||
label_map: dict[str, dict[str, Any]],
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
) -> list[str]:
|
||||
labels = [
|
||||
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
||||
*[f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))],
|
||||
]
|
||||
expressions: list[str] = []
|
||||
used: set[str] = set()
|
||||
for label in labels:
|
||||
slot = label_map.get(label)
|
||||
if not slot:
|
||||
continue
|
||||
if not _slot_expression_enabled(slot):
|
||||
continue
|
||||
intensity = _slot_expression_intensity(slot)
|
||||
if intensity is None:
|
||||
intensity = fallback_intensity
|
||||
entries = _compatible_entries(
|
||||
_expression_entries_for_intensity(expression_pool, intensity),
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
if not entries:
|
||||
continue
|
||||
choice = ""
|
||||
for _attempt in range(5):
|
||||
candidate = _choose_text(rng, entries)
|
||||
if candidate not in used:
|
||||
choice = candidate
|
||||
break
|
||||
if not choice:
|
||||
choice = _choose_text(rng, entries)
|
||||
used.add(choice)
|
||||
expressions.append(f"{label} has {choice}")
|
||||
return expressions
|
||||
|
||||
|
||||
def _descriptor_detail_for_subject(subject: Any, descriptor_detail: Any) -> str:
|
||||
detail = _normalize_descriptor_detail(descriptor_detail)
|
||||
if detail != "auto":
|
||||
@@ -1883,6 +2045,8 @@ def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
||||
"hair": _slot_value(slot.get("hair")),
|
||||
"eyes": _slot_value(slot.get("eyes")),
|
||||
"descriptor_detail": _normalize_descriptor_detail(slot.get("descriptor_detail")),
|
||||
"expression_enabled": not _is_false(slot.get("expression_enabled", True)),
|
||||
"expression_intensity": _normalize_slot_expression_intensity(slot.get("expression_intensity")),
|
||||
}
|
||||
normalized["summary"] = _character_slot_summary(normalized)
|
||||
return normalized
|
||||
@@ -1927,6 +2091,12 @@ def _character_slot_summary(slot: dict[str, Any]) -> str:
|
||||
f"body={slot.get('body', 'random')}",
|
||||
f"detail={slot.get('descriptor_detail', 'auto')}",
|
||||
]
|
||||
if not _slot_expression_enabled(slot):
|
||||
parts.append("expression=disabled")
|
||||
else:
|
||||
expression_intensity = _slot_expression_intensity(slot)
|
||||
if expression_intensity is not None:
|
||||
parts.append(f"expression={expression_intensity:.2f}")
|
||||
for key in ("body_phrase", "skin", "hair", "eyes"):
|
||||
value = slot.get(key)
|
||||
if value:
|
||||
@@ -1948,6 +2118,8 @@ def build_character_slot_json(
|
||||
hair: str = "",
|
||||
eyes: str = "",
|
||||
descriptor_detail: str = "auto",
|
||||
expression_enabled: bool = True,
|
||||
expression_intensity: float = -1.0,
|
||||
enabled: bool = True,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
||||
) -> dict[str, str]:
|
||||
@@ -1967,6 +2139,8 @@ def build_character_slot_json(
|
||||
"hair": hair,
|
||||
"eyes": eyes,
|
||||
"descriptor_detail": descriptor_detail,
|
||||
"expression_enabled": expression_enabled,
|
||||
"expression_intensity": expression_intensity,
|
||||
}
|
||||
)
|
||||
slots = existing_slots + ([slot] if enabled else [])
|
||||
@@ -2049,6 +2223,10 @@ def _context_from_character_slot(
|
||||
if value:
|
||||
context[key] = value
|
||||
context["descriptor_detail"] = _normalize_descriptor_detail(slot.get("descriptor_detail"))
|
||||
context["expression_enabled"] = _slot_expression_enabled(slot)
|
||||
expression_intensity = _slot_expression_intensity(slot)
|
||||
if expression_intensity is not None:
|
||||
context["expression_intensity"] = expression_intensity
|
||||
context["subject_type"] = subject_type
|
||||
context["subject"] = subject_type
|
||||
context["subject_phrase"] = subject_type
|
||||
@@ -2084,9 +2262,11 @@ def _apply_character_context_to_row(row: dict[str, Any], context: dict[str, Any]
|
||||
"eyes",
|
||||
"figure",
|
||||
"descriptor_detail",
|
||||
"expression_enabled",
|
||||
"expression_intensity",
|
||||
):
|
||||
value = context.get(key)
|
||||
if value:
|
||||
if value is not None and value != "":
|
||||
row[key] = value
|
||||
if context.get("age"):
|
||||
row["age_band"] = context["age"]
|
||||
@@ -2997,6 +3177,7 @@ def _build_custom_row(
|
||||
men_count: int,
|
||||
seed: int,
|
||||
seed_config: dict[str, int],
|
||||
expression_enabled: bool,
|
||||
expression_intensity: float,
|
||||
character_profile: str | dict[str, Any] | None = None,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||
@@ -3063,6 +3244,29 @@ def _build_custom_row(
|
||||
role_graph = _role_graph(role_rng, subcategory, context, item_axis_values)
|
||||
cast_descriptors: list[str] = []
|
||||
cast_descriptor_text = ""
|
||||
expression_intensity_source = "input"
|
||||
expression_disabled = not bool(expression_enabled)
|
||||
if expression_disabled:
|
||||
expression_intensity_source = "disabled"
|
||||
elif subject_type in ("woman", "man") and applied_slot:
|
||||
slot_label = "Woman A" if subject_type == "woman" else "Man A"
|
||||
if not _slot_expression_enabled(applied_slot):
|
||||
expression_disabled = True
|
||||
expression_intensity_source = f"character_slot:{slot_label}:disabled"
|
||||
else:
|
||||
slot_expression_intensity = _slot_expression_intensity(applied_slot)
|
||||
if slot_expression_intensity is not None:
|
||||
expression_intensity = slot_expression_intensity
|
||||
expression_intensity_source = f"character_slot:{slot_label}"
|
||||
elif subject_type == "configured_cast" and character_slots:
|
||||
expression_intensity, expression_intensity_source = _cast_expression_intensity_override(
|
||||
expression_intensity,
|
||||
character_slot_map,
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
if expression_intensity is None:
|
||||
expression_disabled = True
|
||||
if subject_type == "configured_cast" and character_slots:
|
||||
cast_descriptors, _descriptor_slots = _cast_descriptor_entries(
|
||||
seed_config,
|
||||
@@ -3082,16 +3286,35 @@ def _build_custom_row(
|
||||
pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text(
|
||||
pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count)
|
||||
))
|
||||
expression_entries = _compatible_entries(
|
||||
_expression_entries_for_intensity(_expression_pool(category, subcategory, item), expression_intensity),
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
expression = _choose_text(expression_rng, expression_entries)
|
||||
if subject_type in ("couple", "group") and ";" not in expression:
|
||||
secondary_expression = _choose_distinct_text(expression_rng, expression_entries, expression)
|
||||
if secondary_expression:
|
||||
expression = f"{expression}; {secondary_expression}"
|
||||
expression_pool = _expression_pool(category, subcategory, item)
|
||||
if expression_disabled:
|
||||
expression = ""
|
||||
else:
|
||||
expression_entries = _compatible_entries(
|
||||
_expression_entries_for_intensity(expression_pool, expression_intensity),
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
expression = _choose_text(expression_rng, expression_entries)
|
||||
if subject_type in ("couple", "group") and ";" not in expression:
|
||||
secondary_expression = _choose_distinct_text(expression_rng, expression_entries, expression)
|
||||
if secondary_expression:
|
||||
expression = f"{expression}; {secondary_expression}"
|
||||
shared_expression = expression
|
||||
character_expressions: list[str] = []
|
||||
character_expression_text = ""
|
||||
if not expression_disabled and subject_type == "configured_cast" and character_slots:
|
||||
character_expressions = _character_expression_entries(
|
||||
expression_rng,
|
||||
expression_pool,
|
||||
expression_intensity,
|
||||
character_slot_map,
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
character_expression_text = "; ".join(character_expressions)
|
||||
if character_expression_text:
|
||||
expression = character_expression_text
|
||||
composition = _choose_text(
|
||||
composition_rng,
|
||||
_compatible_entries(_composition_pool(category, subcategory, item, subject_type), women_count, men_count),
|
||||
@@ -3124,7 +3347,13 @@ def _build_custom_row(
|
||||
"scene_slug": scene_slug,
|
||||
"pose": pose,
|
||||
"expression": expression,
|
||||
"shared_expression": shared_expression,
|
||||
"character_expressions": character_expressions,
|
||||
"character_expression_text": character_expression_text,
|
||||
"expression_enabled": not expression_disabled,
|
||||
"expression_disabled": expression_disabled,
|
||||
"expression_intensity": expression_intensity,
|
||||
"expression_intensity_source": expression_intensity_source,
|
||||
"composition": composition,
|
||||
"composition_prompt": _composition_prompt(composition),
|
||||
"role_graph": role_graph,
|
||||
@@ -3191,6 +3420,11 @@ def _build_custom_row(
|
||||
"seed_config": seed_config,
|
||||
"content_seed_axis": content_axis,
|
||||
"role_graph": role_graph,
|
||||
"shared_expression": shared_expression,
|
||||
"character_expressions": character_expressions,
|
||||
"character_expression_text": character_expression_text,
|
||||
"expression_enabled": not expression_disabled,
|
||||
"expression_disabled": expression_disabled,
|
||||
"cast_summary": context.get("cast_summary", ""),
|
||||
"cast_descriptors": cast_descriptors,
|
||||
"cast_descriptor_text": cast_descriptor_text,
|
||||
@@ -3204,11 +3438,15 @@ def _build_custom_row(
|
||||
"character_slot": applied_slot,
|
||||
"character_slot_status": slot_status,
|
||||
"character_cast_slots": character_slots,
|
||||
"expression_intensity": expression_intensity,
|
||||
"expression_intensity_source": expression_intensity_source,
|
||||
"source": "json_category",
|
||||
}
|
||||
)
|
||||
if context.get("figure"):
|
||||
row["figure"] = context["figure"]
|
||||
if expression_disabled:
|
||||
row = _disable_row_expression(row, expression_intensity_source)
|
||||
return row
|
||||
|
||||
|
||||
@@ -3238,6 +3476,7 @@ def build_prompt(
|
||||
expression_intensity: float = 0.5,
|
||||
character_profile: str | dict[str, Any] | None = None,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||
expression_enabled: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
apply_pool_extensions()
|
||||
row_number = max(1, int(row_number))
|
||||
@@ -3247,6 +3486,7 @@ def build_prompt(
|
||||
ethnicity = ethnicity if ethnicity == "any" or ethnicity in ETHNICITY_FILTER_CHOICES or "+" in str(ethnicity) else "any"
|
||||
poses = poses if poses in ("standard", "evocative") else "standard"
|
||||
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
||||
expression_enabled = not _is_false(expression_enabled)
|
||||
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
||||
pose_ratio = _ratio_or_none(standard_pose_ratio)
|
||||
expression_intensity = _clamped_float(expression_intensity, 0.5)
|
||||
@@ -3300,11 +3540,14 @@ def build_prompt(
|
||||
int(men_count),
|
||||
seed,
|
||||
parsed_seed_config,
|
||||
expression_enabled,
|
||||
expression_intensity,
|
||||
character_profile,
|
||||
character_cast,
|
||||
)
|
||||
|
||||
if not expression_enabled:
|
||||
row = _disable_row_expression(row, "disabled")
|
||||
if extra_positive.strip():
|
||||
row["prompt"] = f"{row['prompt'].rstrip()} {extra_positive.strip()}"
|
||||
row = _apply_camera_config(row, camera_config)
|
||||
@@ -3312,7 +3555,8 @@ def build_prompt(
|
||||
row["prompt"] = _prepend_trigger(row["prompt"], active_trigger, bool(prepend_trigger_to_prompt))
|
||||
row["negative_prompt"] = _combined_negative(row.get("negative_prompt", g.NEGATIVE_PROMPT), extra_negative)
|
||||
row["trigger"] = active_trigger
|
||||
row["expression_intensity"] = expression_intensity
|
||||
row.setdefault("expression_intensity", expression_intensity)
|
||||
row.setdefault("expression_intensity_source", "input")
|
||||
return row
|
||||
|
||||
|
||||
@@ -3344,6 +3588,7 @@ def build_prompt_from_configs(
|
||||
clothing=profile["clothing"],
|
||||
ethnicity=filters["ethnicity"],
|
||||
poses=profile["poses"],
|
||||
expression_enabled=profile["expression_enabled"],
|
||||
expression_intensity=profile["expression_intensity"],
|
||||
backside_bias=profile["backside_bias"],
|
||||
figure=filters["figure"],
|
||||
@@ -3538,6 +3783,8 @@ def build_insta_of_options_json(
|
||||
camera_detail: str = "compact",
|
||||
softcore_expression_intensity: float = 0.45,
|
||||
hardcore_expression_intensity: float = 0.85,
|
||||
softcore_expression_enabled: bool = True,
|
||||
hardcore_expression_enabled: bool = True,
|
||||
) -> str:
|
||||
return json.dumps(
|
||||
{
|
||||
@@ -3553,6 +3800,8 @@ def build_insta_of_options_json(
|
||||
"softcore_camera_mode": softcore_camera_mode,
|
||||
"hardcore_camera_mode": hardcore_camera_mode,
|
||||
"camera_detail": camera_detail,
|
||||
"softcore_expression_enabled": not _is_false(softcore_expression_enabled),
|
||||
"hardcore_expression_enabled": not _is_false(hardcore_expression_enabled),
|
||||
"softcore_expression_intensity": _clamped_float(softcore_expression_intensity, 0.45),
|
||||
"hardcore_expression_intensity": _clamped_float(hardcore_expression_intensity, 0.85),
|
||||
},
|
||||
@@ -3575,6 +3824,8 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
|
||||
"softcore_camera_mode": "handheld_selfie",
|
||||
"hardcore_camera_mode": "from_camera_config",
|
||||
"camera_detail": "compact",
|
||||
"softcore_expression_enabled": True,
|
||||
"hardcore_expression_enabled": True,
|
||||
"softcore_expression_intensity": 0.45,
|
||||
"hardcore_expression_intensity": 0.85,
|
||||
}
|
||||
@@ -3608,6 +3859,8 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
|
||||
):
|
||||
parsed["hardcore_camera_mode"] = defaults["hardcore_camera_mode"]
|
||||
parsed["camera_detail"] = parsed["camera_detail"] if parsed["camera_detail"] in CAMERA_DETAIL_CHOICES else defaults["camera_detail"]
|
||||
parsed["softcore_expression_enabled"] = not _is_false(parsed.get("softcore_expression_enabled", True))
|
||||
parsed["hardcore_expression_enabled"] = not _is_false(parsed.get("hardcore_expression_enabled", True))
|
||||
parsed["softcore_expression_intensity"] = _clamped_float(
|
||||
parsed.get("softcore_expression_intensity"),
|
||||
defaults["softcore_expression_intensity"],
|
||||
@@ -3806,6 +4059,22 @@ def build_insta_of_pair(
|
||||
soft_category, soft_subcategory = _insta_of_softcore_category(softcore_level_key)
|
||||
soft_content_rng = _axis_rng(parsed_seed_config, "content", seed, row_number + 311)
|
||||
soft_person_rng = _axis_rng(parsed_seed_config, "person", seed, row_number)
|
||||
soft_expression_women_count = hard_women_count if options["softcore_cast"] == "same_as_hardcore" else 1
|
||||
soft_expression_men_count = hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0
|
||||
soft_expression_enabled = bool(options["softcore_expression_enabled"])
|
||||
soft_expression_intensity = options["softcore_expression_intensity"]
|
||||
soft_expression_intensity_source = "input"
|
||||
if soft_expression_enabled:
|
||||
soft_expression_intensity, soft_expression_intensity_source = _cast_expression_intensity_override(
|
||||
options["softcore_expression_intensity"],
|
||||
character_slot_map,
|
||||
soft_expression_women_count,
|
||||
soft_expression_men_count,
|
||||
)
|
||||
if soft_expression_intensity is None:
|
||||
soft_expression_enabled = False
|
||||
else:
|
||||
soft_expression_intensity_source = "disabled"
|
||||
primary_slot_context = None
|
||||
primary_slot = character_slot_map.get("Woman A")
|
||||
if primary_slot:
|
||||
@@ -3841,14 +4110,18 @@ def build_insta_of_pair(
|
||||
seed_config=parsed_seed_config,
|
||||
women_count=1,
|
||||
men_count=0,
|
||||
expression_intensity=options["softcore_expression_intensity"],
|
||||
expression_enabled=soft_expression_enabled,
|
||||
expression_intensity=soft_expression_intensity,
|
||||
character_profile="" if primary_slot else character_profile or "",
|
||||
character_cast="",
|
||||
)
|
||||
soft_row["expression_intensity_source"] = soft_expression_intensity_source
|
||||
if primary_slot_context:
|
||||
soft_row = _apply_character_context_to_row(soft_row, primary_slot_context)
|
||||
soft_row["character_slot"] = primary_slot
|
||||
soft_row["character_slot_status"] = "applied:Woman A"
|
||||
if not soft_expression_enabled:
|
||||
soft_row = _disable_row_expression(soft_row, soft_expression_intensity_source)
|
||||
soft_row["item"] = _insta_of_softcore_outfit(soft_content_rng, softcore_level_key)
|
||||
soft_row["pose"] = _insta_of_softcore_pose(soft_content_rng, softcore_level_key)
|
||||
soft_row["item_label"] = "Insta/OF softcore outfit"
|
||||
@@ -3876,6 +4149,7 @@ def build_insta_of_pair(
|
||||
seed_config=parsed_seed_config,
|
||||
women_count=hard_women_count,
|
||||
men_count=hard_men_count,
|
||||
expression_enabled=options["hardcore_expression_enabled"],
|
||||
expression_intensity=options["hardcore_expression_intensity"],
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
@@ -3959,7 +4233,8 @@ def build_insta_of_pair(
|
||||
f"{soft_cast_presence}"
|
||||
f"{soft_cast_styling_sentence}"
|
||||
f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
|
||||
f"Facial expression: {soft_row['expression']}. Composition: {soft_row['composition']}. "
|
||||
f"{_labeled_expression_sentence('Facial expression', soft_row.get('expression'))}"
|
||||
f"Composition: {soft_row['composition']}. "
|
||||
f"{soft_camera_sentence}"
|
||||
"Keep the softcore version seductive, creator-shot, and non-explicit. "
|
||||
f"{soft_row['positive_suffix']}."
|
||||
@@ -3971,7 +4246,9 @@ def build_insta_of_pair(
|
||||
"Keep Woman A visually central. "
|
||||
f"{hard_clothing_state} "
|
||||
f"Role graph: {hard_row['role_graph']} Sexual scene: {hard_row['item']}. "
|
||||
f"Setting: {hard_scene}. Facial expressions: {hard_row['expression']}. Composition: {hard_composition}. "
|
||||
f"Setting: {hard_scene}. "
|
||||
f"{_labeled_expression_sentence('Facial expressions', hard_row.get('expression'))}"
|
||||
f"Composition: {hard_composition}. "
|
||||
f"{hard_camera_sentence}"
|
||||
f"{hard_row['positive_suffix']}."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user