滑动参数调节面板-双模式处理流程.md 27 KB

滑动参数调节面板 - 双模式处理流程

SliderAdjustmentPanel 组件的完整处理流程文档

📖 概述

本文档描述了滑动参数调节面板(SliderAdjustmentPanel)的完整处理流程,包括传统模式和WASM模式的双模式支持。


🎯 组件职责

滑动参数调节面板是三级面板,用于调整图像处理参数,支持:

  • 实时参数调节与预览
  • 参数持久化保存
  • 风格预设切换
  • 双处理模式(传统模式 / WASM模式)

🔄 完整处理流程

1. 组件初始化阶段

graph TD
    A[组件挂载] --> B[读取系统配置]
    B --> C{处理模式?}
    C -->|传统模式| D[传统模式初始化]
    C -->|WASM模式| E[WASM模式初始化]
    E --> F[初始化SDK]
    F --> G[注册enhanced loader]
    D --> H[获取图像ID]
    G --> H
    H --> I[加载图像参数]
    I --> J[渲染UI]

1.1 读取处理模式配置

// 从系统设置Redux store读取
const processingMode = useAppSelector(selectProcessingMode);
const isWASMMode = processingMode === 'wasm';

1.2 获取当前图像ID

useEffect(() => {
  // 从 viewer 容器获取当前选中的图像
  const dcmUrls = store.getState().viewerContainer.selectedViewers;
  
  // 验证:必须且仅有一个选中的图像
  if (dcmUrls.length !== 1) {
    console.error('异常:选中图像数量不为1');
    return;
  }

  // 提取 SOP Instance UID
  const sopInstanceUid = getSopInstanceUidFromUrl(dcmUrls[0]);
  
  if (sopInstanceUid) {
    // 加载该图像的处理参数
    dispatch(loadImageProcessingParams(sopInstanceUid));
  }
}, [dispatch]);

1.3 加载图像处理参数

// Redux Thunk 异步操作
loadImageProcessingParams(sopInstanceUid)
  ├─> 调用 API: getImageProcessingParams(sopInstanceUid)
  ├─> 返回参数: { contrast, detail, latitude, noise }
  └─> 更新 Redux state
      ├─> parameters (合并前端参数 brightness, sharpness)
      ├─> currentImageId
      └─> isInitialLoad = true (防止触发自动预览)

2. 传统模式处理流程

2.1 参数调节流程

graph LR
    A[用户拖动滑块] --> B[handleParameterChange]
    B --> C[更新Redux state]
    C --> D{是后端参数?}
    D -->|是| E[触发防抖预览]
    D -->|否| F[仅更新UI]
    E --> G[500ms后执行]
    G --> H[buildProcessedDcmUrl]
    H --> I[添加URL参数]
    I --> J[updateViewerUrl]
    J --> K[Cornerstone重新渲染]

2.2 URL参数构建

// 传统模式:构建带参数的DCM URL
const processedUrl = buildProcessedDcmUrl(currentImageId, {
  contrast: params.contrast.toString(),
  detail: params.detail.toString(),
  latitude: params.latitude.toString(),
  noise: params.noise.toString(),
});

// 示例URL
// /dr/api/v1/pub/dcm/1.2.3.4.5?contrast=5&detail=3&latitude=7&noise=2

2.3 后端参数 vs 前端参数

参数名称 参数类型 处理方式
contrast (增益) 后端参数 URL传递,后端处理
detail (细节) 后端参数 URL传递,后端处理
latitude (动态范围) 后端参数 URL传递,后端处理
noise (噪声模式) 后端参数 URL传递,后端处理
brightness (对比度) 前端参数 仅UI更新,不触发预览
sharpness (亮度) 前端参数 仅UI更新,不触发预览

3. WASM模式处理流程

3.1 SDK初始化

// 使用自定义Hook初始化SDK
const { sdk, isSDKReady, isInitializing, error: sdkError } = useImageEnhancementSDK({
  sopInstanceUid: currentImageId,
  enabled: isWASMMode,  // 仅在WASM模式下启用
});

