# 滑动参数调节面板功能实现文档 ## 功能概述 在"图像处理"面板(AdvancedProcessingPanel)上添加一个"高级"按钮,点击后打开"滑动参数调节"面板(SliderAdjustmentPanel)。这是一个三级面板,点击返回按钮后会返回到"图像处理"面板。 ## 面板层级结构 ``` OperationPanel (二级面板) └── AdvancedProcessingPanel (图像处理 - 三级面板) └── SliderAdjustmentPanel (滑动参数调节 - 四级面板) ``` ## 功能特性 ### 1. 参数调整 - **增益** (contrast): -5~45,步长0.1 - **细节** (detail): -0.5~24.5,步长0.1 - **动态范围** (latitude): -5~20,步长0.1 - **噪声模式** (noise): 0~20,步长0.1 - **对比度** (brightness): -8~8,步长0.1(仅前端维护) - **亮度** (sharpness): -8~8,步长0.1(仅前端维护) ### 2. 下拉选择 - **算法选择**: RSymphony(默认) - **LUT选择**: SIGMOID(默认)、LINER 等 - **风格选择**: 均衡、高对比、锐组织、骨骼 ### 3. 实时预览(新增) - 滑块调整后自动获取预览图像(500ms防抖) - 使用 Blob URL 实现即时预览 - 仅后端参数触发预览(contrast, detail, latitude, noise) - 前端参数调整不触发后端请求 ### 4. 手动操作 - **重置参数**: 重置为当前风格的默认值 - **保存参数**: 永久保存到dcm文件 ## 技术实现 ### 1. 文件结构 ``` src/ ├── types/ │ └── imageProcessing.ts # 类型定义 ├── domain/ │ ├── lutConfig.ts # LUT配置 │ └── processingPresets.ts # 风格预设配置 ├── API/ │ └── imageActions.ts # 新增API接口 ├── states/ │ ├── panelSwitchSliceForView.ts # 面板切换状态(已修改) │ ├── store.ts # Redux Store(已修改) │ └── view/ │ └── sliderAdjustmentPanelSlice.ts # 滑动参数调节面板状态 └── pages/ └── view/ └── components/ ├── ParameterSlider.tsx # 参数滑块组件 ├── SliderAdjustmentPanel.tsx # 滑动参数调节面板 ├── AdvancedProcessingPanel.tsx # 高级处理面板(已修改) └── OperationPanel.tsx # 操作面板(已修改) ``` ### 2. 核心组件 #### ParameterSlider 组件 - 可复用的参数滑块组件 - 包含:标签、范围提示、当前值显示、滑块、加减按钮 - 支持自定义范围、步长、禁用状态 #### SliderAdjustmentPanel 组件 - 主要面板组件 - 集成6个参数滑块 - 集成算法、LUT、风格下拉选择 - 实现防抖自动保存 - 提供重置和手动保存按钮 ### 3. 状态管理 #### sliderAdjustmentPanelSlice - **状态**: - `currentImageId`: 当前图像ID - `parameters`: 当前参数值 - `selectedStyle`: 当前风格 - `selectedAlgorithm`: 当前算法 - `selectedLUT`: 当前LUT - `isLoading`: 加载状态 - `isSaving`: 保存状态 - `isInitialLoad`: 初始加载标志 - `error`: 错误信息 - **异步操作**: - `loadImageProcessingParams`: 加载图像处理参数 - `saveProcessingParams`: 保存图像处理参数 - **同步操作**: - `setCurrentImageId`: 设置当前图像ID - `updateParameter`: 更新单个参数 - `applyPreset`: 应用风格预设 - `resetToPreset`: 重置为当前风格默认值 - `setAlgorithm`: 设置算法 - `setLUT`: 设置LUT - `clearError`: 清除错误 - `resetState`: 重置状态 #### panelSwitchSliceForView(修改) - 新增 `SliderAdjustmentPanel` 面板类型 - 新增 `switchToSliderAdjustmentPanel` action ### 4. API接口 #### getImageProcessingParams ```typescript GET /auth/image/{sopInstanceUid}/preparation ``` 获取当前图像处理参数(增益、细节、动态范围、噪声模式) #### getProcessedDcm(新增) ```typescript GET /auth/image/{sopInstanceUid}/proc?contrast=5.0&detail=9.0&latitude=25.0&noise=12.0 ``` 获取应用指定参数后的处理后dcm文件(用于实时预览) - **返回**: 直接返回dcm文件的二进制数据(Blob) - **用途**: 滑块调整时实时预览 - **机制**: 1. 前端调用API获取Blob 2. 使用 `URL.createObjectURL(blob)` 创建临时URL 3. 更新Viewer显示新图像 4. 适当时机调用 `URL.revokeObjectURL()` 释放内存 #### saveImageProcessingParams ```typescript POST /auth/image/{sopInstanceUid}/proc Body: { contrast: number, // 增益 detail: number, // 细节 latitude: number, // 动态范围 noise: number // 噪声模式 } ``` 保存图像处理参数到DICOM文件(永久保存) - **用途**: 点击"保存参数"按钮时调用 - **效果**: 将处理后的dcm永久保存到文件系统 ### 5. 配置文件 #### processingPresets.ts ```typescript export const PROCESSING_PRESETS: Record = { '均衡': { name: '均衡', params: { contrast: 5.0, detail: 9.0, latitude: 25.0, noise: 12.0, brightness: -2.0, sharpness: 1.0 } }, '高对比': { name: '高对比', params: { contrast: 10.0, detail: 5.0, latitude: 15.0, noise: 8.0, brightness: 0.0, sharpness: 2.0 } }, // ... }; ``` #### lutConfig.ts ```typescript export const LUT_OPTIONS: LUTType[] = [ 'SIGMOID', 'LINER', // ... ]; ``` ## 用户交互流程 ### 1. 进入滑动参数调节面板 ``` 用户在"图像处理"面板 → 点击"高级"按钮 → 进入"滑动参数调节"面板 → 自动加载当前图像的参数 ``` ### 2. 调整参数(实时预览) ``` 用户拖动滑块或点击加减按钮 → 参数值实时更新到Redux → 触发500ms防抖定时器 → 调用GET API获取预览dcm(Blob) → 创建Blob URL → 刷新Viewer显示 ``` ### 3. 保存参数(永久保存) ``` 用户点击"保存参数"按钮 → 调用POST API永久保存 → 成功提示 ``` ### 4. 切换风格 ``` 用户选择不同风格 → 应用对应的预设参数 → 立即保存到后端(POST) ``` ### 5. 返回上级面板 ``` 用户点击返回按钮 → 返回到"图像处理"面板 ``` ## 数据流 ### 实时预览流程 ``` 用户拖动滑块 ↓ UI组件 (SliderAdjustmentPanel) ↓ Redux Action (updateParameter) ↓ Redux State 更新 ↓ 防抖处理 (500ms) ↓ API调用 GET /proc (getProcessedDcm) ↓ 获取Blob数据 ↓ 创建Blob URL ↓ 更新Viewer显示(TODO) ``` ### 永久保存流程 ``` 用户点击"保存参数" ↓ UI组件 (SliderAdjustmentPanel) ↓ Redux Thunk (saveProcessingParams) ↓ API调用 POST /proc (saveImageProcessingParams) ↓ 后端永久保存到dcm文件 ↓ 成功提示 ``` ## 注意事项 ### 1. 参数分类 - **后端参数** (4个): contrast, detail, latitude, noise - 会保存到DICOM文件 - 通过API接口同步 - **前端参数** (2个): brightness, sharpness - 仅在前端维护 - 不会保存到后端 ### 2. 防抖机制 - 参数调整触发500ms防抖 - 避免频繁调用预览API - 初始加载时不触发预览 - 前端参数(brightness, sharpness)不触发后端请求 ### 3. 错误处理 - API调用失败时显示错误提示 - 加载失败时使用默认值 - 错误信息存储在Redux state中 ### 4. 待完善功能 - 当前图像ID获取逻辑(TODO) - 需要从ViewerContainer或其他地方获取当前选中图像的sopInstanceUid ## 测试步骤 ### 1. 面板切换测试 - [ ] 从"图像处理"面板点击"高级"按钮 - [ ] 验证进入"滑动参数调节"面板 - [ ] 点击返回按钮 - [ ] 验证返回到"图像处理"面板 ### 2. 参数调整测试 - [ ] 拖动各个参数滑块 - [ ] 验证参数值实时更新 - [ ] 验证UI显示正确 - [ ] 等待500ms后验证API调用 ### 3. 风格切换测试 - [ ] 选择不同的风格 - [ ] 验证参数值立即更新 - [ ] 验证立即触发保存 ### 4. 手动操作测试 - [ ] 点击"重置参数"按钮 - [ ] 验证参数恢复到当前风格默认值 - [ ] 点击"保存参数"按钮 - [ ] 验证手动触发保存 ### 5. 边界条件测试 - [ ] 参数值达到最大值/最小值时的表现 - [ ] 网络请求失败时的错误处理 - [ ] 快速切换参数时的防抖效果 ## 潜在问题与解决方案 ### 1. 图像ID获取 **问题**: 当前代码中sopInstanceUid获取逻辑未实现 **解决方案**: 需要从ViewerContainer或selectedBodyPosition中获取当前选中图像的ID ### 2. 参数同步 **问题**: 前端参数(brightness, sharpness)与后端参数(contrast, detail等)的同步 **解决方案**: - 后端参数通过API同步 - 前端参数仅在Redux中维护 - 加载时只更新后端返回的4个参数 ### 3. Blob URL内存管理 **问题**: 每次预览都创建新的Blob URL,可能导致内存泄漏 **解决方案**: - 在创建新URL前,先revoke旧的URL - 组件卸载时清理所有Blob URL - 考虑使用useRef存储当前的blobUrl ### 4. 并发请求 **问题**: 用户快速切换参数可能导致多次API调用 **解决方案**: 使用500ms防抖机制,合并多次操作为一次请求 ## 扩展建议 1. **参数预览**: 在调整参数时实时预览效果 2. **参数历史**: 记录参数调整历史,支持撤销/重做 3. **自定义风格**: 允许用户保存自定义的参数组合为新风格 4. **参数对比**: 支持查看调整前后的参数对比 5. **批量应用**: 支持将当前参数应用到多张图像 ## 更新日志 ### 2025-01-22 - ✅ 完成类型定义 - ✅ 完成配置文件创建 - ✅ 完成API接口扩展(包括getProcessedDcm) - ✅ 完成状态管理实现 - ✅ 完成UI组件开发 - ✅ 完成面板集成 - ✅ 实现实时预览API调用逻辑 - ✅ 获取当前图像ID逻辑 - ✅ URL构建和更新机制 - ✅ ViewerContainer URL映射管理 - ✅ Cornerstone图像刷新实现 - ⏳ 待完成:功能测试 ### 实时预览实现细节 **已实现**: 1. 新增 `getProcessedDcm` API - 获取处理后的dcm Blob 2. 新增 `buildProcessedDcmUrl` API - 构建处理后的dcm URL(不发送请求) 3. 修改滑块逻辑 - 构建URL而非调用预览API 4. 区分前端参数和后端参数 - 只有后端参数触发预览 5. 防抖机制 - 500ms延迟避免频繁更新 6. ViewerContainer URL映射 - 管理图像URL和处理参数 7. URL更新机制 - 通过updateViewerUrl触发重新渲染 8. 修正updateViewerUrl action - 只更新viewerUrlMap,保持原始URL不变 9. ViewerContainer getActualUrl辅助函数 - 根据映射获取实际URL 10. StackViewer监听imageUrls变化 - 自动重新加载图像 **数据流**: ``` 用户调整滑块参数 ↓ SliderAdjustmentPanel - updateParameter ↓ 防抖500ms ↓ buildProcessedDcmUrl - 构建带参数的URL ↓ updateViewerUrl - 更新viewerUrlMap[originalUrl] = processedUrl ↓ ViewerContainer重新渲染 ↓ getActualUrl - 从viewerUrlMap获取处理后的URL ↓ StackViewer接收新的imageUrls ↓ useEffect监听imageUrls变化 ↓ viewport.setStack(imageUrls) - 重新加载图像 ↓ viewport.render() - 渲染新图像 ``` **关键设计**: - **数据分离**: allViewers和selectedViewers保存原始URL,viewerUrlMap保存URL映射 - **单向数据流**: 状态变化 → URL映射更新 → 组件重新渲染 → 图像重新加载 - **易于恢复**: 清除viewerUrlMap的条目即可恢复原始图像 - **性能优化**: 防抖机制避免频繁更新,只有URL实际变化时才重新加载 **待实现**: 1. 功能测试和验证 2. 错误处理和用户反馈优化