release: camera AI 0.4.9 worker 0.1.5
This commit is contained in:
parent
9a7e0a3fd0
commit
5a2e95485e
@ -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
|
||||||
|
|||||||
@ -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` 환경 변수로
|
||||||
|
|||||||
BIN
CameraAI~/Tools~/CWCameraWorker/CWCameraWorker.exe
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/CWCameraWorker.exe
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/_cffi_backend.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/_cffi_backend.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/_elementtree.pyd
(Stored with Git LFS)
Normal file
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/_elementtree.pyd
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/ada92cb5d92a588d1b93__mypyc.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/ada92cb5d92a588d1b93__mypyc.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/charset_normalizer/cd.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/charset_normalizer/cd.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/charset_normalizer/md.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/charset_normalizer/md.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/charset_normalizer/md__mypyc.cp312-win_amd64.pyd
(Stored with Git LFS)
Normal file
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/charset_normalizer/md__mypyc.cp312-win_amd64.pyd
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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",
|
||||||
|
|||||||
@ -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": (
|
||||||
|
|||||||
@ -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"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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__
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -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
CameraAI~/Tools~/CWCameraWorker/_internal/llvmlite.libs/msvcp140-8f141b4454fa78db34bc1f28c571b4da.dll
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/llvmlite.libs/msvcp140-8f141b4454fa78db34bc1f28c571b4da.dll
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/llvmlite/binding/llvmlite.dll
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/llvmlite/binding/llvmlite.dll
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/msgpack/_cmsgpack.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/msgpack/_cmsgpack.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_devicearray.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_devicearray.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_dispatcher.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_dispatcher.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_dynfunc.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_dynfunc.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_helperlib.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/_helperlib.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/core/runtime/_nrt_python.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/core/runtime/_nrt_python.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/core/typeconv/_typeconv.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/core/typeconv/_typeconv.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/cuda/cudadrv/_extras.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/cuda/cudadrv/_extras.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/experimental/jitclass/_box.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/experimental/jitclass/_box.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/mviewbuf.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/mviewbuf.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/_internal.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/_internal.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/omppool.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/omppool.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/tbbpool.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/tbbpool.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/workqueue.cp312-win_amd64.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/numba/np/ufunc/workqueue.cp312-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
pip
|
||||||
@ -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.
|
||||||
@ -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>`_.
|
||||||
@ -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
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: setuptools (70.1.1)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
importlib_metadata
|
||||||
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/soxr/soxr_ext.pyd
(Stored with Git LFS)
BIN
CameraAI~/Tools~/CWCameraWorker/_internal/soxr/soxr_ext.pyd
(Stored with Git LFS)
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",
|
||||||
|
|||||||
14
README.md
14
README.md
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user