@lumy-pack/scene-sieve 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Vincent K. Kelvin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,263 @@
1
+ # @lumy-pack/scene-sieve
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@lumy-pack/scene-sieve)](https://www.npmjs.com/package/@lumy-pack/scene-sieve)
4
+ [![license](https://img.shields.io/npm/l/@lumy-pack/scene-sieve)](./LICENSE)
5
+ [![node](https://img.shields.io/node/v/@lumy-pack/scene-sieve)](https://nodejs.org)
6
+
7
+ Automatically extract the most meaningful frames from video and GIF files using computer vision.
8
+
9
+ ```
10
+ Video/GIF ──▶ Extract (FFmpeg) ──▶ Analyze (OpenCV) ──▶ Prune ──▶ Output
11
+ I-frames / FPS AKAZE + DBSCAN MinHeap JPG / Buffer
12
+ ```
13
+
14
+ ## Features
15
+
16
+ - **Smart frame selection** — Identifies visually significant scene changes, not just evenly-spaced samples
17
+ - **Computer vision pipeline** — AKAZE feature detection, DBSCAN clustering, IoU tracking, and information gain scoring
18
+ - **Three input modes** — File path, video Buffer, or pre-extracted frame Buffers
19
+ - **Flexible pruning** — Keep a fixed count, filter by threshold, or combine both
20
+ - **Bundled FFmpeg** — No system-level FFmpeg installation required
21
+ - **Dual output** — ESM and CommonJS compatible
22
+ - **Progress callbacks** — Track extraction progress in real time
23
+ - **JPEG quality control** — Configurable output quality with mozjpeg optimization
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install @lumy-pack/scene-sieve
29
+ # or
30
+ yarn add @lumy-pack/scene-sieve
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ### CLI
36
+
37
+ ```bash
38
+ # Extract 5 key scenes (default)
39
+ npx scene-sieve input.mp4
40
+
41
+ # Keep exactly 8 scenes
42
+ npx scene-sieve input.mp4 -n 8
43
+
44
+ # Use threshold-based selection
45
+ npx scene-sieve input.mp4 -t 0.3
46
+
47
+ # Specify output directory and JPEG quality
48
+ npx scene-sieve input.mp4 -n 10 -o ./scenes -q 90
49
+ ```
50
+
51
+ ### Module
52
+
53
+ ```typescript
54
+ import { extractScenes } from '@lumy-pack/scene-sieve';
55
+
56
+ const result = await extractScenes({
57
+ mode: 'file',
58
+ inputPath: './input.mp4',
59
+ count: 8,
60
+ outputPath: './scenes',
61
+ });
62
+
63
+ console.log(
64
+ `${result.prunedFramesCount} scenes extracted in ${result.executionTimeMs}ms`,
65
+ );
66
+ // Output: scenes/scene_001.jpg, scenes/scene_002.jpg, ...
67
+ ```
68
+
69
+ ## CLI Reference
70
+
71
+ ```
72
+ scene-sieve <input> [options]
73
+ ```
74
+
75
+ | Option | Description | Default |
76
+ | -------------------------- | -------------------------------------- | --------------------------- |
77
+ | `<input>` | Input video or GIF file path | (required) |
78
+ | `-n, --count <number>` | Number of frames to keep | `5` (when no `--threshold`) |
79
+ | `-t, --threshold <number>` | Normalized score threshold (0, 1] | — |
80
+ | `-o, --output <path>` | Output directory | Same directory as input |
81
+ | `--fps <number>` | Fallback FPS for frame extraction | `5` |
82
+ | `-s, --scale <number>` | Scale size for vision analysis (px) | `720` |
83
+ | `-q, --quality <number>` | JPEG output quality (1–100) | `80` |
84
+ | `--debug` | Preserve temp workspace for inspection | `false` |
85
+
86
+ ### Supported Formats
87
+
88
+ | Type | Extensions |
89
+ | --------- | --------------------------------------- |
90
+ | Video | `.mp4`, `.mov`, `.avi`, `.mkv`, `.webm` |
91
+ | Animation | `.gif` |
92
+
93
+ ### Examples
94
+
95
+ ```bash
96
+ # Extract from a GIF
97
+ scene-sieve animation.gif -n 4 -o ./keyframes
98
+
99
+ # High-quality output with threshold filtering
100
+ scene-sieve demo.mov -t 0.2 -q 95
101
+
102
+ # Combine threshold + count cap
103
+ scene-sieve long-video.mp4 -t 0.15 -n 20
104
+
105
+ # Debug mode: keep temp files for inspection
106
+ scene-sieve input.mp4 --debug
107
+ ```
108
+
109
+ ## API Reference
110
+
111
+ ### `extractScenes(options)`
112
+
113
+ Extracts key frames from a video, GIF, or pre-extracted frame buffers.
114
+
115
+ ```typescript
116
+ function extractScenes(options: SieveOptions): Promise<SieveResult>;
117
+ ```
118
+
119
+ ### Input Modes
120
+
121
+ The `mode` field determines how input is provided and what output is returned.
122
+
123
+ #### File Mode
124
+
125
+ Reads a video/GIF from disk, writes JPEG files to the output directory.
126
+
127
+ ```typescript
128
+ const result = await extractScenes({
129
+ mode: 'file',
130
+ inputPath: './video.mp4',
131
+ count: 5,
132
+ outputPath: './output',
133
+ quality: 90,
134
+ });
135
+
136
+ console.log(result.outputFiles);
137
+ // ['./output/scene_001.jpg', './output/scene_002.jpg', ...]
138
+ ```
139
+
140
+ #### Buffer Mode
141
+
142
+ Accepts a video as a Node.js Buffer, returns frame Buffers. Useful for stream processing or serverless environments.
143
+
144
+ ```typescript
145
+ import { readFile } from 'node:fs/promises';
146
+
147
+ const videoBuffer = await readFile('./video.mp4');
148
+
149
+ const result = await extractScenes({
150
+ mode: 'buffer',
151
+ inputBuffer: videoBuffer,
152
+ count: 5,
153
+ });
154
+
155
+ console.log(result.outputBuffers?.length); // 5
156
+ // Each buffer is a JPEG image
157
+ ```
158
+
159
+ #### Frames Mode
160
+
161
+ Accepts pre-extracted frame images as Buffers. **Does not require FFmpeg.** Useful when frames are already available from another source.
162
+
163
+ ```typescript
164
+ const frames: Buffer[] = [
165
+ /* JPEG/PNG buffers */
166
+ ];
167
+
168
+ const result = await extractScenes({
169
+ mode: 'frames',
170
+ inputFrames: frames,
171
+ count: 5,
172
+ });
173
+
174
+ console.log(result.outputBuffers?.length); // 5
175
+ ```
176
+
177
+ ### Options
178
+
179
+ ```typescript
180
+ interface SieveOptionsBase {
181
+ count?: number; // Frames to keep (default: 5 when no threshold)
182
+ threshold?: number; // Score threshold in range (0, 1]
183
+ outputPath?: string; // Output directory (file mode only)
184
+ fps?: number; // Extraction FPS (default: 5)
185
+ scale?: number; // Analysis scale in px (default: 720)
186
+ quality?: number; // JPEG quality 1-100 (default: 80)
187
+ debug?: boolean; // Preserve temp workspace (default: false)
188
+ onProgress?: (phase: ProgressPhase, percent: number) => void;
189
+ }
190
+
191
+ type SieveOptions = SieveOptionsBase & SieveInput;
192
+ ```
193
+
194
+ ### Result
195
+
196
+ ```typescript
197
+ interface SieveResult {
198
+ success: boolean;
199
+ originalFramesCount: number; // Total frames extracted/provided
200
+ prunedFramesCount: number; // Frames selected as key scenes
201
+ outputFiles: string[]; // File paths (file mode)
202
+ outputBuffers?: Buffer[]; // JPEG buffers (buffer/frames mode)
203
+ executionTimeMs: number;
204
+ }
205
+ ```
206
+
207
+ ### Pruning Strategies
208
+
209
+ The pruning strategy is automatically selected based on which options are provided:
210
+
211
+ | Options | Strategy | Behavior |
212
+ | -------------------------- | ---------------------- | ---------------------------------------------------------------- |
213
+ | `count` only | **count** | Greedy merge — removes lowest-scored frames until `count` remain |
214
+ | `threshold` only | **threshold** | Keeps frames with normalized score >= `threshold` |
215
+ | Both `count` + `threshold` | **threshold-with-cap** | Applies threshold filter first, then caps at `count` |
216
+
217
+ ### Progress Tracking
218
+
219
+ ```typescript
220
+ type ProgressPhase = 'EXTRACTING' | 'ANALYZING' | 'PRUNING' | 'FINALIZING';
221
+
222
+ const result = await extractScenes({
223
+ mode: 'file',
224
+ inputPath: './video.mp4',
225
+ onProgress: (phase, percent) => {
226
+ console.log(`${phase}: ${Math.round(percent)}%`);
227
+ },
228
+ });
229
+ ```
230
+
231
+ ## How It Works
232
+
233
+ ### Pipeline
234
+
235
+ scene-sieve processes input through a 5-stage pipeline:
236
+
237
+ 1. **Init** — Creates a temporary workspace and resolves input mode
238
+ 2. **Extract** — Pulls frames via FFmpeg (I-frame priority, FPS fallback; skipped in `frames` mode)
239
+ 3. **Analyze** — Computes an information gain score G(t) for each adjacent frame pair
240
+ 4. **Prune** — Selects frames based on G(t) scores using the chosen pruning strategy
241
+ 5. **Finalize** — Writes output files (atomic rename) or returns Buffers; cleans up workspace
242
+
243
+ ### Vision Analysis
244
+
245
+ The analyzer scores each pair of adjacent frames through 4 stages:
246
+
247
+ 1. **AKAZE Feature Diff** — Detects and matches keypoints between frames; identifies newly appeared and disappeared features
248
+ 2. **DBSCAN Clustering** — Groups new feature points into spatial clusters
249
+ 3. **IoU Tracking** — Tracks cluster bounding boxes across time; applies decay to repeated animation regions
250
+ 4. **G(t) Scoring** — Calculates information gain from cluster area ratio and feature density, discounting animated areas
251
+
252
+ Frames with higher G(t) scores represent greater visual change and are preserved during pruning.
253
+
254
+ ## Requirements
255
+
256
+ - **Node.js** >= 20
257
+ - **FFmpeg**: Bundled via `ffmpeg-static` — no system installation needed
258
+ - **OpenCV**: Bundled as WASM via `@techstark/opencv-js` — no native build needed
259
+ - **sharp**: Requires native binaries. Pre-built binaries are automatically downloaded for most platforms. See the [sharp installation guide](https://sharp.pixelplumbing.com/install) if you encounter build issues.
260
+
261
+ ## License
262
+
263
+ [MIT](./LICENSE)
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};