// Hook内部流程
useImageEnhancementSDK
  ├─> 初始化Cornerstone3D
  ├─> 创建SDK实例 (createImageEnhancementSDK)
  │   ├─> 配置WASM URL: /static/DRENHANCE.js
  │   ├─> 配置TIF URL: /dr/api/v1/pub/tif/{sopInstanceUid}-0001.tif
  │   └─> 配置DCM URL: /dr/api/v1/pub/dcm/{sopInstanceUid}.dcm
  ├─> 注册 'enhanced' schema loader
  └─> 返回SDK实例

3.2 WASM参数调节流程

graph LR
    A[用户拖动滑块] --> B[handleParameterChange]
    B --> C[更新Redux state]
    C --> D[更新SDK ParameterManager]
    D --> E[触发WASM防抖预览]
    E --> F[300ms后执行]
    F --> G[生成新imageId+时间戳]
    G --> H[enhanced:sopInstanceUid_timestamp]
    H --> I[updateViewerUrl]
    I --> J[触发enhanced loader]
    J --> K[SDK WASM处理]
    K --> L[返回增强图像]
    L --> M[Cornerstone渲染]

3.3 WASM预览实现

const debouncedWASMPreview = useCallback(
  (params: FullProcessingParams) => {
    if (isInitialLoad || !sdk || !currentImageId) return;
    
    clearTimeout(saveTimerRef.current);
    
    saveTimerRef.current = setTimeout(async () => {
      // 1. 更新SDK参数管理器
      sdk.parameterManager.updateParameters(params);
      
      // 2. 生成唯一imageId(带时间戳强制重新加载)
      const timestamp = Date.now();
      const enhancedImageId = `enhanced:${currentImageId}_${timestamp}`;
      
      // 3. 更新viewer URL
      dispatch(updateViewerUrl({
        originalUrl: dcmUrls[0],
        newUrl: enhancedImageId,
      }));
      
      // 4. Cornerstone调用enhanced loader
      // 5. SDK处理TIF数据并应用WASM增强
      // 6. 返回增强后的图像
    }, 300); // WASM模式使用更短的防抖时间
  },
  [currentImageId, isInitialLoad, sdk, dispatch]
);

🎯 滑块调整后的完整执行流程

概览:从滑块到屏幕显示

用户滑动滑块
    ↓
handleParameterChange() 被触发
    ↓
1️⃣ 立即更新Redux State(UI响应)
    ↓
2️⃣ 检查参数类型(后端 vs 前端)
    ↓
3️⃣ 判断处理模式(WASM / 传统)
    ↓
4️⃣ 执行对应的防抖预览函数
    ↓
5️⃣ 更新Viewer URL
    ↓
6️⃣ Cornerstone重新渲染图像

📊 完整时间轴对比

WASM模式时间轴(默认)

时间点 0ms
├─ 用户滑动滑块(例如:增益从5调到7)
└─ onChange事件触发

时间点 0ms(立即)
├─ handleParameterChange('contrast', 7) 执行
├─ dispatch(updateParameter({ name: 'contrast', value: 7 }))
├─ Redux state更新:parameters.contrast = 7
└─ UI立即显示新值:滑块位置 = 7

时间点 0ms(立即)
├─ 构建新参数对象:newParams = { ...parameters, contrast: 7 }
├─ 判断:'contrast' 属于后端参数 ✅
├─ 判断:当前模式 = 'wasm' ✅
└─ 调用:debouncedWASMPreview(newParams)

时间点 0-300ms
└─ 防抖等待中...(用户可能继续调参)

时间点 300ms(防抖结束)
├─ 清除之前的定时器
├─ 开始执行WASM预览逻辑
├─ Step 1: sdk.parameterManager.updateParameters(newParams)
│   └─ SDK内部参数存储更新
├─ Step 2: 生成唯一imageId
│   └─ enhancedImageId = `enhanced:1.2.3.4.5_1701364800000`
└─ Step 3: dispatch(updateViewerUrl({ newUrl: enhancedImageId }))

