@lumy-pack/scene-sieve 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -24
- package/dist/{errors.d.ts → cli/errors/classify-error.d.ts} +5 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/{utils → cli/options}/parse-options.d.ts +6 -1
- package/dist/cli.mjs +755 -319
- package/dist/constants/pipeline-defaults.d.ts +13 -0
- package/dist/core/{analyzer.d.ts → analyzer/analyzer.d.ts} +11 -6
- package/dist/core/{dbscan.d.ts → analyzer/clustering/dbscan.d.ts} +1 -1
- package/dist/core/analyzer/constants/vision-tuning.d.ts +12 -0
- package/dist/core/analyzer/features/feature-diff.d.ts +14 -0
- package/dist/core/analyzer/features/frame-features.d.ts +32 -0
- package/dist/core/analyzer/index.d.ts +3 -0
- package/dist/core/constants/workspace-layout.d.ts +9 -0
- package/dist/core/{extractor.d.ts → extractor/extractor.d.ts} +6 -4
- package/dist/core/extractor/index.d.ts +1 -0
- package/dist/core/index.d.ts +8 -9
- package/dist/core/input-resolver/index.d.ts +1 -0
- package/dist/core/{input-resolver.d.ts → input-resolver/input-resolver.d.ts} +11 -1
- package/dist/core/input-resolver/validation/validate-options.d.ts +8 -0
- package/dist/core/orchestrator/index.d.ts +3 -0
- package/dist/core/{orchestrator.d.ts → orchestrator/orchestrator.d.ts} +1 -1
- package/dist/core/{run-in-worker.d.ts → orchestrator/worker/run-in-worker.d.ts} +5 -1
- package/dist/core/pruner/index.d.ts +1 -0
- package/dist/core/{pruner.d.ts → pruner/pruner.d.ts} +2 -2
- package/dist/{utils/math.d.ts → core/pruner/scoring/normalize-scores.d.ts} +4 -0
- package/dist/core/segmenter/index.d.ts +1 -0
- package/dist/core/{segmenter.d.ts → segmenter/segmenter.d.ts} +11 -11
- package/dist/core/utils/metadata/build-video-metadata.d.ts +14 -0
- package/dist/core/workspace/index.d.ts +1 -0
- package/dist/core/{workspace.d.ts → workspace/workspace.d.ts} +1 -1
- package/dist/index.cjs +910 -434
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +913 -434
- package/dist/pipeline-worker.mjs +635 -278
- package/dist/types/index.d.ts +25 -0
- package/package.json +1 -1
- package/dist/constants.d.ts +0 -32
- /package/dist/{commands → cli/commands}/Sieve.d.ts +0 -0
- /package/dist/{utils → cli/commands}/command-registry.d.ts +0 -0
- /package/dist/{components → cli/components}/PhaseStep.d.ts +0 -0
- /package/dist/{components → cli/components}/ProgressBar.d.ts +0 -0
- /package/dist/core/{pipeline-worker.d.ts → orchestrator/worker/pipeline-worker.d.ts} +0 -0
- /package/dist/{utils → core/pruner/heap}/min-heap.d.ts +0 -0
- /package/dist/{utils → core/segmenter/scheduling}/concurrency.d.ts +0 -0
- /package/dist/{utils → core/utils/filesystem}/paths.d.ts +0 -0
- /package/dist/{utils → logging}/logger.d.ts +0 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Automatically extract the most meaningful frames from video and GIF files using
|
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
Video/GIF ──▶ Extract (FFmpeg) ──▶ Analyze (OpenCV) ──▶ Prune ──▶ Output
|
|
11
|
-
|
|
11
|
+
FPS grid AKAZE + DBSCAN MinHeap JPG / Buffer
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Features
|
|
@@ -18,7 +18,7 @@ Video/GIF ──▶ Extract (FFmpeg) ──▶ Analyze (OpenCV) ──▶ Prune
|
|
|
18
18
|
- **Smart frame selection** — Identifies visually significant scene changes, not just evenly-spaced samples
|
|
19
19
|
- **Computer vision pipeline** — AKAZE feature detection, DBSCAN clustering, IoU tracking, and information gain scoring
|
|
20
20
|
- **Three input modes** — File path, video Buffer, or pre-extracted frame Buffers
|
|
21
|
-
- **Flexible pruning** —
|
|
21
|
+
- **Flexible pruning** — Filter by a distribution-normalized threshold, then apply a count cap
|
|
22
22
|
- **Bundled FFmpeg** — No system-level FFmpeg installation required
|
|
23
23
|
- **Dual output** — ESM and CommonJS compatible
|
|
24
24
|
- **Progress callbacks** — Track extraction progress in real time
|
|
@@ -40,7 +40,7 @@ yarn add @lumy-pack/scene-sieve
|
|
|
40
40
|
# Extract 20 key scenes (default)
|
|
41
41
|
npx scene-sieve input.mp4
|
|
42
42
|
|
|
43
|
-
# Keep
|
|
43
|
+
# Keep up to 8 scenes
|
|
44
44
|
npx scene-sieve input.mp4 -n 8
|
|
45
45
|
|
|
46
46
|
# Use threshold-based selection
|
|
@@ -84,9 +84,9 @@ scene-sieve <input> [options]
|
|
|
84
84
|
| `-n, --count <number>` | Max number of frames to keep | `20` |
|
|
85
85
|
| `-t, --threshold <number>` | Normalized score threshold (0, 1] | `0.5` |
|
|
86
86
|
| `-o, --output <path>` | Output directory | Same directory as input |
|
|
87
|
-
| `--fps <number>` |
|
|
88
|
-
| `-mf, --max-frames <number>` |
|
|
89
|
-
| `-s, --scale <number>` |
|
|
87
|
+
| `--fps <number>` | Requested extraction FPS (positive decimals allowed) | `5` |
|
|
88
|
+
| `-mf, --max-frames <number>` | Strict candidate cap (auto-reduces FPS) | `300` |
|
|
89
|
+
| `-s, --scale <number>` | Extraction height / analysis width (px) | `720` |
|
|
90
90
|
| `-q, --quality <number>` | JPEG output quality (1–100) | `80` |
|
|
91
91
|
| `-it, --iou-threshold <number>`| IoU threshold for animation tracking (0–1) | `0.9` |
|
|
92
92
|
| `-at, --anim-threshold <number>`| Min consecutive frames for animation | `5` |
|
|
@@ -114,7 +114,7 @@ Not sure where to start? Here's how each parameter affects the output, based on
|
|
|
114
114
|
|
|
115
115
|
#### `--threshold` — Minimum score to keep a frame
|
|
116
116
|
|
|
117
|
-
Higher values
|
|
117
|
+
Higher values mean stricter filtering and fewer frames. Scores are normalized against the positive-score distribution within the video: min-max for up to 10 positive scores, otherwise a blend of median/MAD-based logistic scores and percentile ranks. `0.5` is neither an absolute change level nor half the maximum score.
|
|
118
118
|
|
|
119
119
|
| Setting | Selected | Notes |
|
|
120
120
|
|---------|----------|-------|
|
|
@@ -124,24 +124,27 @@ Higher values = stricter filtering = fewer frames.
|
|
|
124
124
|
| `-t 0.7` | 19 | Starts filtering subtle changes |
|
|
125
125
|
| `-t 0.9` | 12 | Only major scene transitions survive |
|
|
126
126
|
|
|
127
|
-
> **Tip**:
|
|
127
|
+
> **Tip**: Using `-t` alone still applies the default `count=20` cap. Adjust it with `-n` (e.g., `-t 0.3 -n 10`).
|
|
128
128
|
|
|
129
129
|
#### `--fps` and `--max-frames` — Extraction density
|
|
130
130
|
|
|
131
131
|
These control how many frames are pulled from the video before analysis. More frames = more precision but longer processing.
|
|
132
132
|
|
|
133
|
+
File/buffer candidates obey the strict `maxFrames` cap (integer ≥ 2). Effective FPS is `min(fps, maxFrames / duration)`, with no 0.5 FPS floor. Positive decimals such as `--fps 0.5` are accepted. A one-hour video with a 300-frame budget is sampled at about 0.083 FPS (every 12 seconds). This cap does not apply to frames input.
|
|
134
|
+
|
|
135
|
+
Timestamps describe the FFmpeg output grid `k / effectiveFps`, rather than the original source frame PTS. The FPS filter keeps its default rounding. When the budget binds, the last grid point is `duration - duration / maxFrames`, leaving no candidate in the remaining interval to the end.
|
|
136
|
+
|
|
133
137
|
| Setting | Extracted | Selected | Time |
|
|
134
138
|
|---------|-----------|----------|------|
|
|
135
139
|
| `--fps 1` | 18 | 6 | ~5s |
|
|
136
140
|
| `--fps 5` (default) | 90 | 20 | ~25s |
|
|
137
141
|
| `--fps 10` | 180 | 20 | ~47s |
|
|
138
|
-
| `-mf 50` | 47 | 13 | ~13s |
|
|
139
142
|
|
|
140
143
|
> **Tip**: For quick previews, `--fps 1` is 5x faster. For frame-accurate analysis, `--fps 10` captures finer transitions.
|
|
141
144
|
|
|
142
145
|
#### `--scale` — Analysis resolution
|
|
143
146
|
|
|
144
|
-
|
|
147
|
+
File/buffer extraction scales height to `scale`, while analysis preprocessing scales width to `scale`. Final JPEGs retain extraction dimensions; frames input retains its input dimensions in the output. Lower values are faster but less sensitive.
|
|
145
148
|
|
|
146
149
|
| Setting | Selected | Time | Output Size |
|
|
147
150
|
|---------|----------|------|-------------|
|
|
@@ -283,12 +286,14 @@ interface SieveOptionsBase {
|
|
|
283
286
|
count?: number; // Max frames to keep (default: 20)
|
|
284
287
|
threshold?: number; // Score threshold in range (0, 1] (default: 0.5)
|
|
285
288
|
outputPath?: string; // Output directory (file mode only)
|
|
286
|
-
fps?: number; //
|
|
287
|
-
maxFrames?: number; //
|
|
288
|
-
scale?: number; //
|
|
289
|
+
fps?: number; // Requested FPS, positive decimals allowed (default: 5)
|
|
290
|
+
maxFrames?: number; // Strict file/buffer candidate cap (default: 300)
|
|
291
|
+
scale?: number; // Extraction height / analysis width in px (default: 720)
|
|
289
292
|
quality?: number; // JPEG quality 1-100 (default: 80)
|
|
290
293
|
iouThreshold?: number; // IoU for animation tracking (default: 0.9)
|
|
291
294
|
animationThreshold?: number; // Min frames for animation (default: 5)
|
|
295
|
+
maxSegmentDuration?: number; // Segment duration in seconds (default: 300)
|
|
296
|
+
concurrency?: number; // Parallel segment workers (default: 2)
|
|
292
297
|
debug?: boolean; // Preserve temp workspace (default: false)
|
|
293
298
|
onProgress?: (phase: ProgressPhase, percent: number) => void;
|
|
294
299
|
}
|
|
@@ -296,6 +301,20 @@ interface SieveOptionsBase {
|
|
|
296
301
|
type SieveOptions = SieveOptionsBase & SieveInput;
|
|
297
302
|
```
|
|
298
303
|
|
|
304
|
+
Supplied numeric options are validated before defaults; invalid values produce `INVALID_INPUT`. CLI numeric strings are checked in full, so values such as `5abc` are rejected. Both interactive and JSON CLI failures exit with code 1.
|
|
305
|
+
|
|
306
|
+
| Option | Allowed range |
|
|
307
|
+
| --- | --- |
|
|
308
|
+
| `count`, `animationThreshold`, `concurrency` | Integer ≥ 1 |
|
|
309
|
+
| `maxFrames` | Integer ≥ 2 |
|
|
310
|
+
| `scale` | Integer ≥ 16 |
|
|
311
|
+
| `quality` | Integer 1–100 |
|
|
312
|
+
| `fps`, `maxSegmentDuration` | Finite positive number (decimals allowed) |
|
|
313
|
+
| `threshold` | Finite (0, 1] |
|
|
314
|
+
| `iouThreshold` | Finite [0, 1] |
|
|
315
|
+
|
|
316
|
+
All images in frames input must share the same width and height. Empty arrays and single images are accepted.
|
|
317
|
+
|
|
299
318
|
### Result
|
|
300
319
|
|
|
301
320
|
```typescript
|
|
@@ -313,13 +332,9 @@ interface SieveResult {
|
|
|
313
332
|
|
|
314
333
|
### Pruning Strategies
|
|
315
334
|
|
|
316
|
-
The
|
|
335
|
+
The pipeline always uses **threshold-with-cap**: filter by distribution-normalized score, then prune to the `count` cap. Omitted `threshold` and `count` default to `0.5` and `20`. The selected count may be lower depending on candidates and scores.
|
|
317
336
|
|
|
318
|
-
|
|
319
|
-
| -------------------------- | ---------------------- | ---------------------------------------------------------------- |
|
|
320
|
-
| `count` only | **count** | Greedy merge — removes lowest-scored frames until `count` remain |
|
|
321
|
-
| `threshold` only | **threshold** | Keeps frames with normalized score >= `threshold` |
|
|
322
|
-
| Both `count` + `threshold` | **threshold-with-cap** | Applies threshold filter first, then caps at `count` |
|
|
337
|
+
The first and last candidates are protected, so both remain when `count=1` and at least two candidates exist. Standalone count and threshold strategies exist as internal functions but are not selected by the CLI or `extractScenes`.
|
|
323
338
|
|
|
324
339
|
### Progress Tracking
|
|
325
340
|
|
|
@@ -339,12 +354,20 @@ const result = await extractScenes({
|
|
|
339
354
|
|
|
340
355
|
When running in `file` mode, `scene-sieve` generates a `.metadata.json` file in the output directory.
|
|
341
356
|
|
|
357
|
+
`SieveResult.video` and file metadata use the same values:
|
|
358
|
+
|
|
359
|
+
- `originalDurationMs`: ffprobe source duration for file/buffer; the last candidate timestamp for frames input.
|
|
360
|
+
- `fps`: effective extraction FPS, rather than the requested value. Frames input uses 1, also used by animation tracking.
|
|
361
|
+
- `resolution`: actual output JPEG dimensions of the first selected frame, falling back to the first candidate or 0×0 when empty.
|
|
362
|
+
- `frames[].timestampMs`: extraction-grid time in milliseconds; frames input uses one-second intervals.
|
|
363
|
+
- `animations[].boundingBox`: output-image pixel coordinates, scaled independently on each axis from analysis coordinates, rounded to integers, and clamped to the output bounds. File frame IDs are 1-based; API animation IDs are 0-based.
|
|
364
|
+
|
|
342
365
|
```json
|
|
343
366
|
{
|
|
344
367
|
"video": {
|
|
345
368
|
"originalDurationMs": 15000,
|
|
346
369
|
"fps": 5,
|
|
347
|
-
"resolution": { "width":
|
|
370
|
+
"resolution": { "width": 1280, "height": 720 }
|
|
348
371
|
},
|
|
349
372
|
"frames": [
|
|
350
373
|
{
|
|
@@ -373,16 +396,16 @@ When running in `file` mode, `scene-sieve` generates a `.metadata.json` file in
|
|
|
373
396
|
scene-sieve processes input through a 5-stage pipeline:
|
|
374
397
|
|
|
375
398
|
1. **Init** — Creates a temporary workspace and resolves input mode
|
|
376
|
-
2. **Extract** — Pulls
|
|
399
|
+
2. **Extract** — Pulls candidates on an FFmpeg FPS grid within the strict `maxFrames` cap (skipped in `frames` mode)
|
|
377
400
|
3. **Analyze** — Computes an information gain score G(t) for each adjacent frame pair
|
|
378
|
-
4. **Prune** — Selects frames
|
|
379
|
-
5. **Finalize** —
|
|
401
|
+
4. **Prune** — Selects frames from G(t) scores using threshold-with-cap
|
|
402
|
+
5. **Finalize** — Deletes existing output before renaming staging, or returns Buffers; cleans up workspace
|
|
380
403
|
|
|
381
404
|
### Vision Analysis
|
|
382
405
|
|
|
383
406
|
The analyzer scores each pair of adjacent frames through 4 stages:
|
|
384
407
|
|
|
385
|
-
1. **AKAZE Feature Diff** —
|
|
408
|
+
1. **AKAZE Feature Diff** — Reuses per-frame preprocessing/features and the detector to compute newly appeared points (sNew only)
|
|
386
409
|
2. **DBSCAN Clustering** — Groups new feature points into spatial clusters
|
|
387
410
|
3. **IoU Tracking** — Tracks cluster bounding boxes across time; identifies and records repeated animation regions (e.g. loading spinners)
|
|
388
411
|
4. **G(t) Scoring** — Calculates information gain from cluster area ratio and feature density, discounting animated areas to focus on unique scene content
|
|
@@ -7,4 +7,9 @@ export declare const SieveErrorCode: {
|
|
|
7
7
|
readonly UNKNOWN: "UNKNOWN";
|
|
8
8
|
};
|
|
9
9
|
export type SieveErrorCode = (typeof SieveErrorCode)[keyof typeof SieveErrorCode];
|
|
10
|
+
/**
|
|
11
|
+
* Classify pipeline errors for structured CLI responses.
|
|
12
|
+
* @param error - Failure with a diagnostic message and optional filesystem code.
|
|
13
|
+
* @returns The existing error code matching the failure.
|
|
14
|
+
*/
|
|
10
15
|
export declare function classifyError(error: Error): SieveErrorCode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SieveOptionsBase } from '
|
|
1
|
+
import type { SieveOptionsBase } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Common pipeline options parsed from CLI opts (Commander string values → typed values).
|
|
4
4
|
* Does NOT include mode-specific fields (mode, inputPath, onProgress).
|
|
@@ -18,4 +18,9 @@ export interface RawCliOptions {
|
|
|
18
18
|
concurrency?: string;
|
|
19
19
|
debug?: boolean;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Convert CLI strings to pipeline values for subsequent range validation.
|
|
23
|
+
* @param opts - Commander options with raw numeric strings.
|
|
24
|
+
* @returns Typed settings, preserving invalid numeric input as NaN.
|
|
25
|
+
*/
|
|
21
26
|
export declare function parsePipelineOptions(opts: RawCliOptions): ParsedPipelineOptions;
|