Extract Insta pair camera routing
This commit is contained in:
@@ -147,7 +147,6 @@ Keep here:
|
|||||||
- soft/hard row creation;
|
- soft/hard row creation;
|
||||||
- continuity policy;
|
- continuity policy;
|
||||||
- softcore cast policy;
|
- softcore cast policy;
|
||||||
- pair-level camera routing;
|
|
||||||
- pair metadata shape.
|
- pair metadata shape.
|
||||||
|
|
||||||
Improve later:
|
Improve later:
|
||||||
@@ -155,8 +154,15 @@ Improve later:
|
|||||||
- make a single pair metadata sanitizer that normalizes `softcore_row`,
|
- make a single pair metadata sanitizer that normalizes `softcore_row`,
|
||||||
`hardcore_row`, pair prompts, negatives, captions, and camera fields;
|
`hardcore_row`, pair prompts, negatives, captions, and camera fields;
|
||||||
- split pair assembly into small functions by phase:
|
- split pair assembly into small functions by phase:
|
||||||
`build_soft_row`, `build_hard_row`, `resolve_pair_camera`,
|
`build_soft_row`, `build_hard_row`, `resolve_pair_clothing`,
|
||||||
`resolve_pair_clothing`, `assemble_pair_metadata`.
|
`assemble_pair_metadata`.
|
||||||
|
|
||||||
|
Already isolated:
|
||||||
|
|
||||||
|
- pair-level camera routing lives in `pair_camera.py`, including soft/hard
|
||||||
|
camera config selection, same-as-softcore mode, camera-detail override,
|
||||||
|
same-room hard scene continuity, camera-aware composition mutation, POV camera
|
||||||
|
suppression, and row/root camera metadata synchronization.
|
||||||
|
|
||||||
### Krea2 Formatter Path
|
### Krea2 Formatter Path
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ Core helper ownership:
|
|||||||
| Python module | What it owns |
|
| Python module | What it owns |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `category_library.py` | JSON category loading, subcategory normalization, named scene/expression/composition pool loading, cast compatibility filtering, exact subcategory lookup, and inheritance-based pool merging. |
|
| `category_library.py` | JSON category loading, subcategory normalization, named scene/expression/composition pool loading, cast compatibility filtering, exact subcategory lookup, and inheritance-based pool merging. |
|
||||||
|
| `pair_camera.py` | Insta/OF soft/hard camera route resolution, same-as-softcore camera mode, camera-detail override, camera-aware composition mutation, POV camera suppression, and synchronized row/root camera metadata. |
|
||||||
| `hardcore_role_graphs.py` | Source role graph construction for hardcore configured-cast rows, including POV-aware interaction geometry. |
|
| `hardcore_role_graphs.py` | Source role graph construction for hardcore configured-cast rows, including POV-aware interaction geometry. |
|
||||||
| `hardcore_role_fallback.py` | Solo, same-sex, mixed group fallback, and support-partner role graph wording for configured casts. |
|
| `hardcore_role_fallback.py` | Solo, same-sex, mixed group fallback, and support-partner role graph wording for configured casts. |
|
||||||
| `hardcore_role_interaction.py` | Foreplay, manual stimulation, body worship, clothing transition, dominant guidance, camera performance, aftercare, and group coordination role graph wording. |
|
| `hardcore_role_interaction.py` | Foreplay, manual stimulation, body worship, clothing transition, dominant guidance, camera performance, aftercare, and group coordination role graph wording. |
|
||||||
|
|||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
|
||||||
|
CameraConfigWithMode = Callable[[str | dict[str, Any] | None, str], dict[str, Any]]
|
||||||
|
CameraDirective = Callable[[str | dict[str, Any] | None], tuple[str, dict[str, Any]]]
|
||||||
|
ApplyComposition = Callable[[dict[str, Any], str], dict[str, Any]]
|
||||||
|
CompositionPrompt = Callable[[Any, Any, str], str]
|
||||||
|
CameraSceneDirective = Callable[
|
||||||
|
[Any, Any, str | dict[str, Any] | None, list[str] | None, str],
|
||||||
|
tuple[str, dict[str, Any]],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def camera_config_with_detail(
|
||||||
|
camera_config: dict[str, Any],
|
||||||
|
camera_detail: str,
|
||||||
|
camera_detail_choices: list[str] | tuple[str, ...],
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
if camera_detail in camera_detail_choices:
|
||||||
|
camera_config["camera_detail"] = camera_detail
|
||||||
|
return camera_config
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_insta_pair_camera(
|
||||||
|
*,
|
||||||
|
soft_row: dict[str, Any],
|
||||||
|
hard_row: dict[str, Any],
|
||||||
|
options: dict[str, Any],
|
||||||
|
camera_config: str | dict[str, Any] | None,
|
||||||
|
softcore_camera_config: str | dict[str, Any] | None,
|
||||||
|
hardcore_camera_config: str | dict[str, Any] | None,
|
||||||
|
hard_women_count: int,
|
||||||
|
hard_men_count: int,
|
||||||
|
pov_character_labels: list[str],
|
||||||
|
camera_detail_choices: list[str] | tuple[str, ...],
|
||||||
|
camera_config_with_mode: CameraConfigWithMode,
|
||||||
|
camera_directive: CameraDirective,
|
||||||
|
apply_contextual_composition: ApplyComposition,
|
||||||
|
contextual_composition_prompt: CompositionPrompt,
|
||||||
|
composition_prompt: Callable[[Any], str],
|
||||||
|
camera_scene_directive_for_context: CameraSceneDirective,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
hard_camera_mode = str(options["hardcore_camera_mode"])
|
||||||
|
soft_camera_source = softcore_camera_config or camera_config
|
||||||
|
hard_camera_source = hardcore_camera_config or camera_config
|
||||||
|
if hard_camera_mode == "same_as_softcore":
|
||||||
|
hard_camera_mode = str(options["softcore_camera_mode"])
|
||||||
|
hard_camera_source = soft_camera_source
|
||||||
|
|
||||||
|
soft_camera_config_dict = camera_config_with_mode(soft_camera_source, str(options["softcore_camera_mode"]))
|
||||||
|
hard_camera_config_dict = camera_config_with_mode(hard_camera_source, hard_camera_mode)
|
||||||
|
soft_camera_config_dict = camera_config_with_detail(
|
||||||
|
soft_camera_config_dict,
|
||||||
|
str(options["camera_detail"]),
|
||||||
|
camera_detail_choices,
|
||||||
|
)
|
||||||
|
hard_camera_config_dict = camera_config_with_detail(
|
||||||
|
hard_camera_config_dict,
|
||||||
|
str(options["camera_detail"]),
|
||||||
|
camera_detail_choices,
|
||||||
|
)
|
||||||
|
soft_camera_directive, soft_camera_config_dict = camera_directive(soft_camera_config_dict)
|
||||||
|
hard_camera_directive, hard_camera_config_dict = camera_directive(hard_camera_config_dict)
|
||||||
|
|
||||||
|
soft_subject_kind = "woman" if options["softcore_cast"] == "solo" else "subjects"
|
||||||
|
hard_subject_kind = "couple" if hard_women_count + hard_men_count == 2 else "subjects"
|
||||||
|
soft_row = apply_contextual_composition(soft_row, soft_subject_kind)
|
||||||
|
hard_row = apply_contextual_composition(hard_row, hard_subject_kind)
|
||||||
|
|
||||||
|
hard_scene = soft_row["scene_text"] if options["continuity"] == "same_creator_same_room" else hard_row["scene_text"]
|
||||||
|
if hard_scene != hard_row.get("scene_text"):
|
||||||
|
hard_row["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "")
|
||||||
|
hard_row["scene_text"] = hard_scene
|
||||||
|
|
||||||
|
hard_composition = contextual_composition_prompt(hard_scene, hard_row["composition"], hard_subject_kind)
|
||||||
|
if hard_composition != hard_row["composition"]:
|
||||||
|
hard_row["source_composition"] = hard_row.get("source_composition") or hard_row["composition"]
|
||||||
|
hard_row["composition"] = hard_composition
|
||||||
|
hard_row["composition_prompt"] = composition_prompt(hard_composition)
|
||||||
|
|
||||||
|
soft_pov_camera_labels = pov_character_labels if options["softcore_cast"] == "same_as_hardcore" else []
|
||||||
|
soft_camera_scene_directive, soft_camera_config_dict = camera_scene_directive_for_context(
|
||||||
|
soft_row.get("scene_text"),
|
||||||
|
soft_row.get("composition"),
|
||||||
|
soft_camera_config_dict,
|
||||||
|
soft_pov_camera_labels,
|
||||||
|
soft_subject_kind,
|
||||||
|
)
|
||||||
|
hard_camera_scene_directive, hard_camera_config_dict = camera_scene_directive_for_context(
|
||||||
|
hard_scene,
|
||||||
|
hard_composition,
|
||||||
|
hard_camera_config_dict,
|
||||||
|
pov_character_labels,
|
||||||
|
hard_subject_kind,
|
||||||
|
)
|
||||||
|
|
||||||
|
if soft_pov_camera_labels:
|
||||||
|
soft_camera_directive = ""
|
||||||
|
if pov_character_labels:
|
||||||
|
hard_camera_directive = ""
|
||||||
|
|
||||||
|
soft_row["camera_config"] = soft_camera_config_dict
|
||||||
|
soft_row["camera_directive"] = soft_camera_directive
|
||||||
|
soft_row["camera_scene_directive"] = soft_camera_scene_directive
|
||||||
|
hard_row["camera_config"] = hard_camera_config_dict
|
||||||
|
hard_row["camera_directive"] = hard_camera_directive
|
||||||
|
hard_row["camera_scene_directive"] = hard_camera_scene_directive
|
||||||
|
|
||||||
|
return {
|
||||||
|
"soft_row": soft_row,
|
||||||
|
"hard_row": hard_row,
|
||||||
|
"hard_scene": hard_scene,
|
||||||
|
"hard_composition": hard_composition,
|
||||||
|
"soft_camera_config": soft_camera_config_dict,
|
||||||
|
"hard_camera_config": hard_camera_config_dict,
|
||||||
|
"soft_camera_directive": soft_camera_directive,
|
||||||
|
"hard_camera_directive": hard_camera_directive,
|
||||||
|
"soft_camera_scene_directive": soft_camera_scene_directive,
|
||||||
|
"hard_camera_scene_directive": hard_camera_scene_directive,
|
||||||
|
"soft_camera_scene_sentence": f"{soft_camera_scene_directive} " if soft_camera_scene_directive else "",
|
||||||
|
"hard_camera_scene_sentence": f"{hard_camera_scene_directive} " if hard_camera_scene_directive else "",
|
||||||
|
"soft_camera_sentence": f"Camera control: {soft_camera_directive} " if soft_camera_directive else "",
|
||||||
|
"hard_camera_sentence": f"Camera control: {hard_camera_directive} " if hard_camera_directive else "",
|
||||||
|
}
|
||||||
+33
-63
@@ -25,6 +25,7 @@ try:
|
|||||||
template_list as _template_list,
|
template_list as _template_list,
|
||||||
)
|
)
|
||||||
from . import generate_prompt_batches as g
|
from . import generate_prompt_batches as g
|
||||||
|
from . import pair_camera
|
||||||
from . import scene_camera_adapters
|
from . import scene_camera_adapters
|
||||||
from .hardcore_text_cleanup import (
|
from .hardcore_text_cleanup import (
|
||||||
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
||||||
@@ -54,6 +55,7 @@ except ImportError: # Allows local smoke tests with `python -c`.
|
|||||||
template_list as _template_list,
|
template_list as _template_list,
|
||||||
)
|
)
|
||||||
import generate_prompt_batches as g
|
import generate_prompt_batches as g
|
||||||
|
import pair_camera
|
||||||
import scene_camera_adapters
|
import scene_camera_adapters
|
||||||
from hardcore_text_cleanup import (
|
from hardcore_text_cleanup import (
|
||||||
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
||||||
@@ -6804,12 +6806,6 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
|
|||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
def _insta_camera_config_with_detail(camera_config: dict[str, Any], camera_detail: str) -> dict[str, Any]:
|
|
||||||
if camera_detail in CAMERA_DETAIL_CHOICES:
|
|
||||||
camera_config["camera_detail"] = camera_detail
|
|
||||||
return camera_config
|
|
||||||
|
|
||||||
|
|
||||||
def _insta_of_hardcore_counts(options: dict[str, Any]) -> tuple[int, int]:
|
def _insta_of_hardcore_counts(options: dict[str, Any]) -> tuple[int, int]:
|
||||||
policy = str(options.get("hardcore_cast", "use_counts"))
|
policy = str(options.get("hardcore_cast", "use_counts"))
|
||||||
if policy == "couple":
|
if policy == "couple":
|
||||||
@@ -7448,64 +7444,38 @@ def build_insta_of_pair(
|
|||||||
platform_style = INSTA_OF_PLATFORM_STYLES[options["platform_style"]]
|
platform_style = INSTA_OF_PLATFORM_STYLES[options["platform_style"]]
|
||||||
soft_level = INSTA_OF_SOFT_LEVELS[options["softcore_level"]]
|
soft_level = INSTA_OF_SOFT_LEVELS[options["softcore_level"]]
|
||||||
hard_level = INSTA_OF_HARDCORE_LEVELS[options["hardcore_level"]]
|
hard_level = INSTA_OF_HARDCORE_LEVELS[options["hardcore_level"]]
|
||||||
hard_camera_mode = options["hardcore_camera_mode"]
|
camera_route = pair_camera.resolve_insta_pair_camera(
|
||||||
soft_camera_source = softcore_camera_config or camera_config
|
soft_row=soft_row,
|
||||||
hard_camera_source = hardcore_camera_config or camera_config
|
hard_row=hard_row,
|
||||||
if hard_camera_mode == "same_as_softcore":
|
options=options,
|
||||||
hard_camera_mode = options["softcore_camera_mode"]
|
camera_config=camera_config,
|
||||||
hard_camera_source = soft_camera_source
|
softcore_camera_config=softcore_camera_config,
|
||||||
soft_camera_config = _camera_config_with_mode(soft_camera_source, options["softcore_camera_mode"])
|
hardcore_camera_config=hardcore_camera_config,
|
||||||
hard_camera_config = _camera_config_with_mode(hard_camera_source, hard_camera_mode)
|
hard_women_count=hard_women_count,
|
||||||
soft_camera_config = _insta_camera_config_with_detail(soft_camera_config, options["camera_detail"])
|
hard_men_count=hard_men_count,
|
||||||
hard_camera_config = _insta_camera_config_with_detail(hard_camera_config, options["camera_detail"])
|
pov_character_labels=pov_character_labels,
|
||||||
soft_camera_directive, soft_camera_config = _camera_directive(soft_camera_config)
|
camera_detail_choices=CAMERA_DETAIL_CHOICES,
|
||||||
hard_camera_directive, hard_camera_config = _camera_directive(hard_camera_config)
|
camera_config_with_mode=_camera_config_with_mode,
|
||||||
soft_subject_kind = "woman" if options["softcore_cast"] == "solo" else "subjects"
|
camera_directive=_camera_directive,
|
||||||
hard_subject_kind = "couple" if hard_women_count + hard_men_count == 2 else "subjects"
|
apply_contextual_composition=_apply_coworking_composition,
|
||||||
soft_row = _apply_coworking_composition(soft_row, soft_subject_kind)
|
contextual_composition_prompt=_coworking_composition_prompt,
|
||||||
hard_row = _apply_coworking_composition(hard_row, hard_subject_kind)
|
composition_prompt=_composition_prompt,
|
||||||
hard_scene = soft_row["scene_text"] if options["continuity"] == "same_creator_same_room" else hard_row["scene_text"]
|
camera_scene_directive_for_context=_camera_scene_directive_for_context,
|
||||||
if hard_scene != hard_row.get("scene_text"):
|
|
||||||
hard_row["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "")
|
|
||||||
hard_row["scene_text"] = hard_scene
|
|
||||||
hard_composition = _coworking_composition_prompt(hard_scene, hard_row["composition"], hard_subject_kind)
|
|
||||||
if hard_composition != hard_row["composition"]:
|
|
||||||
hard_row["source_composition"] = hard_row.get("source_composition") or hard_row["composition"]
|
|
||||||
hard_row["composition"] = hard_composition
|
|
||||||
hard_row["composition_prompt"] = _composition_prompt(hard_composition)
|
|
||||||
soft_pov_camera_labels = (
|
|
||||||
pov_character_labels
|
|
||||||
if options["softcore_cast"] == "same_as_hardcore"
|
|
||||||
else []
|
|
||||||
)
|
)
|
||||||
soft_camera_scene_directive, soft_camera_config = _camera_scene_directive_for_context(
|
soft_row = camera_route["soft_row"]
|
||||||
soft_row.get("scene_text"),
|
hard_row = camera_route["hard_row"]
|
||||||
soft_row.get("composition"),
|
hard_scene = camera_route["hard_scene"]
|
||||||
soft_camera_config,
|
hard_composition = camera_route["hard_composition"]
|
||||||
soft_pov_camera_labels,
|
soft_camera_config = camera_route["soft_camera_config"]
|
||||||
soft_subject_kind,
|
hard_camera_config = camera_route["hard_camera_config"]
|
||||||
)
|
soft_camera_directive = camera_route["soft_camera_directive"]
|
||||||
hard_camera_scene_directive, hard_camera_config = _camera_scene_directive_for_context(
|
hard_camera_directive = camera_route["hard_camera_directive"]
|
||||||
hard_scene,
|
soft_camera_scene_directive = camera_route["soft_camera_scene_directive"]
|
||||||
hard_composition,
|
hard_camera_scene_directive = camera_route["hard_camera_scene_directive"]
|
||||||
hard_camera_config,
|
soft_camera_scene_sentence = camera_route["soft_camera_scene_sentence"]
|
||||||
pov_character_labels,
|
hard_camera_scene_sentence = camera_route["hard_camera_scene_sentence"]
|
||||||
hard_subject_kind,
|
soft_camera_sentence = camera_route["soft_camera_sentence"]
|
||||||
)
|
hard_camera_sentence = camera_route["hard_camera_sentence"]
|
||||||
if soft_pov_camera_labels:
|
|
||||||
soft_camera_directive = ""
|
|
||||||
if pov_character_labels:
|
|
||||||
hard_camera_directive = ""
|
|
||||||
soft_row["camera_config"] = soft_camera_config
|
|
||||||
soft_row["camera_directive"] = soft_camera_directive
|
|
||||||
soft_row["camera_scene_directive"] = soft_camera_scene_directive
|
|
||||||
hard_row["camera_config"] = hard_camera_config
|
|
||||||
hard_row["camera_directive"] = hard_camera_directive
|
|
||||||
hard_row["camera_scene_directive"] = hard_camera_scene_directive
|
|
||||||
soft_camera_scene_sentence = f"{soft_camera_scene_directive} " if soft_camera_scene_directive else ""
|
|
||||||
hard_camera_scene_sentence = f"{hard_camera_scene_directive} " if hard_camera_scene_directive else ""
|
|
||||||
soft_camera_sentence = f"Camera control: {soft_camera_directive} " if soft_camera_directive else ""
|
|
||||||
hard_camera_sentence = f"Camera control: {hard_camera_directive} " if hard_camera_directive else ""
|
|
||||||
soft_cast = (
|
soft_cast = (
|
||||||
"solo creator setup with Woman A alone"
|
"solo creator setup with Woman A alone"
|
||||||
if options["softcore_cast"] == "solo"
|
if options["softcore_cast"] == "solo"
|
||||||
|
|||||||
@@ -755,6 +755,14 @@ def smoke_insta_pair_camera_split() -> None:
|
|||||||
_expect("elevated shot" in hard_scene, "hard camera scene missed hard elevation")
|
_expect("elevated shot" in hard_scene, "hard camera scene missed hard elevation")
|
||||||
_expect("front-right quarter view" in str(pair.get("softcore_camera_directive")), "soft pair camera directive was not preserved")
|
_expect("front-right quarter view" in str(pair.get("softcore_camera_directive")), "soft pair camera directive was not preserved")
|
||||||
_expect("back-right quarter view" in str(pair.get("hardcore_camera_directive")), "hard pair camera directive was not preserved")
|
_expect("back-right quarter view" in str(pair.get("hardcore_camera_directive")), "hard pair camera directive was not preserved")
|
||||||
|
soft_row = pair.get("softcore_row") or {}
|
||||||
|
hard_row = pair.get("hardcore_row") or {}
|
||||||
|
_expect(pair.get("softcore_camera_config") == soft_row.get("camera_config"), "soft pair camera config drifted from soft row")
|
||||||
|
_expect(pair.get("hardcore_camera_config") == hard_row.get("camera_config"), "hard pair camera config drifted from hard row")
|
||||||
|
_expect(pair.get("softcore_camera_directive") == soft_row.get("camera_directive"), "soft pair camera directive drifted from soft row")
|
||||||
|
_expect(pair.get("hardcore_camera_directive") == hard_row.get("camera_directive"), "hard pair camera directive drifted from hard row")
|
||||||
|
_expect(pair.get("softcore_camera_scene_directive") == soft_row.get("camera_scene_directive"), "soft pair camera scene drifted from soft row")
|
||||||
|
_expect(pair.get("hardcore_camera_scene_directive") == hard_row.get("camera_scene_directive"), "hard pair camera scene drifted from hard row")
|
||||||
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="auto")
|
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="auto")
|
||||||
_expect("front-right quarter view" in (krea.get("krea_softcore_prompt") or ""), "Krea soft pair lost soft camera geometry")
|
_expect("front-right quarter view" in (krea.get("krea_softcore_prompt") or ""), "Krea soft pair lost soft camera geometry")
|
||||||
_expect("back-right quarter view" in (krea.get("krea_hardcore_prompt") or ""), "Krea hard pair lost hard camera geometry")
|
_expect("back-right quarter view" in (krea.get("krea_hardcore_prompt") or ""), "Krea hard pair lost hard camera geometry")
|
||||||
|
|||||||
Reference in New Issue
Block a user