时间点 300ms+(网络请求开始)
├─ Cornerstone检测到URL变化
├─ 调用注册的Enhanced Loader
└─ EnhancedImageLoader.loadImage(enhancedImageId) 执行

├─ Step 1: prefetchMetadata()
│   ├─ URL: /dr/api/v1/pub/dcm/1.2.3.4.5.dcm
│   ├─ 网络请求1:获取DCM元数据(几KB)
│   └─ DicomMetaDataManager缓存元数据
│
├─ Step 2: DicomMetaDataManager.get()
│   └─ 从缓存读取元数据(窗宽窗位等)
│
├─ Step 3: tifService.fetchImageData() 🎯 关键!
│   ├─ URL: /dr/api/v1/pub/tif/1.2.3.4.5-0001.tif
│   ├─ 网络请求2:下载TIF文件(2-4MB,16-bit原始数据)
│   ├─ 解析TIFF格式
│   └─ 返回: TIFImageData {
│       width: 512,
│       height: 512,
│       bitsPerSample: 16,
│       pixelData: Uint16Array(262144)  // 512×512像素
│     }
│
└─ Step 4: imageCreator.createEnhancedImage()
    ├─ 调用WASM增强算法
    ├─ Module.enhancePixelData(pixelData, params)
    │   └─ 使用新参数处理16-bit像素数据
    ├─ 创建Cornerstone图像对象
    └─ 返回:CornerstoneImage

时间点 300ms++ (渲染)
├─ Cornerstone接收增强后的图像
├─ 应用窗宽窗位
├─ GPU渲染到Canvas
└─ 屏幕显示更新后的图像 ✨

总耗时:约 300ms(防抖)+ 100-300ms(网络+WASM处理)

传统模式时间轴

时间点 0ms
└─ 用户滑动滑块

时间点 0ms(立即)
├─ handleParameterChange('contrast', 7)
├─ Redux state更新
└─ UI显示新值

时间点 0ms(立即)
├─ 判断:当前模式 = 'traditional'
└─ 调用:debouncedPreview(newParams)

时间点 0-500ms
└─ 防抖等待中...

时间点 500ms(防抖结束)
├─ 构建处理URL
│   └─ /dr/api/v1/pub/dcm/xxx.dcm?contrast=7&detail=5&latitude=5&noise=5
├─ dispatch(updateViewerUrl({ newUrl: `dicomweb:${url}` }))
└─ Cornerstone检测到URL变化

时间点 500ms+
├─ 网络请求:带参数的DCM文件
├─ 后端服务器处理图像(应用参数)
├─ 返回处理后的DCM
└─ Cornerstone渲染

总耗时:约 500ms(防抖)+ 200-500ms(后端处理)

🔗 WASM模式TIF获取完整调用链

// 📍 Level 1: 用户交互层
<ParameterSlider onChange={(value) => handleParameterChange('contrast', value)} />

↓

// 📍 Level 2: 参数处理层 (SliderAdjustmentPanel.tsx)
handleParameterChange(name: 'contrast', value: 7) {
  // 1. 立即更新UI
  dispatch(updateParameter({ name: 'contrast', value: 7 }))
  
  // 2. 准备新参数
  const newParams = { ...parameters, contrast: 7 }
  
  // 3. 检查并调用WASM预览
  if (['contrast', 'detail', 'latitude', 'noise'].includes(name)) {
    if (isWASMMode) {
      debouncedWASMPreview(newParams)  // ← 这里
    }
  }
}

↓

// 📍 Level 3: 防抖预览层 (SliderAdjustmentPanel.tsx)
debouncedWASMPreview(params) {
  setTimeout(async () => {
    // 1. 更新SDK参数管理器
    sdk.parameterManager.updateParameters(params)
    
    // 2. 生成唯一ID(带时间戳)
    const timestamp = Date.now()
    const enhancedImageId = `enhanced:${currentImageId}_${timestamp}`
    
    // 3. 触发URL更新
    dispatch(updateViewerUrl({
      originalUrl: dcmUrls[0],
      newUrl: enhancedImageId  // ← 关键:新URL触发重新加载
    }))
  }, 300)
}

↓

// 📍 Level 4: Cornerstone层
// Cornerstone检测到imageId变化,调用注册的loader
cornerstone.imageLoader.loadImage(enhancedImageId)
  → 查找 'enhanced:' schema的loader
  → 调用 EnhancedImageLoader.loadImage()

↓

// 📍 Level 5: 图像加载器层 (EnhancedImageLoader.ts)
async loadImage(imageId: string): Promise<CornerstoneImage> {
  console.log(`🔄 Loader被调用 - imageId: ${imageId}`)
  
  // 清理imageId(移除前缀和时间戳)
  const cleanId = imageId.replace(/^enhanced:/, "").split('_')[0]
  // cleanId = "1.2.3.4.5"
  
  try {
    // 🔑 Step 1: 预加载DCM元数据
    await prefetchMetadata(imageId)
    
    // 🔍 Step 2: 从缓存获取元数据
    const metadataUrl = this.getMetadataUrl(cleanId)
    const dcmMetadata = DicomMetaDataManager.get(metadataUrl)
    
    // 🎯 Step 3: 获取TIF原始数据 ← 关键步骤!
    const tifData = await this.tifService.fetchImageData(cleanId)
    console.log('📦 TIF数据获取成功:', { 
      width: tifData.width,      // 512
      height: tifData.height,    // 512
      bits: tifData.bitsPerSample // 16
    })
    
    // 🏗️ Step 4: WASM增强 + 创建图像
    const enhancedImage = await this.imageCreator
      .createEnhancedImage(tifData, dcmMetadata)
    
    return enhancedImage
    
  } catch (error) {
    console.error('❌ 增强图像加载失败:', error)
    throw error
  }
}

↓

// 📍 Level 6: TIF服务层 (TIFImageService.ts)
async fetchImageData(sopInstanceUID: string): Promise<TIFImageData> {
  // 1. 构建TIF URL
  const tifUrl = `${this.baseUrl}/tif/${sopInstanceUID}-0001.tif`
  // tifUrl = "/dr/api/v1/pub/tif/1.2.3.4.5-0001.tif"
  
  console.log('🌐 开始下载TIF文件:', tifUrl)
  
  // 2. 网络请求获取TIF文件
  const response = await fetch(tifUrl, {
    headers: {
      'Authorization': this.authorization,
      'Accept': 'image/tiff',
      ...this.headers
    }
  })
  
  if (!response.ok) {
    throw new Error(`TIF下载失败: ${response.status}`)
  }
  
  // 3. 获取二进制数据
  const arrayBuffer = await response.arrayBuffer()
  console.log('📦 TIF文件下载完成:', {
    size: `${(arrayBuffer.byteLength / 1024 / 1024).toFixed(2)} MB`
  })
  
  // 4. 解析TIFF格式
  const tiffData = await this.parseTIFF(arrayBuffer)
  
  // 5. 提取16-bit像素数据
  return {
    width: tiffData.width,
    height: tiffData.height,
    bitsPerSample: 16,
    pixelData: new Uint16Array(tiffData.data),
    photometricInterpretation: 'MONOCHROME2'
  }
}

↓

