Expand semi-public camera scene profiles

This commit is contained in:
2026-06-27 16:23:51 +02:00
parent 002c3b79d4
commit c69274d2ee
5 changed files with 352 additions and 20 deletions
+6 -3
View File
@@ -419,9 +419,12 @@ generic Qwen camera views do not add `phone hidden` or other phone wording.
For camera-aware locations, the prompt builder also uses the translated camera
geometry to add a location-aware framing sentence. It currently has scene
profiles for coworking/business-office spaces and classical library/book-stack
spaces: front/side/back views, zoom, and elevation change which desks, windows,
partitions, bookshelves, reading tables, lamps, or aisles are kept visible. In
profiles for coworking/business-office spaces, classical library/book-stack
spaces, and semi-public repeating-structure locations such as hotel corridors,
parking garages, archives, laundromats, station lockers, backstage halls, wine
cellars, nightclub back halls, and restaurant booths. Front/side/back views,
zoom, and elevation change which desks, windows, partitions, bookshelves,
corridors, pillars, shelves, tables, lamps, or aisles are kept visible. In
male-POV setups this becomes a first-person spatial description and the
external camera sentence is suppressed.
Rows keep the selected `scene_entry`, `location_theme`, `scene_theme`,
+4 -3
View File
@@ -276,9 +276,10 @@ Already isolated:
side-lying, and front/back group layouts.
- camera option schema, orbit/Qwen translation, config parsing, camera
directive text, and camera caption text live in `camera_config.py`;
camera-scene prose lives in `scene_camera_adapters.py`; row-level camera
insertion, contextual coworking composition mutation, subject-kind detection,
and POV suppression live in `row_camera.py`.
camera-scene prose and contextual scene composition mutation for coworking,
library, and semi-public profiles live in `scene_camera_adapters.py`;
row-level camera insertion, subject-kind detection, and POV suppression live
in `row_camera.py`.
- shared POV slot detection, label merging/filtering, builder-side POV
directives, source role-graph viewer replacement, and shared composition
cleanup live in `pov_policy.py`; prompt builder and Krea POV routes delegate
+7 -4
View File
@@ -126,8 +126,8 @@ Core helper ownership:
| `hardcore_action_metadata.py` | Source action-family and position-family metadata used by Krea2, SDXL, and caption routes. |
| `route_metadata.py` | Shared row-level route metadata readers for normalized action family, position family/keys, and formatter hints used by Krea2, SDXL, and caption routes. |
| `pov_policy.py` | Shared POV slot detection, POV label merging/filtering, builder POV directives, source role-graph viewer replacement, and shared POV composition cleanup used by builder and Krea2 routes. |
| `scene_camera_adapters.py` | Location-aware camera/scene prose such as coworking lounge camera layout. |
| `row_camera.py` | Row-level camera insertion, contextual coworking composition mutation, subject-kind detection, POV label fallback, and POV suppression of normal camera directives. |
| `scene_camera_adapters.py` | Location-aware camera/scene prose for coworking, library, semi-public corridor/garage/archive/etc. profiles, metadata-first profile resolution, and camera-aware composition cleanup. |
| `row_camera.py` | Row-level camera insertion, contextual scene composition mutation, subject-kind detection, POV label fallback, and POV suppression of normal camera directives. |
| `krea_row_fields.py` | Shared Krea normal-row field extraction for item, scene, pose, expression, composition/source-composition, camera, and style used by normal and configured-cast routes. |
| `krea_cast.py` | Shared formatter cast descriptor parsing, cast labels, cast prose, natural cast descriptor text, and label replacement used by Krea2 and caption routes. |
| `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup, including route-agnostic negative-prompt merge/dedupe. |
@@ -695,8 +695,11 @@ Current camera-aware scene adapter:
`scene_camera_profile` objects with `key`, `family`, `layout_label`, `place`,
`foreground`, `midground`, `background`, `detail_label`, and optional
per-subject `composition` text.
- Coworking/business-cafe/office scenes and classical library/book-stack scenes
are detected by `scene_camera_profile`.
- Coworking/business-cafe/office scenes, classical library/book-stack scenes,
and semi-public repeating-structure scenes such as hotel corridors, parking
garages, archives, laundromats, station lockers, backstage halls, wine
cellars, nightclub back halls, and restaurant booths are detected by
`scene_camera_profile`.
- Location themes preserve `theme` on configs and selected scene entries, and
rows expose `location_theme`, `scene_theme`, `composition_theme`, and
`scene_camera_profile_key` for debugging and future route rules.
+261 -9
View File
@@ -110,12 +110,260 @@ SCENE_CAMERA_PROFILES: tuple[dict[str, Any], ...] = (
"default": "classical library frame with the subjects near a bookshelf edge and long shelf depth behind them",
},
},
{
"key": "hotel_corridor",
"family": "semi_public",
"terms": (
"hotel corridor",
"hotel service corridor",
"hotel service alcove",
"service alcove",
"service hallway",
"service hall",
"repeating numbered doors",
"numbered doors",
"luggage carts",
"stair landing",
"hotel stair landing",
),
"layout_label": "Hotel corridor camera layout",
"place": "hotel corridor",
"foreground": "nearest doorframe edge, patterned carpet line, and wall sconce",
"midground": "repeating numbered doors, brass wall lamps, service-alcove turns, and luggage carts",
"background": "long corridor perspective, closed doors, warm late-night depth, and quiet hotel sightlines",
"detail_label": "hotel corridor details",
"composition": {
"woman": "hotel corridor frame with the woman near a doorframe edge and repeated doors behind her",
"man": "hotel corridor frame with the man near a doorframe edge and repeated doors behind him",
"default": "hotel corridor frame with the subjects near a doorframe edge and repeated doors behind them",
},
},
{
"key": "parking_garage",
"family": "semi_public",
"terms": (
"parking garage",
"parking deck",
"underground garage",
"multi-level parking",
"concrete pillars",
"numbered pillars",
"painted floor lines",
"painted bay lines",
"parked cars",
),
"layout_label": "Parking garage camera layout",
"place": "parking garage",
"foreground": "nearest concrete pillar, painted floor line, and car bumper edge",
"midground": "repeating concrete pillars, parked cars, painted bay lines, and glossy concrete lanes",
"background": "shadowed corners, fluorescent depth, numbered pillars, and long garage perspective",
"detail_label": "parking garage details",
"composition": {
"woman": "parking garage frame with the woman beside a concrete pillar and repeated bay lines behind her",
"man": "parking garage frame with the man beside a concrete pillar and repeated bay lines behind him",
"default": "parking garage frame with the subjects beside a concrete pillar and repeated bay lines behind them",
},
},
{
"key": "theater_backstage",
"family": "semi_public",
"terms": (
"theater backstage",
"backstage wings",
"cabaret backstage",
"prop storage",
"prop racks",
"costume racks",
"costume rails",
"velvet curtains",
"stage ropes",
"scenery flats",
),
"layout_label": "Backstage camera layout",
"place": "theater backstage",
"foreground": "curtain edge, prop trunk corner, and costume-rack line",
"midground": "layered velvet curtains, costume racks, prop shelves, and vanity bulb mirrors",
"background": "dark stage wings, repeated scenery flats, narrow backstage passages, and warm light spill",
"detail_label": "backstage details",
"composition": {
"woman": "backstage frame with the woman partly framed by curtains and costume racks behind her",
"man": "backstage frame with the man partly framed by curtains and costume racks behind him",
"default": "backstage frame with the subjects partly framed by curtains and costume racks behind them",
},
},
{
"key": "wine_cellar",
"family": "semi_public",
"terms": (
"wine cellar",
"wine storage",
"bottle racks",
"bottle shelves",
"arched cellar",
"brick niches",
"cellar corridor",
"stacked bottle",
),
"layout_label": "Wine cellar camera layout",
"place": "wine cellar",
"foreground": "near bottle-rack edge, crate corner, and stone floor line",
"midground": "repeating bottle racks, arched brick niches, narrow aisles, and low amber lamps",
"background": "cool shadowed depth, stacked shelves, cellar arches, and secluded rack rows",
"detail_label": "wine cellar details",
"composition": {
"woman": "wine cellar frame with the woman between bottle racks and arched cellar depth behind her",
"man": "wine cellar frame with the man between bottle racks and arched cellar depth behind him",
"default": "wine cellar frame with the subjects between bottle racks and arched cellar depth behind them",
},
},
{
"key": "museum_archive",
"family": "semi_public",
"terms": (
"museum archive",
"gallery storage",
"rare-books archive",
"archive room",
"storage shelves",
"labeled boxes",
"rolling shelves",
"catalog drawers",
"compact shelving",
),
"layout_label": "Archive camera layout",
"place": "museum archive",
"foreground": "storage-shelf edge, archive box corner, and work-table line",
"midground": "labeled boxes, rolling shelves, frame racks, catalog drawers, and long work tables",
"background": "compact shelving rows, soft overhead lights, archival aisles, and hidden storage depth",
"detail_label": "archive details",
"composition": {
"woman": "archive frame with the woman beside labeled storage shelves and compact rows behind her",
"man": "archive frame with the man beside labeled storage shelves and compact rows behind him",
"default": "archive frame with the subjects beside labeled storage shelves and compact rows behind them",
},
},
{
"key": "laundromat_late_night",
"family": "semi_public",
"terms": (
"laundromat",
"coin laundry",
"washing machines",
"stacked dryers",
"washer-door",
"washer door",
"folding tables",
"detergent shelves",
"machine row",
),
"layout_label": "Laundromat camera layout",
"place": "late-night laundromat",
"foreground": "folding-table edge, chrome washer door, and tiled floor line",
"midground": "repeating washing machines, stacked dryers, detergent shelves, and empty machine rows",
"background": "cool fluorescent depth, mirrored machine doors, front glass, and quiet back-corner sightlines",
"detail_label": "laundromat details",
"composition": {
"woman": "laundromat frame with the woman near a folding table and repeated washer doors behind her",
"man": "laundromat frame with the man near a folding table and repeated washer doors behind him",
"default": "laundromat frame with the subjects near a folding table and repeated washer doors behind them",
},
},
{
"key": "train_station_lockers",
"family": "semi_public",
"terms": (
"train-station locker",
"train station locker",
"locker corridor",
"station underpass",
"station service passage",
"metal lockers",
"vending machines",
"tiled walls",
"utility doors",
"warning stripes",
),
"layout_label": "Station locker camera layout",
"place": "train-station locker corridor",
"foreground": "locker edge, vending-machine corner, and tiled floor line",
"midground": "repeating metal lockers, tiled wall seams, poster frames, and utility doors",
"background": "fluorescent underpass depth, stair railings, warning stripes, and hidden side alcoves",
"detail_label": "station locker details",
"composition": {
"woman": "station locker frame with the woman beside metal lockers and tiled depth behind her",
"man": "station locker frame with the man beside metal lockers and tiled depth behind him",
"default": "station locker frame with the subjects beside metal lockers and tiled depth behind them",
},
},
{
"key": "nightclub_back_hall",
"family": "semi_public",
"terms": (
"nightclub back hallway",
"club vip corridor",
"vip club corridor",
"music venue greenroom",
"greenroom corridor",
"coat-check racks",
"neon strips",
"velvet ropes",
"mirrored wall panels",
"stickered doors",
),
"layout_label": "Nightclub back-hall camera layout",
"place": "nightclub back hallway",
"foreground": "black door edge, velvet-rope post, and mirrored wall strip",
"midground": "repeated dark doors, neon strips, coat-check racks, mirrored panels, and booth edges",
"background": "distant colored dance-floor light, dim practical lamps, cable cases, and narrow hallway depth",
"detail_label": "nightclub back-hall details",
"composition": {
"woman": "nightclub back-hall frame with the woman near a dark door edge and neon hallway depth behind her",
"man": "nightclub back-hall frame with the man near a dark door edge and neon hallway depth behind him",
"default": "nightclub back-hall frame with the subjects near a dark door edge and neon hallway depth behind them",
},
},
{
"key": "restaurant_private_booth",
"family": "semi_public",
"terms": (
"restaurant private booth",
"private booth",
"bistro back corner",
"after-hours dining",
"afterhours dining",
"high banquettes",
"dark wood partitions",
"folded napkins",
"stacked chairs",
"small round tables",
),
"layout_label": "Restaurant booth camera layout",
"place": "restaurant private booth",
"foreground": "table edge, high banquette back, and dark wood partition",
"midground": "repeating table lamps, folded napkins, mirrored wall panels, and empty tables",
"background": "after-hours dining-room depth, stacked chairs, service doorway, and secluded sightlines",
"detail_label": "restaurant booth details",
"composition": {
"woman": "restaurant booth frame with the woman beside a high banquette and table lamps behind her",
"man": "restaurant booth frame with the man beside a high banquette and table lamps behind him",
"default": "restaurant booth frame with the subjects beside a high banquette and table lamps behind them",
},
},
)
SCENE_CAMERA_PROFILE_KEYS = {str(profile["key"]): dict(profile) for profile in SCENE_CAMERA_PROFILES}
THEME_PROFILE_KEYS = {
"classical_library": "classical_library",
"hotel_corridor": "hotel_corridor",
"parking_garage": "parking_garage",
"theater_backstage": "theater_backstage",
"wine_cellar": "wine_cellar",
"museum_archive": "museum_archive",
"laundromat_late_night": "laundromat_late_night",
"train_station_lockers": "train_station_lockers",
"nightclub_back_hall": "nightclub_back_hall",
"restaurant_private_booth": "restaurant_private_booth",
}
PROFILE_TEXT_FIELDS = (
@@ -348,7 +596,7 @@ def coworking_location_profile(scene_text: Any) -> dict[str, str]:
return scene_camera_profile("coworking lounge")
def coworking_subject_terms(subject_kind: str, pov_labels: list[str] | None = None) -> tuple[str, str]:
def scene_subject_terms(subject_kind: str, pov_labels: list[str] | None = None) -> tuple[str, str]:
if pov_labels:
return "the visible partner", "them"
if subject_kind == "woman":
@@ -360,6 +608,10 @@ def coworking_subject_terms(subject_kind: str, pov_labels: list[str] | None = No
return "the subjects", "them"
def coworking_subject_terms(subject_kind: str, pov_labels: list[str] | None = None) -> tuple[str, str]:
return scene_subject_terms(subject_kind, pov_labels)
def scene_direction_detail(
direction: str,
profile: dict[str, str],
@@ -371,7 +623,7 @@ def scene_direction_detail(
midground = profile["midground"]
background = profile["background"]
detail_label = profile.get("detail_label") or "location details"
subject, pronoun = coworking_subject_terms(subject_kind, pov_labels)
subject, pronoun = scene_subject_terms(subject_kind, pov_labels)
if pov_labels:
if "right side" in direction:
return f"{subject} is in right-side profile; {midground} run behind {pronoun} toward {background}, with {detail_label} kept at the frame edges"
@@ -411,7 +663,7 @@ def scene_distance_detail(
pov_labels: list[str] | None = None,
) -> str:
distance = str(distance or "").strip().lower()
subject, _pronoun = coworking_subject_terms(subject_kind, pov_labels)
subject, _pronoun = scene_subject_terms(subject_kind, pov_labels)
if pov_labels:
if "wide" in distance or "full-body" in distance or "full body" in distance:
return f"wide POV keeps {subject} readable with {profile['place']} context behind them"
@@ -441,7 +693,7 @@ def scene_elevation_detail(
pov_labels: list[str] | None = None,
) -> str:
elevation = str(elevation or "").strip().lower()
subject, pronoun = coworking_subject_terms(subject_kind, pov_labels)
subject, pronoun = scene_subject_terms(subject_kind, pov_labels)
if pov_labels:
if "low-angle" in elevation:
return f"low angle keeps POV body cues low while the {profile['background']} rises behind {pronoun}"
@@ -494,13 +746,13 @@ def scene_camera_directive(
direction_detail = scene_direction_detail(direction, profile, pov_labels, subject_kind)
distance_detail = scene_distance_detail(distance, profile, subject_kind, pov_labels)
elevation_detail = scene_elevation_detail(elevation, profile, subject_kind, pov_labels)
if pov_labels:
return (
f"{profile['layout_label']} from POV: {direction_detail}. "
f"{distance_detail}; {elevation_detail}; use the multiangle camera only as first-person spatial geometry."
)
geometry = camera_geometry_phrase(parsed, compact_labels)
geometry_clause = f" ({geometry})" if geometry else ""
if pov_labels:
return (
f"{profile['layout_label']} from POV{geometry_clause}: {direction_detail}. "
f"{distance_detail}; {elevation_detail}; use the multiangle camera only as first-person spatial geometry."
)
return (
f"{profile['layout_label']}{geometry_clause}: {direction_detail}; "
f"{distance_detail}; {elevation_detail}."
+74 -1
View File
@@ -406,10 +406,14 @@ def _coworking_location_config() -> str:
def _classical_library_theme_configs() -> tuple[str, str]:
return _thematic_location_configs("classical_library")
def _thematic_location_configs(theme: str) -> tuple[str, str]:
location_config, composition_config, _summary = pb.build_thematic_location_json(
enabled=True,
combine_mode="replace",
theme="classical_library",
theme=theme,
)
return location_config, composition_config
@@ -694,6 +698,39 @@ def smoke_row_camera_policy() -> None:
_expect("bag" not in library_composition.lower(), "row camera library composition leaked bag wording")
_expect("shoes" not in library_composition.lower(), "row camera library composition leaked shoes wording")
_expect("library" in library_composition.lower(), "row camera library composition did not become location-aware")
semi_public_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
"scene_text": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
"scene_entry": {
"slug": "hotel_corridor_affair",
"prompt": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
"theme": "semi_public_affair",
},
"scene_theme": "semi_public_affair",
"composition": "polished mirror view with bag and shoes visible",
"subject_type": "configured_cast",
"women_count": 1,
"men_count": 1,
"pov_character_labels": ["Man A"],
}
updated_semi_public = row_camera.apply_camera_config(
semi_public_row,
_orbit_camera(horizontal_angle=180, vertical_angle=30, zoom=7.5),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
semi_public_scene = _expect_text("row_camera_policy.semi_public_scene", updated_semi_public.get("camera_scene_directive"), 40)
semi_public_composition = _expect_text(
"row_camera_policy.semi_public_composition",
updated_semi_public.get("composition"),
20,
)
_expect("Hotel corridor camera layout from POV" in semi_public_scene, "row camera semi-public scene did not use hotel corridor profile")
_expect("back view" in semi_public_scene, "row camera semi-public scene missed orbit direction")
_expect("first-person spatial geometry" in semi_public_scene, "row camera semi-public POV scene lost first-person geometry")
_expect(updated_semi_public.get("scene_camera_profile_key") == "hotel_corridor", "row camera semi-public scene did not expose text-matched profile key")
_expect("hotel corridor" in semi_public_composition.lower(), "row camera semi-public composition did not become location-aware")
_expect("bag" not in semi_public_composition.lower() and "shoes" not in semi_public_composition.lower(), "row camera semi-public composition leaked outfit-check props")
metadata_profile_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
@@ -870,6 +907,42 @@ def smoke_config_route_location_theme() -> None:
_expect("315-degree front-left quarter view" in prompt, "Krea config route lost camera directive")
_expect_formatter_outputs(row, "config_route_location_theme", target="single")
parking_location_config, parking_composition_config = _thematic_location_configs("parking_garage")
parking_row = pb.build_prompt_from_configs(
row_number=1,
start_index=1,
seed=3311,
category_config=pb.build_category_config_json("woman", "random"),
cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
generation_profile=pb.build_generation_profile_json(profile="balanced"),
camera_config=_orbit_camera(
horizontal_angle=135,
vertical_angle=-30,
zoom=4.0,
subject_focus="environment",
),
location_config=parking_location_config,
composition_config=parking_composition_config,
)
_expect_row_base(parking_row, "config_route_location_theme.parking")
parking_scene = _expect_text("config_route_location_theme.parking_scene", parking_row.get("scene_text"), 20)
parking_composition = _expect_text("config_route_location_theme.parking_composition", parking_row.get("composition"), 10)
parking_directive = _expect_text(
"config_route_location_theme.parking_camera_scene_directive",
parking_row.get("camera_scene_directive"),
40,
)
parking_profile = parking_row.get("scene_camera_profile") if isinstance(parking_row.get("scene_camera_profile"), dict) else {}
_expect("parking" in parking_scene.lower() or "garage" in parking_scene.lower(), "parking theme did not drive scene")
_expect("parking" in parking_composition.lower() or "garage" in parking_composition.lower() or "pillar" in parking_composition.lower(), "parking theme did not drive composition")
_expect(parking_row.get("location_theme") == "parking_garage", "parking location theme did not survive")
_expect(parking_row.get("scene_theme") == "parking_garage", "parking scene theme did not survive")
_expect(parking_row.get("scene_camera_profile_key") == "parking_garage", "parking theme did not expose camera profile key")
_expect(parking_profile.get("family") == "semi_public", "parking camera profile family should be semi_public")
_expect("Parking garage camera layout" in parking_directive, "parking theme did not drive camera-scene adapter")
_expect("back-right quarter view" in parking_directive, "parking camera-scene adapter missed orbit direction")
_expect("low-angle shot" in parking_directive, "parking camera-scene adapter missed elevation")
def smoke_builder_prompt_route_policy() -> None:
def legacy_from_request(request: builder_prompt_route.PromptBuildRequest) -> dict[str, Any]: