release: camera AI 0.4.9 worker 0.1.5
This commit is contained in:
@@ -1,5 +1,32 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.4.9 - 2026-08-09
|
||||||
|
|
||||||
|
- Restored the authored same-audio cut prior for production generation inputs
|
||||||
|
without admitting the target camera trajectory into the planner or template
|
||||||
|
library. The Arisa regression song now recovers its music-shaped 69-shot
|
||||||
|
cadence, while a silent-tail fallback cut is removed.
|
||||||
|
- Replaced performer-screen-motion penalties with stabilized residual framing
|
||||||
|
diagnostics so expressive dance motion no longer suppresses orbit, drift,
|
||||||
|
lead-room, or authored camera movement. Hard person, distance, step, aim, and
|
||||||
|
dynamics gates remain fail-closed.
|
||||||
|
- Calibrated front-facing safety once over the full performance. Choreography
|
||||||
|
turns can keep a stable stage-front camera without rewarding side or rear
|
||||||
|
placement, while true 180-degree rear candidates remain rejected.
|
||||||
|
- Added a versioned selection-safety contract for selected-shot regeneration,
|
||||||
|
verified exact-audio reference hashes across bundled and project data roots,
|
||||||
|
and deterministically deduplicated identical packaged reference copies.
|
||||||
|
- Added a final-world post-clamp kinematic repair and hard audit. Distance
|
||||||
|
clamping can no longer reintroduce an unbraked reversal after the YAMO
|
||||||
|
deceleration/acceleration pass; all 69 regression shots pass the final
|
||||||
|
post-clamp contract and Shot 66 has zero remaining abrupt reversal events.
|
||||||
|
- Rebuilt the self-contained Windows worker as 0.1.5 with Python 3.12.13 in a
|
||||||
|
disposable, exact-pinned environment with one-thread scientific-library
|
||||||
|
limits, artifact size/file-count guards, and a distribution provenance
|
||||||
|
manifest. The clean worker contains 647 files (267,608,825 bytes), passes
|
||||||
|
`doctor`, and is byte-identical to source generation for camera, time, and
|
||||||
|
shot outputs.
|
||||||
|
|
||||||
## 0.4.8 - 2026-08-09
|
## 0.4.8 - 2026-08-09
|
||||||
|
|
||||||
- Replaced the unstable frame-local aim controller with a causal, feed-forward
|
- Replaced the unstable frame-local aim controller with a causal, feed-forward
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ must remain together.
|
|||||||
In Unity Package Manager, choose **Add package from git URL** and enter:
|
In Unity Package Manager, choose **Add package from git URL** and enter:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.7
|
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.15
|
||||||
```
|
```
|
||||||
|
|
||||||
The initial package download is large because the frozen Windows worker is
|
The initial package download is large because the frozen Windows worker is
|
||||||
@@ -26,7 +26,7 @@ it, remove the package lock entry, and add the package again.
|
|||||||
|
|
||||||
## Reference data
|
## Reference data
|
||||||
|
|
||||||
Version 0.1.7 includes a compact, read-only `RuntimeData~` bundle with the
|
Version 0.1.15 includes a compact, read-only `RuntimeData~` bundle with the
|
||||||
263 prepared reference songs, cut policy, and ranker model. An artist
|
263 prepared reference songs, cut policy, and ranker model. An artist
|
||||||
workstation does not need a separate `CW-AI` checkout or Python installation.
|
workstation does not need a separate `CW-AI` checkout or Python installation.
|
||||||
If a newer access-controlled library is available, it remains an optional
|
If a newer access-controlled library is available, it remains an optional
|
||||||
|
|||||||
+6
-5
@@ -16,7 +16,7 @@ Cinemachine Track이나 기존 카메라 애니메이션은 필요하지 않으
|
|||||||
Unity Package Manager의 `Add package from git URL`에는 다음 주소를 사용할 수
|
Unity Package Manager의 `Add package from git URL`에는 다음 주소를 사용할 수
|
||||||
있습니다.
|
있습니다.
|
||||||
|
|
||||||
`https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.7`
|
`https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.15`
|
||||||
|
|
||||||
배포 패키지에는 Windows x64용 `CWCameraWorker` 폴더 전체가 포함됩니다.
|
배포 패키지에는 Windows x64용 `CWCameraWorker` 폴더 전체가 포함됩니다.
|
||||||
Python은 따로 설치하지 않아도 되지만, Git 패키지의 대용량 바이너리를 받으려면
|
Python은 따로 설치하지 않아도 되지만, Git 패키지의 대용량 바이너리를 받으려면
|
||||||
@@ -24,10 +24,11 @@ Unity를 열기 전에 Git과 Git LFS를 설치해야 합니다. Worker는 단
|
|||||||
아니므로 `CWCameraWorker.exe`와 같은 폴더의 `_internal` 내용을 함께 유지해야
|
아니므로 `CWCameraWorker.exe`와 같은 폴더의 `_internal` 내용을 함께 유지해야
|
||||||
합니다.
|
합니다.
|
||||||
|
|
||||||
카메라 생성에 필요한 사내 참조 카메라·모션 데이터는 권한이 있는 `CW-AI`
|
카메라 생성에 필요한 준비된 참조 카메라·모션 데이터와 컷 모델은 패키지의
|
||||||
데이터 루트에서 읽습니다. 해당 데이터는 이 유틸리티 패키지에 포함되지 않습니다.
|
읽기 전용 `RuntimeData~`에 포함됩니다. 별도의 `CW-AI` 체크아웃은 필요하지
|
||||||
다른 PC에서는 `CWAI_ROOT` 환경 변수, Unity 프로젝트 옆의 `CW-AI` 폴더 또는
|
않습니다. 더 최신의 권한 제어 데이터가 있다면 `CWAI_ROOT` 환경 변수, Unity
|
||||||
생성 창의 `고급 · 진단 > 참조 데이터 루트` 중 하나로 위치를 지정합니다.
|
프로젝트 옆의 `CW-AI` 폴더 또는 생성 창의
|
||||||
|
`고급 · 진단 > 참조 데이터 루트`에서 선택적으로 덮어쓸 수 있습니다.
|
||||||
상세 설치 절차는 `Documentation~/EXTERNAL_INSTALLATION.md`를 참고하세요.
|
상세 설치 절차는 `Documentation~/EXTERNAL_INSTALLATION.md`를 참고하세요.
|
||||||
참조 데이터는 읽기 전용이어도 됩니다. 반복 생성 캐시는 기본적으로 현재 Unity
|
참조 데이터는 읽기 전용이어도 됩니다. 반복 생성 캐시는 기본적으로 현재 Unity
|
||||||
프로젝트의 `Library/CWAI`에 저장되며, 필요하면 `CWAI_CACHE_ROOT` 환경 변수로
|
프로젝트의 `Library/CWAI`에 저장되며, 필요하면 `CWAI_CACHE_ROOT` 환경 변수로
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+10
-10
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"schemaVersion": "cw-camera-worker-build-identity-v1",
|
"schemaVersion": "cw-camera-worker-build-identity-v1",
|
||||||
"workerVersion": "0.1.4",
|
"workerVersion": "0.1.5",
|
||||||
"createdUtc": "2026-08-08T17:11:19.216870+00:00",
|
"createdUtc": "2026-08-08T20:03:04.908909+00:00",
|
||||||
"python": "3.12.13",
|
"python": "3.12.13",
|
||||||
"sourceRootRelative": "cwai_sources/repository",
|
"sourceRootRelative": "cwai_sources/repository",
|
||||||
"sourceSha256": {
|
"sourceSha256": {
|
||||||
"MachineLearning/CameraDirector/adjacent_transition.py": "6c1b62c2996960af23d62e9c3acd3114600effda03273b006f7076c467c4bdfa",
|
"MachineLearning/CameraDirector/adjacent_transition.py": "6c1b62c2996960af23d62e9c3acd3114600effda03273b006f7076c467c4bdfa",
|
||||||
"MachineLearning/CameraDirector/build_hybrid_preparation_cache.py": "164a3574d5fad05a627bf6d0c6169d42039043bf9a2cd62effbd978359e828f2",
|
"MachineLearning/CameraDirector/build_hybrid_preparation_cache.py": "f948b1a68352893b078ac1f6ef458df2107565378eed32890276776ca0dfdad6",
|
||||||
"MachineLearning/CameraDirector/camera_kinematics.py": "c7b85616ef44a0f28f11702459d67d78aad7cf26ed00c2548ecb78b06fc27025",
|
"MachineLearning/CameraDirector/camera_kinematics.py": "75816a55ac1dc655731432f061c2f4356cc3b583f3ce33797578431adcd9ba39",
|
||||||
"MachineLearning/CameraDirector/camera_runtime_data.py": "29fd30a1c97d384f7bb22d024a4f88178f6245858925c302b059a7a1afdeaac1",
|
"MachineLearning/CameraDirector/camera_runtime_data.py": "29fd30a1c97d384f7bb22d024a4f88178f6245858925c302b059a7a1afdeaac1",
|
||||||
"MachineLearning/CameraDirector/cw_camera_cli.py": "943870a4cdec7e4b830b9c945d690e700902cf2a37d1e49987bba85bf0b0ea87",
|
"MachineLearning/CameraDirector/cw_camera_cli.py": "943870a4cdec7e4b830b9c945d690e700902cf2a37d1e49987bba85bf0b0ea87",
|
||||||
"MachineLearning/CameraDirector/cw_camera_runtime.py": "94d9f03488978cf34cadbc9495542983c15c8e5050be6d7b9b0317845ca25242",
|
"MachineLearning/CameraDirector/cw_camera_runtime.py": "c4cfbc61173cb541932632e3b0a0751ae75c9d6ca3b56680722caa78f74e797d",
|
||||||
"MachineLearning/CameraDirector/data_driven_cut_planner.py": "b6d48d03f8725aa8327abe81afa483c43edc368e7dee9f9e7228e21982f71ad2",
|
"MachineLearning/CameraDirector/data_driven_cut_planner.py": "36cf86a80577278d2e966cb0ebf7c3110b5b1dda5abd1d8a6b0a76e31d10e32b",
|
||||||
"MachineLearning/CameraDirector/generate_hybrid.py": "fbb7bd07fb32f81efd259b4bb56e30404df59eb98fe05c9c92429a214f666659",
|
"MachineLearning/CameraDirector/generate_hybrid.py": "4bf19ffdd8e244a82b4ea0656bad030a30470a9e2065bdccde8bfb9d99922fea",
|
||||||
"MachineLearning/CameraDirector/hybrid_candidate_cache.py": "a0a5b6a8f612f18cb2875394f17ed89950e1e62e38c1226457d9e35848f6e380",
|
"MachineLearning/CameraDirector/hybrid_candidate_cache.py": "a0a5b6a8f612f18cb2875394f17ed89950e1e62e38c1226457d9e35848f6e380",
|
||||||
"MachineLearning/CameraDirector/hybrid_cut_reference.py": "53521d9e31d315c011892961887baf0cde6451020bf39c9d080a5072d4707fcb",
|
"MachineLearning/CameraDirector/hybrid_cut_reference.py": "53521d9e31d315c011892961887baf0cde6451020bf39c9d080a5072d4707fcb",
|
||||||
"MachineLearning/CameraDirector/hybrid_preparation_cache.py": "1b0df53124109dee2d744777979a0553999cb276cb6f538a17ddce6516e3da8b",
|
"MachineLearning/CameraDirector/hybrid_preparation_cache.py": "1b0df53124109dee2d744777979a0553999cb276cb6f538a17ddce6516e3da8b",
|
||||||
@@ -20,11 +20,11 @@
|
|||||||
"MachineLearning/CameraDirector/planner.py": "8575788c0ff1e984f8238354dcd6de2dab9f62afeb71fa4b77ebfc7c66400372",
|
"MachineLearning/CameraDirector/planner.py": "8575788c0ff1e984f8238354dcd6de2dab9f62afeb71fa4b77ebfc7c66400372",
|
||||||
"MachineLearning/CameraDirector/shot_features.py": "fd912b00320ea53ca682c010de6ed1102edcd021ceb4822a5785033a500786fe",
|
"MachineLearning/CameraDirector/shot_features.py": "fd912b00320ea53ca682c010de6ed1102edcd021ceb4822a5785033a500786fe",
|
||||||
"MachineLearning/CameraDirector/train.py": "40e9e9a9ab824d596b14f987b2977f187695c3d5a2a2bde9da67e95d38d78836",
|
"MachineLearning/CameraDirector/train.py": "40e9e9a9ab824d596b14f987b2977f187695c3d5a2a2bde9da67e95d38d78836",
|
||||||
"MachineLearning/CameraDirector/trajectory_quality.py": "8ede816e1d155e9d35fd6d18b3aaa145cd4957e205d898e11c1691222ef420a2"
|
"MachineLearning/CameraDirector/trajectory_quality.py": "20cd2ddc1b2349f8c33f6d1460580f37964a521eaa86ceee5b8ae457512ba49d"
|
||||||
},
|
},
|
||||||
"preparationLogicIdentifier": "acba2a46d9d979b013b7154bada6b6a8a7adfd7acd1721876ce6699ed4e03e84",
|
"preparationLogicIdentifier": "acba2a46d9d979b013b7154bada6b6a8a7adfd7acd1721876ce6699ed4e03e84",
|
||||||
"candidateLogicIdentifier": "a9503f8764910d763fe0135f6b1425b0cba289dbd1dd149609d2c00e508470c1",
|
"candidateLogicIdentifier": "7c771be40d4c6ebc6b15a96c64ed05062f6109d20605670be9b0e07587f2b25a",
|
||||||
"generationCodeIdentifier": "05e85df5ac7eb614f68e340502cb03a9c5a073f91eba5d16d642340728d51d67",
|
"generationCodeIdentifier": "470c77fe5d69046aa7bcadfbbb1337d6f0025fe0be374cb2302ea41ae00cdc47",
|
||||||
"generationCodeFiles": [
|
"generationCodeFiles": [
|
||||||
"adjacent_transition.py",
|
"adjacent_transition.py",
|
||||||
"camera_kinematics.py",
|
"camera_kinematics.py",
|
||||||
|
|||||||
+3
-4
@@ -260,10 +260,9 @@ def run(args: argparse.Namespace) -> dict:
|
|||||||
summary = {
|
summary = {
|
||||||
"targetSongId": target_record.id,
|
"targetSongId": target_record.id,
|
||||||
"targetDatasetId": target_record.dataset_id,
|
"targetDatasetId": target_record.dataset_id,
|
||||||
"targetEvaluationGroupId": (
|
"targetEvaluationGroupId": generate_hybrid.target_reference_evaluation_group(
|
||||||
target_entry["evaluationGroupId"]
|
entries,
|
||||||
if target_entry is not None
|
target_record,
|
||||||
else None
|
|
||||||
),
|
),
|
||||||
"targetTrainingIndexMembership": target_entry is not None,
|
"targetTrainingIndexMembership": target_entry is not None,
|
||||||
"targetInputKind": (
|
"targetInputKind": (
|
||||||
|
|||||||
+319
-9
@@ -19,7 +19,7 @@ import numpy as np
|
|||||||
|
|
||||||
|
|
||||||
TRANSLATION_KINEMATIC_POLICY_VERSION = (
|
TRANSLATION_KINEMATIC_POLICY_VERSION = (
|
||||||
"yamo-angle-aware-turn-v4-c2-continuity-guard"
|
"yamo-angle-aware-turn-v6-natural-easing-impulse-c2"
|
||||||
)
|
)
|
||||||
DEFAULT_REVERSAL_ANGLE_DEGREES = 40.0
|
DEFAULT_REVERSAL_ANGLE_DEGREES = 40.0
|
||||||
DEFAULT_STOP_SPEED_RATIO = 0.12
|
DEFAULT_STOP_SPEED_RATIO = 0.12
|
||||||
@@ -44,6 +44,31 @@ DEFAULT_ACCELERATION_GUARD_FLOOR_MPS2 = 25.0
|
|||||||
DEFAULT_MAXIMUM_JERK_REGRESSION_RATIO = 1.35
|
DEFAULT_MAXIMUM_JERK_REGRESSION_RATIO = 1.35
|
||||||
DEFAULT_MAXIMUM_JERK_REGRESSION_DELTA_MPS3 = 350.0
|
DEFAULT_MAXIMUM_JERK_REGRESSION_DELTA_MPS3 = 350.0
|
||||||
DEFAULT_JERK_GUARD_FLOOR_MPS3 = 350.0
|
DEFAULT_JERK_GUARD_FLOOR_MPS3 = 350.0
|
||||||
|
# A second, deliberately narrow gate catches a concentrated turn that evades
|
||||||
|
# the 40-degree rule yet still carries an edit-visible sideways impulse. The
|
||||||
|
# thresholds were calibrated against 2,479 authored turn events: the combined
|
||||||
|
# gate adds only 10 events (0.4%) beyond the existing abrupt-turn policy while
|
||||||
|
# covering the reviewed Shot_066 profile (high stop ratio plus ~22 m/s^2 and
|
||||||
|
# ~200 m/s^3 local lateral dynamics).
|
||||||
|
DEFAULT_IMPULSIVE_TURN_ANGLE_DEGREES = 15.0
|
||||||
|
DEFAULT_IMPULSIVE_STOP_SPEED_RATIO = 0.78
|
||||||
|
DEFAULT_IMPULSIVE_CORNER_CONCENTRATION = 0.40
|
||||||
|
DEFAULT_IMPULSIVE_LATERAL_ACCELERATION_MPS2 = 22.0
|
||||||
|
DEFAULT_IMPULSIVE_LATERAL_JERK_MPS3 = 200.0
|
||||||
|
DEFAULT_IMPULSIVE_TARGET_SPEED_RATIO = 0.72
|
||||||
|
DEFAULT_IMPULSIVE_REVERSAL_WINDOW_SECONDS = 0.50
|
||||||
|
# A generated curve can satisfy the broad monotonic-speed definition of
|
||||||
|
# ``natural_easing`` while its turn is still concentrated enough to read as a
|
||||||
|
# lateral kick. Keep this as a separate, narrower contradiction gate rather
|
||||||
|
# than weakening the micro-turn thresholds above. The reviewed Shot_066 is
|
||||||
|
# 59.60 degrees / 0.812 stop ratio / 0.355 concentration / 22.69 m/s^2 /
|
||||||
|
# 167.84 m/s^3. A scan of 1,395 detected events across 2,006 authored shots
|
||||||
|
# found only three grouped events satisfying every condition below (0.22%);
|
||||||
|
# all three also carried substantially larger 645--958 m/s^3 lateral jerk.
|
||||||
|
DEFAULT_NATURAL_IMPULSE_TARGET_EXCESS_RATIO = 0.08
|
||||||
|
DEFAULT_NATURAL_IMPULSE_CORNER_CONCENTRATION = 0.35
|
||||||
|
DEFAULT_NATURAL_IMPULSE_LATERAL_ACCELERATION_MPS2 = 22.0
|
||||||
|
DEFAULT_NATURAL_IMPULSE_LATERAL_JERK_MPS3 = 160.0
|
||||||
|
|
||||||
|
|
||||||
def turn_speed_ratio_limit(angle_degrees: float) -> float:
|
def turn_speed_ratio_limit(angle_degrees: float) -> float:
|
||||||
@@ -99,9 +124,16 @@ class TranslationReversal:
|
|||||||
target_speed_ratio: float
|
target_speed_ratio: float
|
||||||
corner_concentration: float
|
corner_concentration: float
|
||||||
natural_easing_detected: bool
|
natural_easing_detected: bool
|
||||||
|
impulsive: bool = False
|
||||||
|
lateral_acceleration_mps2: float = 0.0
|
||||||
|
lateral_jerk_mps3: float = 0.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def abrupt(self) -> bool:
|
def abrupt(self) -> bool:
|
||||||
|
if self.impulsive:
|
||||||
|
return self.stop_speed_ratio > (
|
||||||
|
self.target_speed_ratio + DEFAULT_SPEED_RATIO_TOLERANCE
|
||||||
|
)
|
||||||
if self.natural_easing_detected and self.stop_speed_ratio <= (
|
if self.natural_easing_detected and self.stop_speed_ratio <= (
|
||||||
natural_turn_speed_ratio_limit(self.angle_degrees)
|
natural_turn_speed_ratio_limit(self.angle_degrees)
|
||||||
+ DEFAULT_SPEED_RATIO_TOLERANCE
|
+ DEFAULT_SPEED_RATIO_TOLERANCE
|
||||||
@@ -184,16 +216,71 @@ def _group_reversal_candidates(
|
|||||||
group,
|
group,
|
||||||
key=lambda event: (event.stop_speed_ratio, event.frame),
|
key=lambda event: (event.stop_speed_ratio, event.frame),
|
||||||
)
|
)
|
||||||
|
impulsive_events = [event for event in group if event.impulsive]
|
||||||
|
representative = (
|
||||||
|
max(
|
||||||
|
impulsive_events,
|
||||||
|
key=lambda event: (
|
||||||
|
event.lateral_jerk_mps3,
|
||||||
|
event.lateral_acceleration_mps2,
|
||||||
|
-event.frame,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if impulsive_events
|
||||||
|
else low_speed_event
|
||||||
|
)
|
||||||
maximum_angle = max(event.angle_degrees for event in group)
|
maximum_angle = max(event.angle_degrees for event in group)
|
||||||
|
maximum_concentration = max(
|
||||||
|
event.corner_concentration for event in group
|
||||||
|
)
|
||||||
|
maximum_lateral_acceleration = max(
|
||||||
|
event.lateral_acceleration_mps2 for event in group
|
||||||
|
)
|
||||||
|
maximum_lateral_jerk = max(
|
||||||
|
event.lateral_jerk_mps3 for event in group
|
||||||
|
)
|
||||||
|
maximum_angle_target = turn_speed_ratio_limit(maximum_angle)
|
||||||
|
# A detector group represents one physical corner, but its clearest
|
||||||
|
# slowdown, largest angle and largest lateral impulse commonly occur
|
||||||
|
# on neighbouring centers. Evaluate this contradiction after
|
||||||
|
# grouping; requiring every signal on one center missed Shot_066 even
|
||||||
|
# though the aggregate event was an edit-visible kick.
|
||||||
|
natural_easing_impulse = bool(
|
||||||
|
low_speed_event.natural_easing_detected
|
||||||
|
and maximum_angle
|
||||||
|
>= DEFAULT_REVERSAL_ANGLE_DEGREES
|
||||||
|
- DEFAULT_ANGLE_COMPARISON_TOLERANCE_DEGREES
|
||||||
|
and low_speed_event.stop_speed_ratio
|
||||||
|
>= DEFAULT_IMPULSIVE_STOP_SPEED_RATIO
|
||||||
|
and low_speed_event.stop_speed_ratio - maximum_angle_target
|
||||||
|
>= DEFAULT_NATURAL_IMPULSE_TARGET_EXCESS_RATIO
|
||||||
|
and maximum_concentration
|
||||||
|
>= DEFAULT_NATURAL_IMPULSE_CORNER_CONCENTRATION
|
||||||
|
and maximum_lateral_acceleration
|
||||||
|
>= DEFAULT_NATURAL_IMPULSE_LATERAL_ACCELERATION_MPS2
|
||||||
|
and maximum_lateral_jerk
|
||||||
|
>= DEFAULT_NATURAL_IMPULSE_LATERAL_JERK_MPS3
|
||||||
|
)
|
||||||
|
impulsive = bool(impulsive_events) or natural_easing_impulse
|
||||||
events.append(
|
events.append(
|
||||||
TranslationReversal(
|
TranslationReversal(
|
||||||
frame=low_speed_event.frame,
|
frame=representative.frame,
|
||||||
angle_degrees=maximum_angle,
|
angle_degrees=maximum_angle,
|
||||||
stop_speed_ratio=low_speed_event.stop_speed_ratio,
|
stop_speed_ratio=representative.stop_speed_ratio,
|
||||||
flank_speed_mps=low_speed_event.flank_speed_mps,
|
flank_speed_mps=representative.flank_speed_mps,
|
||||||
target_speed_ratio=turn_speed_ratio_limit(maximum_angle),
|
target_speed_ratio=(
|
||||||
corner_concentration=max(event.corner_concentration for event in group),
|
min(
|
||||||
natural_easing_detected=(low_speed_event.natural_easing_detected),
|
maximum_angle_target,
|
||||||
|
DEFAULT_IMPULSIVE_TARGET_SPEED_RATIO,
|
||||||
|
)
|
||||||
|
if impulsive
|
||||||
|
else maximum_angle_target
|
||||||
|
),
|
||||||
|
corner_concentration=maximum_concentration,
|
||||||
|
natural_easing_detected=(representative.natural_easing_detected),
|
||||||
|
impulsive=impulsive,
|
||||||
|
lateral_acceleration_mps2=maximum_lateral_acceleration,
|
||||||
|
lateral_jerk_mps3=maximum_lateral_jerk,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return events
|
return events
|
||||||
@@ -364,9 +451,73 @@ def analyze_translational_reversals(
|
|||||||
)
|
)
|
||||||
valid &= radius[centers] >= 0.1
|
valid &= radius[centers] >= 0.1
|
||||||
|
|
||||||
|
# Keep the original, angle-aware policy result separate. The impulsive
|
||||||
|
# gate below is a narrow fallback for turns that the established policy
|
||||||
|
# would otherwise preserve; it must not strengthen the slowdown already
|
||||||
|
# selected for an ordinary 40+ degree corner.
|
||||||
|
standard_valid = valid.copy()
|
||||||
|
|
||||||
local_speed_windows = np.lib.stride_tricks.sliding_window_view(speed, 4)
|
local_speed_windows = np.lib.stride_tricks.sliding_window_view(speed, 4)
|
||||||
local_minimum_speeds = np.min(local_speed_windows[centers - 2], axis=1)
|
local_minimum_speeds = np.min(local_speed_windows[centers - 2], axis=1)
|
||||||
ratios = local_minimum_speeds / np.maximum(flank_speeds, 1e-12)
|
ratios = local_minimum_speeds / np.maximum(flank_speeds, 1e-12)
|
||||||
|
lateral_acceleration_peaks = np.zeros(len(centers), dtype=np.float64)
|
||||||
|
lateral_jerk_peaks = np.zeros(len(centers), dtype=np.float64)
|
||||||
|
if family != "orbit":
|
||||||
|
acceleration = np.diff(velocity, axis=0) * sample_rate
|
||||||
|
jerk = np.diff(acceleration, axis=0) * sample_rate
|
||||||
|
for result_index, center in enumerate(centers):
|
||||||
|
incoming = before_vectors[result_index]
|
||||||
|
incoming_norm = float(np.linalg.norm(incoming))
|
||||||
|
if incoming_norm <= 1e-12:
|
||||||
|
continue
|
||||||
|
incoming_unit = incoming / incoming_norm
|
||||||
|
window_start = max(0, int(center) - persistence_frames)
|
||||||
|
acceleration_window = acceleration[
|
||||||
|
window_start : min(len(acceleration), int(center) + persistence_frames)
|
||||||
|
]
|
||||||
|
jerk_window = jerk[
|
||||||
|
window_start : min(len(jerk), int(center) + persistence_frames)
|
||||||
|
]
|
||||||
|
if len(acceleration_window):
|
||||||
|
lateral = acceleration_window - (
|
||||||
|
acceleration_window @ incoming_unit
|
||||||
|
)[:, None] * incoming_unit
|
||||||
|
lateral_acceleration_peaks[result_index] = float(
|
||||||
|
np.max(np.linalg.norm(lateral, axis=1))
|
||||||
|
)
|
||||||
|
if len(jerk_window):
|
||||||
|
lateral = jerk_window - (
|
||||||
|
jerk_window @ incoming_unit
|
||||||
|
)[:, None] * incoming_unit
|
||||||
|
lateral_jerk_peaks[result_index] = float(
|
||||||
|
np.max(np.linalg.norm(lateral, axis=1))
|
||||||
|
)
|
||||||
|
impulsive_valid = (
|
||||||
|
(flank_speeds >= minimum_flank_speed_mps)
|
||||||
|
& (
|
||||||
|
angles
|
||||||
|
>= DEFAULT_IMPULSIVE_TURN_ANGLE_DEGREES
|
||||||
|
- DEFAULT_ANGLE_COMPARISON_TOLERANCE_DEGREES
|
||||||
|
)
|
||||||
|
& (before_coherence >= minimum_flank_direction_coherence)
|
||||||
|
& (after_coherence >= minimum_flank_direction_coherence)
|
||||||
|
& (
|
||||||
|
corner_concentration
|
||||||
|
>= max(
|
||||||
|
minimum_corner_concentration,
|
||||||
|
DEFAULT_IMPULSIVE_CORNER_CONCENTRATION,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
& (np.minimum(before_travel, after_travel) >= minimum_flank_travel_meters)
|
||||||
|
& (ratios >= DEFAULT_IMPULSIVE_STOP_SPEED_RATIO)
|
||||||
|
& (
|
||||||
|
lateral_acceleration_peaks
|
||||||
|
>= DEFAULT_IMPULSIVE_LATERAL_ACCELERATION_MPS2
|
||||||
|
)
|
||||||
|
& (lateral_jerk_peaks >= DEFAULT_IMPULSIVE_LATERAL_JERK_MPS3)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
impulsive_valid = np.zeros(len(centers), dtype=bool)
|
||||||
persistence_speed_windows = np.lib.stride_tricks.sliding_window_view(
|
persistence_speed_windows = np.lib.stride_tricks.sliding_window_view(
|
||||||
speed,
|
speed,
|
||||||
persistence_frames,
|
persistence_frames,
|
||||||
@@ -393,6 +544,30 @@ def analyze_translational_reversals(
|
|||||||
& (after_monotonic_ratio >= 0.75)
|
& (after_monotonic_ratio >= 0.75)
|
||||||
& (ratios <= natural_speed_limits + DEFAULT_SPEED_RATIO_TOLERANCE)
|
& (ratios <= natural_speed_limits + DEFAULT_SPEED_RATIO_TOLERANCE)
|
||||||
)
|
)
|
||||||
|
standard_speed_limits = np.asarray(
|
||||||
|
[turn_speed_ratio_limit(float(angle)) for angle in angles],
|
||||||
|
dtype=np.float64,
|
||||||
|
)
|
||||||
|
standard_abrupt = standard_valid & ~(
|
||||||
|
natural_easing
|
||||||
|
& (ratios <= natural_speed_limits + DEFAULT_SPEED_RATIO_TOLERANCE)
|
||||||
|
) & (ratios > standard_speed_limits + DEFAULT_SPEED_RATIO_TOLERANCE)
|
||||||
|
# Only upgrade events that evade the established abrupt-turn rule. This
|
||||||
|
# keeps authored 40--180 degree slowdown curves on their calibrated policy
|
||||||
|
# while admitting the reviewed concentrated, high-jerk micro-turn class.
|
||||||
|
impulsive_valid &= ~standard_abrupt
|
||||||
|
if np.any(standard_abrupt):
|
||||||
|
# Adjacent detector centers represent one physical corner and are
|
||||||
|
# grouped later. Clear the fallback throughout that grouping radius,
|
||||||
|
# otherwise a nearby 39.9-degree center could relabel an ordinary
|
||||||
|
# 40-degree event as impulsive and incorrectly strengthen its target.
|
||||||
|
standard_abrupt_centers = centers[standard_abrupt]
|
||||||
|
impulsive_valid &= ~np.any(
|
||||||
|
np.abs(centers[:, None] - standard_abrupt_centers[None, :])
|
||||||
|
<= persistence_frames,
|
||||||
|
axis=1,
|
||||||
|
)
|
||||||
|
valid |= impulsive_valid
|
||||||
candidates = [
|
candidates = [
|
||||||
TranslationReversal(
|
TranslationReversal(
|
||||||
frame=int(frame),
|
frame=int(frame),
|
||||||
@@ -402,6 +577,9 @@ def analyze_translational_reversals(
|
|||||||
target_speed_ratio=turn_speed_ratio_limit(float(angle)),
|
target_speed_ratio=turn_speed_ratio_limit(float(angle)),
|
||||||
corner_concentration=float(concentration),
|
corner_concentration=float(concentration),
|
||||||
natural_easing_detected=bool(is_naturally_eased),
|
natural_easing_detected=bool(is_naturally_eased),
|
||||||
|
impulsive=bool(is_impulsive),
|
||||||
|
lateral_acceleration_mps2=float(lateral_acceleration),
|
||||||
|
lateral_jerk_mps3=float(lateral_jerk),
|
||||||
)
|
)
|
||||||
for (
|
for (
|
||||||
frame,
|
frame,
|
||||||
@@ -410,6 +588,9 @@ def analyze_translational_reversals(
|
|||||||
flank_speed,
|
flank_speed,
|
||||||
concentration,
|
concentration,
|
||||||
is_naturally_eased,
|
is_naturally_eased,
|
||||||
|
is_impulsive,
|
||||||
|
lateral_acceleration,
|
||||||
|
lateral_jerk,
|
||||||
) in zip(
|
) in zip(
|
||||||
centers[valid],
|
centers[valid],
|
||||||
angles[valid],
|
angles[valid],
|
||||||
@@ -417,6 +598,9 @@ def analyze_translational_reversals(
|
|||||||
flank_speeds[valid],
|
flank_speeds[valid],
|
||||||
corner_concentration[valid],
|
corner_concentration[valid],
|
||||||
natural_easing[valid],
|
natural_easing[valid],
|
||||||
|
impulsive_valid[valid],
|
||||||
|
lateral_acceleration_peaks[valid],
|
||||||
|
lateral_jerk_peaks[valid],
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -611,14 +795,27 @@ def _dynamics_regression_exceeds_guard(
|
|||||||
|
|
||||||
def _event_metadata(events: list[TranslationReversal]) -> dict[str, object]:
|
def _event_metadata(events: list[TranslationReversal]) -> dict[str, object]:
|
||||||
abrupt = [event for event in events if event.abrupt]
|
abrupt = [event for event in events if event.abrupt]
|
||||||
|
impulsive = [event for event in events if event.impulsive]
|
||||||
return {
|
return {
|
||||||
"count": len(events),
|
"count": len(events),
|
||||||
"abruptCount": len(abrupt),
|
"abruptCount": len(abrupt),
|
||||||
"frames": [event.frame for event in events],
|
"frames": [event.frame for event in events],
|
||||||
"abruptFrames": [event.frame for event in abrupt],
|
"abruptFrames": [event.frame for event in abrupt],
|
||||||
"anglesDegrees": [event.angle_degrees for event in events],
|
"anglesDegrees": [event.angle_degrees for event in events],
|
||||||
|
"stopSpeedRatios": [event.stop_speed_ratio for event in events],
|
||||||
"targetSpeedRatios": [event.target_speed_ratio for event in events],
|
"targetSpeedRatios": [event.target_speed_ratio for event in events],
|
||||||
"cornerConcentrations": [event.corner_concentration for event in events],
|
"cornerConcentrations": [event.corner_concentration for event in events],
|
||||||
|
"naturalEasingDetected": [
|
||||||
|
event.natural_easing_detected for event in events
|
||||||
|
],
|
||||||
|
"impulsiveCount": len(impulsive),
|
||||||
|
"impulsiveFrames": [event.frame for event in impulsive],
|
||||||
|
"lateralAccelerationMetersPerSecondSquared": [
|
||||||
|
event.lateral_acceleration_mps2 for event in events
|
||||||
|
],
|
||||||
|
"lateralJerkMetersPerSecondCubed": [
|
||||||
|
event.lateral_jerk_mps3 for event in events
|
||||||
|
],
|
||||||
"worstStopSpeedRatio": (
|
"worstStopSpeedRatio": (
|
||||||
float(max(event.stop_speed_ratio for event in events)) if events else 0.0
|
float(max(event.stop_speed_ratio for event in events)) if events else 0.0
|
||||||
),
|
),
|
||||||
@@ -641,12 +838,15 @@ def regularize_translational_reversals(
|
|||||||
*,
|
*,
|
||||||
sample_rate: float = 60.0,
|
sample_rate: float = 60.0,
|
||||||
reversal_window_seconds: float = DEFAULT_REVERSAL_WINDOW_SECONDS,
|
reversal_window_seconds: float = DEFAULT_REVERSAL_WINDOW_SECONDS,
|
||||||
|
apply_policy: bool = True,
|
||||||
) -> tuple[np.ndarray, dict[str, object]]:
|
) -> tuple[np.ndarray, dict[str, object]]:
|
||||||
"""Apply angle-aware braking to abrupt turns and orbit reversals."""
|
"""Apply angle-aware braking to abrupt turns and orbit reversals."""
|
||||||
|
|
||||||
positions = _validated_positions(positions_meters)
|
positions = _validated_positions(positions_meters)
|
||||||
if not math.isfinite(reversal_window_seconds) or reversal_window_seconds <= 0.0:
|
if not math.isfinite(reversal_window_seconds) or reversal_window_seconds <= 0.0:
|
||||||
raise ValueError("reversal_window_seconds must be finite and positive.")
|
raise ValueError("reversal_window_seconds must be finite and positive.")
|
||||||
|
if not isinstance(apply_policy, bool):
|
||||||
|
raise ValueError("apply_policy must be a boolean.")
|
||||||
family = motion_family(motion_type)
|
family = motion_family(motion_type)
|
||||||
before = analyze_translational_reversals(
|
before = analyze_translational_reversals(
|
||||||
positions,
|
positions,
|
||||||
@@ -658,21 +858,37 @@ def regularize_translational_reversals(
|
|||||||
applied_frames: list[int] = []
|
applied_frames: list[int] = []
|
||||||
applied_angles: list[float] = []
|
applied_angles: list[float] = []
|
||||||
applied_target_speed_ratios: list[float] = []
|
applied_target_speed_ratios: list[float] = []
|
||||||
|
applied_impulsive_frames: list[int] = []
|
||||||
|
|
||||||
pass_count = 0
|
pass_count = 0
|
||||||
pending_events = abrupt_before
|
pending_events = abrupt_before if apply_policy else []
|
||||||
if family not in {"static", "unsupported"} and pending_events:
|
if family not in {"static", "unsupported"} and pending_events:
|
||||||
requested_radius = max(
|
requested_radius = max(
|
||||||
6,
|
6,
|
||||||
int(round(reversal_window_seconds * sample_rate)),
|
int(round(reversal_window_seconds * sample_rate)),
|
||||||
)
|
)
|
||||||
|
impulsive_requested_radius = max(
|
||||||
|
requested_radius,
|
||||||
|
int(
|
||||||
|
round(
|
||||||
|
DEFAULT_IMPULSIVE_REVERSAL_WINDOW_SECONDS * sample_rate
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
# Only abrupt events detected on the untouched input are eligible.
|
# Only abrupt events detected on the untouched input are eligible.
|
||||||
# Re-detecting and rewriting new window-boundary events caused a
|
# Re-detecting and rewriting new window-boundary events caused a
|
||||||
# three-pass cascade in real shots, multiplying acceleration and jerk
|
# three-pass cascade in real shots, multiplying acceleration and jerk
|
||||||
# even though the final turn counter eventually reached zero.
|
# even though the final turn counter eventually reached zero.
|
||||||
event_frames = [event.frame for event in pending_events]
|
event_frames = [event.frame for event in pending_events]
|
||||||
for event_index, event in enumerate(pending_events):
|
for event_index, event in enumerate(pending_events):
|
||||||
neighbor_limit = requested_radius
|
# The high-impulse fallback is intentionally rare, so give its
|
||||||
|
# braking envelope a slightly longer C2 runway instead of forcing
|
||||||
|
# a stronger speed change into the normal 0.35-second window.
|
||||||
|
neighbor_limit = (
|
||||||
|
impulsive_requested_radius
|
||||||
|
if event.impulsive
|
||||||
|
else requested_radius
|
||||||
|
)
|
||||||
if event_index > 0:
|
if event_index > 0:
|
||||||
neighbor_limit = min(
|
neighbor_limit = min(
|
||||||
neighbor_limit,
|
neighbor_limit,
|
||||||
@@ -700,6 +916,8 @@ def regularize_translational_reversals(
|
|||||||
applied_frames.append(event.frame)
|
applied_frames.append(event.frame)
|
||||||
applied_angles.append(event.angle_degrees)
|
applied_angles.append(event.angle_degrees)
|
||||||
applied_target_speed_ratios.append(event.target_speed_ratio)
|
applied_target_speed_ratios.append(event.target_speed_ratio)
|
||||||
|
if event.impulsive:
|
||||||
|
applied_impulsive_frames.append(event.frame)
|
||||||
pass_count = int(bool(applied_frames))
|
pass_count = int(bool(applied_frames))
|
||||||
|
|
||||||
endpoint_adjustment = output[-1] - positions[-1]
|
endpoint_adjustment = output[-1] - positions[-1]
|
||||||
@@ -734,6 +952,7 @@ def regularize_translational_reversals(
|
|||||||
applied_frames.clear()
|
applied_frames.clear()
|
||||||
applied_angles.clear()
|
applied_angles.clear()
|
||||||
applied_target_speed_ratios.clear()
|
applied_target_speed_ratios.clear()
|
||||||
|
applied_impulsive_frames.clear()
|
||||||
pass_count = 0
|
pass_count = 0
|
||||||
endpoint_adjustment_clamped = False
|
endpoint_adjustment_clamped = False
|
||||||
|
|
||||||
@@ -753,6 +972,7 @@ def regularize_translational_reversals(
|
|||||||
"translationKinematicPolicyVersion": (TRANSLATION_KINEMATIC_POLICY_VERSION),
|
"translationKinematicPolicyVersion": (TRANSLATION_KINEMATIC_POLICY_VERSION),
|
||||||
"translationKinematicMotionFamily": family,
|
"translationKinematicMotionFamily": family,
|
||||||
"translationKinematicPolicyEligible": family not in {"static", "unsupported"},
|
"translationKinematicPolicyEligible": family not in {"static", "unsupported"},
|
||||||
|
"translationKinematicPolicyApplicationEnabled": apply_policy,
|
||||||
"translationKinematicPolicyApplied": bool(applied_frames),
|
"translationKinematicPolicyApplied": bool(applied_frames),
|
||||||
"translationKinematicDynamicsGuardTriggered": dynamics_guard_triggered,
|
"translationKinematicDynamicsGuardTriggered": dynamics_guard_triggered,
|
||||||
"translationKinematicAttemptedRegularizationCount": len(
|
"translationKinematicAttemptedRegularizationCount": len(
|
||||||
@@ -765,16 +985,71 @@ def regularize_translational_reversals(
|
|||||||
"translationAbruptReversalCountBefore": before_metadata["abruptCount"],
|
"translationAbruptReversalCountBefore": before_metadata["abruptCount"],
|
||||||
"translationDirectionReversalFramesBefore": before_metadata["frames"],
|
"translationDirectionReversalFramesBefore": before_metadata["frames"],
|
||||||
"translationAbruptReversalFramesBefore": before_metadata["abruptFrames"],
|
"translationAbruptReversalFramesBefore": before_metadata["abruptFrames"],
|
||||||
|
"translationDirectionReversalAnglesDegreesBefore": before_metadata[
|
||||||
|
"anglesDegrees"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalStopSpeedRatiosBefore": before_metadata[
|
||||||
|
"stopSpeedRatios"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalTargetSpeedRatiosBefore": before_metadata[
|
||||||
|
"targetSpeedRatios"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalCornerConcentrationsBefore": before_metadata[
|
||||||
|
"cornerConcentrations"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalNaturalEasingDetectedBefore": before_metadata[
|
||||||
|
"naturalEasingDetected"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalLateralAccelerationMetersPerSecondSquaredBefore": (
|
||||||
|
before_metadata["lateralAccelerationMetersPerSecondSquared"]
|
||||||
|
),
|
||||||
|
"translationDirectionReversalLateralJerkMetersPerSecondCubedBefore": (
|
||||||
|
before_metadata["lateralJerkMetersPerSecondCubed"]
|
||||||
|
),
|
||||||
"translationWorstStopSpeedRatioBefore": before_metadata["worstStopSpeedRatio"],
|
"translationWorstStopSpeedRatioBefore": before_metadata["worstStopSpeedRatio"],
|
||||||
"translationRegularizedReversalCount": len(applied_frames),
|
"translationRegularizedReversalCount": len(applied_frames),
|
||||||
"translationKinematicRegularizationPassCount": pass_count,
|
"translationKinematicRegularizationPassCount": pass_count,
|
||||||
"translationRegularizedReversalFrames": applied_frames,
|
"translationRegularizedReversalFrames": applied_frames,
|
||||||
"translationRegularizedTurnAnglesDegrees": applied_angles,
|
"translationRegularizedTurnAnglesDegrees": applied_angles,
|
||||||
"translationRegularizedTargetSpeedRatios": (applied_target_speed_ratios),
|
"translationRegularizedTargetSpeedRatios": (applied_target_speed_ratios),
|
||||||
|
"translationImpulsiveTurnCountBefore": before_metadata[
|
||||||
|
"impulsiveCount"
|
||||||
|
],
|
||||||
|
"translationImpulsiveTurnFramesBefore": before_metadata[
|
||||||
|
"impulsiveFrames"
|
||||||
|
],
|
||||||
|
"translationImpulsiveTurnRegularizedFrames": applied_impulsive_frames,
|
||||||
|
"translationImpulsiveTurnCountAfter": after_metadata[
|
||||||
|
"impulsiveCount"
|
||||||
|
],
|
||||||
|
"translationImpulsiveTurnFramesAfter": after_metadata[
|
||||||
|
"impulsiveFrames"
|
||||||
|
],
|
||||||
"translationDirectionReversalCountAfter": after_metadata["count"],
|
"translationDirectionReversalCountAfter": after_metadata["count"],
|
||||||
"translationAbruptReversalCountAfter": after_metadata["abruptCount"],
|
"translationAbruptReversalCountAfter": after_metadata["abruptCount"],
|
||||||
"translationDirectionReversalFramesAfter": after_metadata["frames"],
|
"translationDirectionReversalFramesAfter": after_metadata["frames"],
|
||||||
"translationAbruptReversalFramesAfter": after_metadata["abruptFrames"],
|
"translationAbruptReversalFramesAfter": after_metadata["abruptFrames"],
|
||||||
|
"translationDirectionReversalAnglesDegreesAfter": after_metadata[
|
||||||
|
"anglesDegrees"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalStopSpeedRatiosAfter": after_metadata[
|
||||||
|
"stopSpeedRatios"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalTargetSpeedRatiosAfter": after_metadata[
|
||||||
|
"targetSpeedRatios"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalCornerConcentrationsAfter": after_metadata[
|
||||||
|
"cornerConcentrations"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalNaturalEasingDetectedAfter": after_metadata[
|
||||||
|
"naturalEasingDetected"
|
||||||
|
],
|
||||||
|
"translationDirectionReversalLateralAccelerationMetersPerSecondSquaredAfter": (
|
||||||
|
after_metadata["lateralAccelerationMetersPerSecondSquared"]
|
||||||
|
),
|
||||||
|
"translationDirectionReversalLateralJerkMetersPerSecondCubedAfter": (
|
||||||
|
after_metadata["lateralJerkMetersPerSecondCubed"]
|
||||||
|
),
|
||||||
"translationWorstStopSpeedRatioAfter": after_metadata["worstStopSpeedRatio"],
|
"translationWorstStopSpeedRatioAfter": after_metadata["worstStopSpeedRatio"],
|
||||||
"translationWorstTargetSpeedRatioExcessAfter": after_metadata[
|
"translationWorstTargetSpeedRatioExcessAfter": after_metadata[
|
||||||
"worstTargetSpeedRatioExcess"
|
"worstTargetSpeedRatioExcess"
|
||||||
@@ -782,6 +1057,41 @@ def regularize_translational_reversals(
|
|||||||
"translationStopSpeedRatioLimit": DEFAULT_STOP_SPEED_RATIO,
|
"translationStopSpeedRatioLimit": DEFAULT_STOP_SPEED_RATIO,
|
||||||
"translationReversalAngleMinimumDegrees": (DEFAULT_REVERSAL_ANGLE_DEGREES),
|
"translationReversalAngleMinimumDegrees": (DEFAULT_REVERSAL_ANGLE_DEGREES),
|
||||||
"translationMinimumCornerConcentration": (DEFAULT_MINIMUM_CORNER_CONCENTRATION),
|
"translationMinimumCornerConcentration": (DEFAULT_MINIMUM_CORNER_CONCENTRATION),
|
||||||
|
"translationImpulsiveTurnPolicy": {
|
||||||
|
"minimumAngleDegrees": DEFAULT_IMPULSIVE_TURN_ANGLE_DEGREES,
|
||||||
|
"minimumStopSpeedRatio": DEFAULT_IMPULSIVE_STOP_SPEED_RATIO,
|
||||||
|
"minimumCornerConcentration": (
|
||||||
|
DEFAULT_IMPULSIVE_CORNER_CONCENTRATION
|
||||||
|
),
|
||||||
|
"minimumLateralAccelerationMetersPerSecondSquared": (
|
||||||
|
DEFAULT_IMPULSIVE_LATERAL_ACCELERATION_MPS2
|
||||||
|
),
|
||||||
|
"minimumLateralJerkMetersPerSecondCubed": (
|
||||||
|
DEFAULT_IMPULSIVE_LATERAL_JERK_MPS3
|
||||||
|
),
|
||||||
|
"maximumTargetSpeedRatio": DEFAULT_IMPULSIVE_TARGET_SPEED_RATIO,
|
||||||
|
"decelerationAndAccelerationWindowSeconds": (
|
||||||
|
DEFAULT_IMPULSIVE_REVERSAL_WINDOW_SECONDS
|
||||||
|
),
|
||||||
|
"naturalEasingContradiction": {
|
||||||
|
"minimumOriginalTurnAngleDegrees": (
|
||||||
|
DEFAULT_REVERSAL_ANGLE_DEGREES
|
||||||
|
),
|
||||||
|
"minimumTargetSpeedRatioExcess": (
|
||||||
|
DEFAULT_NATURAL_IMPULSE_TARGET_EXCESS_RATIO
|
||||||
|
),
|
||||||
|
"minimumCornerConcentration": (
|
||||||
|
DEFAULT_NATURAL_IMPULSE_CORNER_CONCENTRATION
|
||||||
|
),
|
||||||
|
"minimumLateralAccelerationMetersPerSecondSquared": (
|
||||||
|
DEFAULT_NATURAL_IMPULSE_LATERAL_ACCELERATION_MPS2
|
||||||
|
),
|
||||||
|
"minimumLateralJerkMetersPerSecondCubed": (
|
||||||
|
DEFAULT_NATURAL_IMPULSE_LATERAL_JERK_MPS3
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"continuousOrbitExcluded": True,
|
||||||
|
},
|
||||||
"translationSpeedMetersPerSecondMaxBefore": dynamics_before[
|
"translationSpeedMetersPerSecondMaxBefore": dynamics_before[
|
||||||
"speedMetersPerSecondMax"
|
"speedMetersPerSecondMax"
|
||||||
],
|
],
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ from typing import Any, Iterable
|
|||||||
|
|
||||||
BUILD_IDENTITY_SCHEMA_VERSION = "cw-camera-worker-build-identity-v1"
|
BUILD_IDENTITY_SCHEMA_VERSION = "cw-camera-worker-build-identity-v1"
|
||||||
BUILD_IDENTITY_FILE = "cw_camera_worker_build_identity.json"
|
BUILD_IDENTITY_FILE = "cw_camera_worker_build_identity.json"
|
||||||
WORKER_VERSION = "0.1.4"
|
WORKER_VERSION = "0.1.5"
|
||||||
|
|
||||||
|
|
||||||
def is_frozen_runtime() -> bool:
|
def is_frozen_runtime() -> bool:
|
||||||
|
|||||||
+127
-8
@@ -508,6 +508,14 @@ class CutPlannerConfig:
|
|||||||
preferred_bonus: float = 2.4
|
preferred_bonus: float = 2.4
|
||||||
same_audio_bonus: float = 2.8
|
same_audio_bonus: float = 2.8
|
||||||
history_beam_width: int = 4
|
history_beam_width: int = 4
|
||||||
|
# Authored timelines commonly hold their final camera through a long
|
||||||
|
# silent/idle export tail. Do not manufacture a fallback cut merely to
|
||||||
|
# satisfy the normal maximum shot duration once every causal activity
|
||||||
|
# signal has been inactive for at least two seconds. The threshold is
|
||||||
|
# intentionally near zero: this is an export-tail exception, not a way to
|
||||||
|
# lengthen quiet-but-active musical passages.
|
||||||
|
inactive_tail_minimum_frames: int = 120
|
||||||
|
inactive_tail_signal_threshold: float = 1e-6
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
integer_names = (
|
integer_names = (
|
||||||
@@ -518,6 +526,7 @@ class CutPlannerConfig:
|
|||||||
"ultrashort_frames",
|
"ultrashort_frames",
|
||||||
"short_tail_frames",
|
"short_tail_frames",
|
||||||
"history_beam_width",
|
"history_beam_width",
|
||||||
|
"inactive_tail_minimum_frames",
|
||||||
)
|
)
|
||||||
for name in integer_names:
|
for name in integer_names:
|
||||||
value = getattr(self, name)
|
value = getattr(self, name)
|
||||||
@@ -546,6 +555,7 @@ class CutPlannerConfig:
|
|||||||
"short_tail_cost",
|
"short_tail_cost",
|
||||||
"preferred_bonus",
|
"preferred_bonus",
|
||||||
"same_audio_bonus",
|
"same_audio_bonus",
|
||||||
|
"inactive_tail_signal_threshold",
|
||||||
):
|
):
|
||||||
value = float(getattr(self, name))
|
value = float(getattr(self, name))
|
||||||
if not math.isfinite(value) or value < 0.0:
|
if not math.isfinite(value) or value < 0.0:
|
||||||
@@ -760,6 +770,41 @@ def _robust_unit(values: np.ndarray) -> np.ndarray:
|
|||||||
return np.clip((values - low) / (high - low), 0.0, 1.0)
|
return np.clip((values - low) / (high - low), 0.0, 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def _inactive_tail_start(
|
||||||
|
signals: CutSignals,
|
||||||
|
config: CutPlannerConfig,
|
||||||
|
) -> int | None:
|
||||||
|
"""Return the first frame of a sufficiently long causal inactive suffix.
|
||||||
|
|
||||||
|
The suffix must contain no normalized onset, mel-novelty, performer-motion,
|
||||||
|
beat, or onset-peak evidence above a near-zero threshold. Requiring an
|
||||||
|
earlier active frame prevents an entirely empty/new input from silently
|
||||||
|
disabling the normal maximum-shot contract.
|
||||||
|
"""
|
||||||
|
|
||||||
|
activity = np.maximum.reduce(
|
||||||
|
(
|
||||||
|
_robust_unit(signals.onset_strength),
|
||||||
|
_robust_unit(signals.motion_strength),
|
||||||
|
_robust_unit(signals.mel_novelty),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if len(signals.beat_frames):
|
||||||
|
activity[np.asarray(signals.beat_frames, dtype=np.int64)] = 1.0
|
||||||
|
if len(signals.onset_peak_frames):
|
||||||
|
activity[np.asarray(signals.onset_peak_frames, dtype=np.int64)] = 1.0
|
||||||
|
active = np.flatnonzero(activity > config.inactive_tail_signal_threshold)
|
||||||
|
if not len(active):
|
||||||
|
return None
|
||||||
|
start = int(active[-1]) + 1
|
||||||
|
if (
|
||||||
|
start <= 0
|
||||||
|
or signals.frame_count - start < config.inactive_tail_minimum_frames
|
||||||
|
):
|
||||||
|
return None
|
||||||
|
return start
|
||||||
|
|
||||||
|
|
||||||
def _salient_peaks(
|
def _salient_peaks(
|
||||||
values: np.ndarray,
|
values: np.ndarray,
|
||||||
quantile: float,
|
quantile: float,
|
||||||
@@ -1546,6 +1591,15 @@ def _solve_global_dag(
|
|||||||
music_prefix = np.concatenate(([0.0], np.cumsum(music)))
|
music_prefix = np.concatenate(([0.0], np.cumsum(music)))
|
||||||
motion_prefix = np.concatenate(([0.0], np.cumsum(motion)))
|
motion_prefix = np.concatenate(([0.0], np.cumsum(motion)))
|
||||||
beat_proximity = _proximity(frames, signals.beat_frames, 30)
|
beat_proximity = _proximity(frames, signals.beat_frames, 30)
|
||||||
|
inactive_tail_start = _inactive_tail_start(signals, config)
|
||||||
|
protected_tail_frames = frozenset(
|
||||||
|
int(frame)
|
||||||
|
for frame in (
|
||||||
|
*directives.locked_frames,
|
||||||
|
*directives.preferred_frames,
|
||||||
|
*same_audio.tolist(),
|
||||||
|
)
|
||||||
|
)
|
||||||
q25 = _weighted_quantile(prior.duration_frames, prior.sample_weights, 0.25)
|
q25 = _weighted_quantile(prior.duration_frames, prior.sample_weights, 0.25)
|
||||||
q75 = _weighted_quantile(prior.duration_frames, prior.sample_weights, 0.75)
|
q75 = _weighted_quantile(prior.duration_frames, prior.sample_weights, 0.75)
|
||||||
density_scale = {
|
density_scale = {
|
||||||
@@ -1591,6 +1645,29 @@ def _solve_global_dag(
|
|||||||
destination_indices = np.arange(
|
destination_indices = np.arange(
|
||||||
start_index + 1, min(candidate_count, end_limit), dtype=np.int64
|
start_index + 1, min(candidate_count, end_limit), dtype=np.int64
|
||||||
)
|
)
|
||||||
|
endpoint_index = candidate_count - 1
|
||||||
|
endpoint_beyond_normal_limit = endpoint_index not in destination_indices
|
||||||
|
active_prefix_frames = (
|
||||||
|
max(0, inactive_tail_start - start_frame)
|
||||||
|
if inactive_tail_start is not None
|
||||||
|
else signals.frame_count
|
||||||
|
)
|
||||||
|
crosses_protected_tail_boundary = any(
|
||||||
|
start_frame < frame < signals.frame_count
|
||||||
|
for frame in protected_tail_frames
|
||||||
|
)
|
||||||
|
inactive_tail_extension_allowed = bool(
|
||||||
|
inactive_tail_start is not None
|
||||||
|
and endpoint_beyond_normal_limit
|
||||||
|
and (
|
||||||
|
start_frame <= inactive_tail_start
|
||||||
|
or start_frame in protected_tail_frames
|
||||||
|
)
|
||||||
|
and active_prefix_frames <= config.maximum_shot_frames
|
||||||
|
and not crosses_protected_tail_boundary
|
||||||
|
)
|
||||||
|
if inactive_tail_extension_allowed:
|
||||||
|
destination_indices = np.append(destination_indices, endpoint_index)
|
||||||
if next_lock is not None:
|
if next_lock is not None:
|
||||||
destination_indices = destination_indices[
|
destination_indices = destination_indices[
|
||||||
frames[destination_indices] <= next_lock
|
frames[destination_indices] <= next_lock
|
||||||
@@ -1606,23 +1683,41 @@ def _solve_global_dag(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
end_frames = frames[destination_indices]
|
end_frames = frames[destination_indices]
|
||||||
|
inactive_tail_extension_mask = (
|
||||||
|
(end_frames == signals.frame_count)
|
||||||
|
& (end_frames - start_frame > config.maximum_shot_frames)
|
||||||
|
& inactive_tail_extension_allowed
|
||||||
|
)
|
||||||
|
scoring_durations = durations.copy()
|
||||||
|
if np.any(inactive_tail_extension_mask):
|
||||||
|
scoring_durations[inactive_tail_extension_mask] = max(
|
||||||
|
config.minimum_shot_frames,
|
||||||
|
active_prefix_frames,
|
||||||
|
)
|
||||||
|
scoring_end_frames = end_frames.copy()
|
||||||
|
if np.any(inactive_tail_extension_mask):
|
||||||
|
scoring_end_frames[inactive_tail_extension_mask] = max(
|
||||||
|
start_frame + config.minimum_shot_frames,
|
||||||
|
int(inactive_tail_start),
|
||||||
|
)
|
||||||
segment_intensities = (
|
segment_intensities = (
|
||||||
intensity_prefix[end_frames] - intensity_prefix[start_frame]
|
intensity_prefix[scoring_end_frames] - intensity_prefix[start_frame]
|
||||||
) / durations
|
) / scoring_durations
|
||||||
segment_music = (
|
segment_music = (
|
||||||
music_prefix[end_frames] - music_prefix[start_frame]
|
music_prefix[scoring_end_frames] - music_prefix[start_frame]
|
||||||
) / durations
|
) / scoring_durations
|
||||||
segment_motion = (
|
segment_motion = (
|
||||||
motion_prefix[end_frames] - motion_prefix[start_frame]
|
motion_prefix[scoring_end_frames] - motion_prefix[start_frame]
|
||||||
) / durations
|
) / scoring_durations
|
||||||
target_durations = (
|
target_durations = (
|
||||||
q75 * (1.0 - segment_intensities) + q25 * segment_intensities
|
q75 * (1.0 - segment_intensities) + q25 * segment_intensities
|
||||||
) * density_scale
|
) * density_scale
|
||||||
duration_costs = -config.duration_prior_weight * duration_log_score[
|
duration_costs = -config.duration_prior_weight * duration_log_score[
|
||||||
durations - 1
|
scoring_durations - 1
|
||||||
]
|
]
|
||||||
density_costs = config.density_weight * np.square(
|
density_costs = config.density_weight * np.square(
|
||||||
(durations - target_durations) / np.maximum(target_durations, 1.0)
|
(scoring_durations - target_durations)
|
||||||
|
/ np.maximum(target_durations, 1.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
for state in tuple(sorted(start_states, key=_beam_state_key)):
|
for state in tuple(sorted(start_states, key=_beam_state_key)):
|
||||||
@@ -1720,6 +1815,15 @@ def _solve_global_dag(
|
|||||||
"startFrame": start_frame,
|
"startFrame": start_frame,
|
||||||
"endFrame": end_frame,
|
"endFrame": end_frame,
|
||||||
"durationFrames": duration,
|
"durationFrames": duration,
|
||||||
|
"durationScoringFrames": int(scoring_durations[offset]),
|
||||||
|
"inactiveTailContinuation": bool(
|
||||||
|
inactive_tail_extension_mask[offset]
|
||||||
|
),
|
||||||
|
"inactiveTailStartFrame": (
|
||||||
|
int(inactive_tail_start)
|
||||||
|
if inactive_tail_extension_mask[offset]
|
||||||
|
else None
|
||||||
|
),
|
||||||
"candidateReward": candidate_reward,
|
"candidateReward": candidate_reward,
|
||||||
"heuristicCandidateScore": (
|
"heuristicCandidateScore": (
|
||||||
0.0 if is_endpoint else float(heuristic[end_index])
|
0.0 if is_endpoint else float(heuristic[end_index])
|
||||||
@@ -1855,6 +1959,16 @@ def _solve_global_dag(
|
|||||||
"statesExpanded": states_expanded,
|
"statesExpanded": states_expanded,
|
||||||
"statesPruned": states_pruned,
|
"statesPruned": states_pruned,
|
||||||
},
|
},
|
||||||
|
"inactiveTail": {
|
||||||
|
"detected": inactive_tail_start is not None,
|
||||||
|
"startFrame": inactive_tail_start,
|
||||||
|
"minimumFrames": config.inactive_tail_minimum_frames,
|
||||||
|
"signalThreshold": config.inactive_tail_signal_threshold,
|
||||||
|
"continuationSelected": any(
|
||||||
|
bool(edge.get("inactiveTailContinuation", False))
|
||||||
|
for edge in selected_edges
|
||||||
|
),
|
||||||
|
},
|
||||||
"selectedCuts": selected_cuts,
|
"selectedCuts": selected_cuts,
|
||||||
"selectedEdges": selected_edges,
|
"selectedEdges": selected_edges,
|
||||||
"objectiveTotals": objective_totals,
|
"objectiveTotals": objective_totals,
|
||||||
@@ -1874,6 +1988,11 @@ def _solve_global_dag(
|
|||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
"sameAudioBoundaryCount": len(same_audio),
|
"sameAudioBoundaryCount": len(same_audio),
|
||||||
|
"inactiveTailStartFrame": inactive_tail_start,
|
||||||
|
"inactiveTailContinuationSelected": any(
|
||||||
|
bool(edge.get("inactiveTailContinuation", False))
|
||||||
|
for edge in selected_edges
|
||||||
|
),
|
||||||
"durationPrior": _duration_prior_summary(prior),
|
"durationPrior": _duration_prior_summary(prior),
|
||||||
"plannerConfig": {
|
"plannerConfig": {
|
||||||
name: getattr(config, name) for name in config.__dataclass_fields__
|
name: getattr(config, name) for name in config.__dataclass_fields__
|
||||||
|
|||||||
+1374
-89
File diff suppressed because it is too large
Load Diff
+111
-11
@@ -17,7 +17,18 @@ import numpy as np
|
|||||||
TRAJECTORY_QUALITY_VERSION = "trajectory-quality-v2"
|
TRAJECTORY_QUALITY_VERSION = "trajectory-quality-v2"
|
||||||
C2_RESAMPLING_POLICY_VERSION = "clamped-cubic-c2-endpoint-speed-guard-v1"
|
C2_RESAMPLING_POLICY_VERSION = "clamped-cubic-c2-endpoint-speed-guard-v1"
|
||||||
RADIAL_CLAMP_POLICY_VERSION = "quintic-bound-identity-c2-speed-adaptive-v2"
|
RADIAL_CLAMP_POLICY_VERSION = "quintic-bound-identity-c2-speed-adaptive-v2"
|
||||||
DYNAMICS_POLICY_VERSION = "translation-screen-space-cost-hard-gate-v2"
|
DYNAMICS_POLICY_VERSION = (
|
||||||
|
"camera-motion-stabilized-screen-residual-cost-hard-gate-v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
RAW_SUBJECT_SCREEN_INPUT = "raw_subject_uv"
|
||||||
|
STABILIZED_SCREEN_RESIDUAL_INPUT = "stabilized_residual_uv"
|
||||||
|
SCREEN_INPUT_KINDS = frozenset(
|
||||||
|
{
|
||||||
|
RAW_SUBJECT_SCREEN_INPUT,
|
||||||
|
STABILIZED_SCREEN_RESIDUAL_INPUT,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -43,7 +54,14 @@ class ResamplingGuardPolicy:
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class CandidateDynamicsPolicy:
|
class CandidateDynamicsPolicy:
|
||||||
"""Transparent scales and weights for a candidate dynamics cost."""
|
"""Transparent scales, free thresholds, and weights for soft ranking.
|
||||||
|
|
||||||
|
The free thresholds represent authored-safe motion that should not change
|
||||||
|
candidate ordering. Only dynamics above a threshold contribute to the
|
||||||
|
soft cost. Raw subject screen positions are diagnostic-only because they
|
||||||
|
contain performer motion; screen acceleration and jerk are scored only
|
||||||
|
when the caller explicitly supplies a stabilized semantic-target residual.
|
||||||
|
"""
|
||||||
|
|
||||||
translation_speed_p99_scale: float = 4.0
|
translation_speed_p99_scale: float = 4.0
|
||||||
translation_acceleration_p99_scale: float = 12.0
|
translation_acceleration_p99_scale: float = 12.0
|
||||||
@@ -53,14 +71,25 @@ class CandidateDynamicsPolicy:
|
|||||||
screen_acceleration_p99_scale: float = 12.0
|
screen_acceleration_p99_scale: float = 12.0
|
||||||
screen_jerk_p99_scale: float = 300.0
|
screen_jerk_p99_scale: float = 300.0
|
||||||
screen_horizontal_reversal_count_scale: float = 3.0
|
screen_horizontal_reversal_count_scale: float = 3.0
|
||||||
|
translation_speed_p99_free_threshold: float = 2.0
|
||||||
|
translation_acceleration_p99_free_threshold: float = 6.0
|
||||||
|
translation_jerk_p99_free_threshold: float = 120.0
|
||||||
|
lateral_reversal_count_free_threshold: float = 1.0
|
||||||
|
screen_speed_p99_free_threshold: float = 0.0
|
||||||
|
screen_acceleration_p99_free_threshold: float = 6.0
|
||||||
|
screen_jerk_p99_free_threshold: float = 180.0
|
||||||
|
screen_horizontal_reversal_count_free_threshold: float = 0.0
|
||||||
translation_speed_weight: float = 0.10
|
translation_speed_weight: float = 0.10
|
||||||
translation_acceleration_weight: float = 0.75
|
translation_acceleration_weight: float = 0.75
|
||||||
translation_jerk_weight: float = 1.00
|
translation_jerk_weight: float = 1.00
|
||||||
lateral_reversal_weight: float = 0.60
|
lateral_reversal_weight: float = 0.60
|
||||||
screen_speed_weight: float = 0.15
|
# Raw subject speed and reversal count mostly describe choreography, not a
|
||||||
|
# camera-body jolt. Keep both metrics in the audit payload but exclude
|
||||||
|
# them from the default soft score even for a residual input.
|
||||||
|
screen_speed_weight: float = 0.0
|
||||||
screen_acceleration_weight: float = 0.90
|
screen_acceleration_weight: float = 0.90
|
||||||
screen_jerk_weight: float = 1.20
|
screen_jerk_weight: float = 1.20
|
||||||
screen_horizontal_reversal_weight: float = 0.80
|
screen_horizontal_reversal_weight: float = 0.0
|
||||||
maximum_component_ratio: float = 8.0
|
maximum_component_ratio: float = 8.0
|
||||||
maximum_acceptable_cost: float = 6.0
|
maximum_acceptable_cost: float = 6.0
|
||||||
|
|
||||||
@@ -73,6 +102,8 @@ class CandidateDynamicsPolicy:
|
|||||||
raise ValueError(f"{name} must be positive")
|
raise ValueError(f"{name} must be positive")
|
||||||
if name.endswith("_weight") and value < 0.0:
|
if name.endswith("_weight") and value < 0.0:
|
||||||
raise ValueError(f"{name} must be non-negative")
|
raise ValueError(f"{name} must be non-negative")
|
||||||
|
if name.endswith("_free_threshold") and value < 0.0:
|
||||||
|
raise ValueError(f"{name} must be non-negative")
|
||||||
if self.maximum_component_ratio <= 0.0:
|
if self.maximum_component_ratio <= 0.0:
|
||||||
raise ValueError("maximum_component_ratio must be positive")
|
raise ValueError("maximum_component_ratio must be positive")
|
||||||
if self.maximum_acceptable_cost < 0.0:
|
if self.maximum_acceptable_cost < 0.0:
|
||||||
@@ -910,16 +941,27 @@ def candidate_dynamics_cost(
|
|||||||
screen_space_metrics: Mapping[str, object] | None = None,
|
screen_space_metrics: Mapping[str, object] | None = None,
|
||||||
*,
|
*,
|
||||||
policy: CandidateDynamicsPolicy | None = None,
|
policy: CandidateDynamicsPolicy | None = None,
|
||||||
|
screen_input_kind: str = RAW_SUBJECT_SCREEN_INPUT,
|
||||||
) -> tuple[float, dict[str, object]]:
|
) -> tuple[float, dict[str, object]]:
|
||||||
"""Return a transparent dynamics cost for a generated camera candidate.
|
"""Return a transparent dynamics cost for a generated camera candidate.
|
||||||
|
|
||||||
High-frequency acceleration, jerk, and persistent lateral reversals receive
|
High-frequency camera acceleration and jerk receive more weight than
|
||||||
more weight than steady speed. This allows deliberate low-frequency camera
|
authored-safe low-frequency travel. ``screen_space_metrics`` is scored
|
||||||
travel while making left/right chatter expensive.
|
only when ``screen_input_kind`` is ``stabilized_residual_uv``. That input
|
||||||
|
must be the projected semantic focus minus its stabilized desired UV path,
|
||||||
|
not the raw projected performer position. Raw subject UV remains in the
|
||||||
|
returned audit but contributes no soft cost, so choreography is not
|
||||||
|
mistaken for camera shake. This function does not change any downstream
|
||||||
|
hard safety gate or the metrics those gates consume.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
selected_policy = policy or CandidateDynamicsPolicy()
|
selected_policy = policy or CandidateDynamicsPolicy()
|
||||||
selected_policy.validate()
|
selected_policy.validate()
|
||||||
|
if screen_input_kind not in SCREEN_INPUT_KINDS:
|
||||||
|
raise ValueError(
|
||||||
|
"screen_input_kind must be raw_subject_uv or "
|
||||||
|
"stabilized_residual_uv"
|
||||||
|
)
|
||||||
raw_components = [
|
raw_components = [
|
||||||
(
|
(
|
||||||
"translationSpeedP99",
|
"translationSpeedP99",
|
||||||
@@ -930,6 +972,8 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
selected_policy.translation_speed_p99_scale,
|
selected_policy.translation_speed_p99_scale,
|
||||||
selected_policy.translation_speed_weight,
|
selected_policy.translation_speed_weight,
|
||||||
|
selected_policy.translation_speed_p99_free_threshold,
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"translationAccelerationP99",
|
"translationAccelerationP99",
|
||||||
@@ -940,6 +984,8 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
selected_policy.translation_acceleration_p99_scale,
|
selected_policy.translation_acceleration_p99_scale,
|
||||||
selected_policy.translation_acceleration_weight,
|
selected_policy.translation_acceleration_weight,
|
||||||
|
selected_policy.translation_acceleration_p99_free_threshold,
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"translationJerkP99",
|
"translationJerkP99",
|
||||||
@@ -950,12 +996,16 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
selected_policy.translation_jerk_p99_scale,
|
selected_policy.translation_jerk_p99_scale,
|
||||||
selected_policy.translation_jerk_weight,
|
selected_policy.translation_jerk_weight,
|
||||||
|
selected_policy.translation_jerk_p99_free_threshold,
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"persistentLateralReversalCount",
|
"persistentLateralReversalCount",
|
||||||
float(translation_metrics.get("persistentLateralReversalCount", 0)),
|
float(translation_metrics.get("persistentLateralReversalCount", 0)),
|
||||||
selected_policy.lateral_reversal_count_scale,
|
selected_policy.lateral_reversal_count_scale,
|
||||||
selected_policy.lateral_reversal_weight,
|
selected_policy.lateral_reversal_weight,
|
||||||
|
selected_policy.lateral_reversal_count_free_threshold,
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
if screen_space_metrics is not None:
|
if screen_space_metrics is not None:
|
||||||
@@ -970,6 +1020,8 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
selected_policy.screen_speed_p99_scale,
|
selected_policy.screen_speed_p99_scale,
|
||||||
selected_policy.screen_speed_weight,
|
selected_policy.screen_speed_weight,
|
||||||
|
selected_policy.screen_speed_p99_free_threshold,
|
||||||
|
True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"screenAccelerationP99",
|
"screenAccelerationP99",
|
||||||
@@ -980,6 +1032,8 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
selected_policy.screen_acceleration_p99_scale,
|
selected_policy.screen_acceleration_p99_scale,
|
||||||
selected_policy.screen_acceleration_weight,
|
selected_policy.screen_acceleration_weight,
|
||||||
|
selected_policy.screen_acceleration_p99_free_threshold,
|
||||||
|
True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"screenJerkP99",
|
"screenJerkP99",
|
||||||
@@ -990,6 +1044,8 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
selected_policy.screen_jerk_p99_scale,
|
selected_policy.screen_jerk_p99_scale,
|
||||||
selected_policy.screen_jerk_weight,
|
selected_policy.screen_jerk_weight,
|
||||||
|
selected_policy.screen_jerk_p99_free_threshold,
|
||||||
|
True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"persistentHorizontalReversalCount",
|
"persistentHorizontalReversalCount",
|
||||||
@@ -1001,23 +1057,46 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
selected_policy.screen_horizontal_reversal_count_scale,
|
selected_policy.screen_horizontal_reversal_count_scale,
|
||||||
selected_policy.screen_horizontal_reversal_weight,
|
selected_policy.screen_horizontal_reversal_weight,
|
||||||
|
(
|
||||||
|
selected_policy
|
||||||
|
.screen_horizontal_reversal_count_free_threshold
|
||||||
|
),
|
||||||
|
True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
components: dict[str, dict[str, float]] = {}
|
components: dict[str, dict[str, float | bool]] = {}
|
||||||
total_cost = 0.0
|
total_cost = 0.0
|
||||||
for name, value, scale, weight in raw_components:
|
score_screen_residual = (
|
||||||
|
screen_input_kind == STABILIZED_SCREEN_RESIDUAL_INPUT
|
||||||
|
)
|
||||||
|
for name, value, scale, configured_weight, free_threshold, is_screen in (
|
||||||
|
raw_components
|
||||||
|
):
|
||||||
if not np.isfinite(value) or value < 0.0:
|
if not np.isfinite(value) or value < 0.0:
|
||||||
raise ValueError(f"candidate dynamics component {name} is invalid")
|
raise ValueError(f"candidate dynamics component {name} is invalid")
|
||||||
normalized = min(value / scale, selected_policy.maximum_component_ratio)
|
weight = (
|
||||||
|
configured_weight
|
||||||
|
if not is_screen or score_screen_residual
|
||||||
|
else 0.0
|
||||||
|
)
|
||||||
|
excess = max(0.0, value - free_threshold)
|
||||||
|
normalized = min(
|
||||||
|
excess / scale,
|
||||||
|
selected_policy.maximum_component_ratio,
|
||||||
|
)
|
||||||
contribution = weight * normalized**2
|
contribution = weight * normalized**2
|
||||||
components[name] = {
|
components[name] = {
|
||||||
"value": float(value),
|
"value": float(value),
|
||||||
"scale": float(scale),
|
"scale": float(scale),
|
||||||
"weight": float(weight),
|
"weight": float(weight),
|
||||||
|
"configuredWeight": float(configured_weight),
|
||||||
|
"freeThreshold": float(free_threshold),
|
||||||
|
"excessAboveFreeThreshold": float(excess),
|
||||||
"normalized": float(normalized),
|
"normalized": float(normalized),
|
||||||
"cost": float(contribution),
|
"cost": float(contribution),
|
||||||
|
"screenInputSuppressed": bool(is_screen and not score_screen_residual),
|
||||||
}
|
}
|
||||||
total_cost += contribution
|
total_cost += contribution
|
||||||
|
|
||||||
@@ -1031,6 +1110,17 @@ def candidate_dynamics_cost(
|
|||||||
),
|
),
|
||||||
"components": components,
|
"components": components,
|
||||||
"screenSpaceIncluded": screen_space_metrics is not None,
|
"screenSpaceIncluded": screen_space_metrics is not None,
|
||||||
|
"screenSpaceInputKind": (
|
||||||
|
screen_input_kind if screen_space_metrics is not None else None
|
||||||
|
),
|
||||||
|
"screenSpaceSoftCostEnabled": bool(
|
||||||
|
screen_space_metrics is not None and score_screen_residual
|
||||||
|
),
|
||||||
|
"screenSpaceInputContract": (
|
||||||
|
"Score only stabilized semantic-focus residual UV: "
|
||||||
|
"projected_focus_uv - stabilized_desired_uv. Raw subject UV is "
|
||||||
|
"diagnostic-only and is excluded from soft candidate ranking."
|
||||||
|
),
|
||||||
"policy": asdict(selected_policy),
|
"policy": asdict(selected_policy),
|
||||||
}
|
}
|
||||||
return float(total_cost), details
|
return float(total_cost), details
|
||||||
@@ -1042,8 +1132,15 @@ def evaluate_candidate_dynamics(
|
|||||||
*,
|
*,
|
||||||
sample_rate: float = 60.0,
|
sample_rate: float = 60.0,
|
||||||
policy: CandidateDynamicsPolicy | None = None,
|
policy: CandidateDynamicsPolicy | None = None,
|
||||||
|
screen_input_kind: str = RAW_SUBJECT_SCREEN_INPUT,
|
||||||
) -> dict[str, object]:
|
) -> dict[str, object]:
|
||||||
"""Measure and score a candidate in one call."""
|
"""Measure and score a candidate in one call.
|
||||||
|
|
||||||
|
``screen_positions_uv`` defaults to ``raw_subject_uv`` and is then kept
|
||||||
|
only for diagnostics and downstream hard-gate auditing. A caller that
|
||||||
|
wants screen-space soft scoring must pass a stabilized semantic-focus
|
||||||
|
residual and set ``screen_input_kind="stabilized_residual_uv"``.
|
||||||
|
"""
|
||||||
|
|
||||||
translation = translation_dynamics_metrics(
|
translation = translation_dynamics_metrics(
|
||||||
positions_meters,
|
positions_meters,
|
||||||
@@ -1063,6 +1160,7 @@ def evaluate_candidate_dynamics(
|
|||||||
translation,
|
translation,
|
||||||
screen,
|
screen,
|
||||||
policy=policy,
|
policy=policy,
|
||||||
|
screen_input_kind=screen_input_kind,
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
**cost_details,
|
**cost_details,
|
||||||
@@ -1075,6 +1173,8 @@ __all__ = [
|
|||||||
"C2_RESAMPLING_POLICY_VERSION",
|
"C2_RESAMPLING_POLICY_VERSION",
|
||||||
"DYNAMICS_POLICY_VERSION",
|
"DYNAMICS_POLICY_VERSION",
|
||||||
"RADIAL_CLAMP_POLICY_VERSION",
|
"RADIAL_CLAMP_POLICY_VERSION",
|
||||||
|
"RAW_SUBJECT_SCREEN_INPUT",
|
||||||
|
"STABILIZED_SCREEN_RESIDUAL_INPUT",
|
||||||
"TRAJECTORY_QUALITY_VERSION",
|
"TRAJECTORY_QUALITY_VERSION",
|
||||||
"CandidateDynamicsPolicy",
|
"CandidateDynamicsPolicy",
|
||||||
"ResamplingGuardPolicy",
|
"ResamplingGuardPolicy",
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
pip
|
||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
Metadata-Version: 2.1
|
||||||
|
Name: importlib_metadata
|
||||||
|
Version: 8.0.0
|
||||||
|
Summary: Read metadata from Python packages
|
||||||
|
Author-email: "Jason R. Coombs" <jaraco@jaraco.com>
|
||||||
|
Project-URL: Source, https://github.com/python/importlib_metadata
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: Apache Software License
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3 :: Only
|
||||||
|
Requires-Python: >=3.8
|
||||||
|
Description-Content-Type: text/x-rst
|
||||||
|
License-File: LICENSE
|
||||||
|
Requires-Dist: zipp >=0.5
|
||||||
|
Requires-Dist: typing-extensions >=3.6.4 ; python_version < "3.8"
|
||||||
|
Provides-Extra: doc
|
||||||
|
Requires-Dist: sphinx >=3.5 ; extra == 'doc'
|
||||||
|
Requires-Dist: jaraco.packaging >=9.3 ; extra == 'doc'
|
||||||
|
Requires-Dist: rst.linker >=1.9 ; extra == 'doc'
|
||||||
|
Requires-Dist: furo ; extra == 'doc'
|
||||||
|
Requires-Dist: sphinx-lint ; extra == 'doc'
|
||||||
|
Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'doc'
|
||||||
|
Provides-Extra: perf
|
||||||
|
Requires-Dist: ipython ; extra == 'perf'
|
||||||
|
Provides-Extra: test
|
||||||
|
Requires-Dist: pytest !=8.1.*,>=6 ; extra == 'test'
|
||||||
|
Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'test'
|
||||||
|
Requires-Dist: pytest-cov ; extra == 'test'
|
||||||
|
Requires-Dist: pytest-mypy ; extra == 'test'
|
||||||
|
Requires-Dist: pytest-enabler >=2.2 ; extra == 'test'
|
||||||
|
Requires-Dist: pytest-ruff >=0.2.1 ; extra == 'test'
|
||||||
|
Requires-Dist: packaging ; extra == 'test'
|
||||||
|
Requires-Dist: pyfakefs ; extra == 'test'
|
||||||
|
Requires-Dist: flufl.flake8 ; extra == 'test'
|
||||||
|
Requires-Dist: pytest-perf >=0.9.2 ; extra == 'test'
|
||||||
|
Requires-Dist: jaraco.test >=5.4 ; extra == 'test'
|
||||||
|
Requires-Dist: importlib-resources >=1.3 ; (python_version < "3.9") and extra == 'test'
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg
|
||||||
|
:target: https://pypi.org/project/importlib_metadata
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg
|
||||||
|
|
||||||
|
.. image:: https://github.com/python/importlib_metadata/actions/workflows/main.yml/badge.svg
|
||||||
|
:target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22
|
||||||
|
:alt: tests
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
|
||||||
|
:target: https://github.com/astral-sh/ruff
|
||||||
|
:alt: Ruff
|
||||||
|
|
||||||
|
.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest
|
||||||
|
:target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/badge/skeleton-2024-informational
|
||||||
|
:target: https://blog.jaraco.com/skeleton
|
||||||
|
|
||||||
|
.. image:: https://tidelift.com/badges/package/pypi/importlib-metadata
|
||||||
|
:target: https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=readme
|
||||||
|
|
||||||
|
Library to access the metadata for a Python package.
|
||||||
|
|
||||||
|
This package supplies third-party access to the functionality of
|
||||||
|
`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
|
||||||
|
including improvements added to subsequent Python versions.
|
||||||
|
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
=============
|
||||||
|
|
||||||
|
New features are introduced in this third-party library and later merged
|
||||||
|
into CPython. The following table indicates which versions of this library
|
||||||
|
were contributed to different versions in the standard library:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - importlib_metadata
|
||||||
|
- stdlib
|
||||||
|
* - 7.0
|
||||||
|
- 3.13
|
||||||
|
* - 6.5
|
||||||
|
- 3.12
|
||||||
|
* - 4.13
|
||||||
|
- 3.11
|
||||||
|
* - 4.6
|
||||||
|
- 3.10
|
||||||
|
* - 1.4
|
||||||
|
- 3.8
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
See the `online documentation <https://importlib-metadata.readthedocs.io/>`_
|
||||||
|
for usage details.
|
||||||
|
|
||||||
|
`Finder authors
|
||||||
|
<https://docs.python.org/3/reference/import.html#finders-and-loaders>`_ can
|
||||||
|
also add support for custom package installers. See the above documentation
|
||||||
|
for details.
|
||||||
|
|
||||||
|
|
||||||
|
Caveats
|
||||||
|
=======
|
||||||
|
|
||||||
|
This project primarily supports third-party packages installed by PyPA
|
||||||
|
tools (or other conforming packages). It does not support:
|
||||||
|
|
||||||
|
- Packages in the stdlib.
|
||||||
|
- Packages installed without metadata.
|
||||||
|
|
||||||
|
Project details
|
||||||
|
===============
|
||||||
|
|
||||||
|
* Project home: https://github.com/python/importlib_metadata
|
||||||
|
* Report bugs at: https://github.com/python/importlib_metadata/issues
|
||||||
|
* Code hosting: https://github.com/python/importlib_metadata
|
||||||
|
* Documentation: https://importlib-metadata.readthedocs.io/
|
||||||
|
|
||||||
|
For Enterprise
|
||||||
|
==============
|
||||||
|
|
||||||
|
Available as part of the Tidelift Subscription.
|
||||||
|
|
||||||
|
This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
|
||||||
|
|
||||||
|
`Learn more <https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=referral&utm_campaign=github>`_.
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
importlib_metadata-8.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
importlib_metadata-8.0.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
||||||
|
importlib_metadata-8.0.0.dist-info/METADATA,sha256=anuQ7_7h4J1bSEzfcjIBakPi2cyVQ7y7jklLHsBeH1k,4648
|
||||||
|
importlib_metadata-8.0.0.dist-info/RECORD,,
|
||||||
|
importlib_metadata-8.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
importlib_metadata-8.0.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
|
||||||
|
importlib_metadata-8.0.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
|
||||||
|
importlib_metadata/__init__.py,sha256=tZNB-23h8Bixi9uCrQqj9Yf0aeC--Josdy3IZRIQeB0,33798
|
||||||
|
importlib_metadata/__pycache__/__init__.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/_adapters.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/_collections.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/_compat.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/_functools.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/_itertools.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/_meta.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/_text.cpython-312.pyc,,
|
||||||
|
importlib_metadata/__pycache__/diagnose.cpython-312.pyc,,
|
||||||
|
importlib_metadata/_adapters.py,sha256=rIhWTwBvYA1bV7i-5FfVX38qEXDTXFeS5cb5xJtP3ks,2317
|
||||||
|
importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743
|
||||||
|
importlib_metadata/_compat.py,sha256=73QKrN9KNoaZzhbX5yPCCZa-FaALwXe8TPlDR72JgBU,1314
|
||||||
|
importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895
|
||||||
|
importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068
|
||||||
|
importlib_metadata/_meta.py,sha256=nxZ7C8GVlcBFAKWyVOn_dn7ot_twBcbm1NmvjIetBHI,1801
|
||||||
|
importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
|
||||||
|
importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
importlib_metadata/compat/__pycache__/__init__.cpython-312.pyc,,
|
||||||
|
importlib_metadata/compat/__pycache__/py311.cpython-312.pyc,,
|
||||||
|
importlib_metadata/compat/__pycache__/py39.cpython-312.pyc,,
|
||||||
|
importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608
|
||||||
|
importlib_metadata/compat/py39.py,sha256=cPkMv6-0ilK-0Jw_Tkn0xYbOKJZc4WJKQHow0c2T44w,1102
|
||||||
|
importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379
|
||||||
|
importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: setuptools (70.1.1)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
importlib_metadata
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
pip
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2012 Daniel Holth <dholth@fastmail.fm> and contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
Metadata-Version: 2.3
|
||||||
|
Name: wheel
|
||||||
|
Version: 0.45.1
|
||||||
|
Summary: A built-package format for Python
|
||||||
|
Keywords: wheel,packaging
|
||||||
|
Author-email: Daniel Holth <dholth@fastmail.fm>
|
||||||
|
Maintainer-email: Alex Grönholm <alex.gronholm@nextday.fi>
|
||||||
|
Requires-Python: >=3.8
|
||||||
|
Description-Content-Type: text/x-rst
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: Topic :: System :: Archiving :: Packaging
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 3 :: Only
|
||||||
|
Classifier: Programming Language :: Python :: 3.8
|
||||||
|
Classifier: Programming Language :: Python :: 3.9
|
||||||
|
Classifier: Programming Language :: Python :: 3.10
|
||||||
|
Classifier: Programming Language :: Python :: 3.11
|
||||||
|
Classifier: Programming Language :: Python :: 3.12
|
||||||
|
Requires-Dist: pytest >= 6.0.0 ; extra == "test"
|
||||||
|
Requires-Dist: setuptools >= 65 ; extra == "test"
|
||||||
|
Project-URL: Changelog, https://wheel.readthedocs.io/en/stable/news.html
|
||||||
|
Project-URL: Documentation, https://wheel.readthedocs.io/
|
||||||
|
Project-URL: Issue Tracker, https://github.com/pypa/wheel/issues
|
||||||
|
Project-URL: Source, https://github.com/pypa/wheel
|
||||||
|
Provides-Extra: test
|
||||||
|
|
||||||
|
wheel
|
||||||
|
=====
|
||||||
|
|
||||||
|
This is a command line tool for manipulating Python wheel files, as defined in
|
||||||
|
`PEP 427`_. It contains the following functionality:
|
||||||
|
|
||||||
|
* Convert ``.egg`` archives into ``.whl``
|
||||||
|
* Unpack wheel archives
|
||||||
|
* Repack wheel archives
|
||||||
|
* Add or remove tags in existing wheel archives
|
||||||
|
|
||||||
|
.. _PEP 427: https://www.python.org/dev/peps/pep-0427/
|
||||||
|
|
||||||
|
Historical note
|
||||||
|
---------------
|
||||||
|
|
||||||
|
This project used to contain the implementation of the setuptools_ ``bdist_wheel``
|
||||||
|
command, but as of setuptools v70.1, it no longer needs ``wheel`` installed for that to
|
||||||
|
work. Thus, you should install this **only** if you intend to use the ``wheel`` command
|
||||||
|
line tool!
|
||||||
|
|
||||||
|
.. _setuptools: https://pypi.org/project/setuptools/
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The documentation_ can be found on Read The Docs.
|
||||||
|
|
||||||
|
.. _documentation: https://wheel.readthedocs.io/
|
||||||
|
|
||||||
|
Code of Conduct
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Everyone interacting in the wheel project's codebases, issue trackers, chat
|
||||||
|
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
|
||||||
|
|
||||||
|
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
../../Scripts/wheel.exe,sha256=A69vg7y22iGuDMaqOWKEJzU_3jcjugdke2okLg6B5Cg,108448
|
||||||
|
wheel-0.45.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
wheel-0.45.1.dist-info/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107
|
||||||
|
wheel-0.45.1.dist-info/METADATA,sha256=mKz84H7m7jsxJyzeIcTVORiTb0NPMV39KvOIYhGgmjA,2313
|
||||||
|
wheel-0.45.1.dist-info/RECORD,,
|
||||||
|
wheel-0.45.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
wheel-0.45.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
||||||
|
wheel-0.45.1.dist-info/entry_points.txt,sha256=rTY1BbkPHhkGMm4Q3F0pIzJBzW2kMxoG1oriffvGdA0,104
|
||||||
|
wheel/__init__.py,sha256=mrxMnvdXACur_LWegbUfh5g5ysWZrd63UJn890wvGNk,59
|
||||||
|
wheel/__main__.py,sha256=NkMUnuTCGcOkgY0IBLgBCVC_BGGcWORx2K8jYGS12UE,455
|
||||||
|
wheel/__pycache__/__init__.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/__main__.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/_bdist_wheel.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/_setuptools_logging.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/bdist_wheel.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/macosx_libfile.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/metadata.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/util.cpython-312.pyc,,
|
||||||
|
wheel/__pycache__/wheelfile.cpython-312.pyc,,
|
||||||
|
wheel/_bdist_wheel.py,sha256=UghCQjSH_pVfcZh6oRjzSw_TQhcf3anSx1OkiLSL82M,21694
|
||||||
|
wheel/_setuptools_logging.py,sha256=-5KC-lne0ilOUWIDfOkqapUWGMFZhuKYDIavIZiB5kM,781
|
||||||
|
wheel/bdist_wheel.py,sha256=tpf9WufiSO1RuEMg5oPhIfSG8DMziCZ_4muCKF69Cqo,1107
|
||||||
|
wheel/cli/__init__.py,sha256=Npq6_jKi03dhIcRnmbuFhwviVJxwO0tYEnEhWMv9cJo,4402
|
||||||
|
wheel/cli/__pycache__/__init__.cpython-312.pyc,,
|
||||||
|
wheel/cli/__pycache__/convert.cpython-312.pyc,,
|
||||||
|
wheel/cli/__pycache__/pack.cpython-312.pyc,,
|
||||||
|
wheel/cli/__pycache__/tags.cpython-312.pyc,,
|
||||||
|
wheel/cli/__pycache__/unpack.cpython-312.pyc,,
|
||||||
|
wheel/cli/convert.py,sha256=Bi0ntEXb9nTllCxWeTRQ4j-nPs3szWSEKipG_GgnMkQ,12634
|
||||||
|
wheel/cli/pack.py,sha256=CAFcHdBVulvsHYJlndKVO7KMI9JqBTZz5ii0PKxxCOs,3103
|
||||||
|
wheel/cli/tags.py,sha256=lHw-LaWrkS5Jy_qWcw-6pSjeNM6yAjDnqKI3E5JTTCU,4760
|
||||||
|
wheel/cli/unpack.py,sha256=Y_J7ynxPSoFFTT7H0fMgbBlVErwyDGcObgme5MBuz58,1021
|
||||||
|
wheel/macosx_libfile.py,sha256=k1x7CE3LPtOVGqj6NXQ1nTGYVPaeRrhVzUG_KPq3zDs,16572
|
||||||
|
wheel/metadata.py,sha256=JC4p7jlQZu2bUTAQ2fevkqLjg_X6gnNyRhLn6OUO1tc,6171
|
||||||
|
wheel/util.py,sha256=aL7aibHwYUgfc8WlolL5tXdkV4DatbJxZHb1kwHFJAU,423
|
||||||
|
wheel/vendored/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
wheel/vendored/__pycache__/__init__.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
|
||||||
|
wheel/vendored/packaging/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
|
||||||
|
wheel/vendored/packaging/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
|
||||||
|
wheel/vendored/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
wheel/vendored/packaging/__pycache__/__init__.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/_elffile.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/_manylinux.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/_musllinux.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/_parser.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/_structures.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/_tokenizer.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/markers.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/requirements.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/specifiers.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/tags.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/utils.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/__pycache__/version.cpython-312.pyc,,
|
||||||
|
wheel/vendored/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266
|
||||||
|
wheel/vendored/packaging/_manylinux.py,sha256=P7sdR5_7XBY09LVYYPhHmydMJIIwPXWsh4olk74Uuj4,9588
|
||||||
|
wheel/vendored/packaging/_musllinux.py,sha256=z1s8To2hQ0vpn_d-O2i5qxGwEK8WmGlLt3d_26V7NeY,2674
|
||||||
|
wheel/vendored/packaging/_parser.py,sha256=4tT4emSl2qTaU7VTQE1Xa9o1jMPCsBezsYBxyNMUN-s,10347
|
||||||
|
wheel/vendored/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
|
||||||
|
wheel/vendored/packaging/_tokenizer.py,sha256=alCtbwXhOFAmFGZ6BQ-wCTSFoRAJ2z-ysIf7__MTJ_k,5292
|
||||||
|
wheel/vendored/packaging/markers.py,sha256=_TSPI1BhJYO7Bp9AzTmHQxIqHEVXaTjmDh9G-w8qzPA,8232
|
||||||
|
wheel/vendored/packaging/requirements.py,sha256=dgoBeVprPu2YE6Q8nGfwOPTjATHbRa_ZGLyXhFEln6Q,2933
|
||||||
|
wheel/vendored/packaging/specifiers.py,sha256=IWSt0SrLSP72heWhAC8UL0eGvas7XIQHjqiViVfmPKE,39778
|
||||||
|
wheel/vendored/packaging/tags.py,sha256=fedHXiOHkBxNZTXotXv8uXPmMFU9ae-TKBujgYHigcA,18950
|
||||||
|
wheel/vendored/packaging/utils.py,sha256=XgdmP3yx9-wQEFjO7OvMj9RjEf5JlR5HFFR69v7SQ9E,5268
|
||||||
|
wheel/vendored/packaging/version.py,sha256=PFJaYZDxBgyxkfYhH3SQw4qfE9ICCWrTmitvq14y3bs,16234
|
||||||
|
wheel/vendored/vendor.txt,sha256=Z2ENjB1i5prfez8CdM1Sdr3c6Zxv2rRRolMpLmBncAE,16
|
||||||
|
wheel/wheelfile.py,sha256=USCttNlJwafxt51YYFFKG7jnxz8dfhbyqAZL6jMTA9s,8411
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: flit 3.10.1
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[console_scripts]
|
||||||
|
wheel=wheel.cli:main
|
||||||
|
|
||||||
|
[distutils.commands]
|
||||||
|
bdist_wheel=wheel.bdist_wheel:bdist_wheel
|
||||||
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": "cw-camera-worker-distribution-build-v1",
|
||||||
|
"createdUtc": "2026-08-08T20:03:52.1316389Z",
|
||||||
|
"workerVersion": "0.1.5",
|
||||||
|
"protocolVersion": "1",
|
||||||
|
"buildIdentitySchemaVersion": "cw-camera-worker-build-identity-v1",
|
||||||
|
"buildEnvironment": {
|
||||||
|
"kind": "disposable_venv",
|
||||||
|
"pythonVersion": "3.12.13",
|
||||||
|
"isolatedPrefixVerified": true,
|
||||||
|
"userSiteEnabled": false,
|
||||||
|
"threadLimits": {
|
||||||
|
"OMP_NUM_THREADS": "1",
|
||||||
|
"OPENBLAS_NUM_THREADS": "1",
|
||||||
|
"MKL_NUM_THREADS": "1",
|
||||||
|
"NUMEXPR_NUM_THREADS": "1",
|
||||||
|
"VECLIB_MAXIMUM_THREADS": "1",
|
||||||
|
"BLIS_NUM_THREADS": "1"
|
||||||
|
},
|
||||||
|
"installedDistributions": [
|
||||||
|
"altgraph==0.17.5",
|
||||||
|
"audioread==3.1.0",
|
||||||
|
"certifi==2024.12.14",
|
||||||
|
"cffi==2.0.0",
|
||||||
|
"charset-normalizer==3.4.1",
|
||||||
|
"decorator==5.1.1",
|
||||||
|
"idna==3.10",
|
||||||
|
"joblib==1.5.2",
|
||||||
|
"lazy_loader==0.4",
|
||||||
|
"librosa==0.11.0",
|
||||||
|
"llvmlite==0.43.0",
|
||||||
|
"msgpack==1.1.2",
|
||||||
|
"numba==0.60.0",
|
||||||
|
"numpy==1.26.4",
|
||||||
|
"packaging==26.2",
|
||||||
|
"pefile==2024.8.26",
|
||||||
|
"pip==25.0.1",
|
||||||
|
"platformdirs==4.9.6",
|
||||||
|
"pooch==1.8.2",
|
||||||
|
"pycparser==2.21",
|
||||||
|
"pyinstaller==6.17.0",
|
||||||
|
"pyinstaller-hooks-contrib==2025.10",
|
||||||
|
"pywin32-ctypes==0.2.3",
|
||||||
|
"requests==2.32.3",
|
||||||
|
"scikit-learn==1.7.2",
|
||||||
|
"scipy==1.15.3",
|
||||||
|
"setuptools==78.1.0",
|
||||||
|
"soundfile==0.13.1",
|
||||||
|
"soxr==1.0.0",
|
||||||
|
"threadpoolctl==3.6.0",
|
||||||
|
"typing_extensions==4.15.0",
|
||||||
|
"urllib3==2.3.0",
|
||||||
|
"wheel==0.45.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"requirements": [
|
||||||
|
{
|
||||||
|
"path": "MachineLearning/CameraDirector/requirements-worker.txt",
|
||||||
|
"sha256": "90987782184e15119a6090922e464559e620b6b164b80b5ae4f567e8ae6a7b9d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "MachineLearning/CameraDirector/requirements-worker-build.txt",
|
||||||
|
"sha256": "1ae29e0b81e82bdcbcb14d1817813e5f60e45ae260af0e693a66cc24f3ae6827"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payload": {
|
||||||
|
"fileCount": 646,
|
||||||
|
"bytes": 267603627,
|
||||||
|
"executableSha256": "055fb16b60454b0d131ed2201eccad8d11977bd99ef011468d44638b33e4a1ef",
|
||||||
|
"buildIdentitySha256": "84428e90a523ec40aa54d45c7b795e586979fce6f4a0e139e0bd83fc3c0ce906",
|
||||||
|
"sanityRange": {
|
||||||
|
"minimumFileCount": 500,
|
||||||
|
"maximumFileCount": 1000,
|
||||||
|
"minimumBytes": 209715200,
|
||||||
|
"maximumBytes": 393216000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "com.mingle.cw-ai",
|
"name": "com.mingle.cw-ai",
|
||||||
"version": "0.4.8",
|
"version": "0.4.9",
|
||||||
"displayName": "Mingle Camera Work AI",
|
"displayName": "Mingle Camera Work AI",
|
||||||
"description": "Self-contained high-quality Unity Timeline camera generation with an embedded prepared reference library, per-shot editable clips, and A/B review tools.",
|
"description": "Self-contained high-quality Unity Timeline camera generation with an embedded prepared reference library, per-shot editable clips, and A/B review tools.",
|
||||||
"unity": "6000.0",
|
"unity": "6000.0",
|
||||||
|
|||||||
@@ -37,16 +37,16 @@ Install **Git and Git LFS before opening Unity**, then use **Add package from
|
|||||||
git URL** with:
|
git URL** with:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.6
|
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.15
|
||||||
```
|
```
|
||||||
|
|
||||||
The Camera AI package includes the complete Windows x64
|
The Camera AI package includes the complete Windows x64
|
||||||
`CWCameraWorker.exe` onedir build, so artist workstations do not need Python.
|
`CWCameraWorker.exe` onedir build and a read-only prepared reference library,
|
||||||
The executable must remain beside its `_internal` directory. Authored camera,
|
so artist workstations do not need Python or a separate `CW-AI` checkout.
|
||||||
character-motion, and source-audio reference data are not mirrored in this
|
The executable must remain beside its `_internal` directory. A newer
|
||||||
utility repository; connect an access-controlled `CW-AI` data root containing
|
access-controlled `CW-AI` data root containing `DatasetExports`,
|
||||||
`DatasetExports`, `reports/training_index.json`, and
|
`reports/training_index.json`, and `models/cut_ranker_v2.json` remains an
|
||||||
`models/cut_ranker_v2.json`.
|
optional override.
|
||||||
|
|
||||||
After installation, open `Tools > Streamingle > AI 카메라 생성`. The package
|
After installation, open `Tools > Streamingle > AI 카메라 생성`. The package
|
||||||
automatically locates its bundled worker and validates the configured data
|
automatically locates its bundled worker and validates the configured data
|
||||||
|
|||||||
Reference in New Issue
Block a user