> ## Documentation Index
> Fetch the complete documentation index at: https://docs.facebetter.net/llms.txt
> Use this file to discover all available pages before exploring further.

# 实现美颜

> 接入 Facebetter Web SDK 2.0

<Note>
  SDK **2.0.0**。鉴权：[授权与许可](/zh/intro/license)。枚举取值：[参数枚举](/zh/intro/makeup)。
</Note>

## 安装

```bash theme={null}
npm install facebetter
```

```javascript theme={null}
import {
  BeautyEffectEngine,
  EngineConfig,
  FrameType,
  MirrorMode,
  Reshape,
  SmoothingStyle,
  WhiteningStyle,
  LipstickColor,
  ChromaKeyColor,
  EngineEventCode,
  FacebetterError,
} from 'facebetter';
```

## 创建引擎

Web 的 `EngineConfig` **只有**下列字段，配置对象上没有 `appId` / `appKey`。

| 字段 | 用途 |
| - | - |
| `licenseToken` | 许可证 token，或 `{token}` JSON。**必填。** 在 `init()` 之前换好。 |

引擎运行时文件在 `init()` 时由 SDK 加载，不必在站点根路径放 `resource.fbd`。可选下载进度：`init({ onProgress })`。引擎不会去换 token。Web **不支持**离线 `.lic`。

```javascript theme={null}
const licenseToken = await fetch('/api/facebetter/auth', { method: 'POST' }).then((r) => r.text());
const engine = new BeautyEffectEngine(new EngineConfig({ licenseToken }));

await engine.setLogConfig({
  consoleEnabled: true,
  fileEnabled: false,
  level: 2, // TRACE=0 … CRITICAL=5
});

await engine.init();
```

<Warning>
  若要看到加载 / 鉴权日志，请在 `init()` **之前**调用 `setLogConfig`。浏览器不支持文件日志（`fileEnabled` 会被忽略）。
</Warning>

```javascript theme={null}
try {
  await engine.init();
} catch (error) {
  if (error instanceof FacebetterError) {
    console.error(error.code, error.message);
  }
  throw error;
}
```

## 皮肤美颜

强度范围 **`[0.0, 1.0]`**。`0` 表示关闭。

```javascript theme={null}
engine.setSmoothing(0.8);
engine.setSmoothingStyle(SmoothingStyle.Natural);
engine.setWhitening(0.5);
engine.setWhiteningStyle(WhiteningStyle.ColdWhite);
engine.setSharpening(0.3);
engine.setRosiness(0.2);
engine.setBeautySkinOnly(true);
```

<Tip>
  `setBeautySkinOnly(true)` 后，磨皮 / 美白 / 锐化 / 红润只作用在检出的皮肤上，衣服和背景不受影响。
</Tip>

风格枚举见 [参数枚举](/zh/intro/makeup)。

## 美型

```javascript theme={null}
engine.setReshape(Reshape.FaceThin, 0.3);
engine.setReshape(Reshape.EyeSize, 0.2);
```

范围是 **`[-1.0, 1.0]`**。`0` 为关闭。正负是相反方向（例如 `FaceThin`：瘦脸 / 丰颊）。完整列表：[参数枚举](/zh/intro/makeup)。

## 美体

```javascript theme={null}
const bodyPack = new Uint8Array(
  await (await fetch('/resource_body.fbd')).arrayBuffer()
);
engine.addResourcePack(bodyPack);
engine.setBodyReshape(BodyReshape.WaistSlim, 0.4);
engine.setBodyReshape(BodyReshape.LegStretch, 0.3);
engine.setBodyReshape(BodyReshape.TorsoLong, 0.3);
```

范围是 **`[0.0, 1.0]`**。`0` 关闭。先在 `init()` 之后拉取 `resource_body.fbd` 并 `addResourcePack`。完整列表：[参数枚举](/zh/intro/makeup)。资源包：[可选资源包](/zh/intro/resource-packs)。

## 美妆

强度、形状、色号分开设置。强度 `[0.0, 1.0]`。预设见 [参数枚举](/zh/intro/makeup)。

```javascript theme={null}
engine.setLipstick(0.4);
engine.setLipstickColor(LipstickColor.Rouge);

engine.setBlush(0.3);
engine.setBlushStyle(/* BlushStyle.SunKissed */);
engine.setBlushColor(/* BlushColor.CoralPink */);

engine.setContour(0.4);
engine.setContourStyle(/* ContourStyle.Natural */);

engine.setEyeShadow(0.35);
engine.setEyeShadowStyle(/* EyeShadowStyle.Soft */);
engine.setEyeShadowColor(/* EyeShadowColor.Plum */);

engine.setEyeLiner(0.3);
engine.setEyeLinerStyle(/* ... */);
engine.setEyeLinerColor(/* ... */);

engine.setEyebrow(0.3);
engine.setEyebrowStyle(/* ... */);
engine.setEyebrowColor(/* ... */);

engine.setEyelash(0.3);
engine.setEyelashStyle(/* ... */);
engine.setEyelashColor(/* ... */);

engine.setPupil(0.4);
engine.setPupilColor(/* PupilColor.Hazel */);
```

