docs: align Markdown table columns in README
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
db572ee51d
commit
1b3a6d5a4a
83
README.md
83
README.md
@ -107,6 +107,7 @@ import '@shopify/react-native-skia';
|
||||
**几乎总是**因为 Metro 同时解析到多份 reanimated / skia / gesture-handler / fast-opencv / safe-area 等包。
|
||||
|
||||
**最佳实践**:
|
||||
|
||||
- 直接复制 `example/metro.config.js` 里的 `singletonPackages` + extraNodeModules + blockList 写法
|
||||
- 在你的 `index.js` 最顶部加入 gesture-handler → reanimated → skia 三个 import
|
||||
- 重启 Metro (`--reset-cache`) + 重装 app
|
||||
@ -141,8 +142,9 @@ import MaskSegmentCanvas, {
|
||||
|
||||
主要导出一览:
|
||||
|
||||
|
||||
| 类别 | 名称 |
|
||||
| ---- | ---- |
|
||||
| -------------- | ----------------------------------------------------------------------------------- |
|
||||
| 组件 | `MaskSegmentCanvas`(default) |
|
||||
| Ref / Props 类型 | `MaskSegmentCanvasRef`、`MaskSegmentCanvasProps` |
|
||||
| 会话 / 回调类型 | `MaskSegmentSession`、`PaintCallbackPayload`、`PaintedRegionRecord`、`SavePaintResult` |
|
||||
@ -152,6 +154,7 @@ import MaskSegmentCanvas, {
|
||||
| 工具 | `prewarmPngBgrCacheAsync`、`prewarmPngBgrCache` |
|
||||
| 运行时 | `DEFAULT_*_CONFIG`、`getMaskSegmentRuntimeConfig`、`setMaskSegmentRuntimeConfig` |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 推荐:通过 example/ 目录学习集成
|
||||
@ -385,15 +388,15 @@ export function PaintScreen() {
|
||||
|
||||
|
||||
| state | 类型 | 用途 |
|
||||
| ------------------ | -------------------------- | ---- |
|
||||
| `imagePaths` | `{ origin, mask } \| null` | 业务侧解析后的本地/远程图片路径 |
|
||||
| ----------------- | ---------------------------- | ------------------------------------------------ |
|
||||
| `imagePaths` | `{ origin, mask } | null` | 业务侧解析后的本地/远程图片路径 |
|
||||
| `pathsError` | `string` | 路径解析或 PNG 预热失败文案 |
|
||||
| `watchState` | `MaskSegmentWatchState \| ''` | `onWatch` 上报的初始化阶段 |
|
||||
| `watchState` | `MaskSegmentWatchState | ''` | `onWatch` 上报的初始化阶段 |
|
||||
| `isInteractive` | 派生 | `interactive` 或 `mask_paths_ready` 时为 true,可开放操作 |
|
||||
| `isOutlineReady` | 派生 | `mask_paths_ready` 时为 true,轮播虚线已就绪 |
|
||||
| `isCanvasLoading` | 派生 | 画布初始化阻塞 Loading(不含 PNG 路径等待) |
|
||||
| `errorMessage` | `string` | 由 `onError` 写入的分割/加载失败文案 |
|
||||
| `sessionDraft` | `MaskSegmentSession \| null` | MMKV 等恢复的草稿 |
|
||||
| `sessionDraft` | `MaskSegmentSession | null` | MMKV 等恢复的草稿 |
|
||||
|
||||
|
||||
#### 配置项怎么选
|
||||
@ -447,8 +450,9 @@ const hasError = watchState === 'error';
|
||||
|
||||
### 图片与初始化
|
||||
|
||||
|
||||
| 属性 | 类型 | 必填 | 默认 | 说明 |
|
||||
| ---- | ---- | ---- | ---- | ---- |
|
||||
| ------------------------ | ------------------------- | --- | --- | ----------------------------------------------------------------- |
|
||||
| `originUrl` | `string` | 是* | — | 原图地址(`file://`、绝对路径或 `http(s)://`) |
|
||||
| `maskUrl` | `string` | 是* | — | 掩码图地址(语义色块图,建议与原图同尺寸) |
|
||||
| `originImgPath` | `string` | — | — | **deprecated**,请用 `originUrl` |
|
||||
@ -457,13 +461,16 @@ const hasError = watchState === 'error';
|
||||
| `initialPaintColor` | `BgrColor` | 否 | — | **可选**。初始自定义笔刷色 `{ b, g, r }`;不传则默认无笔刷,需用户选色或 `ref.setPaintColor` |
|
||||
| `initialPaintConfigJson` | `Record<string, unknown>` | 否 | — | **可选**。与 `initialPaintColor` 配套的笔刷配置,上色成功时随 `onPaintCallback` 回传 |
|
||||
|
||||
|
||||
### 识别色与虚线(顶层便捷配置)
|
||||
|
||||
|
||||
| 属性 | 类型 | 默认 | 说明 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| -------------------- | --------------------- | -------------------------- | ------------------------------------------ |
|
||||
| `semanticColors` | `MaskSemanticColor[]` | `MASK_SEMANTIC_COLORS` | 掩码语义识别色,等同 `maskConfig.semanticColors` |
|
||||
| `regionOutlineColor` | `string` | `rgba(20, 120, 235, 0.58)` | 分区虚线高亮色,等同 `paintConfig.regionOverlayFill` |
|
||||
|
||||
|
||||
顶层属性优先级高于嵌套 `maskConfig` / `paintConfig`。
|
||||
|
||||
`MaskSemanticColor` 结构:
|
||||
@ -480,8 +487,9 @@ const hasError = watchState === 'error';
|
||||
|
||||
### maskConfig
|
||||
|
||||
|
||||
| 字段 | 类型 | 默认 | 说明 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| ------------------------------ | --------------------- | ------------------------ | ------------------------------------- |
|
||||
| `semanticColors` | `MaskSemanticColor[]` | 内置色表 | 掩码语义色(可被顶层 `semanticColors` 覆盖) |
|
||||
| `blackThreshold` | `number` | `30` | BGR 最大值低于此值的像素视为黑色背景 |
|
||||
| `maxRegionColors` | `number` | `6` | 最终保留的最大语义分区数 |
|
||||
@ -505,12 +513,14 @@ const hasError = watchState === 'error';
|
||||
| `splitWallsChromaBlurRadius` | `number` | `5` | 预留:色度平滑半径 |
|
||||
| `splitWallsNeutralChromaMax` | `number` | `14` | 白/灰墙低饱和判定半径;与有色墙强制分界 |
|
||||
|
||||
|
||||
开启 `splitWalls` 后,原有单一 `wall` 区域会被替换为多个 `wall-N` 子区,各自独立上色与撤销。旧 Session 中 `regionName: 'wall'` 无法映射到新子区名,需重新上色。
|
||||
|
||||
### pipelineConfig
|
||||
|
||||
|
||||
| 字段 | 类型 | 默认 | 说明 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| -------------------------- | -------- | ------- | ----------------------- |
|
||||
| `maxImageLongSide` | `number` | `720` | 分割 / pickMap / 工作区缩放最长边 |
|
||||
| `paintFreqMaxLongSide` | `number` | `480` | OpenCV LAB 高低频最长边 |
|
||||
| `originPreviewMaxLongSide` | `number` | `360` | 预览最长边(主路径走工作区分辨率) |
|
||||
@ -519,10 +529,12 @@ const hasError = watchState === 'error';
|
||||
| `contourApproxEpsilon` | `number` | `0.003` | 轮廓多边形逼近系数 |
|
||||
| `maxRegions` | `number` | `500` | 分割阶段最大区域数上限 |
|
||||
|
||||
|
||||
### paintConfig
|
||||
|
||||
|
||||
| 字段 | 类型 | 默认 | 说明 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| -------------------------- | ------------ | ----------------------- | -------------------------- |
|
||||
| `palette` | `BgrColor[]` | 6 色内置盘 | 底部笔刷色条 |
|
||||
| `colorBaseOpacity` | `number` | `0.88` | 底色不透明度 |
|
||||
| `lLightOpacity` | `number` | `0.50` | L 通道叠加强度 |
|
||||
@ -536,10 +548,12 @@ const hasError = watchState === 'error';
|
||||
| `regionOverlayFill` | `string` | `rgba(20,120,235,0.58)` | 虚线/高亮填充色 |
|
||||
| `regionOutlineStrokeWidth` | `number` | `4` | 虚线描边宽度 |
|
||||
|
||||
|
||||
### interactionConfig
|
||||
|
||||
|
||||
| 字段 | 类型 | 默认 | 说明 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| ----------------------- | --------- | ------- | ------------------- |
|
||||
| `pickMapSearchRadiusPx` | `number` | `14` | 点击 pickMap 搜索半径(像素) |
|
||||
| `kickMaskPickRadiusPx` | `number` | `36` | 踢脚线掩码拾取半径 |
|
||||
| `thinStripPadding` | `number` | `0.008` | 细条带(踢脚线)点击扩展比例 |
|
||||
@ -547,6 +561,7 @@ const hasError = watchState === 'error';
|
||||
| `initRegionFlashMs` | `number` | `1000` | 初始化轮播每条虚线停留毫秒 |
|
||||
| `enableInitRegionFlash` | `boolean` | `true` | 是否启用初始化轮播 |
|
||||
|
||||
|
||||
> 完整默认值常量:`DEFAULT_MASK_CONFIG`、`DEFAULT_PIPELINE_CONFIG`、`DEFAULT_PAINT_CONFIG`、`DEFAULT_INTERACTION_CONFIG`(自包入口导出)。
|
||||
|
||||
### UI 开关与样式
|
||||
@ -573,12 +588,14 @@ const hasError = watchState === 'error';
|
||||
|
||||
### 回调
|
||||
|
||||
|
||||
| 属性 | 签名 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| ----------------- | ----------------------------------------- | ---------------------------------- |
|
||||
| `onWatch` | `(state, durationMs, detail?) => void` | 初始化阶段回调;`durationMs` 自本次 `init` 起算 |
|
||||
| `onPaintCallback` | `(payload: PaintCallbackPayload) => void` | 上色成功或未选笔刷时点击分区 |
|
||||
| `onError` | `(message, error?) => void` | 分割或加载失败 |
|
||||
|
||||
|
||||
`PaintCallbackPayload`(判别联合,`payload.kind` 区分):
|
||||
|
||||
```ts
|
||||
@ -614,13 +631,15 @@ onPaintCallback={payload => {
|
||||
|
||||
`onWatch` 的 `detail`(`MaskSegmentWatchDetail`):
|
||||
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| ----------------- | --------- | ----------------- |
|
||||
| `regionCount` | `number` | 当前有效分区数 |
|
||||
| `maskPathsReady` | `boolean` | 轮廓 Skia 路径是否就绪 |
|
||||
| `freqLayersReady` | `boolean` | 高低频 Shader 纹理是否就绪 |
|
||||
| `errorMessage` | `string` | `error` 状态下的失败说明 |
|
||||
|
||||
|
||||
#### onWatch 状态流转
|
||||
|
||||
```
|
||||
@ -643,8 +662,9 @@ init
|
||||
|
||||
通过 `ref` 调用(类型 `MaskSegmentCanvasRef`):
|
||||
|
||||
|
||||
| 方法 | 签名 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| ------------------- | ---------------------------------------- | ------------------------------------- |
|
||||
| `reset` | `() => void` | 撤销上一步上色(按 `paintHistory`) |
|
||||
| `swap` | `(showOrigin?: boolean) => void` | 对比原图;不传参 toggle,传 `true`/`false` 显式开关 |
|
||||
| `save` | `(options?) => Promise<SavePaintResult>` | 合成并保存 PNG;`options.destDir` 可选输出目录 |
|
||||
@ -657,6 +677,7 @@ init
|
||||
| `getRegions` | `() => SegmentRegion[]` | 当前分区列表快照 |
|
||||
| `getPaintedRegions` | `() => PaintedRegionRecord[]` | 当前上色记录快照 |
|
||||
|
||||
|
||||
`SavePaintResult`:`{ filePath, width, height, paintedCount, previewPath? }`
|
||||
|
||||
代码示例:
|
||||
@ -832,68 +853,80 @@ MaskSegmentApp/ # 仓库根目录(npm 包 react-n
|
||||
|
||||
Demo 在挂载画布前调用 `prewarmPngBgrCacheAsync([origin, mask])`,PNG 解码命中内存缓存。典型日志:
|
||||
|
||||
|
||||
| 阶段 | watchState | 约耗时 | 说明 |
|
||||
| ---- | ---------- | ------ | ---- |
|
||||
| ------- | -------------------------------- | -------------- | ------------------------------------ |
|
||||
| 掩码对齐 | `mask_aligned` | ~160ms | 掩码缩放到分割工作分辨率 |
|
||||
| 分区完成 | `regions_ready` / `mask_sampled` | ~320ms | 布局扫描 + 踢脚线 + pickMap |
|
||||
| **可交互** | **`interactive`** | **~320–450ms** | 可点击选区、选色、Shader 上色 |
|
||||
| **可交互** | `**interactive`** | **~320–450ms** | 可点击选区、选色、Shader 上色 |
|
||||
| 轮廓就绪 | `mask_paths_ready` | ~430–550ms | 比 `interactive` 晚 **~100ms**,轮播虚线可显示 |
|
||||
|
||||
|
||||
`interactive` **不等待**轮廓路径;`mask_paths_ready` 仅影响初始化轮播虚线与可选 UI 提示。
|
||||
|
||||
同图各子步骤(`__DEV__` 日志,默认 pipeline)量级:
|
||||
|
||||
|
||||
| 子步骤 | 约耗时 | 工作分辨率 |
|
||||
| ------ | ------ | ---------- |
|
||||
| ----------------- | --------- | ------------------------------ |
|
||||
| OpenCV LAB 高低频 | ~10–40ms | 270×480 |
|
||||
| 高低频 Skia 纹理 | ~20–30ms | 同上 |
|
||||
| 布局扫描 + 踢脚线 + 点击查表 | ~90–120ms | 405×720(1080p 缩至 longSide 720) |
|
||||
| 全量轮廓路径(异步,不阻塞交互) | ~80–150ms | 270×480 |
|
||||
|
||||
|
||||
### 分辨率与 `pipelineConfig` 的关系
|
||||
|
||||
计算密集型步骤被 **最长边上限** 截断,**不随 4K/8K 原图线性放大**;**PNG 全图解码**仍随像素量线性增长。
|
||||
|
||||
|
||||
| 步骤 | 配置项 | 1080×1920 实际处理尺寸 | 随原图像素增长 |
|
||||
| ---- | ------ | ---------------------- | -------------- |
|
||||
| -------------- | --------------------------- | ---------------- | ------------------------ |
|
||||
| PNG 解码 | — | 1080×1920 × 2 张 | **是** |
|
||||
| 掩码分割 / pickMap | `maxImageLongSide: 720` | ~405×720 | **否**(长边 >720 时固定) |
|
||||
| Shader 高低频 | `paintFreqMaxLongSide: 480` | ~270×480 | **否** |
|
||||
| 工作区 Skia 原图 | 同 `maxImageLongSide` | ~405×720 | **否** |
|
||||
| 虚线轮廓 | `maskPathMaxLongSide: 480` | ~270×480 | **否**(不阻塞 `interactive`) |
|
||||
|
||||
|
||||
### `interactive` 预估(默认 pipeline)
|
||||
|
||||
|
||||
| 原图规格 | 相对 1080p 像素 | 有 PNG 预热 | 冷启动(无预热) |
|
||||
| -------- | --------------- | ----------- | ---------------- |
|
||||
| -------------- | ----------- | ------------- | -------------- |
|
||||
| 1080×1920 | 1× | **320–450ms** | **450–700ms** |
|
||||
| 1440×2560 (2K) | ~1.8× | **400–550ms** | **600–900ms** |
|
||||
| 3840×2160 (4K) | ~4× | **500–750ms** | **800–1200ms** |
|
||||
| 7680×4320 (8K) | ~16× | **0.8–1.5s** | **1.5–3s+** |
|
||||
|
||||
|
||||
> **300ms 内可交互**:在 1080p + 预热 + 默认 pipeline + 中高端机上**接近但偏乐观**;不宜作为全机型 SLA。
|
||||
|
||||
### 机型档位(1080p,默认 pipeline)
|
||||
|
||||
相对上述开发环境 ~320ms 的量级:
|
||||
|
||||
|
||||
| 档位 | 相对倍数 | 有预热 `interactive` | 冷启动 |
|
||||
| ---- | -------- | -------------------- | ------ |
|
||||
| -------------------- | -------- | ----------------- | ---------- |
|
||||
| 旗舰 iOS / 新旗舰 Android | 0.8–1.2× | 300–450ms | 500–800ms |
|
||||
| 中端 Android | 1.5–2.5× | 500–800ms | 700ms–1.2s |
|
||||
| 低端 Android(4GB、老 U) | 2.5–4× | 800ms–1.3s | 1–2s+ |
|
||||
|
||||
|
||||
Android 额外开销主要来自:JS ↔ OpenCV bridge、内存带宽/GC、Skia 纹理上传。
|
||||
|
||||
### 提高 `maxImageLongSide` 的影响
|
||||
|
||||
若将 `pipelineConfig.maxImageLongSide` 设为 **1280**(高于默认 720),分割工作区约 **720×1280**,像素约为 720 档的 **3×**:
|
||||
|
||||
|
||||
| 场景 | 默认 720 | 改为 1280 |
|
||||
| ---- | -------- | --------- |
|
||||
| ------------------------ | ---------- | ------------- |
|
||||
| 1080p `interactive`(中端机) | ~320–800ms | **500ms–1s+** |
|
||||
| 分割 / pickMap 耗时 | ~90–120ms | ~250–350ms |
|
||||
|
||||
|
||||
更高精度换更长初始化;若目标仍是 **<500ms 可交互**,建议维持默认 **720**,必要时降至 **640**。
|
||||
|
||||
### 优化建议
|
||||
@ -907,10 +940,10 @@ await prewarmPngBgrCacheAsync([originPath, maskPath]);
|
||||
// 再挂载 MaskSegmentCanvas
|
||||
```
|
||||
|
||||
2. **Loading 时机**:阻塞式 Loading 在 `interactive` 关闭;「轮廓准备中」可选监听 `mask_paths_ready`。
|
||||
3. **大图 / 低端机**:保持默认 `maxImageLongSide: 720`;可再将 `paintFreqMaxLongSide` 降至 **360**。
|
||||
4. **4K 素材**:业务侧先下采样再传入,或接受 **0.8–1.5s** 量级的 `interactive`(预热后)。
|
||||
5. **观测**:开发环境关注 Metro 中 `[MaskSegment]`、`[⏱ ...]` 与 `onWatch` 的 `durationMs`。
|
||||
1. **Loading 时机**:阻塞式 Loading 在 `interactive` 关闭;「轮廓准备中」可选监听 `mask_paths_ready`。
|
||||
2. **大图 / 低端机**:保持默认 `maxImageLongSide: 720`;可再将 `paintFreqMaxLongSide` 降至 **360**。
|
||||
3. **4K 素材**:业务侧先下采样再传入,或接受 **0.8–1.5s** 量级的 `interactive`(预热后)。
|
||||
4. **观测**:开发环境关注 Metro 中 `[MaskSegment]`、`[⏱ ...]` 与 `onWatch` 的 `durationMs`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user