// 📍 Level 7: WASM增强层 (EnhancedImageCreator.ts)
async createEnhancedImage(
  tifData: TIFImageData, 
  dcmMetadata?: DicomMetadata
): Promise<CornerstoneImage> {
  
  // 1. 获取当前参数
  const params = this.parameterManager.getCurrentParameters()
  console.log('🎛️ 应用增强参数:', params)
  
  // 2. WASM算法处理像素数据
  const enhancedPixels = await this.wasmEnhancer.enhancePixelData(
    tifData.pixelData,  // Uint16Array(262144) - 16bit原始数据
    params              // { contrast: 7, detail: 5, ... }
  )
  
  // 3. 创建Cornerstone图像对象
  const image: CornerstoneImage = {
    imageId: this.generateImageId(),
    width: tifData.width,
    height: tifData.height,
    color: false,
    
    // 使用增强后的像素数据
    getPixelData: () => enhancedPixels,
    
    // 从DCM元数据获取
    minPixelValue: dcmMetadata?.image?.smallestPixelValue || 0,
    maxPixelValue: dcmMetadata?.image?.largestPixelValue || 65535,
    slope: dcmMetadata?.modalityLut?.rescaleSlope || 1,
    intercept: dcmMetadata?.modalityLut?.rescaleIntercept || 0,
    windowCenter: dcmMetadata?.voiLut?.windowCenter?.[0] || 32768,
    windowWidth: dcmMetadata?.voiLut?.windowWidth?.[0] || 65536,
    
    // 其他属性...
  }
  
  return image
}

↓

// 📍 Level 8: WASM模块层 (WASMEnhancer.ts)
async enhancePixelData(
  pixelData: Uint16Array,
  params: EnhancementParams
): Promise<Uint16Array> {
  
  console.log('⚙️ WASM模块开始处理...')
  
  // 调用编译好的WASM函数
  const result = this.wasmModule._enhance_image(
    pixelData,
    params.contrast,   // 7 (新值)
    params.detail,     // 5
    params.latitude,   // 5
    params.noise       // 5
  )
  
  console.log('✅ WASM处理完成')
  return new Uint16Array(result)
}

↓

// 📍 Level 9: 渲染层
// Cornerstone接收增强后的图像对象
// → 应用窗宽窗位
// → GPU渲染到Canvas
// → 屏幕显示更新 ✨

📡 网络请求详解

WASM模式的两次网络请求

// 请求1: DCM元数据(首次加载时,后续从缓存读取)
GET /dr/api/v1/pub/dcm/1.2.3.4.5.dcm
Headers: {
  Authorization: "Bearer xxx",
  Accept: "application/dicom"
}
Response: 
  - Size: 几KB到几十KB
  - Content: DICOM元数据(患者信息、窗宽窗位等)
  - 用途: 提供图像显示参数
  - 缓存: DicomMetaDataManager(整个会话期间)

// 请求2: TIF原始数据(每次参数调整都会请求)
GET /dr/api/v1/pub/tif/1.2.3.4.5-0001.tif
Headers: {
  Authorization: "Bearer xxx",
  Accept: "image/tiff"
}
Response:
  - Size: 2-4MB (512×512×2字节)
  - Content: 16-bit TIFF图像
  - 用途: 提供原始像素数据用于WASM增强
  - 缓存: 无(每次重新获取以应用新参数)

传统模式的一次网络请求

// 请求: 带参数的DCM文件
GET /dr/api/v1/pub/dcm/1.2.3.4.5.dcm?contrast=7&detail=5&latitude=5&noise=5
Headers: {
  Authorization: "Bearer xxx"
}
Response:
  - Size: 完整DCM文件
  - Content: 后端处理后的DICOM图像
  - 用途: 直接用于显示
  - 缓存: Cornerstone缓存机制

🔄 数据流转详解

16-bit像素数据的转换流程

1️⃣ 服务器TIF文件
   └─ TIFF格式,16-bit灰度图像
   └─ 文件大小:约2-4MB(512×512×2)

      ↓ fetch网络请求

2️⃣ ArrayBuffer(二进制数据)
   └─ JavaScript二进制缓冲区
   └─ 包含完整TIFF文件内容

      ↓ parseTIFF解析

