@maravilla-labs/platform 0.15.0 → 0.17.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/CHANGELOG.md +45 -0
- package/dist/config.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +67 -1
- package/dist/index.js.map +1 -1
- package/dist/{transforms-BPrlJoYd.d.ts → transforms-C2zafn5k.d.ts} +126 -1
- package/package.json +1 -1
- package/src/transforms.ts +206 -0
- package/tests/focal-css.test.ts +156 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @maravilla-labs/platform
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 16d4f8d: Extend `resize({ crop: 'focal' })` framing with three more opt-in knobs (on top
|
|
8
|
+
of the initial `zoom` factor):
|
|
9
|
+
- **`zoom`** now also accepts values `< 1` (crop _into_ the subject) and
|
|
10
|
+
`'auto'` (a curated, consistent occupancy); range is `0.4..=100.0` | `'auto'`.
|
|
11
|
+
- **`padding`** — margin around the subject box (`0.0..=2.0`, fraction per side)
|
|
12
|
+
so a tight crop keeps breathing room around the head.
|
|
13
|
+
- **`subject`** — `'union'` (all faces, default) or `'primary'` (the
|
|
14
|
+
highest-confidence face, re-centred on it), so a group photo can crop to one
|
|
15
|
+
person.
|
|
16
|
+
|
|
17
|
+
Still fully backward compatible: the new fields never serialize when omitted, so
|
|
18
|
+
existing derived keys are byte-identical. The SDK CSS helpers gain the same
|
|
19
|
+
knobs plus a new `primaryFaceBox()` for parity.
|
|
20
|
+
|
|
21
|
+
## 0.16.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 4b6b127: Add a `zoom` option to `resize({ crop: 'focal' })` for face-box-relative crop
|
|
26
|
+
framing. Until now a focal crop only centered a fixed-size window on the stored
|
|
27
|
+
focal _point_ — the detected face's bounding-box size was discarded, so there
|
|
28
|
+
was no way to control how tightly the subject was framed. `zoom` (a scale
|
|
29
|
+
factor, `1.0..=100.0`) now sizes the crop window from the union of the source's
|
|
30
|
+
detected faces: `1.0` frames tightly (a headshot), larger pulls back for
|
|
31
|
+
headroom/context, and a value large enough to exceed the image saturates to the
|
|
32
|
+
plain scale-to-cover crop. It's declarable in the `maravilla.config.ts`
|
|
33
|
+
`transforms` block too.
|
|
34
|
+
|
|
35
|
+
Fully backward compatible: `zoom` is optional and never serializes when omitted,
|
|
36
|
+
so every existing focal/center crop hashes to the same derived key and is
|
|
37
|
+
byte-identical. `zoom` is only honoured for `crop: 'focal'` on a source that has
|
|
38
|
+
stored face data (run `detectFaces` first); it's ignored for `crop: 'center'`
|
|
39
|
+
and a no-op when no faces were detected.
|
|
40
|
+
|
|
41
|
+
Also adds two pure, isomorphic, framework-agnostic helpers to
|
|
42
|
+
`@maravilla-labs/platform` — `focalCssFrame()` and `unionFaceBox()` — the
|
|
43
|
+
browser-side counterpart to the server crop: they turn a source's focal
|
|
44
|
+
point + detected faces into `object-position` + `transform: scale` CSS values,
|
|
45
|
+
so one `<img>` can re-frame around the face(s) for any container aspect ratio
|
|
46
|
+
and zoom without a server round-trip or a pre-rendered crop per shape.
|
|
47
|
+
|
|
3
48
|
## 0.15.0
|
|
4
49
|
|
|
5
50
|
### Minor Changes
|
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as TransformsConfig } from './transforms-
|
|
2
|
-
export { a as TransformsPatternSpec } from './transforms-
|
|
1
|
+
import { T as TransformsConfig } from './transforms-C2zafn5k.js';
|
|
2
|
+
export { a as TransformsPatternSpec } from './transforms-C2zafn5k.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @fileoverview Typed schema for `maravilla.config.{ts,yaml,json}` files.
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { ActAsContext, AddCircleMemberOptions, AddRelationOptions, ApiKeyInfo, A
|
|
|
3
3
|
export { b as RenClient, a as RenClientOptions, R as RenEvent, g as getOrCreateClientId, r as renFetch, c as storageDelete, s as storageUpload } from './ren-DetHRhCL.js';
|
|
4
4
|
import { LocalParticipant } from 'livekit-client';
|
|
5
5
|
export { RegisterPushOptions, RegisterPushResult, offsetBefore, registerPush, unregisterPush } from './push.js';
|
|
6
|
-
export { D as DetachAudioOpts, e as DetectFacesOpts, l as DocConvertOpts, i as DocFormat, q as DocInsertQrCodeOpts, p as DocReplaceImagesOpts, s as DocTemplateMergeOpts, k as DocThumbnailOpts, n as DocToHtmlOpts, m as DocToMarkdownOpts, j as DocToPdfOpts, d as ExtractAudioOpts, E as ExtractFramesOpts, g as FaceBox, f as FocalPoint, h as FocalPointRecord, F as FramesetManifest, I as ImageFormat, o as ImageRef, u as JobHandle, J as JobStatus, v as JobStatusResponse, M as MediaInfo, O as OcrOpts, Q as QrCodeSpec, r as QrPayload, R as ResizeOpts, c as ThumbnailOpts, b as TranscodeOpts, t as TransformSpec, T as TransformsConfig, a as TransformsPatternSpec, w as TransformsService, V as VideoFormat, x as keyFor,
|
|
6
|
+
export { D as DetachAudioOpts, e as DetectFacesOpts, l as DocConvertOpts, i as DocFormat, q as DocInsertQrCodeOpts, p as DocReplaceImagesOpts, s as DocTemplateMergeOpts, k as DocThumbnailOpts, n as DocToHtmlOpts, m as DocToMarkdownOpts, j as DocToPdfOpts, d as ExtractAudioOpts, E as ExtractFramesOpts, g as FaceBox, z as FocalCssFrame, y as FocalCssFrameInput, f as FocalPoint, h as FocalPointRecord, F as FramesetManifest, I as ImageFormat, o as ImageRef, u as JobHandle, J as JobStatus, v as JobStatusResponse, M as MediaInfo, O as OcrOpts, Q as QrCodeSpec, r as QrPayload, R as ResizeOpts, S as SubjectBox, c as ThumbnailOpts, b as TranscodeOpts, t as TransformSpec, T as TransformsConfig, a as TransformsPatternSpec, w as TransformsService, V as VideoFormat, C as focalCssFrame, x as keyFor, B as primaryFaceBox, G as transforms, A as unionFaceBox } from './transforms-C2zafn5k.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Media service for video/audio room management.
|
package/dist/index.js
CHANGED
|
@@ -2096,8 +2096,71 @@ function keyFor(srcKey, spec) {
|
|
|
2096
2096
|
const ext = outputExtension(spec);
|
|
2097
2097
|
return `__derived/${srcHash}/${variantHash}.${ext}`;
|
|
2098
2098
|
}
|
|
2099
|
+
var AUTO_ZOOM = 1.22;
|
|
2100
|
+
var clamp01 = (n) => Math.min(1, Math.max(0, n));
|
|
2101
|
+
function unionFaceBox(faces) {
|
|
2102
|
+
if (!faces || faces.length === 0) return null;
|
|
2103
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
2104
|
+
for (const f of faces) {
|
|
2105
|
+
x0 = Math.min(x0, f.x);
|
|
2106
|
+
y0 = Math.min(y0, f.y);
|
|
2107
|
+
x1 = Math.max(x1, f.x + f.w);
|
|
2108
|
+
y1 = Math.max(y1, f.y + f.h);
|
|
2109
|
+
}
|
|
2110
|
+
x0 = clamp01(x0);
|
|
2111
|
+
y0 = clamp01(y0);
|
|
2112
|
+
x1 = clamp01(x1);
|
|
2113
|
+
y1 = clamp01(y1);
|
|
2114
|
+
const w = x1 - x0;
|
|
2115
|
+
const h = y1 - y0;
|
|
2116
|
+
if (w <= 0 || h <= 0) return null;
|
|
2117
|
+
return { x: x0, y: y0, w, h };
|
|
2118
|
+
}
|
|
2119
|
+
function primaryFaceBox(faces) {
|
|
2120
|
+
if (!faces || faces.length === 0) return null;
|
|
2121
|
+
const f = faces.reduce((a, b) => b.score > a.score ? b : a);
|
|
2122
|
+
const x0 = clamp01(f.x);
|
|
2123
|
+
const y0 = clamp01(f.y);
|
|
2124
|
+
const x1 = clamp01(f.x + f.w);
|
|
2125
|
+
const y1 = clamp01(f.y + f.h);
|
|
2126
|
+
const w = x1 - x0;
|
|
2127
|
+
const h = y1 - y0;
|
|
2128
|
+
if (w <= 0 || h <= 0) return null;
|
|
2129
|
+
return {
|
|
2130
|
+
box: { x: x0, y: y0, w, h },
|
|
2131
|
+
focal: { x: x0 + w / 2, y: clamp01(y0 + 0.4 * h) }
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
function focalCssFrame(input) {
|
|
2135
|
+
const { faces, imgWidth, imgHeight, containerAspect } = input;
|
|
2136
|
+
let box;
|
|
2137
|
+
let focal = input.focal;
|
|
2138
|
+
if (input.subject === "primary") {
|
|
2139
|
+
const p = primaryFaceBox(faces);
|
|
2140
|
+
box = p?.box ?? null;
|
|
2141
|
+
if (p) focal = p.focal;
|
|
2142
|
+
} else {
|
|
2143
|
+
box = unionFaceBox(faces);
|
|
2144
|
+
}
|
|
2145
|
+
const objectPosition = `${(clamp01(focal.x) * 100).toFixed(1)}% ${(clamp01(focal.y) * 100).toFixed(1)}%`;
|
|
2146
|
+
const base = { objectPosition, scale: 1, transformOrigin: objectPosition };
|
|
2147
|
+
if (!box || imgWidth <= 0 || imgHeight <= 0 || !(containerAspect > 0)) return base;
|
|
2148
|
+
const iar = imgWidth / imgHeight;
|
|
2149
|
+
const car = containerAspect;
|
|
2150
|
+
const z = input.zoom === "auto" ? AUTO_ZOOM : Math.max(0.01, input.zoom ?? 1);
|
|
2151
|
+
const grow = 1 + 2 * Math.max(0, input.padding ?? 0);
|
|
2152
|
+
const bw = box.w * grow;
|
|
2153
|
+
const bh = box.h * grow;
|
|
2154
|
+
const targetW = z * Math.max(bw, bh * car / iar);
|
|
2155
|
+
const coverW = car >= iar ? 1 : car / iar;
|
|
2156
|
+
const scale = Math.max(1, coverW / targetW);
|
|
2157
|
+
return { objectPosition, scale: Math.round(scale * 1e3) / 1e3, transformOrigin: objectPosition };
|
|
2158
|
+
}
|
|
2099
2159
|
var transforms = {
|
|
2100
|
-
keyFor
|
|
2160
|
+
keyFor,
|
|
2161
|
+
focalCssFrame,
|
|
2162
|
+
unionFaceBox,
|
|
2163
|
+
primaryFaceBox
|
|
2101
2164
|
};
|
|
2102
2165
|
|
|
2103
2166
|
// src/index.ts
|
|
@@ -2144,17 +2207,20 @@ export {
|
|
|
2144
2207
|
attachTrack,
|
|
2145
2208
|
clearPlatformCache,
|
|
2146
2209
|
detachTrack,
|
|
2210
|
+
focalCssFrame,
|
|
2147
2211
|
getCurrentRequestStore,
|
|
2148
2212
|
getOrCreateClientId,
|
|
2149
2213
|
getPlatform,
|
|
2150
2214
|
keyFor,
|
|
2151
2215
|
offsetBefore,
|
|
2216
|
+
primaryFaceBox,
|
|
2152
2217
|
registerPush,
|
|
2153
2218
|
renFetch,
|
|
2154
2219
|
runWithRequest,
|
|
2155
2220
|
storageDelete,
|
|
2156
2221
|
storageUpload,
|
|
2157
2222
|
transforms,
|
|
2223
|
+
unionFaceBox,
|
|
2158
2224
|
unregisterPush
|
|
2159
2225
|
};
|
|
2160
2226
|
//# sourceMappingURL=index.js.map
|