release(camera-ai): publish 0.4.11 worker bundle
This commit is contained in:
Binary file not shown.
+12
-12
@@ -1,30 +1,30 @@
|
||||
{
|
||||
"schemaVersion": "cw-camera-worker-build-identity-v1",
|
||||
"workerVersion": "0.1.6",
|
||||
"createdUtc": "2026-08-09T07:15:01.579387+00:00",
|
||||
"workerVersion": "0.1.7",
|
||||
"createdUtc": "2026-08-09T23:32:08.208961+00:00",
|
||||
"python": "3.12.13",
|
||||
"sourceRootRelative": "cwai_sources/repository",
|
||||
"sourceSha256": {
|
||||
"MachineLearning/CameraDirector/adjacent_transition.py": "6c1b62c2996960af23d62e9c3acd3114600effda03273b006f7076c467c4bdfa",
|
||||
"MachineLearning/CameraDirector/build_hybrid_preparation_cache.py": "f948b1a68352893b078ac1f6ef458df2107565378eed32890276776ca0dfdad6",
|
||||
"MachineLearning/CameraDirector/build_hybrid_preparation_cache.py": "427b3cd8fa6eb444517281ef2e21ebc5d751985f4ad271eee49617fda0fb7b33",
|
||||
"MachineLearning/CameraDirector/camera_kinematics.py": "e94eefcb56c4ebdccbc57ae8d3650109b2c912e3642892ac13add9409db21a7f",
|
||||
"MachineLearning/CameraDirector/camera_runtime_data.py": "29fd30a1c97d384f7bb22d024a4f88178f6245858925c302b059a7a1afdeaac1",
|
||||
"MachineLearning/CameraDirector/camera_runtime_data.py": "8ef2e84a9d79b24c3f3232b1f18da49741988765044dffc6075f6e6dd5ff001d",
|
||||
"MachineLearning/CameraDirector/cw_camera_cli.py": "943870a4cdec7e4b830b9c945d690e700902cf2a37d1e49987bba85bf0b0ea87",
|
||||
"MachineLearning/CameraDirector/cw_camera_runtime.py": "62421300af470389b5e356267d91e4dcb31626762705cd1c1d20c3fa11d271f3",
|
||||
"MachineLearning/CameraDirector/cw_camera_runtime.py": "7530d24a65a55c235726676b7991b7a375a2739d8b6257f53cc0340f6cc7de1c",
|
||||
"MachineLearning/CameraDirector/data_driven_cut_planner.py": "36cf86a80577278d2e966cb0ebf7c3110b5b1dda5abd1d8a6b0a76e31d10e32b",
|
||||
"MachineLearning/CameraDirector/generate_hybrid.py": "b6b1e7d02d96794e57c0ae9867ad18de7c6698d7bac05da1834490e2c0f3b4e3",
|
||||
"MachineLearning/CameraDirector/generate_hybrid.py": "b7e0053563065ebb6d563e72fa0221d31a9e36c1c94c37992f13974b25bb5dfd",
|
||||
"MachineLearning/CameraDirector/hybrid_candidate_cache.py": "a0a5b6a8f612f18cb2875394f17ed89950e1e62e38c1226457d9e35848f6e380",
|
||||
"MachineLearning/CameraDirector/hybrid_cut_reference.py": "53521d9e31d315c011892961887baf0cde6451020bf39c9d080a5072d4707fcb",
|
||||
"MachineLearning/CameraDirector/hybrid_preparation_cache.py": "1b0df53124109dee2d744777979a0553999cb276cb6f538a17ddce6516e3da8b",
|
||||
"MachineLearning/CameraDirector/hybrid_cut_reference.py": "306e650dc193fd0d9c42fd52d3f14eef112d1a0da2a27c13b88256d42d8542aa",
|
||||
"MachineLearning/CameraDirector/hybrid_preparation_cache.py": "e4d062a48cf39e01ea97951d103a37817c860e94f440f49f080c622b4969fb5a",
|
||||
"MachineLearning/CameraDirector/hybrid_single_shot_cache.py": "f540e603b0ccffb86ee003d441c3889c245a208db8fd8b2b966fdacfeea30950",
|
||||
"MachineLearning/CameraDirector/planner.py": "8575788c0ff1e984f8238354dcd6de2dab9f62afeb71fa4b77ebfc7c66400372",
|
||||
"MachineLearning/CameraDirector/shot_features.py": "fd912b00320ea53ca682c010de6ed1102edcd021ceb4822a5785033a500786fe",
|
||||
"MachineLearning/CameraDirector/train.py": "40e9e9a9ab824d596b14f987b2977f187695c3d5a2a2bde9da67e95d38d78836",
|
||||
"MachineLearning/CameraDirector/train.py": "070d1c4159c56a2107ae0d383ab0b716b12c13186d45383e94b83d3ba6305d03",
|
||||
"MachineLearning/CameraDirector/trajectory_quality.py": "20cd2ddc1b2349f8c33f6d1460580f37964a521eaa86ceee5b8ae457512ba49d"
|
||||
},
|
||||
"preparationLogicIdentifier": "acba2a46d9d979b013b7154bada6b6a8a7adfd7acd1721876ce6699ed4e03e84",
|
||||
"candidateLogicIdentifier": "bb9e883085df6a95de09f2181191c83c13cf1b3adff5b64e41b3d60d99350522",
|
||||
"generationCodeIdentifier": "8973f4642cd18f5645d33871c544e5394bb538ec5db86d4abc0536f50a00a9ed",
|
||||
"preparationLogicIdentifier": "1cb808e207d54854267a1020a658812c79e9bad5a5296f78140228b3a15eeb3b",
|
||||
"candidateLogicIdentifier": "8dfa557f33c17f4b59849431523cef3650091dc2db3c6c48074888f1f6aef143",
|
||||
"generationCodeIdentifier": "58a7d5d440e5bea630c060823fea7280d81d533e5c4741259ee974bce27e6eb4",
|
||||
"generationCodeFiles": [
|
||||
"adjacent_transition.py",
|
||||
"camera_kinematics.py",
|
||||
|
||||
+27
@@ -113,6 +113,16 @@ def run(args: argparse.Namespace) -> dict:
|
||||
target_record,
|
||||
args.library_split,
|
||||
)
|
||||
training_mask_audit = (
|
||||
generate_hybrid.audio_aligned_template_mask_audit(
|
||||
training_records,
|
||||
entries,
|
||||
)
|
||||
)
|
||||
planner_mask_audit = generate_hybrid.audio_aligned_template_mask_audit(
|
||||
planner_records,
|
||||
entries,
|
||||
)
|
||||
build_inputs = preparation_cache.build_inputs_for_records(
|
||||
repository_root,
|
||||
camera_director_root,
|
||||
@@ -288,6 +298,23 @@ def run(args: argparse.Namespace) -> dict:
|
||||
),
|
||||
"buildTimingBeforePublish": timer.snapshot(),
|
||||
}
|
||||
training_mask_audit = (
|
||||
generate_hybrid.finalized_audio_aligned_template_mask_audit(
|
||||
training_mask_audit,
|
||||
templates,
|
||||
)
|
||||
)
|
||||
planner_mask_audit = (
|
||||
generate_hybrid.finalized_audio_aligned_template_mask_audit(
|
||||
planner_mask_audit,
|
||||
planner_templates,
|
||||
)
|
||||
)
|
||||
if training_mask_audit is not None:
|
||||
summary["audioAlignedFrameMasking"] = {
|
||||
"trainingTemplates": training_mask_audit,
|
||||
"shotPlanner": planner_mask_audit,
|
||||
}
|
||||
|
||||
with timer.stage("publish_and_verify"):
|
||||
bundle = preparation_cache.publish_cache_bundle(
|
||||
|
||||
+120
-1
@@ -29,10 +29,14 @@ PIPELINE_VERSION = 1
|
||||
AUDIO_RATE = 24_000
|
||||
AUDIO_HOP = 400 # Exactly 60 audio feature frames per second.
|
||||
MEL_BANDS = 32
|
||||
REVISION_DATASET_PATTERN = re.compile(
|
||||
r"^(?P<base>.+)_rev_(?P<fingerprint>[0-9a-f]{16})$"
|
||||
)
|
||||
GENERATION_INPUT_CACHE_IDENTITY_VERSION = "generation-input-content-v1"
|
||||
GENERATION_INPUT_CACHE_FORMAT = "cw-ai-generation-input-prepared"
|
||||
GENERATION_INPUT_CACHE_SCHEMA_VERSION = 1
|
||||
GENERATION_INPUT_PREPARATION_LOGIC_VERSION = "generation-input-preparation-v2"
|
||||
AUDIO_ALIGNED_FRAME_MASK_POLICY_VERSION = "audio-aligned-half-open-v1"
|
||||
GENERATION_INPUT_VOLATILE_MANIFEST_FIELDS = frozenset(
|
||||
{
|
||||
"audioAssetPath",
|
||||
@@ -114,13 +118,25 @@ class SongRecord:
|
||||
def id(self) -> str:
|
||||
scene_path = str(self.manifest.get("scenePath", ""))
|
||||
folder_name = str(self.song["folderName"])
|
||||
value = f"{scene_path}\0{self.name}\0{folder_name}".encode("utf-8")
|
||||
value = f"{scene_path}\0{self.name}\0{folder_name}"
|
||||
if self.is_revision:
|
||||
value += f"\0revision:{self.dataset_id}"
|
||||
value = value.encode("utf-8")
|
||||
return hashlib.sha256(value).hexdigest()[:16]
|
||||
|
||||
@property
|
||||
def dataset_id(self) -> str:
|
||||
return self.dataset_dir.name
|
||||
|
||||
@property
|
||||
def revision_of_dataset_id(self) -> str | None:
|
||||
match = REVISION_DATASET_PATTERN.fullmatch(self.dataset_id)
|
||||
return match.group("base") if match is not None else None
|
||||
|
||||
@property
|
||||
def is_revision(self) -> bool:
|
||||
return self.revision_of_dataset_id is not None
|
||||
|
||||
@property
|
||||
def song_dir(self) -> Path:
|
||||
return self.dataset_dir / self.song["folderName"]
|
||||
@@ -307,6 +323,97 @@ def select_record(
|
||||
)
|
||||
|
||||
|
||||
def normalize_audio_aligned_frame_mask_ranges(
|
||||
value: Any,
|
||||
frame_count: int,
|
||||
*,
|
||||
label: str = "audioAlignedFrameMaskRanges",
|
||||
) -> tuple[tuple[int, int], ...]:
|
||||
"""Validate canonical half-open frame ranges without mutating source data.
|
||||
|
||||
Ranges must be strictly ordered, non-overlapping, and non-adjacent. Adjacent
|
||||
ranges are rejected because their canonical representation is one merged
|
||||
range. An empty list is valid and deliberately admits no frames.
|
||||
"""
|
||||
|
||||
if isinstance(frame_count, bool) or not isinstance(frame_count, int):
|
||||
raise ValueError(f"{label} frame count must be an integer.")
|
||||
if frame_count < 0:
|
||||
raise ValueError(f"{label} frame count must be non-negative.")
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(f"{label} must be an array of [start, end) ranges.")
|
||||
|
||||
normalized: list[tuple[int, int]] = []
|
||||
previous_end: int | None = None
|
||||
for index, raw_range in enumerate(value):
|
||||
range_label = f"{label}[{index}]"
|
||||
if not isinstance(raw_range, list) or len(raw_range) != 2:
|
||||
raise ValueError(f"{range_label} must be a two-item array.")
|
||||
start, end = raw_range
|
||||
if (
|
||||
isinstance(start, bool)
|
||||
or isinstance(end, bool)
|
||||
or not isinstance(start, int)
|
||||
or not isinstance(end, int)
|
||||
):
|
||||
raise ValueError(f"{range_label} bounds must be integers.")
|
||||
if not 0 <= start < end <= frame_count:
|
||||
raise ValueError(
|
||||
f"{range_label} must satisfy 0 <= start < end <= {frame_count}."
|
||||
)
|
||||
if previous_end is not None and start <= previous_end:
|
||||
raise ValueError(
|
||||
f"{range_label} overlaps, touches, or is out of order; "
|
||||
"canonical ranges must be merged and strictly separated."
|
||||
)
|
||||
normalized.append((start, end))
|
||||
previous_end = end
|
||||
return tuple(normalized)
|
||||
|
||||
|
||||
def audio_aligned_frame_mask_range_index(
|
||||
start: int,
|
||||
end: int,
|
||||
ranges: tuple[tuple[int, int], ...],
|
||||
) -> int | None:
|
||||
"""Return the single range containing ``[start, end)``, else ``None``.
|
||||
|
||||
A span crossing a range boundary or falling fully outside the mask is
|
||||
intentionally indistinguishable to consumers: both must fail closed.
|
||||
"""
|
||||
|
||||
if (
|
||||
isinstance(start, bool)
|
||||
or isinstance(end, bool)
|
||||
or not isinstance(start, int)
|
||||
or not isinstance(end, int)
|
||||
or start < 0
|
||||
or end <= start
|
||||
):
|
||||
raise ValueError("Frame spans must be non-empty half-open integer ranges.")
|
||||
for index, (range_start, range_end) in enumerate(ranges):
|
||||
if range_start <= start and end <= range_end:
|
||||
return index
|
||||
return None
|
||||
|
||||
|
||||
def audio_aligned_frame_mask_span_status(
|
||||
start: int,
|
||||
end: int,
|
||||
ranges: tuple[tuple[int, int], ...],
|
||||
) -> tuple[str, int | None]:
|
||||
"""Classify a source span as contained, boundary-crossing, or outside."""
|
||||
|
||||
range_index = audio_aligned_frame_mask_range_index(start, end, ranges)
|
||||
if range_index is not None:
|
||||
return "contained", range_index
|
||||
intersects = any(
|
||||
start < range_end and end > range_start
|
||||
for range_start, range_end in ranges
|
||||
)
|
||||
return ("boundary_crossing" if intersects else "outside"), None
|
||||
|
||||
|
||||
def filter_records_by_training_index(
|
||||
records: Iterable[SongRecord],
|
||||
index_path: Path,
|
||||
@@ -348,6 +455,18 @@ def filter_records_by_training_index(
|
||||
sort_keys=True,
|
||||
)
|
||||
)
|
||||
ineligible_validation = sorted(
|
||||
song_id
|
||||
for song_id in validation_ids
|
||||
if song_id in entries
|
||||
and entries[song_id].get("usage", {}).get(purpose) is not True
|
||||
)
|
||||
if ineligible_validation:
|
||||
raise ValueError(
|
||||
f"Validation songs are not eligible for training purpose "
|
||||
f"{purpose!r}: "
|
||||
+ ", ".join(ineligible_validation)
|
||||
)
|
||||
validation_groups = {
|
||||
str(entries[song_id].get("evaluationGroupId", ""))
|
||||
for song_id in validation_ids
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ from typing import Any, Iterable
|
||||
|
||||
BUILD_IDENTITY_SCHEMA_VERSION = "cw-camera-worker-build-identity-v1"
|
||||
BUILD_IDENTITY_FILE = "cw_camera_worker_build_identity.json"
|
||||
WORKER_VERSION = "0.1.6"
|
||||
WORKER_VERSION = "0.1.7"
|
||||
|
||||
|
||||
def is_frozen_runtime() -> bool:
|
||||
|
||||
+821
-83
File diff suppressed because it is too large
Load Diff
+225
-14
@@ -31,6 +31,7 @@ import camera_runtime_data as train
|
||||
SAMPLE_RATE = 60
|
||||
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
||||
_CHARACTER_ID = re.compile(r"@[0-9A-Za-z_-]+")
|
||||
_AUDIO_PATTERN_TICKS_PER_FRAME = 1_000_000
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -53,6 +54,9 @@ class CutReferenceCorpus:
|
||||
same_audio_patterns: tuple[SameAudioCutPattern, ...]
|
||||
eligible_record_count: int
|
||||
unique_audio_pattern_count: int
|
||||
masked_record_count: int
|
||||
retained_authored_shot_count: int
|
||||
censored_authored_shot_count: int
|
||||
|
||||
def as_metadata(self) -> dict[str, Any]:
|
||||
weights = np.asarray(self.duration_weights, dtype=np.float64)
|
||||
@@ -83,6 +87,13 @@ class CutReferenceCorpus:
|
||||
"sameCharacterPatternCount": sum(
|
||||
pattern.same_character for pattern in self.same_audio_patterns
|
||||
),
|
||||
"audioAlignedMaskedRecordCount": self.masked_record_count,
|
||||
"audioAlignedRetainedAuthoredShotCount": (
|
||||
self.retained_authored_shot_count
|
||||
),
|
||||
"audioAlignedCensoredAuthoredShotCount": (
|
||||
self.censored_authored_shot_count
|
||||
),
|
||||
}
|
||||
|
||||
def weighted_duration_frame_samples(
|
||||
@@ -129,9 +140,16 @@ def audio_fingerprint(record: train.SongRecord) -> str:
|
||||
raise ValueError(
|
||||
f"Invalid audioFingerprint for cut reference {record.id}."
|
||||
)
|
||||
return declared
|
||||
if record.audio_path.is_file():
|
||||
return sha256_file(record.audio_path)
|
||||
actual = sha256_file(record.audio_path)
|
||||
if declared and actual != declared:
|
||||
raise ValueError(
|
||||
f"Declared audioFingerprint does not match the materialized "
|
||||
f"audio for cut reference {record.id}."
|
||||
)
|
||||
return actual
|
||||
if declared:
|
||||
return declared
|
||||
# Portable RuntimeData omits raw audio; the catalog fingerprint is the
|
||||
# authoritative identity in that mode.
|
||||
prepared = record.song.get("preparedFile", "prepared_v1.npz")
|
||||
@@ -142,6 +160,104 @@ def audio_fingerprint(record: train.SongRecord) -> str:
|
||||
raise FileNotFoundError(f"No audio or declared fingerprint for {record.id}.")
|
||||
|
||||
|
||||
def _verified_masked_audio_fingerprint(
|
||||
record: train.SongRecord,
|
||||
indexed_fingerprint: str,
|
||||
) -> str:
|
||||
"""Bind an explicit frame mask to the materialized source-audio bytes."""
|
||||
|
||||
audio_path = record.audio_path
|
||||
if not audio_path.is_file():
|
||||
raise ValueError(
|
||||
f"Masked cut reference {record.id} audio file is missing: {audio_path}"
|
||||
)
|
||||
lfs_pointer_prefix = b"version https://git-lfs.github.com/spec/v1"
|
||||
with audio_path.open("rb") as stream:
|
||||
if stream.read(len(lfs_pointer_prefix)) == lfs_pointer_prefix:
|
||||
raise ValueError(
|
||||
f"Masked cut reference {record.id} audio is still a Git LFS "
|
||||
f"pointer: {audio_path}"
|
||||
)
|
||||
actual = sha256_file(audio_path)
|
||||
if actual != indexed_fingerprint:
|
||||
raise ValueError(
|
||||
f"Masked cut reference {record.id} audioFingerprint does not match "
|
||||
f"the materialized audio file: {audio_path}"
|
||||
)
|
||||
return actual
|
||||
|
||||
|
||||
def _explicit_audio_mask_ranges(
|
||||
record: train.SongRecord,
|
||||
entry: Mapping[str, Any],
|
||||
indexed_fingerprint: str,
|
||||
) -> tuple[tuple[int, int], ...] | None:
|
||||
"""Return a strict explicit mask, preserving legacy full-record behavior."""
|
||||
|
||||
if "audioAlignedFrameMaskRanges" not in entry:
|
||||
partial = sorted(
|
||||
key
|
||||
for key in ("audioAlignedEndFrame", "audioAlignedFrameCount")
|
||||
if key in entry
|
||||
)
|
||||
if partial:
|
||||
raise ValueError(
|
||||
f"Cut reference audio mask for {record.id} is incomplete: "
|
||||
"audioAlignedFrameMaskRanges is missing while "
|
||||
+ ", ".join(partial)
|
||||
+ " is present."
|
||||
)
|
||||
return None
|
||||
|
||||
manifest_frame_count = record.song.get("frameCount")
|
||||
indexed_frame_count = entry.get("frameCount")
|
||||
if (
|
||||
isinstance(manifest_frame_count, bool)
|
||||
or not isinstance(manifest_frame_count, int)
|
||||
or isinstance(indexed_frame_count, bool)
|
||||
or not isinstance(indexed_frame_count, int)
|
||||
or manifest_frame_count < 1
|
||||
or indexed_frame_count != manifest_frame_count
|
||||
):
|
||||
raise ValueError(
|
||||
f"Cut reference audio mask frameCount mismatch for {record.id}."
|
||||
)
|
||||
ranges = train.normalize_audio_aligned_frame_mask_ranges(
|
||||
entry["audioAlignedFrameMaskRanges"],
|
||||
manifest_frame_count,
|
||||
label=f"Cut reference audio mask for {record.id}",
|
||||
)
|
||||
if not ranges:
|
||||
raise ValueError(
|
||||
f"Active cut reference {record.id} has an empty audio frame mask."
|
||||
)
|
||||
expected_count = sum(end - start for start, end in ranges)
|
||||
if "audioAlignedFrameCount" in entry:
|
||||
aligned_count = entry["audioAlignedFrameCount"]
|
||||
if (
|
||||
isinstance(aligned_count, bool)
|
||||
or not isinstance(aligned_count, int)
|
||||
or aligned_count != expected_count
|
||||
):
|
||||
raise ValueError(
|
||||
f"Cut reference audioAlignedFrameCount is inconsistent for "
|
||||
f"{record.id}."
|
||||
)
|
||||
if "audioAlignedEndFrame" in entry:
|
||||
aligned_end = entry["audioAlignedEndFrame"]
|
||||
if (
|
||||
isinstance(aligned_end, bool)
|
||||
or not isinstance(aligned_end, int)
|
||||
or aligned_end != ranges[-1][1]
|
||||
):
|
||||
raise ValueError(
|
||||
f"Cut reference audioAlignedEndFrame is inconsistent for "
|
||||
f"{record.id}."
|
||||
)
|
||||
_verified_masked_audio_fingerprint(record, indexed_fingerprint)
|
||||
return ranges
|
||||
|
||||
|
||||
def character_id(record: train.SongRecord) -> str:
|
||||
value = str(record.song.get("characterPath", ""))
|
||||
match = _CHARACTER_ID.search(value)
|
||||
@@ -213,6 +329,35 @@ def _align_pattern(
|
||||
return tuple(sorted(set(mapped)))
|
||||
|
||||
|
||||
def _audio_relative_boundary_signature(
|
||||
boundaries: Sequence[int],
|
||||
source: train.SongRecord,
|
||||
) -> tuple[int, ...]:
|
||||
"""Identify one edit pattern in audio-relative micro-frame ticks.
|
||||
|
||||
Raw Timeline frame boundaries are not a sufficient identity because two
|
||||
exports can share those integers while using different ``audioStart``
|
||||
offsets. Grouping those records would union shifted cuts into false
|
||||
near-duplicate pairs. Micro-frame ticks preserve sub-frame audio offsets
|
||||
while remaining deterministic JSON/float input material.
|
||||
"""
|
||||
|
||||
audio_start = float(source.song.get("audioStart", 0.0))
|
||||
if not math.isfinite(audio_start):
|
||||
raise ValueError("audioStart must be finite for cut-pattern identity.")
|
||||
audio_start_ticks = int(
|
||||
round(
|
||||
audio_start
|
||||
* SAMPLE_RATE
|
||||
* _AUDIO_PATTERN_TICKS_PER_FRAME
|
||||
)
|
||||
)
|
||||
return tuple(
|
||||
int(boundary) * _AUDIO_PATTERN_TICKS_PER_FRAME - audio_start_ticks
|
||||
for boundary in boundaries
|
||||
)
|
||||
|
||||
|
||||
def build_cut_reference_corpus(
|
||||
target: train.SongRecord,
|
||||
records: Sequence[train.SongRecord],
|
||||
@@ -223,9 +368,19 @@ def build_cut_reference_corpus(
|
||||
target_fingerprint = audio_fingerprint(target)
|
||||
target_character = character_id(target)
|
||||
pattern_records: dict[
|
||||
tuple[str, tuple[int, ...]], list[tuple[train.SongRecord, tuple[int, ...]]]
|
||||
tuple[str, tuple[int, ...]],
|
||||
list[
|
||||
tuple[
|
||||
train.SongRecord,
|
||||
tuple[tuple[int, int], ...],
|
||||
tuple[int, ...],
|
||||
]
|
||||
],
|
||||
] = defaultdict(list)
|
||||
eligible_count = 0
|
||||
masked_record_count = 0
|
||||
retained_authored_shot_count = 0
|
||||
censored_authored_shot_count = 0
|
||||
for record in records:
|
||||
entry = index_entries.get(record.id)
|
||||
if not isinstance(entry, Mapping):
|
||||
@@ -241,34 +396,87 @@ def build_cut_reference_corpus(
|
||||
if not _SHA256.fullmatch(fingerprint):
|
||||
raise ValueError(f"Invalid indexed audio fingerprint for {record.id}.")
|
||||
boundaries = authored_boundaries(record)
|
||||
pattern_records[(fingerprint, boundaries)].append((record, boundaries))
|
||||
mask_ranges = _explicit_audio_mask_ranges(record, entry, fingerprint)
|
||||
if mask_ranges is None:
|
||||
retained_spans = tuple(zip(boundaries, boundaries[1:]))
|
||||
retained_cuts = boundaries[1:-1]
|
||||
else:
|
||||
masked_record_count += 1
|
||||
retained_spans = tuple(
|
||||
(left, right)
|
||||
for left, right in zip(boundaries, boundaries[1:])
|
||||
if train.audio_aligned_frame_mask_range_index(
|
||||
left, right, mask_ranges
|
||||
)
|
||||
is not None
|
||||
)
|
||||
retained_cuts = tuple(
|
||||
boundary
|
||||
for boundary in boundaries[1:-1]
|
||||
if any(start < boundary < end for start, end in mask_ranges)
|
||||
)
|
||||
retained_authored_shot_count += len(retained_spans)
|
||||
censored_authored_shot_count += (
|
||||
len(boundaries) - 1 - len(retained_spans)
|
||||
)
|
||||
pattern_signature = _audio_relative_boundary_signature(boundaries, record)
|
||||
pattern_records[(fingerprint, pattern_signature)].append(
|
||||
(record, retained_spans, retained_cuts)
|
||||
)
|
||||
eligible_count += 1
|
||||
|
||||
durations: list[float] = []
|
||||
duration_weights: list[float] = []
|
||||
same_audio_patterns: list[SameAudioCutPattern] = []
|
||||
for (fingerprint, boundaries), sources in sorted(
|
||||
for (fingerprint, _pattern_signature), sources in sorted(
|
||||
pattern_records.items(), key=lambda value: (value[0][0], value[0][1])
|
||||
):
|
||||
shot_durations = np.diff(np.asarray(boundaries)) / SAMPLE_RATE
|
||||
# One unique audio/cut pattern contributes one unit in total, regardless
|
||||
# of how many duplicated exports or shots it contains.
|
||||
per_shot_weight = 1.0 / len(shot_durations)
|
||||
durations.extend(float(value) for value in shot_durations)
|
||||
duration_weights.extend(per_shot_weight for _ in shot_durations)
|
||||
retained_spans = sorted(
|
||||
{
|
||||
span
|
||||
for _, source_spans, _ in sources
|
||||
for span in source_spans
|
||||
}
|
||||
)
|
||||
if retained_spans:
|
||||
shot_durations = np.asarray(
|
||||
[right - left for left, right in retained_spans],
|
||||
dtype=np.float64,
|
||||
) / SAMPLE_RATE
|
||||
# One unique audio/cut pattern contributes one unit in total,
|
||||
# regardless of how many duplicated exports or shots it contains.
|
||||
per_shot_weight = 1.0 / len(shot_durations)
|
||||
durations.extend(float(value) for value in shot_durations)
|
||||
duration_weights.extend(per_shot_weight for _ in shot_durations)
|
||||
if fingerprint != target_fingerprint:
|
||||
continue
|
||||
aligned = _align_pattern(boundaries, sources[0][0], target)
|
||||
aligned_internal = {
|
||||
boundary
|
||||
for source, _, source_cuts in sources
|
||||
for boundary in _align_pattern(
|
||||
(0, *source_cuts, int(source.song["frameCount"])),
|
||||
source,
|
||||
target,
|
||||
)[1:-1]
|
||||
}
|
||||
if not aligned_internal:
|
||||
continue
|
||||
aligned = (0, *sorted(aligned_internal), int(target.song["frameCount"]))
|
||||
same_character = bool(
|
||||
target_character
|
||||
and any(character_id(source) == target_character for source, _ in sources)
|
||||
and any(
|
||||
character_id(source) == target_character
|
||||
for source, _, _ in sources
|
||||
)
|
||||
)
|
||||
same_audio_patterns.append(
|
||||
SameAudioCutPattern(
|
||||
boundaries=aligned,
|
||||
weight=2.0 if same_character else 1.0,
|
||||
same_character=same_character,
|
||||
source_record_ids=tuple(sorted(source.id for source, _ in sources)),
|
||||
source_record_ids=tuple(
|
||||
sorted(source.id for source, _, _ in sources)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -285,4 +493,7 @@ def build_cut_reference_corpus(
|
||||
same_audio_patterns=tuple(same_audio_patterns),
|
||||
eligible_record_count=eligible_count,
|
||||
unique_audio_pattern_count=len(pattern_records),
|
||||
masked_record_count=masked_record_count,
|
||||
retained_authored_shot_count=retained_authored_shot_count,
|
||||
censored_authored_shot_count=censored_authored_shot_count,
|
||||
)
|
||||
|
||||
+13
-1
@@ -339,7 +339,7 @@ def build_inputs_for_records(
|
||||
|
||||
def policy_identity(record: Any) -> dict[str, Any]:
|
||||
entry = entries[str(record.id)]
|
||||
return {
|
||||
identity = {
|
||||
"songId": str(record.id),
|
||||
"datasetId": str(record.dataset_id),
|
||||
"contentFingerprint": entry.get("contentFingerprint"),
|
||||
@@ -351,6 +351,18 @@ def build_inputs_for_records(
|
||||
"aspectRatio": entry.get("aspectRatio"),
|
||||
"aspectSource": entry.get("aspectSource"),
|
||||
}
|
||||
if "audioAlignedFrameMaskRanges" in entry:
|
||||
frame_count = entry.get("frameCount")
|
||||
ranges = runtime_data.normalize_audio_aligned_frame_mask_ranges(
|
||||
entry["audioAlignedFrameMaskRanges"],
|
||||
frame_count,
|
||||
label=f"Training index audio mask for {record.id}",
|
||||
)
|
||||
identity["frameCount"] = frame_count
|
||||
identity["audioAlignedFrameMaskRanges"] = [
|
||||
list(frame_range) for frame_range in ranges
|
||||
]
|
||||
return identity
|
||||
|
||||
def target_identity(record: Any) -> dict[str, Any]:
|
||||
if bool(getattr(record, "is_generation_input", False)):
|
||||
|
||||
+1240
-26
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
../../Scripts/wheel.exe,sha256=A40JvpbJRmAo327Gh4g5jNGRWEobaBMdktVtpIqzJAk,108448
|
||||
../../Scripts/wheel.exe,sha256=1GLKUxJuQuNenSR7B5UFoFKuXyK1UHvT9Dqwy-JntDE,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
|
||||
|
||||
Reference in New Issue
Block a user