# IWatcher **Repository Path**: odin1994/iwatcher ## Basic Information - **Project Name**: IWatcher - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-13 - **Last Updated**: 2026-06-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # IWatcher > 本地医学影像(DICOM)浏览器。参考 RadiAnt DICOM Viewer 2023.1 布局,浅色 editorial 配色。Avalonia / .NET 10 桌面应用。 ![v1.7.0 status](https://img.shields.io/badge/status-v1.7.0%20shipped-brightgreen) ![.NET](https://img.shields.io/badge/.NET-10.0-512BD4) ![Avalonia](https://img.shields.io/badge/Avalonia-12.0.3-8B44AC) ![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey) ![fo-dicom](https://img.shields.io/badge/fo--dicom-5.2.6-blue) ![codecs](https://img.shields.io/badge/fo--dicom.Codecs-5.16.7-blue) ![license](https://img.shields.io/badge/license-MIT--style%20personal-yellow) --- ## 项目代号 **IWatcher** · *Image Watcher*(前身:MIV,Medical Image Viewer;v0.1 文档沿用旧名,v1.0 起统一为 IWatcher) ## 文档导航 | 文档 | 路径 | 用途 | |---|---|---| | 产品 PRD | [`docs/PRD.md`](docs/PRD.md) | 完整产品需求(已对齐到 v1.7.0 实际状态) | | 功能说明 | [`docs/产品功能说明.md`](docs/产品功能说明.md) | P0/P1/P2 功能拆解 + 验收清单(按代码现状逐项勾选) | | 设计 Mockup | [`docs/mockup/viewer-v2-light.html`](docs/mockup/viewer-v2-light.html) | 视觉参考(直接浏览器打开) | | 变更日志 | [`docs/CHANGELOG.md`](docs/CHANGELOG.md) | 版本演进(v0.1 → v1.7.0) | ## 当前版本:v1.7.0 代码现状对应 v1.7.0。**v1.4.0** 加 Welcome 屏 + 空载态 + 拖放;**v1.5.0** 补多帧浏览 + Measurement 抽象层(标注 model + renderer,**鼠标交互 deferred**);**v1.5.1 + v1.5.2** 装上 `fo-dicom.Codecs` + 显式注册 `NativeTranscoderManager` —— 医院 RLE / JPEG / JPEG-LS / JPEG2000 DICOM 真正能解码;**v1.6.0** 修旋转 / 镜像(data-layer)+ crosshair + 比例尺 ruler;**v1.6.4** folder load 后台扫描 + 并行解析 + 进度条 + Cancel 按钮(不卡 UI 线程);**v1.6.6** 装上中文病人名解码(`(0010,1001) OtherPatientNames` + GB18030 重解码);**v1.6.7** wheel 方向 fix + 4-column status bar;**v1.6.8** 多文件 series `SourcePaths` 拆 + autoplay(▶ / ⏸ + 0.5×/1×/2×/4× + P 键);**v1.6.9** autoplay overlay 移出 status bar + slider 整数化 + About 文案更新;**v1.6.10** 删 sidebar Frame N/N 角标 + 固定 status bar Frame chip 宽度;**v1.6.11** LRU 帧缓存(容量 32,命中跳过 `DicomFile.Open` + 解压 + windowing)+ 清 unreachable-code 警告;**v1.7.0** 自定义窗位预设保存(`+` chip → PromptWindow → `CustomPresetStore` JSON 持久化)+ DICOMDIR 支持(`DicomDirectory.Open` 解析索引树,`ReferencedFileID` 拼绝对路径 + 按 series UID 分组)。详细 CHANGELOG 见 `docs/CHANGELOG.md`。 ### Empty state vs loaded state | 空载(v1.4 新增) | 加载 | |---|---| | ![empty](docs/screenshots/v1.4-empty-state/empty.png) | ![loaded](docs/screenshots/v1.4-empty-state/loaded.png) | | Welcome 屏:眼睛 logo + 3 按钮(Open file / Open folder / Load sample)+ drop 提示 | 真实 DICOM 像素 + 病人卡 + 系列缩略图 | ### Autoplay + crosshair + ruler(v1.6.8 / v1.6.9) ![autoplay](docs/screenshots/v1.6.10-stable-frame/frame-9.png) | ![chinese-name](docs/screenshots/v1.6.6-architecture/chinese-patient-name.png) | ![folder-load](docs/screenshots/v1.6.4-architecture/multi-file-thumbnail.png) --- | --- | --- 多帧 series 自动播放(▶ / ⏸,0.5×-4× 速度) | 中文病人名 `(0010,1001) OtherPatientNames` GB18030 重解码 | 35 帧 CT folder 加载,4 个 series 真实缩略图 ### 已实现(v1.7.0) #### 数据 - ✅ fo-dicom 5.2.6 真实 DICOM 解析(单文件 + 文件夹递归) - ✅ **压缩 DICOM 解码**(fo-dicom.Codecs 5.16.7 + `NativeTranscoderManager` 注册;RLE / JPEG / JPEG-LS / JPEG2000 直接 `GetFrame()` 解码) - ✅ **多文件 series 切帧**(每个 .dcm 一个 frame 的 series 用 `SourcePaths[k]` 选 file) - ✅ **解析失败友好提示**(状态栏 `Status` + `ex.Message`,非 DICOM 静默跳过) #### 像素 + 渲染 - ✅ 8/16-bit MONOCHROME1/2 + RescaleSlope/Intercept + WL/WW override - ✅ 主视图渲染真实 bitmap + 缩略图异步解码(UI thread 跑 `WriteableBitmap` ctor) - ✅ **中文病人名**(`PickPatientName` helper:读 `(0010,1001) OtherPatientNames` Ideographic / Phonetic + GB18030 重解码(医院 SpecificCharacterSet 错标 `ISO_IR 100` 但实际存 GB18030)) - ✅ SAMPLE 占位(pixel 坏数据 + Welcome 屏 `NOT CLINICAL DATA` 水印) - ✅ 头显截图模式(`--screenshot`,`--open` / `--set-wl` / `--set-ww` / `--set-frame` / `--set-rotation` / `--set-flipped` / `--add-sample-measurements`) #### 交互 - ✅ **滚轮切帧**(v1.6.3 方向,v1.6.7 reverse 修对,v1.6.8 修多文件 SourcePaths bug 真修复) - ✅ **Ctrl+滚轮缩放**(以指针为中心,SmallChange=1 整数化) - ✅ **中键拖动平移** + `ClampPanToViewport()` 5% 留边 - ✅ **右键拖动调窗位窗宽**(水平 WW,垂直 WL,RadiAnt 约定) - ✅ **十字准星 + 实时坐标 + CT 值**(`C` 键 toggle,TOOLTIP 显示 `(ix,iy) px=value CT=stored`) - ✅ **比例尺 ruler**(基于 `(0028,0030) PixelSpacing`,左下角 1 cm / 5 mm / 0.5 mm 自适应) - ✅ **旋转 / 镜像**(R / F 键,data-layer 旋转 90° 倍数 + 水平翻转,PNG 导出跟着转) - ✅ **键盘快捷键全套**(1-5 切预设 / 0 复位 / ↑↓ 切帧 / Space 抓手 / Ctrl+O/C/S/Z / F / R / F1 / F11 / Esc / **P 自动播放**) #### 视图 - ✅ 5 个窗位预设 chip:Default / Bone / Soft T. / Lung / Brain(真写 VM) - ✅ 状态栏 4 column 布局:Status / LoadProgress / Windowing / Frame chip(不重叠) - ✅ **序列自动播放**(▶ / ⏸,0.5× / 1× / 2× / 4× 速度,12 fps base,末尾自动 wrap,P 键 toggle;UI 位置 = viewport 底部居中 overlay) - ✅ **侧栏系列列表** 真实缩略图(多文件 series 用 `SourcePaths[0]` 算 representative) - ✅ **病人卡**(姓名 + 年龄 + 性别 + 病人 ID + 模态 + 部位) - ✅ **拖放打开**(Avalonia 12 `DataTransfer.TryGetFiles()` + `DataFormat.File`) - ✅ **空载态 / Welcome 屏**(v1.4 + 改进:3 按钮 + drop 提示) #### 系统 - ✅ **后台扫描 + 并行解析**(v1.6.4:`Directory.EnumerateFiles` 在 worker thread;`Parallel.ForEachAsync` 并发 4 核 + 进度节流) - ✅ **进度条 + Cancel 按钮 + Esc 取消**(v1.6.4 + v1.6.5) - ✅ **状态文字 trim** + **slider 整数化**(v1.6.6 TextTrimming + MaxWidth=280;v1.6.9 SmallChange=1 + StringFormat=F0 不晃) ### 暂不做 > 2026-06-15 用户决策:先做基本影像浏览,把"标注"(长度 / 文字 / 角度 / 椭圆)全部 deferred。v1.5.0 留了 model 层(`Measurement` 抽象 + 3 个子类 + `MeasurementPrimitive` tagged-union)作为 reference,UI 接到 v1.7+(如果你要的话)。 - ⏸ **测量 / 标注**(长度 / 角度 / 椭圆 / 文字)—— 鼠标交互 deferred - ⏸ **导出原 DICOM**(带标注烧入或私有 tag)—— 标注 deferred - ⏸ **主题切换**(当前单主题 Editorial Light)—— v1.7+ 候选 - ⏸ **DICOMDIR 支持**(光盘 / U 盘根目录)—— v1.7+ 候选 - ⏸ **3D 体渲染 / PACS 对接 / AI 标注**—— v2.0+ ## 技术栈 | 层级 | 选型 | 理由 | |---|---|---| | 运行时 | **.NET 10** | 最新 LTS(2025-11),跨平台 | | UI 框架 | **Avalonia 12.0.3** | XAML + MVVM,跨平台(Windows / macOS / Linux) | | MVVM 工具 | **CommunityToolkit.Mvvm 8.4.1** | 源生成器,零样板代码 | | DICOM 解析 + 像素解码 | **fo-dicom 5.2.6** + **fo-dicom.Codecs 5.16.7 (Efferent, MS-PL)** | .NET 生态最成熟 DICOM 库 + R/JPEG/J2K 解码 | | 主题 | 自研 editorial 浅色单主题 | 详见 `src/IWatcher.App/Themes/` | > "WPF 风格"指 XAML + MVVM + DataBinding 那一套开发范式;Avalonia 的 XAML 跟 WPF 几乎同构,跨平台。 ## 项目结构 ``` D:\works\code\iwatcher\ ├── README.md ← 本文件 ├── .gitignore ← Git 忽略规则 │ ├── docs/ │ ├── PRD.md ← 产品需求文档(v1.3.0 对齐) │ ├── 产品功能说明.md ← P0/P1/P2 任务清单 + 验收 │ ├── CHANGELOG.md │ └── mockup/ │ ├── viewer-v1.html ← 深色冷青版(v1 弃用) │ └── viewer-v2-light.html ← 浅色 editorial 版(当前视觉参考) │ └── src/ ├── IWatcher.App/ ← Avalonia 桌面应用项目 │ ├── IWatcher.App.csproj │ ├── Program.cs ← 入口(控制台子系统的 .exe) │ ├── App.axaml ← 应用级主题与样式加载 │ ├── App.axaml.cs ← 启动路径 + 截图模式参数解析 │ ├── app.manifest │ │ │ ├── Models/ ← 领域模型 │ │ ├── Patient.cs ← DICOM (0010,*) 映射 │ │ ├── Series.cs ← DICOM (0020,000E) 等映射 │ │ └── Instance.cs ← 单帧(SOPInstanceUID),v1.3+ 启用 │ │ │ ├── Services/ ← 数据层 │ │ ├── IDicomService.cs ← 抽象 + DicomStudy 聚合根 │ │ ├── SampleDicomService.cs← 合成 study(Ma, Si · 髋关节 X 光) │ │ ├── FoDicomService.cs ← fo-dicom 实现(OpenAsync / LoadSampleAsync) │ │ └── DicomFrameDecoder.cs ← 像素 → BGRA8888 WriteableBitmap │ │ │ ├── ViewModels/ ← MVVM │ │ ├── ViewModelBase.cs ← ObservableObject 子类 │ │ ├── MainWindowViewModel.cs ← 顶/侧/底栏状态 + Open/Decode 编排 │ │ ├── PatientViewModel.cs │ │ ├── SeriesViewModel.cs │ │ ├── ToolViewModel.cs ← 12 个工具,Activated 事件总线 │ │ └── PresetViewModel.cs │ │ │ ├── Views/ ← 视图 │ │ ├── MainWindow.axaml ← 5 段布局 │ │ └── MainWindow.axaml.cs ← 文件/文件夹 picker(VM ↔ StorageProvider 桥) │ │ │ ├── Controls/ ← 自定义渲染控件 │ │ ├── ToolIcon.cs ← 12 个工具按钮的线条图标 │ │ ├── ThumbnailPlaceholder.cs ← 76×76 缩略图(占位 + 真实 bitmap) │ │ └── ViewportCanvas.cs ← 中央影像画布(黑底 + 像素 + 鼠标交互 + IWindowLevelController 桥) │ │ │ └── Themes/ ← 浅色 editorial 主题 │ ├── Colors.axaml ← 颜色 / 字体 / 几何 token │ └── IWatcherLight.axaml ← 全局控件样式 │ └── IWatcher.App.Tools.WriteTestDcm/ ← 测试夹具:写一个最小可解析的 .dcm ├── IWatcher.App.Tools.WriteTestDcm.csproj └── Program.cs ← 8×8×16-bit MONOCHROME2 dummy ``` ## 快速开始 ### 前置要求 - .NET SDK 10.0.200 或更新:`dotnet --list-sdks` - 操作系统:Windows / macOS / Linux 任一 ### Build ```bash cd D:\works\code\iwatcher\src\IWatcher.App dotnet restore dotnet build -c Debug ``` 期望输出:`0 个警告 / 0 个错误`。 ### Run(交互模式) ```bash cd D:\works\code\iwatcher\src\IWatcher.App dotnet run ``` 或直接跑编译产物: ```bash ./bin/Debug/net10.0/IWatcher.exe # Windows ./bin/Debug/net10.0/IWatcher # macOS / Linux ``` 启动后窗口加载 sample study(Ma, Si · 髋关节 X 光,3 个 series)。可用顶栏"打开文件"或"打开文件夹"按钮加载真实 DICOM。 ### Run(截图模式 / 无头) 桌面 app 可以在没有 GUI session 的环境下渲染并保存 PNG,用于 CI / 文档: ```bash ./bin/Debug/net10.0/IWatcher.exe --screenshot path/to/output.png ``` 可选参数: - `--open ` 预加载真实 study(跳过 sample) - `--set-wl --set-ww ` 注入初始窗位窗宽,验证 VM↔UI 双向绑定 截图分辨率 1440×900,与 mockup 一致。 ### 写一个测试 DICOM ```bash cd D:\works\code\iwatcher\src\IWatcher.App.Tools.WriteTestDcm dotnet run -- --write-test-dcm path/to/test.dcm ``` 生成 8×8×16-bit MONOCHROME2 假图,可用于 CI 验证 open / decode / WL-WW 全链路。 ## 主题系统 视觉风格的真相之源是 `src/IWatcher.App/Themes/Colors.axaml`——所有颜色、字体、几何尺寸都定义在这里。修改后全 app 即时生效。 ```axaml ``` 全局样式在 `IWatcherLight.axaml`。新增自定义控件时,把对应的 `