@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 +21 -0
- package/README.md +263 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +948 -0
- package/dist/constants.d.ts +20 -0
- package/dist/core/analyzer.d.ts +29 -0
- package/dist/core/dbscan.d.ts +10 -0
- package/dist/core/extractor.d.ts +7 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/input-resolver.d.ts +13 -0
- package/dist/core/orchestrator.d.ts +2 -0
- package/dist/core/pruner.d.ts +42 -0
- package/dist/core/workspace.d.ts +19 -0
- package/dist/index.cjs +936 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +895 -0
- package/dist/types/index.d.ts +79 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/min-heap.d.ts +16 -0
- package/dist/utils/paths.d.ts +9 -0
- package/package.json +73 -0
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
|
+
[](https://www.npmjs.com/package/@lumy-pack/scene-sieve)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
[](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 {};
|