3️⃣ TIFImageData对象
   {
     width: 512,
     height: 512,
     bitsPerSample: 16,
     pixelData: Uint16Array(262144),  // 512×512个16位整数
     photometricInterpretation: 'MONOCHROME2'
   }

      ↓ WASM增强算法

4️⃣ 增强后的像素数据
   └─ Uint16Array(262144)
   └─ 应用了新参数的增强结果
   └─ 例如:对比度增强、细节提升、噪声抑制

      ↓ 创建Cornerstone图像

5️⃣ CornerstoneImage对象
   {
     imageId: "enhanced:1.2.3.4.5_timestamp",
     width: 512,
     height: 512,
     getPixelData: () => enhancedPixels,
     windowCenter: 32768,
     windowWidth: 65536,
     // ... 其他属性
   }

      ↓ Cornerstone渲染管线

6️⃣ GPU渲染
   └─ 应用窗宽窗位映射
   └─ 转换为8-bit RGB显示
   └─ WebGL渲染到Canvas

      ↓

7️⃣ 屏幕显示
   └─ 用户看到增强后的图像 ✨

⚡ 性能优化分析

防抖机制的作用

// 场景:用户快速拖动滑块
时间 0ms: 滑块值 5 → 触发,设置300ms定时器A
时间 50ms: 滑块值 6 → 取消A,设置300ms定时器B
时间 100ms: 滑块值 7 → 取消B,设置300ms定时器C
时间 150ms: 滑块值 8 → 取消C,设置300ms定时器D
时间 200ms: 用户停止拖动
时间 500ms: 定时器D触发,仅执行一次WASM处理(参数=8)

优势:
✅ 避免频繁网络请求(TIF下载)
✅ 避免频繁WASM处理(CPU密集)
✅ 最终状态一致(参数=8)
✅ 用户体验流畅(UI立即响应)

WASM vs 传统模式性能对比

场景:用户调节参数10次

传统模式:
├─ 防抖: 500ms × 10次 = 不一定(可能合并)
├─ 网络请求: 10次后端DCM处理请求
├─ 后端处理: 10次图像处理(服务器负载)
└─ 总耗时: 较长(网络往返+后端处理)

WASM模式:
├─ 防抖: 300ms × 10次 = 不一定(可能合并)
├─ 网络请求: 10次TIF下载(可优化为缓存)
├─ 本地处理: 10次WASM增强(浏览器本地)
└─ 总耗时: 较短(本地处理更快)

优化建议:
💡 WASM模式可增加TIF缓存(首次下载后复用)
💡 调参时仅更新SDK参数,不重新下载TIF
💡 进一步缩短防抖时间(如200ms)

🐛 常见问题排查

问题1:滑块移动但图像不更新

可能原因

// 检查1:是否为初始加载状态
if (isInitialLoad) {
  // 初始加载时不会触发预览
  console.log('初始加载中,跳过预览')
}

// 检查2:是否为前端参数
if (['brightness', 'sharpness'].includes(paramName)) {
  // 前端参数不触发预览
  console.log('前端参数,不触发预览')
}

// 检查3:WASM模式SDK是否就绪
if (isWASMMode && !isSDKReady) {
  console.error('SDK未就绪,无法预览')
}

// 检查4:是否有网络错误
// 查看浏览器Network标签

问题2:TIF下载失败

// 检查URL是否正确
const expectedUrl = `/dr/api/v1/pub/tif/${sopInstanceUid}-0001.tif`

// 检查认证令牌
headers: {
  'Authorization': `Bearer ${token}`
}

// 检查CORS配置
// 服务器需允许跨域请求

// 检查文件是否存在
// 后端日志查看TIF文件路径

问题3:WASM处理很慢

// 可能原因:
1. WASM模块未正确加载
   → 检查 /static/DRENHANCE.js 是否可访问
   
2. 参数范围过大导致计算密集
   → 优化WASM算法或限制参数范围
   
3. 图像尺寸过大
   → 大图像(如1024×1024)处理时间更长

4. 风格切换流程

