Add Qwen camera translator

This commit is contained in:
2026-06-24 19:15:30 +02:00
parent a469e6cef2
commit 54dc3f5f80
3 changed files with 208 additions and 2 deletions
+130
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import json
import math
import random
import re
from pathlib import Path
@@ -1666,6 +1667,135 @@ def build_camera_orbit_config_json(
return json.dumps(config, ensure_ascii=True, sort_keys=True)
QWEN_CAMERA_DIRECTIONS = {
"front-right quarter view": 45,
"right side view": 90,
"back-right quarter view": 135,
"back view": 180,
"back-left quarter view": 225,
"left side view": 270,
"front-left quarter view": 315,
"front view": 0,
}
QWEN_CAMERA_ELEVATIONS = {
"low-angle shot": -30,
"eye-level shot": 0,
"elevated shot": 30,
"high-angle shot": 60,
}
QWEN_CAMERA_ZOOMS = {
"wide shot": 0.0,
"medium shot": 5.0,
"close-up": 8.0,
}
QWEN_CAMERA_SCENE_CENTER_Y = 0.5
def _qwen_prompt_camera_values(qwen_prompt: Any) -> tuple[int, int, float]:
text = _clean_prompt_punctuation(str(qwen_prompt or "").lower().replace(",", " "))
horizontal_angle = 0
vertical_angle = 0
zoom = 5.0
for label, value in QWEN_CAMERA_DIRECTIONS.items():
if label in text:
horizontal_angle = value
break
for label, value in QWEN_CAMERA_ELEVATIONS.items():
if label in text:
vertical_angle = value
break
for label, value in QWEN_CAMERA_ZOOMS.items():
if label in text:
zoom = value
break
return horizontal_angle, vertical_angle, zoom
def _camera_info_dict(camera_info: Any) -> dict[str, Any] | None:
if not camera_info:
return None
if isinstance(camera_info, dict):
return camera_info
if isinstance(camera_info, str):
try:
raw = json.loads(camera_info)
except json.JSONDecodeError:
return None
return raw if isinstance(raw, dict) else None
return None
def _qwen_camera_info_values(camera_info: Any) -> tuple[int, int, float] | None:
info = _camera_info_dict(camera_info)
if not info:
return None
position = info.get("position") if isinstance(info.get("position"), dict) else {}
target = info.get("target") if isinstance(info.get("target"), dict) else {}
try:
dx = float(position.get("x", 0.0)) - float(target.get("x", 0.0))
dy = float(position.get("y", QWEN_CAMERA_SCENE_CENTER_Y)) - float(
target.get("y", QWEN_CAMERA_SCENE_CENTER_Y)
)
dz = float(position.get("z", 0.0)) - float(target.get("z", 0.0))
except (TypeError, ValueError):
return None
distance = math.sqrt(dx * dx + dy * dy + dz * dz)
if distance <= 0:
return None
horizontal_angle = int(round(math.degrees(math.atan2(dx, dz)))) % 360
vertical_angle = int(round(math.degrees(math.asin(max(-1.0, min(1.0, dy / distance))))))
zoom = max(0.0, min(10.0, ((2.6 - distance) / 2.0) * 10.0))
return horizontal_angle, vertical_angle, round(zoom, 2)
def build_qwen_camera_config_json(
qwen_prompt: str = "",
camera_info: Any = None,
prefer_camera_info: bool = True,
camera_mode: str = "standard",
subject_focus: str = "auto",
lens: str = "auto",
orientation: str = "auto",
phone_visibility: str = "auto",
priority: str = "locked",
camera_detail: str = "compact",
include_degrees: bool = False,
) -> str:
info_values = _qwen_camera_info_values(camera_info)
if prefer_camera_info and info_values is not None:
horizontal_angle, vertical_angle, zoom = info_values
source = "qwen_multiangle_camera_info"
else:
horizontal_angle, vertical_angle, zoom = _qwen_prompt_camera_values(qwen_prompt)
source = "qwen_multiangle_prompt"
config = json.loads(
build_camera_orbit_config_json(
enabled=True,
camera_mode=camera_mode,
horizontal_angle=horizontal_angle,
vertical_angle=vertical_angle,
zoom=zoom,
framing="from_zoom",
subject_focus=subject_focus,
lens=lens,
orientation=orientation,
phone_visibility=phone_visibility,
priority=priority,
camera_detail=camera_detail,
include_degrees=include_degrees,
)
)
config["camera_source"] = source
config["qwen_prompt"] = str(qwen_prompt or "").strip()
if info_values is not None:
config["qwen_camera_info_values"] = {
"horizontal_angle": info_values[0],
"vertical_angle": info_values[1],
"zoom": info_values[2],
}
return json.dumps(config, ensure_ascii=True, sort_keys=True)
def _choice(value: Any, choices: dict[str, str], default: str) -> str:
value = str(value or default)
return value if value in choices else default