## 滤镜与贴纸

把 `.fbd` 的 URL 或内存中的 `Uint8Array` 直接传给 setter。

```javascript theme={null}
engine.setFilter('/assets/filters/portrait/natural/natural.fbd');
engine.setFilterIntensity(0.8);
engine.clearFilter();

engine.setSticker('/stickers/face/fox.fbd');
engine.clearSticker();
```

3D 贴纸需要可选包 `resource_3d.fbd`（不在 Web SDK 内）。见 [可选资源包](/zh/intro/resource-packs)。`init()` 之后用字节登记：

```javascript theme={null}
const pack = new Uint8Array(
  await (await fetch('/resource_3d.fbd')).arrayBuffer()
);
engine.addResourcePack(pack);
engine.set3DSticker('/stickers/3d/oculos.fbd');
engine.clear3DSticker();
```

```javascript theme={null}
const bytes = new Uint8Array(await (await fetch('/filters/chuxin.fbd')).arrayBuffer());
engine.setFilter(bytes);
```

## 虚拟背景与色度键

```javascript theme={null}
engine.setVirtualBackgroundBlur(0.6);          // [0, 1]；传 0 会关掉虚化
engine.setVirtualBackground('/background.jpg'); // 路径或 Uint8Array（png/jpg）
engine.clearVirtualBackground();

engine.setChromaKey(ChromaKeyColor.Green);
engine.setChromaKeySimilarity(0.4);
engine.setChromaKeySmoothness(0.2);
engine.setChromaKeyDesaturation(0.3);
engine.clearChromaKey();
```

<Tip>
  色度键只换**遮罩**。填充仍由 `setVirtualBackgroundBlur` 或 `setVirtualBackground` 控制。虚化与背景图互斥。
</Tip>

## 回调

```javascript theme={null}
engine.setCallbacks({
  onEngineEvent: (code, message) => {
    if (code === EngineEventCode.LicenseValidationSuccess) {
      console.log('license ok');
    } else if (code === EngineEventCode.LicenseValidationFailed) {
      console.error('license failed', message);
    } else if (code === EngineEventCode.InitializationComplete) {
      console.log('engine ready');
    } else if (code === EngineEventCode.InitializationFailed) {
      console.error('init failed', message);
    }
  },
  onFaceLandmarks: (faces) => {
    // faces[].rect, key_points, visibility, face_id, score, pitch, roll, yaw
  },
  maxFaces: 10,
});
```

| 码 | 名称 |
| - | - |
| `0` | `EngineEventCode.LicenseValidationSuccess` |
| `1` | `EngineEventCode.LicenseValidationFailed` |
| `100` | `EngineEventCode.InitializationComplete` |
| `101` | `EngineEventCode.InitializationFailed` |

不需要关键点就不要传 `onFaceLandmarks`，否则每帧都会做人脸检测。

## 处理画面

优先用 `processImage`，同步返回 `ImageData`。

```javascript theme={null}
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const video = document.querySelector('video');

function loop() {
  if (engine.initialized && video.readyState >= 2) {
    const result = engine.processImage(
      video,
      video.videoWidth,
      video.videoHeight,
      FrameType.Video,
      MirrorMode.Horizontal, // 前置摄像头
    );
    if (result) {
      if (canvas.width !== result.width) canvas.width = result.width;
      if (canvas.height !== result.height) canvas.height = result.height;
      ctx.putImageData(result, 0, 0);
    }
  }
  requestAnimationFrame(loop);
}
loop();
```

`input` 可以是 `ImageData`、`HTMLImageElement`、`HTMLCanvasElement`、`HTMLVideoElement`，或 `Uint8ClampedArray`（此时必须传 `width` / `height`）。

* `FrameType.Video` — 摄像头 / 直播（带时域平滑）
* `FrameType.Image` — 静态图

`processTexture(textureHandle, width, height, stride, frameType, mirrorMode)` 用于自定义 GPU 纹理输入。**该路径不应用镜像。** 普通 Web 应用请用 `processImage`。

## 性能统计

```javascript theme={null}
const { fps, avgProcessTimeMs, sessionTimeS } = engine.getStats();
```

## 销毁

```javascript theme={null}
engine.destroy();
```

页面卸载或 React/Vue 组件销毁时必须调用。

接入 TRTC / 声网 / LiveKit：见 [第三方对接](/zh/web/third-party-integration)。

## 相关文档

* [第三方对接](/zh/web/third-party-integration)
* [错误处理](/zh/web/error-handling)
* [最佳实践](/zh/web/best-practices)
* [API 参考](/zh/web/api-reference)
* [常见问题](/zh/web/faq)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.