graph TD
    A[用户选择风格] --> B[handleStyleChange]
    B --> C[applyPreset - 应用预设参数]
    C --> D[更新Redux state]
    D --> E{处理模式?}
    E -->|传统模式| F[触发URL预览]
    E -->|WASM模式| G[触发WASM预览]
    F --> H[延迟100ms保存参数]
    G --> H
    H --> I[saveProcessingParams]
    I --> J[后端持久化]

风格预设映射

const PROCESSING_PRESETS = {
  '均衡': {
    contrast: 5,
    detail: 5,
    latitude: 5,
    noise: 5,
    brightness: 0,
    sharpness: 0,
  },
  '高对比': {
    contrast: 8,
    detail: 3,
    latitude: 7,
    noise: 2,
    brightness: 0,
    sharpness: 0,
  },
  '锐组织': {
    contrast: 6,
    detail: 8,
    latitude: 4,
    noise: 3,
    brightness: 0,
    sharpness: 0,
  },
  '骨骼': {
    contrast: 7,
    detail: 2,
    latitude: 8,
    noise: 1,
    brightness: 0,
    sharpness: 0,
  },
};

5. 参数保存流程

5.1 手动保存

handleSave()
  ├─> 提取后端参数 (contrast, detail, latitude, noise)
  ├─> dispatch(saveProcessingParams)
  ├─> API调用: POST /api/image-processing-params/{sopInstanceUid}
  ├─> 后端持久化到数据库
  └─> 显示成功消息

5.2 自动保存时机

  • ✅ 风格切换后
  • ✅ 参数重置后
  • ❌ 参数调节中(不自动保存,避免频繁请求)

6. 重置参数流程

handleReset()
  ├─> dispatch(resetToPreset)
  │   └─> 恢复当前风格的默认参数
  ├─> 显示成功消息
  ├─> 延迟100ms保存参数
  └─> 持久化到后端

7. 双模式对比总结

特性 传统模式 WASM模式
数据源 DCM文件 TIF原始数据 (16-bit)
处理位置 后端服务器 浏览器本地 (WASM)
URL Schema dicomweb:/dr/api/v1/pub/dcm/xxx?params enhanced:sopInstanceUid_timestamp
参数传递 URL查询参数 SDK ParameterManager
防抖延迟 500ms 300ms (更快)
处理参数 4个后端参数 4个后端参数 + 2个前端参数
图像质量 标准 高质量 (16-bit增强)
网络依赖 每次调参需请求 仅初始加载TIF
适用场景 标准工作流 高质量增强需求

8. 参数类型说明

8.1 FullProcessingParams 接口

interface FullProcessingParams {
  // 后端参数(会发送到服务器)
  contrast: number;   // 增益 (0-10)
  detail: number;     // 细节 (0-10)
  latitude: number;   // 动态范围 (0-10)
  noise: number;      // 噪声模式 (0-10)
  
  // 前端参数(仅本地维护)
  brightness: number; // 对比度 (-5 to 5)
  sharpness: number;  // 亮度 (-5 to 5)
}

8.2 参数范围定义

const PARAMETER_RANGES = {
  gain: { min: 0, max: 10, step: 0.1 },      // 增益
  detail: { min: 0, max: 10, step: 0.1 },    // 细节
  latitude: { min: 0, max: 10, step: 0.1 },  // 动态范围
  noise: { min: 0, max: 10, step: 0.1 },     // 噪声模式
  brightness: { min: -5, max: 5, step: 0.1 }, // 对比度
  sharpness: { min: -5, max: 5, step: 0.1 },  // 亮度
};

9. 错误处理

9.1 初始化错误

// 图像选择异常
if (dcmUrls.length !== 1) {
  console.error('没有选中的图像或数量大于1,无法加载参数');
  return; // 阻止后续执行
}

// SDK初始化失败(WASM模式)
if (isWASMMode && sdkError) {
  // UI显示: SDK错误指示器
  // 降级: 可考虑自动切换到传统模式
}

9.2 参数加载错误

// Redux error state监听
useEffect(() => {
  if (error) {
    message.error(error); // 显示错误消息
    // 使用默认参数继续工作
  }
}, [error]);

9.3 预览更新错误

try {
  // 更新预览逻辑
} catch (error) {
  console.error('更新预览图像失败:', error);
  message.error('更新预览图像失败');
  // 不阻止用户继续操作
}

10. 性能优化要点

10.1 防抖机制

  • 传统模式: 500ms 防抖,减少后端请求
  • WASM模式: 300ms 防抖,本地处理更快响应

10.2 初始加载优化

const isInitialLoad = useAppSelector(selectIsInitialLoad);

// 初始加载时不触发预览,避免不必要的渲染
if (isInitialLoad) {
  return; // 跳过预览
}

10.3 定时器清理

useEffect(() => {
  return () => {
    // 组件卸载时清理定时器
    if (saveTimerRef.current) {
      clearTimeout(saveTimerRef.current);
    }
  };
}, []);

11. 模式切换时机

用户在系统设置 > 首选项 > 高级处理模式中配置处理模式后:

  1. 配置保存到后端数据库
  2. Redux state更新
  3. 需要重新进入SliderAdjustmentPanel才生效
  4. 面板根据新配置初始化对应模式

12. 未来扩展点

12.1 模式自动降级

// WASM模式初始化失败时自动降级到传统模式
if (isWASMMode && sdkError) {
  console.warn('WASM模式初始化失败,降级到传统模式');
  dispatch(updateProcessingMode('traditional'));
}

12.2 参数预设模板

支持用户自定义参数预设,保存常用的参数组合。

12.3 实时性能监控

  • WASM处理耗时
  • 网络请求耗时
  • 渲染性能指标

📊 状态流转图

stateDiagram-v2
    [*] --> Initializing: 组件挂载
    Initializing --> LoadingConfig: 读取处理模式
    LoadingConfig --> InitTraditional: 传统模式
    LoadingConfig --> InitWASM: WASM模式
    InitTraditional --> LoadingParams: 初始化完成
    InitWASM --> LoadingParams: SDK就绪
    LoadingParams --> Ready: 参数加载完成
    Ready --> Adjusting: 用户调参
    Adjusting --> Previewing: 防抖触发
    Previewing --> Ready: 预览完成
    Ready --> Saving: 保存参数
    Saving --> Ready: 保存完成
    Ready --> [*]: 返回上级

🔧 关键代码片段

防抖预览核心逻辑

const debouncedPreview = useCallback(
  (params: FullProcessingParams) => {
    if (isInitialLoad) return;
    
    clearTimeout(saveTimerRef.current);
    
    saveTimerRef.current = setTimeout(() => {
      if (!currentImageId) return;
      
      try {
        const dcmUrls = store.getState().viewerContainer.selectedViewers;
        if (dcmUrls.length !== 1) return;
        
        const processedUrl = buildProcessedDcmUrl(currentImageId, {
          contrast: params.contrast.toString(),
          detail: params.detail.toString(),
          latitude: params.latitude.toString(),
          noise: params.noise.toString(),
        });
        
        dispatch(updateViewerUrl({
          originalUrl: dcmUrls[0],
          newUrl: `dicomweb:${processedUrl}`,
        }));
      } catch (error) {
        message.error('更新预览图像失败');
      }
    }, 500);
  },
  [currentImageId, isInitialLoad, dispatch]
);

📝 总结

SliderAdjustmentPanel通过配置驱动的方式支持双处理模式:

  • 传统模式:稳定可靠,适合标准工作流
  • WASM模式:高质量增强,适合专业场景

两种模式在用户体验上保持一致,底层处理逻辑根据配置自动切换,实现了灵活性和可维护性的平衡。


文档版本: 1.0
创建日期: 2025-11-30
最后更新: 2025-11-30